├── .babelrc ├── .env.sample ├── .eslintrc ├── .github ├── pull_request_template.md └── workflows │ ├── createrelease.yml │ └── npmpublish.bak ├── .gitignore ├── .npmignore ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── RELEASE_NOTES.md ├── jsconf.json ├── package-lock.json ├── package.json ├── rollup.config.js ├── src ├── constants.js ├── entities │ ├── account.js │ ├── blog.js │ ├── calendar.js │ ├── company.js │ ├── contacts-lists.js │ ├── contacts-properties.js │ ├── contacts.js │ ├── deals.js │ ├── domains.js │ ├── email-events.js │ ├── email-subscriptions.js │ ├── engagements.js │ ├── files.js │ ├── forms.js │ ├── hubdb.js │ ├── layouts.js │ ├── oauth.js │ ├── pages.js │ ├── social.js │ ├── templates.js │ ├── transactional-emails.js │ ├── url-mappings.js │ └── workflows.js ├── index.js └── utilities.js ├── test ├── account.spec.js ├── blog.spec.js ├── calendar.spec.js ├── contacts-properties.spec.js ├── contacts.spec.js ├── deals.spec.js ├── engagements.spec.js ├── files.spec.js ├── forms.spec.js ├── hubdb.spec.js ├── layouts.spec.js ├── mocha.opts ├── page.spec.js ├── schemas │ ├── account.js │ ├── blog.js │ ├── calendar.js │ ├── contacts.js │ ├── contactsProperties.js │ ├── deal.js │ ├── engagement.js │ ├── file.js │ ├── hubdb.js │ ├── page.js │ ├── template.js │ └── workflows.js ├── templates.spec.js ├── transactional-emails.spec.js ├── utilities.js └── workflow.spec.js ├── types └── main.d.ts └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/env"], 3 | "plugins": [ 4 | "@babel/plugin-transform-async-to-generator", 5 | "@babel/plugin-transform-regenerator", 6 | [ 7 | "@babel/plugin-transform-runtime", 8 | { 9 | "helpers": true, 10 | "regenerator": true 11 | } 12 | ] 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /.env.sample: -------------------------------------------------------------------------------- 1 | # Please keep these in alphabetical order 2 | E2E_TESTS_BLOG_ID= 3 | E2E_TESTS_CONTACT_EMAIL= 4 | E2E_TESTS_CONTACT_ID= 5 | E2E_TESTS_EMAIL_ID= 6 | E2E_TESTS_FORM_ID= 7 | E2E_TESTS_HAPI_KEY= 8 | E2E_TESTS_HUBDB_PORTAL_ID= 9 | E2E_TESTS_HUBDB_TABLE_ID= 10 | E2E_TESTS_LAYOUT_ID= 11 | E2E_TESTS_LAYOUT_VERSION_ID= 12 | E2E_TESTS_PAGE_ID= 13 | E2E_TESTS_PORTAL_ID= 14 | E2E_TESTS_TEMPLATE_ID= 15 | E2E_TESTS_TEMPLATE_PATH= 16 | E2E_TESTS_WORKFLOW_ID= -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb", 3 | "settings": { 4 | "import/resolver": "webpack" 5 | }, 6 | "rules": { 7 | "no-bitwise": 0, 8 | "comma-dangle": 0, 9 | "no-param-reassign": 1, 10 | "camelcase": 0, 11 | "no-underscore-dangle": 0, 12 | "arrow-parens": 0, 13 | "import/extensions": "off", 14 | "import/no-unresolved": "off", 15 | "no-plusplus": 0, 16 | "jsx-a11y/no-static-element-interactions": 0, 17 | "react/require-extension": "off", 18 | "react/prefer-stateless-function": "off", 19 | "react/forbid-prop-types": "off", 20 | "complexity": "off", 21 | "comma-spacing": "off", 22 | "array-bracket-spacing": "off", 23 | "max-len": 0, 24 | "no-unused-vars": "warn" 25 | }, 26 | "globals": { 27 | "Request": true, 28 | "Headers": true, 29 | "fetch": true, 30 | "describe": true, 31 | "xdescribe": true, 32 | "xit": true, 33 | "it": true 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | * **Please check if the PR fulfills these requirements** 2 | 3 | - [ ] Package version *NOT* bumped (we do this when we release) 4 | - [ ] The commit message follows our guidelines 5 | - [ ] Tests for the changes have been added (for bug fixes / features) 6 | - [ ] Docs have been added / updated (for bug fixes / features) 7 | 8 | 9 | * **What kind of change does this PR introduce?** (Bug fix, feature, docs update, ...) 10 | 11 | 12 | 13 | * **What is the current behavior?** (You can also link to an open issue here) 14 | 15 | 16 | 17 | * **What is the new behavior (if this is a feature change)?** 18 | 19 | 20 | 21 | * **Does this PR introduce a breaking change?** (What changes might users need to make in their application due to this PR?) 22 | 23 | 24 | 25 | * **Other information**: 26 | -------------------------------------------------------------------------------- /.github/workflows/createrelease.yml: -------------------------------------------------------------------------------- 1 | name: Create release & publish 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | tags: 8 | - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10 9 | 10 | jobs: 11 | test: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v1 15 | - uses: actions/setup-node@v1 16 | with: 17 | node-version: 12 18 | - run: npm ci 19 | - run: npm run build 20 | - run: npm test 21 | env: 22 | # Please keep these in alphabetical order. 23 | E2E_TESTS_BLOG_ID: ${{secrets.E2E_TESTS_BLOG_ID}} 24 | E2E_TESTS_CONTACT_EMAIL: ${{secrets.E2E_TESTS_CONTACT_EMAIL}} 25 | E2E_TESTS_CONTACT_ID: ${{secrets.E2E_TESTS_CONTACT_ID}} 26 | E2E_TESTS_EMAIL_ID: ${{secrets.E2E_TESTS_EMAIL_ID}} 27 | E2E_TESTS_FORM_ID: ${{secrets.E2E_TESTS_FORM_ID}} 28 | E2E_TESTS_HAPI_KEY: ${{secrets.E2E_TESTS_HAPI_KEY}} 29 | E2E_TESTS_HUBDB_PORTAL_ID: ${{secrets.E2E_TESTS_HUBDB_PORTAL_ID}} 30 | E2E_TESTS_HUBDB_TABLE_ID: ${{secrets.E2E_TESTS_HUBDB_TABLE_ID}} 31 | E2E_TESTS_LAYOUT_ID: ${{secrets.E2E_TESTS_LAYOUT_ID}} 32 | E2E_TESTS_LAYOUT_VERSION_ID: ${{secrets.E2E_TESTS_LAYOUT_VERSION_ID}} 33 | E2E_TESTS_PAGE_ID: ${{secrets.E2E_TESTS_PAGE_ID}} 34 | E2E_TESTS_PORTAL_ID: ${{secrets.E2E_TESTS_PORTAL_ID}} 35 | E2E_TESTS_TEMPLATE_ID: ${{secrets.E2E_TESTS_TEMPLATE_ID}} 36 | E2E_TESTS_TEMPLATE_PATH: ${{secrets.E2E_TESTS_TEMPLATE_PATH}} 37 | E2E_TESTS_WORKFLOW_ID: ${{secrets.E2E_TESTS_WORKFLOW_ID}} 38 | 39 | # create-release: 40 | # runs-on: ubuntu-latest 41 | # steps: 42 | # - uses: actions/checkout@v1 43 | # - uses: ncipollo/release-action@v1 44 | # with: 45 | # bodyFile: "RELEASE_NOTES.md" 46 | # token: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /.github/workflows/npmpublish.bak: -------------------------------------------------------------------------------- 1 | # name: Build and publish to npm 2 | 3 | # on: 4 | # push: 5 | # branches: 6 | # - release 7 | 8 | # jobs: 9 | 10 | # publish-npm: 11 | # needs: build 12 | # runs-on: ubuntu-latest 13 | # steps: 14 | # - uses: actions/checkout@v1 15 | # - uses: actions/setup-node@v1 16 | # with: 17 | # node-version: 12 18 | # registry-url: https://registry.npmjs.org/ 19 | # - name: Download built assets 20 | # uses: actions/download-artifact@v1 21 | # with: 22 | # name: built-assets 23 | # path: ./dist/ 24 | # - name: Setup git config 25 | # run: | 26 | # git config user.email "$GITHUB_ACTOR@users.noreply.github.com" 27 | # git config user.name "$GITHUB_ACTOR" 28 | # - name: Publish to npm 29 | # run: | 30 | # npm publish 31 | # env: 32 | # NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} 33 | 34 | # generate-docs: 35 | # needs: publish-npm 36 | # runs-on: ubuntu-latest 37 | # steps: 38 | # - uses: actions/checkout@v1 39 | # - uses: actions/setup-node@v1 40 | # with: 41 | # node-version: 10 # Needs to remain at 10 as requizzle doesn't currently support 12. 42 | # - run: npm ci 43 | # - run: npm run generate-docs 44 | # - name: Deploy 45 | # uses: smcelhinney/ghpages@master 46 | # env: 47 | # GH_PAT: ${{ secrets.GH_PAT }} 48 | # BUILD_DIR: ./docs 49 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | distribution/ 2 | node_modules/ 3 | build/ 4 | lib/ 5 | dist/ 6 | .env 7 | sandbox* 8 | test.js 9 | settings.json 10 | docs/ 11 | .vscode/ 12 | gh-pages* 13 | .DS_Store -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | src/ 2 | test/ 3 | tools/ 4 | package-lock.json 5 | yarn.lock 6 | jsconf.json 7 | .eslintrc 8 | .env 9 | .babelrc 10 | .gitignore 11 | .github 12 | docs/ 13 | test.js 14 | rollup.config.js -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Release changelog 2 | 3 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to Contribute 2 | 3 | 1. Fork the project & clone locally 4 | 5 | 2. Create an upstream remote and sync your local copy before you branch 6 | 7 | ```sh 8 | git remote add upstream git@github.com:HubSpotWebTeam/hs-node-api.git 9 | ``` 10 | 11 | 3. Create a `.env` file in the root of your folder for all `process.env` tokens, such as the HAPIkey. e.g. 12 | ``` 13 | E2E_TESTS_HAPI_KEY="your-hapi-key" 14 | E2E_TESTS_BLOG_ID="1234567" 15 | E2E_TESTS_WORKFLOW_ID="9876542" 16 | E2E_TESTS_CONTACT_EMAIL="coolrobot@hubspot.com" 17 | E2E_TESTS_CONTACT_ID="1234" 18 | E2E_TESTS_HUBDB_TABLE_ID="999999" 19 | E2E_TESTS_HUBDB_PORTAL_ID="111111" 20 | E2E_TESTS_LAYOUT_ID="1234567785" 21 | E2E_TESTS_LAYOUT_VERSION_ID="1234567894" 22 | E2E_TESTS_PAGE_ID="1234567" 23 | E2E_TESTS_TEMPLATE_PATH="generated_layouts/1234567.html" 24 | E2E_TESTS_EMAIL_ID="" 25 | ``` 26 | 4. Create a new branch for each separate piece of work following the convention `contribution_type/some-detail` e.g. `improvement/update-contribution-docs` 27 | 28 | 5. Make your changes & ensure tests are passing 29 | ```sh 30 | npm run build && npm test 31 | ``` 32 | 6. Create a new PR in GitHub and within the body notify one of the repo maintainers 33 | 34 | # PR Process 35 | 36 | The current process: 37 | 38 | 1. Open PR: master <- feature/my_awesome_branch 39 | 2. PR approved & merged into master 40 | 3. Maintainers merge master into release 41 | 4. Bump version number in release branch 42 | 3. Build & Publish to npm 43 | 5. Merge release back into master (with version bump) -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | Copyright (c) 2019 HubSpot Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status (Release)](https://github.com/hubspotwebteam/hs-node-api/workflows/Build%20and%20publish%20npm%20package/badge.svg)](https://github.com/HubSpotWebTeam/hs-node-api/actions?workflow=Build+and+publish+npm+package) 2 | # Hubspot Client API 3 | 4 | > ## IMPORTANT! 5 | > :warning: This package is now deprecated and no longer maintained, please use the [@hubspot/api-client](https://www.npmjs.com/package/@hubspot/api-client) instead. 6 | 7 | A client wrapper for the HubSpot API at https://developers.hubspot.com/docs/overview. 8 | 9 | ## Installation 10 | 11 | ``` 12 | npm install --save hubspot-api 13 | ``` 14 | 15 | ## Docs 16 | 17 | Full documentation available at https://hubspotwebteam.github.io/hs-node-api/ 18 | 19 | ## Example 20 | 21 | ``` 22 | const HubSpotClient = require('hubspot-api'); 23 | const hs = new HubSpotClient({ hapikey }); 24 | // or const hs = new HubSpotClient({ accessToken }); 25 | // or const hs = new HubSpotClient(); # for public methods, eg. Forms, HubDB 26 | 27 | async function getContact(id){ 28 | const contact = await hs.contacts.getById(827398123); 29 | return contact 30 | } 31 | 32 | // Or using traditional thenable Promises. 33 | hs.contacts.getById(827398123).then(contact => { 34 | console.log(contact); 35 | }); 36 | ``` 37 | 38 | Entities available 39 | 40 | - Blogs 41 | - Blog posts 42 | - Blog authors 43 | - Blog topics 44 | - Contacts 45 | - Contacts Properties (partial implementation) 46 | - Companies 47 | - Calendar 48 | - Deals (partial implementation) 49 | - Domains 50 | - Email Events (partial implementation) 51 | - Engagements (partial implementation) 52 | - Forms (partial implementation) 53 | - HubDB (partial implementation) 54 | - Layouts 55 | - Page Publishing 56 | - Social (partial implementation) 57 | - Workflows 58 | 59 | Please check out the full documentation available at https://hubspotwebteam.github.io/hs-node-api/ 60 | 61 | ## CORS 62 | 63 | At the moment of writing the HubSpot APIs do not support CORS / AJAX requests due to [security reasons](https://developers.hubspot.com/docs/faq/do-hubspot-apis-support-ajax-request), so you must use this library only on a Node.js server. 64 | 65 | ## Authors and Contributors 66 | 67 | Currently maintained by the lovely folks on HubSpot's Web Team, but we need your help. Please feel free to submit pull requests to add new functionality. 68 | 69 | ### How to Contribute 70 | 71 | Contributions are welcome. Please refer to the [contributing guidelines](https://github.com/HubSpotWebTeam/hs-node-api/blob/master/CONTRIBUTING.md) 72 | -------------------------------------------------------------------------------- /RELEASE_NOTES.md: -------------------------------------------------------------------------------- 1 | # Releases 2 | 3 | ## Release v2.17.0 4 | 5 | - Dependabot updates 6 | - Added a pull request template 7 | - Added some functionality for templates/pages 8 | - Removes duplicates in `hs/contactsLists` methods 9 | - Typo fixes in docs 10 | - HubSpot API Folders endpoint support added 11 | 12 | 13 | ## Release v2.15.0 14 | 15 | * Changes the response of `createOrUpdateContact` in contacts entity to show create/update status -------------------------------------------------------------------------------- /jsconf.json: -------------------------------------------------------------------------------- 1 | { 2 | "tags": { 3 | "allowUnknownTags": true 4 | }, 5 | "opts": { 6 | "template": "./node_modules/@pixi/jsdoc-template" 7 | }, 8 | "plugins": [ 9 | "plugins/markdown" 10 | ], 11 | "templates": { 12 | "applicationName": "HubSpot Client API", 13 | "logoFile": "https://www.hubspot.com/hubfs/assets/hubspot.com/style-guide/brand-guidelines/guidelines_the-sprocket.svg", 14 | "cleverLinks": false, 15 | "monospaceLinks": false, 16 | "dateFormat": "ddd MMM Do YYYY", 17 | "outputSourceFiles": false, 18 | "outputSourcePath": false, 19 | "systemName": "DocStrap", 20 | "footer": "", 21 | "copyright": "DocStrap Copyright © 2012-2015 The contributors to the JSDoc3 and DocStrap projects.", 22 | "navType": "vertical", 23 | "theme": "lumen", 24 | "linenums": true, 25 | "collapseSymbols": false, 26 | "inverseNav": true, 27 | "protocol": "file://", 28 | "methodHeadingReturns": false 29 | }, 30 | "markdown": { 31 | "parser": "gfm", 32 | "hardwrap": true 33 | } 34 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hubspot-api", 3 | "version": "2.17.3", 4 | "description": "HubSpot API Wrapper", 5 | "keywords": [ 6 | "hubspot", 7 | "hubspot-api" 8 | ], 9 | "repository": "github:HubSpotWebTeam/hs-node-api", 10 | "main": "dist/bundle.min.js", 11 | "types": "./types/main.d.ts", 12 | "scripts": { 13 | "build": "rollup -c", 14 | "test": "mocha -r esm", 15 | "test:watch": "npm run build && mocha --watch -r esm", 16 | "generate-docs": "./node_modules/.bin/jsdoc -R ./README.md -c ./jsconf.json -r ./src -d ./docs/" 17 | }, 18 | "author": "Stephen McElhinney", 19 | "license": "ISC", 20 | "devDependencies": { 21 | "@babel/plugin-transform-async-to-generator": "^7.12.1", 22 | "@babel/plugin-transform-regenerator": "^7.12.1", 23 | "@babel/plugin-transform-runtime": "^7.12.10", 24 | "@babel/preset-env": "^7.12.10", 25 | "@pixi/jsdoc-template": "^2.6.0", 26 | "chai": "^4.1.2", 27 | "del": "^3.0.0", 28 | "dotenv": "^8.2.0", 29 | "eslint": "^6.8.0", 30 | "eslint-config-airbnb": "^18.2.1", 31 | "eslint-plugin-import": "^2.22.1", 32 | "eslint-plugin-jsx-a11y": "^6.4.1", 33 | "eslint-plugin-react": "^7.21.5", 34 | "esm": "^3.2.25", 35 | "ink-docstrap": "^1.3.0", 36 | "joi": "^13.1.2", 37 | "jsdoc": "^3.6.6", 38 | "mocha": "^5.0.5", 39 | "rimraf": "^2.7.1", 40 | "rollup": "^1.32.1", 41 | "rollup-plugin-babel": "^4.4.0", 42 | "rollup-plugin-commonjs": "^10.1.0", 43 | "rollup-plugin-json": "^4.0.0", 44 | "rollup-plugin-node-builtins": "^2.1.2", 45 | "rollup-plugin-node-globals": "^1.4.0", 46 | "rollup-plugin-node-resolve": "^5.2.0", 47 | "rollup-plugin-uglify": "^6.0.4" 48 | }, 49 | "dependencies": { 50 | "@babel/core": "^7.12.10", 51 | "@babel/runtime": "^7.12.5", 52 | "axios": "^0.26.1", 53 | "debug": "^3.2.7", 54 | "interpolate": "^0.1.0", 55 | "lodash.omit": "^4.5.0", 56 | "lodash.pick": "^4.4.0", 57 | "node-uuid": "^1.4.8" 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import babel from 'rollup-plugin-babel'; 2 | import resolve from 'rollup-plugin-node-resolve'; 3 | import commonjs from 'rollup-plugin-commonjs'; 4 | import json from 'rollup-plugin-json'; 5 | import builtins from 'rollup-plugin-node-builtins'; 6 | import globals from 'rollup-plugin-node-globals'; 7 | import { uglify } from 'rollup-plugin-uglify'; 8 | import { version, author } from './package.json'; 9 | 10 | 11 | const banner = 12 | `${'/*!\n * Hubspot Client API v'}${version}\n` + 13 | ` * (c) 2014-${new Date().getFullYear()} ${author}\n` + 14 | ' * Released under the MIT License.\n' + 15 | ' */'; 16 | 17 | export default { 18 | input: './src/index.js', 19 | external: [ 20 | 'axios', 21 | '@babel/runtime/regenerator', 22 | '@babel/runtime/helpers/asyncToGenerator' 23 | ], 24 | output: { 25 | file: './dist/bundle.min.js', 26 | format: 'cjs', 27 | name: 'bundle', 28 | banner, 29 | globals: { 30 | axios: 'axios', 31 | '@babel/runtime/regenerator': '_regeneratorRuntime', 32 | '@babel/runtime/helpers/asyncToGenerator': '_asyncToGenerator' 33 | } 34 | }, 35 | plugins: [ 36 | babel({ 37 | exclude: 'node_modules/**', 38 | externalHelpers: true, 39 | runtimeHelpers: true 40 | }), 41 | resolve({ 42 | mainFields: [ 43 | 'jsnext', 44 | 'main' 45 | ], 46 | browser: false, 47 | preferBuiltins: true 48 | }), 49 | commonjs(), 50 | json(), 51 | globals(), 52 | builtins(), 53 | uglify() 54 | ] 55 | }; 56 | -------------------------------------------------------------------------------- /src/constants.js: -------------------------------------------------------------------------------- 1 | const defaultApiHost = process.env.COS_API_HOST || 'https://api.hubapi.com'; 2 | 3 | export default { 4 | api: { 5 | account: { 6 | details: `${defaultApiHost}/integrations/v1/me`, 7 | dailyLimit: `${defaultApiHost}/integrations/v1/limit/daily` 8 | }, 9 | files: { 10 | getFilesInFolder: `${defaultApiHost}/filemanager/api/v2/files`, 11 | getFolders: `${defaultApiHost}/filemanager/api/v2/folders` 12 | }, 13 | pages: { 14 | create: `${defaultApiHost}/content/api/v2/pages`, 15 | byId: `${defaultApiHost}/content/api/v2/pages/{id}`, 16 | clone: `${defaultApiHost}/content/api/v2/pages/{id}/clone`, 17 | list: `${defaultApiHost}/content/api/v2/pages`, 18 | buffer: `${defaultApiHost}/content/api/v2/pages/{id}/buffer`, 19 | bufferedChanges: `${defaultApiHost}/content/api/v2/pages/{id}/has-buffered-changes`, 20 | publishAction: `${defaultApiHost}/content/api/v2/pages/{id}/publish-action`, 21 | pushBufferLive: `${defaultApiHost}/content/api/v2/pages/{id}/push-buffer-live`, 22 | restoreDeleted: `${defaultApiHost}/content/api/v2/pages/{id}/restore-deleted`, 23 | validateBuffer: `${defaultApiHost}/content/api/v2/pages/{id}/validate-buffer`, 24 | versions: `${defaultApiHost}/content/api/v2/pages/{id}/versions`, 25 | restoreVersion: `${defaultApiHost}/content/api/v2/pages/{id}/versions/restore` 26 | }, 27 | deals: { 28 | recentlyCreated: `${defaultApiHost}/deals/v1/deal/recent/created`, 29 | getAll: `${defaultApiHost}/deals/v1/deal/paged`, 30 | byId: `${defaultApiHost}/deals/v1/deal/{id}`, 31 | create: `${defaultApiHost}/deals/v1/deal`, 32 | update: `${defaultApiHost}/deals/v1/deal/{id}`, 33 | batchUpdate: `${defaultApiHost}/deals/v1/batch-async/update` 34 | }, 35 | emailEvents: { 36 | campaignsWithRecentActivity: `${defaultApiHost}/email/public/v1/campaigns`, 37 | campaign: `${defaultApiHost}/email/public/v1/campaigns/{campaignId}` 38 | }, 39 | emailSubscriptions: { 40 | updateStatus: `${defaultApiHost}/email/public/v1/subscriptions/{email}`, 41 | getStatus: `${defaultApiHost}/email/public/v1/subscriptions/{email}` 42 | }, 43 | forms: { 44 | getForm: `${defaultApiHost}/forms/v2/forms/{formId}`, 45 | submissions: `${defaultApiHost}/form-integrations/v1/submissions/forms/{formId}`, 46 | submitForm: 47 | 'https://forms.hubspot.com/uploads/form/v2/{portalId}/{formId}', 48 | formFields: `${defaultApiHost}/forms/v2/fields/{formId}`, 49 | submitFormV3: 50 | 'https://api.hsforms.com/submissions/v3/integration/submit/{portalId}/{formId}' 51 | }, 52 | social: { 53 | channels: `${defaultApiHost}/broadcast/v1/channels/setting/publish/current`, 54 | createBroadcastMessage: `${defaultApiHost}/broadcast/v1/broadcasts` 55 | }, 56 | domains: { 57 | getAll: `${defaultApiHost}/content/api/v4/domains`, 58 | byId: `${defaultApiHost}/content/api/v4/domains/{id}` 59 | }, 60 | layouts: { 61 | getAll: `${defaultApiHost}/content/api/v2/layouts`, 62 | byId: `${defaultApiHost}/content/api/v2/layouts/{id}`, 63 | getBuffer: `${defaultApiHost}/content/api/v2/layouts/{id}/buffer`, 64 | hasBufferedChanges: `${defaultApiHost}/content/api/v2/layouts/{id}/has-buffered-changes`, 65 | getPreviousVersions: `${defaultApiHost}/content/api/v2/layouts/{id}/versions`, 66 | getPreviousVersion: `${defaultApiHost}/content/api/v2/layouts/{id}/versions/{versionId}` 67 | }, 68 | templates: { 69 | base: `${defaultApiHost}/content/api/v2/templates`, 70 | byId: `${defaultApiHost}/content/api/v2/templates/{templateId}`, 71 | buffer: `${defaultApiHost}/content/api/v2/templates/{templateId}/buffer`, 72 | pages: `${defaultApiHost}/cosindex/api/v1/connections/{templateId}/parents`, 73 | hasBufferedChanges: `${defaultApiHost}/content/api/v2/templates/{templateId}/has-buffered-changes`, 74 | pushBufferLive: `${defaultApiHost}/content/api/v2/templates/{templateId}/push-buffer-live`, 75 | versions: `${defaultApiHost}/content/api/v2/templates/{templateId}/versions`, 76 | version: `${defaultApiHost}/content/api/v2/templates/{templateId}/versions/{versionId}`, 77 | restore: `${defaultApiHost}/content/api/v2/templates/{templateId}/versions/restore`, 78 | }, 79 | email: { 80 | getSubscriptions: `${defaultApiHost}/email/public/v1/subscriptions` 81 | }, 82 | transactionalEmail: { 83 | singleSend: `${defaultApiHost}/email/public/v1/singleEmail/send` 84 | }, 85 | blog: { 86 | authors: `${defaultApiHost}/blogs/v3/blog-authors`, 87 | authorById: `${defaultApiHost}/blogs/v3/blog-authors/{id}`, 88 | authorSearch: `${defaultApiHost}/blogs/v3/blog-authors/search`, 89 | comments: `${defaultApiHost}/comments/v3/comments`, 90 | commentById: `${defaultApiHost}/comments/v3/comments/{id}`, 91 | restoreDeletedComment: `${defaultApiHost}/comments/v3/comments/{id}/restore`, 92 | getAll: `${defaultApiHost}/content/api/v2/blogs`, 93 | byId: `${defaultApiHost}/content/api/v2/blogs/{id}`, 94 | getVersions: `${defaultApiHost}/content/api/v2/blogs/{blog_id}/versions`, 95 | getVersion: `${defaultApiHost}/content/api/v2/blogs/{blog_id}/versions/{revision_id}`, 96 | posts: `${defaultApiHost}/content/api/v2/blog-posts`, 97 | postById: `${defaultApiHost}/content/api/v2/blog-posts/{id}`, 98 | clonePostById: `${defaultApiHost}/content/api/v2/blog-posts/{id}/clone`, 99 | restorePostById: `${defaultApiHost}/content/api/v2/blog-posts/{id}/restore-deleted`, 100 | publishOrSchedulePost: `${defaultApiHost}/content/api/v2/blog-posts/{id}/publish-action`, 101 | postAutoSaveBuffer: `${defaultApiHost}/content/api/v2/blog-posts/{id}/buffer`, 102 | validatePostAutoSaveBuffer: `${defaultApiHost}/content/api/v2/blog-posts/{id}/validate-buffer`, 103 | postAutoSaveBufferStatus: `${defaultApiHost}/content/api/v2/blog-posts/{id}/has-buffered-changes`, 104 | postVersions: `${defaultApiHost}/content/api/v2/blog-posts/{id}/versions`, 105 | restorePostVersion: `${defaultApiHost}/content/api/v2/blog-posts/{id}/versions/restore`, 106 | postVersionById: `${defaultApiHost}/content/api/v2/blog-posts/{id}/versions/{version_id}`, 107 | pushPostAutosaveBufferToLive: `${defaultApiHost}/content/api/v2/blog-posts/{id}/push-buffer-live`, 108 | topics: `${defaultApiHost}/blogs/v3/topics`, 109 | groupTopics: `${defaultApiHost}/blogs/v3/topics/group-topics`, 110 | topic: `${defaultApiHost}/blogs/v3/topics/{id}`, 111 | topicSearch: `${defaultApiHost}/blogs/v3/topics/search` 112 | }, 113 | calendar: { 114 | events: `${defaultApiHost}/calendar/v1/events`, 115 | createTask: `${defaultApiHost}/calendar/v1/events/task`, 116 | taskById: `${defaultApiHost}/calendar/v1/events/task/{taskId}` 117 | }, 118 | contacts: { 119 | getAll: `${defaultApiHost}/contacts/v1/lists/all/contacts/all`, 120 | deleteById: `${defaultApiHost}/contacts/v1/contact/vid/{vid}`, 121 | byId: `${defaultApiHost}/contacts/v1/contact/vid/{vid}/profile`, 122 | byEmail: `${defaultApiHost}/contacts/v1/contact/email/{email}/profile`, 123 | byUtk: `${defaultApiHost}/contacts/v1/contact/utk/{utk}/profile`, 124 | createContact: `${defaultApiHost}/contacts/v1/contact/createOrUpdate/email/{email}/`, 125 | batchUpdateContacts: `${defaultApiHost}/contacts/v1/contact/batch/`, 126 | getRecentlyModified: `${defaultApiHost}/contacts/v1/lists/recently_updated/contacts/recent`, 127 | search: `${defaultApiHost}/contacts/v1/search/query` 128 | }, 129 | contactsList: { 130 | byId: `${defaultApiHost}/contacts/v1/lists/{listId}`, 131 | contactsByListId: `${defaultApiHost}/contacts/v1/lists/{listId}/contacts/all`, 132 | addContactsToList: `${defaultApiHost}/contacts/v1/lists/{listId}/add` 133 | }, 134 | contactsProperties: { 135 | getAllContactsProperties: `${defaultApiHost}/properties/v1/contacts/properties` 136 | }, 137 | company: { 138 | create: `${defaultApiHost}/companies/v2/companies/`, 139 | batchUpdate: `${defaultApiHost}/companies/v1/batch-async/update`, 140 | byId: `${defaultApiHost}/companies/v2/companies/{companyId}`, 141 | contacts: `${defaultApiHost}/companies/v2/companies/{companyId}/contacts`, 142 | byDomain: `${defaultApiHost}/companies/v2/domains/{domain}/companies` 143 | }, 144 | workflows: { 145 | eventLogs: `${defaultApiHost}/automation/v3/logevents/workflows/{workflowId}/filter`, 146 | enrollments: `${defaultApiHost}/automation/v2/workflows/enrollments/contacts/{id}`, 147 | enrollContact: `${defaultApiHost}/automation/v2/workflows/{workflowId}/enrollments/contacts/{email}`, 148 | create: `${defaultApiHost}/automation/v3/workflows`, 149 | getAll: `${defaultApiHost}/automation/v3/workflows`, 150 | byId: `${defaultApiHost}/automation/v3/workflows/{id}` 151 | }, 152 | hubdb: { 153 | tables: `${defaultApiHost}/hubdb/api/v2/tables`, 154 | rows: `${defaultApiHost}/hubdb/api/v2/tables/{tableId}/rows`, 155 | table: `${defaultApiHost}/hubdb/api/v2/tables/{tableId}`, 156 | row: `${defaultApiHost}/hubdb/api/v2/tables/{tableId}/rows/{id}`, 157 | rowsBatchUpdate: `${defaultApiHost}/hubdb/api/v2/tables/{tableId}/rows/batch/update`, 158 | cell: `${defaultApiHost}/hubdb/api/v2/tables/{tableId}/rows/{rowId}/cells/{cellId}`, 159 | cloneTable: `${defaultApiHost}/hubdb/api/v2/tables/{tableId}/clone`, 160 | cloneRow: `${defaultApiHost}/hubdb/api/v2/tables/{tableId}/rows/{rowId}/clone`, 161 | importCsv: `${defaultApiHost}/hubdb/api/v2/tables/{tableId}/import`, 162 | publishTable: `${defaultApiHost}/hubdb/api/v2/tables/{tableId}/publish` 163 | }, 164 | engagements: { 165 | create: `${defaultApiHost}/engagements/v1/engagements` 166 | }, 167 | oauth: { 168 | tokenInfo: `${defaultApiHost}/oauth/v1/access-tokens/{token}` 169 | }, 170 | urlMappings: { 171 | getAll: `${defaultApiHost}/url-mappings/v3/url-mappings`, 172 | byId: `${defaultApiHost}/url-mappings/v3/url-mappings/{id}`, 173 | create: `${defaultApiHost}/url-mappings/v3/url-mappings`, 174 | update: `${defaultApiHost}/url-mappings/v3/url-mappings/{id}`, 175 | delete: `${defaultApiHost}/url-mappings/v3/url-mappings/{id}` 176 | } 177 | } 178 | }; 179 | -------------------------------------------------------------------------------- /src/entities/account.js: -------------------------------------------------------------------------------- 1 | import createRequest, { requiresAuthentication } from '../utilities'; 2 | import constants from '../constants'; 3 | 4 | const debug = require('debug')('hubspot-api:tests'); // eslint-disable-line 5 | 6 | const defaults = {}; 7 | let _baseOptions; 8 | 9 | const getAccountDetails = async () => { 10 | try { 11 | requiresAuthentication(_baseOptions); 12 | const mergedProps = Object.assign({}, defaults, _baseOptions); 13 | const accountDetails = await createRequest( 14 | constants.api.account.details, 15 | {}, 16 | mergedProps 17 | ); 18 | return Promise.resolve(accountDetails); 19 | } catch (e) { 20 | return Promise.reject(e); 21 | } 22 | }; 23 | 24 | const getDailyLimit = async () => { 25 | try { 26 | requiresAuthentication(_baseOptions); 27 | const mergedProps = Object.assign({}, defaults, _baseOptions); 28 | const dailyLimit = await createRequest( 29 | constants.api.account.dailyLimit, 30 | {}, 31 | mergedProps 32 | ); 33 | return Promise.resolve(dailyLimit); 34 | } catch (e) { 35 | return Promise.reject(e); 36 | } 37 | }; 38 | 39 | export default function accounts(baseOptions) { 40 | _baseOptions = baseOptions; 41 | // API 42 | return { 43 | /** 44 | * Get account info 45 | * @async 46 | * @memberof hs/account 47 | * @method getAccountDetails 48 | * @example 49 | * const hs = new HubspotClient(props); 50 | * const accountDetails = await hs.account.getAccountDetails(); 51 | * @returns {Promise} 52 | */ 53 | getAccountDetails, 54 | /** 55 | * Check to see how many API calls have been made for a portal for the current day as well as the time that the limit will reset 56 | * @async 57 | * @memberof hs/account 58 | * @method getDailyLimit 59 | * @example 60 | * const hs = new HubspotClient(props); 61 | * const dailyLimit = await hs.account.getDailyLimit(); 62 | * @returns {Promise} 63 | */ 64 | getDailyLimit 65 | }; 66 | } 67 | -------------------------------------------------------------------------------- /src/entities/calendar.js: -------------------------------------------------------------------------------- 1 | // NOTE: FULLY_IMPLEMENTED 2 | // NOTE: REQUIRES_TESTS 3 | 4 | import createRequest, { 5 | sanitizeObject, 6 | requiresAuthentication 7 | } from '../utilities'; 8 | import constants from '../constants'; 9 | 10 | const defaults = {}; 11 | let _baseOptions; 12 | 13 | const events = async ({ 14 | startDate, 15 | endDate, 16 | limit, 17 | contentCategory, 18 | campaignGuid, 19 | includeNoCampaigns, 20 | type 21 | }) => { 22 | try { 23 | requiresAuthentication(_baseOptions); 24 | if (!startDate || !endDate) { 25 | throw new Error( 26 | 'Both "startDate" and "endDate" in ms are required fields' 27 | ); 28 | } 29 | const mergedProps = Object.assign({}, defaults, _baseOptions, { 30 | startDate, 31 | endDate, 32 | limit, 33 | contentCategory, 34 | campaignGuid, 35 | includeNoCampaigns 36 | }); 37 | if (type) { 38 | Object.assign(mergedProps, { type }); 39 | } 40 | const filteredEvents = await createRequest( 41 | constants.api.calendar.events, 42 | {}, 43 | mergedProps 44 | ); 45 | return Promise.resolve(filteredEvents); 46 | } catch (e) { 47 | return Promise.reject(e.message); 48 | } 49 | }; 50 | 51 | const getTask = async taskId => { 52 | try { 53 | requiresAuthentication(_baseOptions); 54 | const mergedProps = Object.assign({}, defaults, _baseOptions); 55 | const task = await createRequest( 56 | constants.api.calendar.taskById, 57 | { taskId }, 58 | mergedProps 59 | ); 60 | return Promise.resolve(task); 61 | } catch (e) { 62 | return Promise.reject(e.message); 63 | } 64 | }; 65 | 66 | const deleteTask = async taskId => { 67 | try { 68 | requiresAuthentication(_baseOptions); 69 | const mergedProps = Object.assign({}, defaults, _baseOptions); 70 | const method = 'DELETE'; 71 | await createRequest( 72 | constants.api.calendar.taskById, 73 | { method, taskId }, 74 | mergedProps 75 | ); 76 | return Promise.resolve({ msg: `Task ${taskId} removed` }); 77 | } catch (e) { 78 | return Promise.reject(e.message); 79 | } 80 | }; 81 | 82 | const updateTask = async ( 83 | taskId, 84 | { 85 | eventDate, 86 | eventType, 87 | category, 88 | state, 89 | campaignGuid, 90 | topicIds, 91 | name, 92 | description, 93 | ownerId 94 | } 95 | ) => { 96 | try { 97 | requiresAuthentication(_baseOptions); 98 | const mergedProps = Object.assign({}, defaults, _baseOptions); 99 | const method = 'PUT'; 100 | let body = { 101 | eventDate, 102 | eventType, 103 | category, 104 | state, 105 | campaignGuid, 106 | topicIds, 107 | name, 108 | description, 109 | ownerId 110 | }; 111 | body = sanitizeObject(body); 112 | const updatedTask = await createRequest( 113 | constants.api.calendar.taskById, 114 | { body, method, taskId }, 115 | mergedProps 116 | ); 117 | return Promise.resolve(updatedTask); 118 | } catch (e) { 119 | return Promise.reject(e.message); 120 | } 121 | }; 122 | 123 | const createTask = async ({ 124 | eventDate, 125 | eventType, 126 | category, 127 | state, 128 | campaignGuid, 129 | contentGroupId, 130 | topicIds, 131 | templatePath, 132 | name, 133 | description, 134 | ownerId, 135 | createdBy 136 | }) => { 137 | try { 138 | requiresAuthentication(_baseOptions); 139 | const mergedProps = Object.assign({}, defaults, _baseOptions); 140 | const method = 'POST'; 141 | let body = { 142 | eventDate, 143 | eventType, 144 | category, 145 | state, 146 | campaignGuid, 147 | contentGroupId, 148 | topicIds, 149 | templatePath, 150 | name, 151 | description, 152 | ownerId, 153 | createdBy 154 | }; 155 | 156 | // Set defaults if not set. 157 | if (!state) { 158 | Object.assign(body, { state: 'TODO' }); 159 | } 160 | if (!eventType) { 161 | Object.assign(body, { eventType: 'PUBLISHING_TASK' }); 162 | } 163 | body = sanitizeObject(body); 164 | 165 | const filteredEvents = await createRequest( 166 | constants.api.calendar.createTask, 167 | { body, method }, 168 | mergedProps 169 | ); 170 | return Promise.resolve(filteredEvents); 171 | } catch (e) { 172 | return Promise.reject(e.message); 173 | } 174 | }; 175 | 176 | const contentEvents = ({ 177 | startDate, 178 | endDate, 179 | limit, 180 | contentCategory, 181 | campaignGuid, 182 | includeNoCampaigns 183 | }) => 184 | events({ 185 | startDate, 186 | endDate, 187 | limit, 188 | contentCategory, 189 | campaignGuid, 190 | includeNoCampaigns, 191 | type: 'CONTENT' 192 | }); 193 | 194 | const socialEvents = ({ 195 | startDate, 196 | endDate, 197 | limit, 198 | contentCategory, 199 | campaignGuid, 200 | includeNoCampaigns 201 | }) => 202 | events({ 203 | startDate, 204 | endDate, 205 | limit, 206 | contentCategory, 207 | campaignGuid, 208 | includeNoCampaigns, 209 | type: 'SOCIAL' 210 | }); 211 | 212 | const taskEvents = ({ 213 | startDate, 214 | endDate, 215 | limit, 216 | contentCategory, 217 | campaignGuid, 218 | includeNoCampaigns 219 | }) => 220 | events({ 221 | startDate, 222 | endDate, 223 | limit, 224 | contentCategory, 225 | campaignGuid, 226 | includeNoCampaigns, 227 | type: 'PUBLISHING_TASK' 228 | }); 229 | 230 | export default function calendar(baseOptions) { 231 | _baseOptions = baseOptions; 232 | 233 | return { 234 | /** 235 | * Retrieve all event types 236 | * @async 237 | * @memberof hs/calendar 238 | * @method events 239 | * @param {object} eventProperties An object containing event properties to search for 240 | * @example 241 | * const hs = new HubspotClient(props); 242 | * hs.calendar.events(eventProperties).then(response => console.log(response)) 243 | * @property {number} eventProperties.startDate 244 | * @property {number} eventProperties.endDate 245 | * @property {number} [eventProperties.limit] 246 | * @returns {Promise} 247 | */ 248 | events, 249 | /** 250 | * Retrieve all events of type 'content' 251 | * @async 252 | * @memberof hs/calendar 253 | * @method contentEvents 254 | * @param {object} eventProperties An object containing event properties to search for 255 | * @example 256 | * const hs = new HubspotClient(props); 257 | * hs.calendar.contentEvents(eventProperties).then(response => console.log(response)) 258 | * @property {number} eventProperties.startDate 259 | * @property {number} eventProperties.endDate 260 | * @property {number} [eventProperties.limit] 261 | * @returns {Promise} 262 | */ 263 | contentEvents, 264 | /** 265 | * Retrieve all events of type 'social' 266 | * @async 267 | * @memberof hs/calendar 268 | * @method socialEvents 269 | * @param {object} eventProperties An object containing event properties to search for 270 | * @example 271 | * const hs = new HubspotClient(props); 272 | * hs.calendar.socialEvents(eventProperties).then(response => console.log(response)) 273 | * @property {number} eventProperties.startDate 274 | * @property {number} eventProperties.endDate 275 | * @property {number} [eventProperties.limit] 276 | * @returns {Promise} 277 | */ 278 | socialEvents, 279 | /** 280 | * Retrieve all events of type 'task' 281 | * @async 282 | * @memberof hs/calendar 283 | * @method taskEvents 284 | * @param {object} eventProperties An object containing event properties to search for 285 | * @example 286 | * const hs = new HubspotClient(props); 287 | * hs.calendar.taskEvents(eventProperties).then(response => console.log(response)) 288 | * @property {number} eventProperties.startDate 289 | * @property {number} eventProperties.endDate 290 | * @property {number} [eventProperties.limit] 291 | * @returns {Promise} 292 | */ 293 | taskEvents, 294 | /** 295 | * Create new task 296 | * @async 297 | * @memberof hs/calendar 298 | * @method createTask 299 | * @param {object} taskProperties An object containing task properties to create 300 | * @example 301 | * const hs = new HubspotClient(props); 302 | * hs.calendar.createTask(taskProperties).then(response => console.log(response)) 303 | * @property {number} taskProperties.eventDate 304 | * @property {number} [taskProperties.eventType=PUBLISHING_TASK] 305 | * @property {number} taskProperties.category 306 | * @property {number} [taskProperties.state=TODO] 307 | * @property {number} [taskProperties.campaignGuid] 308 | * @property {number} [taskProperties.contentGroupId] - Required if category=BLOG_POST 309 | * @property {number} [taskProperties.topicIds] 310 | * @property {number} [taskProperties.templatePath] 311 | * @property {number} [taskProperties.name] 312 | * @property {number} [taskProperties.description] 313 | * @property {number} [taskProperties.ownerId] 314 | * @returns {Promise} 315 | */ 316 | createTask, 317 | /** 318 | * Get Task By ID 319 | * @async 320 | * @memberof hs/calendar 321 | * @method getTask 322 | * @param {number} taskId ID of task to retrieve 323 | * @example 324 | * const hs = new HubspotClient(props); 325 | * hs.calendar.getTask(taskId).then(response => console.log(response)) 326 | * @returns {Promise} 327 | */ 328 | getTask, 329 | /** 330 | * Update existing task 331 | * @async 332 | * @memberof hs/calendar 333 | * @method updateTask 334 | * @param {number} taskId ID of task to update 335 | * @param {object} taskProperties An object containing task properties to update 336 | * @example 337 | * const hs = new HubspotClient(props); 338 | * hs.calendar.updateTask(taskProperties).then(response => console.log(response)) 339 | * @property {number} [taskProperties.eventDate] 340 | * @property {number} [taskProperties.eventType] 341 | * @property {number} [taskProperties.category] 342 | * @property {number} [taskProperties.state] 343 | * @property {number} [taskProperties.campaignGuid] 344 | * @property {number} [taskProperties.topicIds] 345 | * @property {number} [taskProperties.name] 346 | * @property {number} [taskProperties.description] 347 | * @property {number} [taskProperties.ownerId] 348 | * @returns {Promise} 349 | */ 350 | updateTask, 351 | /** 352 | * Delete Task By ID 353 | * @async 354 | * @memberof hs/calendar 355 | * @method deleteTask 356 | * @param {number} taskId ID of task to delete 357 | * @example 358 | * const hs = new HubspotClient(props); 359 | * hs.calendar.deleteTask(taskId).then(response => console.log(response)) 360 | * @returns {Promise} 361 | */ 362 | deleteTask 363 | }; 364 | } 365 | -------------------------------------------------------------------------------- /src/entities/company.js: -------------------------------------------------------------------------------- 1 | // NOTE: FULLY_IMPLEMENTED 2 | // NOTE: REQUIRES_TESTS 3 | 4 | import createRequest, { 5 | sanitizeObject, 6 | requiresAuthentication 7 | } from '../utilities'; 8 | import constants from '../constants'; 9 | 10 | const defaults = {}; 11 | let _baseOptions; 12 | 13 | // await hs.company.create({ name: 'Hubspot', no_of_employees: 1000 }) 14 | 15 | const create = async properties => { 16 | try { 17 | requiresAuthentication(_baseOptions); 18 | const method = 'POST'; 19 | const body = { 20 | properties: Object.keys(properties).map(key => ({ 21 | name: key, 22 | value: properties[key] 23 | })) 24 | }; 25 | const response = await createRequest( 26 | constants.api.company.create, 27 | { method, body }, 28 | _baseOptions 29 | ); 30 | return Promise.resolve(response); 31 | } catch (e) { 32 | return Promise.reject(e.message); 33 | } 34 | }; 35 | 36 | const update = async (companyId, properties) => { 37 | try { 38 | requiresAuthentication(_baseOptions); 39 | if (!companyId) { 40 | throw new Error('Field "companyId" is required.'); 41 | } 42 | 43 | const method = 'PUT'; 44 | const body = { 45 | properties: Object.keys(properties).map(key => ({ 46 | name: key, 47 | value: properties[key] 48 | })) 49 | }; 50 | 51 | const response = await createRequest( 52 | constants.api.company.byId, 53 | { method, body, companyId }, 54 | _baseOptions 55 | ); 56 | 57 | return Promise.resolve(response); 58 | } catch (e) { 59 | return Promise.reject(e.message); 60 | } 61 | }; 62 | 63 | const batchUpdate = async options => { 64 | // FIXME: Implement this 65 | try { 66 | requiresAuthentication(_baseOptions); 67 | const method = 'POST'; 68 | const body = options.map(option => { 69 | const properties = Object.keys(option.updates).map(i => ({ 70 | name: i, 71 | value: option.updates[i] 72 | })); 73 | return { 74 | objectId: option.id, 75 | properties 76 | }; 77 | }); 78 | 79 | await createRequest( 80 | constants.api.company.batchUpdate, 81 | { method, body }, 82 | _baseOptions 83 | ); 84 | return Promise.resolve({ msg: 'Successfully updated company properties' }); 85 | } catch (e) { 86 | return Promise.reject(e); 87 | } 88 | }; 89 | 90 | const deleteCompany = async companyId => { 91 | try { 92 | requiresAuthentication(_baseOptions); 93 | const method = 'DELETE'; 94 | const response = await createRequest( 95 | constants.api.company.byId, 96 | { method, companyId }, 97 | _baseOptions 98 | ); 99 | return Promise.resolve(response); 100 | } catch (e) { 101 | return Promise.reject(e.message); 102 | } 103 | }; 104 | 105 | const getAll = async props => { 106 | try { 107 | requiresAuthentication(_baseOptions); 108 | const method = 'GET'; 109 | const passedProps = props || {}; 110 | const { limit, offset, properties, propertiesWithHistory } = passedProps; 111 | let mergedProps = Object.assign({}, defaults, _baseOptions, { 112 | limit, 113 | offset, 114 | properties, 115 | propertiesWithHistory 116 | }); 117 | mergedProps = sanitizeObject(mergedProps); 118 | 119 | const companies = await createRequest( 120 | constants.api.company.byId, 121 | { method, companyId: 'paged' }, 122 | mergedProps 123 | ); 124 | return Promise.resolve(companies); 125 | } catch (e) { 126 | return Promise.reject(e.message); 127 | } 128 | }; 129 | 130 | const getRecentlyModified = async props => { 131 | try { 132 | requiresAuthentication(_baseOptions); 133 | const method = 'GET'; 134 | const passedProps = props || {}; 135 | const { offset, count } = passedProps; 136 | let mergedProps = Object.assign({}, defaults, _baseOptions, { 137 | offset, 138 | count 139 | }); 140 | mergedProps = sanitizeObject(mergedProps); 141 | const companies = await createRequest( 142 | constants.api.company.byId, 143 | { method, companyId: 'recent/modified' }, 144 | mergedProps 145 | ); 146 | return Promise.resolve(companies); 147 | } catch (e) { 148 | return Promise.reject(e.message); 149 | } 150 | }; 151 | 152 | const getRecentlyCreated = async props => { 153 | try { 154 | requiresAuthentication(_baseOptions); 155 | const method = 'GET'; 156 | const passedProps = props || {}; 157 | const { offset, count } = passedProps; 158 | let mergedProps = Object.assign({}, defaults, _baseOptions, { 159 | offset, 160 | count 161 | }); 162 | mergedProps = sanitizeObject(mergedProps); 163 | const companies = await createRequest( 164 | constants.api.company.byId, 165 | { method, companyId: 'recent/created' }, 166 | mergedProps 167 | ); 168 | return Promise.resolve(companies); 169 | } catch (e) { 170 | return Promise.reject(e.message); 171 | } 172 | }; 173 | 174 | const byId = async companyId => { 175 | try { 176 | requiresAuthentication(_baseOptions); 177 | const method = 'GET'; 178 | let mergedProps = Object.assign({}, defaults, _baseOptions, {}); 179 | mergedProps = sanitizeObject(mergedProps); 180 | const companies = await createRequest( 181 | constants.api.company.byId, 182 | { method, companyId }, 183 | mergedProps 184 | ); 185 | return Promise.resolve(companies); 186 | } catch (e) { 187 | return Promise.reject(e.message); 188 | } 189 | }; 190 | 191 | const getContacts = async (companyId, count = 100, vidOffset) => { 192 | try { 193 | requiresAuthentication(_baseOptions); 194 | const method = 'GET'; 195 | let mergedProps = Object.assign({}, defaults, _baseOptions, { 196 | count, 197 | vidOffset 198 | }); 199 | 200 | mergedProps = sanitizeObject(mergedProps); 201 | const companies = await createRequest( 202 | constants.api.company.contacts, 203 | { method, companyId }, 204 | mergedProps 205 | ); 206 | return Promise.resolve(companies); 207 | } catch (e) { 208 | return Promise.reject(e.message); 209 | } 210 | }; 211 | 212 | const byDomain = async (domain, props) => { 213 | try { 214 | requiresAuthentication(_baseOptions); 215 | const method = 'POST'; 216 | const passedProps = props || {}; 217 | const { limit } = passedProps; 218 | let { properties, offset } = passedProps; 219 | if (!properties) { 220 | properties = ['domain', 'createdate', 'name', 'hs_lastmodifieddate']; 221 | } 222 | if (!offset) { 223 | offset = 0; 224 | } 225 | 226 | let body = { 227 | limit, 228 | requestOptions: { properties }, 229 | offset: { 230 | isPrimary: true, 231 | companyId: offset 232 | } 233 | }; 234 | body = sanitizeObject(body); 235 | let mergedProps = Object.assign({}, defaults, _baseOptions); 236 | mergedProps = sanitizeObject(mergedProps); 237 | // return Promise.resolve(JSON.stringify(body)); 238 | const companies = await createRequest( 239 | constants.api.company.byDomain, 240 | { method, domain, body }, 241 | mergedProps 242 | ); 243 | return Promise.resolve(companies); 244 | } catch (e) { 245 | return Promise.reject(e.message); 246 | } 247 | }; 248 | 249 | export default function company(baseOptions) { 250 | _baseOptions = baseOptions; 251 | 252 | return { 253 | /** 254 | * Create a company with properties - see {@link https://developers.hubspot.com/docs/methods/companies/create_company|developer docs} for an example of the properties object. 255 | * @async 256 | * @memberof hs/company 257 | * @method create 258 | * @param {object} companyProperties An object containing company properties in key/value format. At least 1 property is required 259 | * @example 260 | * const hs = new HubspotClient(props); 261 | * hs.company.create({ name: 'Foobar' }).then(response => console.log(response)) 262 | * @returns {Promise} 263 | */ 264 | create, 265 | /** 266 | * Update a company with properties - see {@link https://developers.hubspot.com/docs/methods/companies/create_company|developer docs} for an example of the properties object. 267 | * @async 268 | * @memberof hs/company 269 | * @method update 270 | * @param {number} companyId The ID of the company you wih to update 271 | * @param {object} companyProperties An object containing company properties in key/value format. At least 1 property is required 272 | * @example 273 | * const hs = new HubspotClient(props); 274 | * hs.company.update(companyId, companyProperties).then(response => console.log(response)) 275 | * @returns {Promise} 276 | */ 277 | update, 278 | /** 279 | * Update multiple companies with properties - see {@link https://developers.hubspot.com/docs/methods/companies/create_company|developer docs} for an example of the properties object. 280 | * @async 281 | * @memberof hs/company 282 | * @method batchUpdate 283 | * @param {array} updates Updates to be actioned (see example below) 284 | * @example 285 | * const hs = new HubspotClient(props); 286 | * const updates = [{ 287 | * id: 1234, 288 | * updates: { name: 'Something else' } 289 | * }, 290 | * { 291 | * id: 5678, 292 | * updates: { name: 'Blah blah', ownerId: 12341231 } 293 | * }]; 294 | * hs.company.batchUpdate(updates).then(response => console.log(response)) 295 | * @returns {Promise} 296 | */ 297 | batchUpdate, 298 | /** 299 | * Delete company 300 | * @async 301 | * @memberof hs/company 302 | * @method delete 303 | * @param {number} companyId Id of company to delete 304 | * @example 305 | * const hs = new HubspotClient(props); 306 | * hs.company.delete(companyId).then(response => console.log(response)); 307 | * @returns {Promise} 308 | */ 309 | delete: deleteCompany, 310 | /** 311 | * Retrieve all companies (max 250 at a time) 312 | * @async 313 | * @memberof hs/company 314 | * @method getAll 315 | * @param {object} pagingProperties Paging criteria for the current request 316 | * @example 317 | * const hs = new HubspotClient(props); 318 | * hs.company.getAll(pagingProperties).then(response => console.log(response)) 319 | * @property {number} [pagingProperties.limit] - The number of records to return. Defaults to 100, has a maximum value of 250. 320 | * @property {number} [pagingProperties.offset] - Used to page through the results. If there are more records in your portal than the limit= parameter, you will need to use the offset returned in the first request to get the next set of results. 321 | * @property {array} [pagingProperties.properties] - Used to include specific company properties in the results. By default, the results will only include the company ID, and will not include the values for any properties for your companies. Including this parameter will include the data for the specified property in the results. 322 | * @property {array} [pagingProperties.propertiesWithHistory] - Works similarly to pagingProperties.properties, but this parameter will include the history for the specified property, instead of just including the current value. Use this parameter when you need the full history of changes to a property's value. 323 | * @returns {Promise} 324 | */ 325 | getAll, 326 | /** 327 | * This endpoint will only return records modified in the last 30 days, or the 10k most recently modified records. 328 | * @async 329 | * @memberof hs/company 330 | * @method getRecentlyModified 331 | * @param {object} pagingProperties Paging criteria for the current request 332 | * @example 333 | * const hs = new HubspotClient(props); 334 | * hs.company.getRecentlyModified({count: 5}).then(response => console.log(response)) 335 | * @property {number} [pagingProperties.count] - Specifies the number of companies to be returned. 336 | * @property {number} [pagingProperties.offset] - This is used to get the next page of results. Each request will return an offset in the response, and you'd use that offset in the URL of your next request to get the next page of results. 337 | * @returns {Promise} 338 | */ 339 | getRecentlyModified, 340 | /** 341 | * This endpoint will only return records created in the last 30 days, or the 10k most recently created records. 342 | * @async 343 | * @memberof hs/company 344 | * @method getRecentlyCreated 345 | * @param {object} pagingProperties Paging criteria for the current request 346 | * @example 347 | * const hs = new HubspotClient(props); 348 | * hs.company.getRecentlyCreated({count: 5}).then(response => console.log(response)) 349 | * @property {number} [pagingProperties.count] - Specifies the number of companies to be returned. 350 | * @property {number} [pagingProperties.offset] - This is used to get the next page of results. Each request will return an offset in the response, and you'd use that offset in the URL of your next request to get the next page of results. 351 | * @returns {Promise} 352 | */ 353 | getRecentlyCreated, 354 | /** 355 | * Search for companies by domain name. 356 | * @async 357 | * @memberof hs/company 358 | * @method byDomain 359 | * @param {string} domain Domain name to search for 360 | * @param {object} pagingProperties Paging & property criteria for the current request 361 | * @example 362 | * const hs = new HubspotClient(props); 363 | * hs.company.byDomain('www.hubspot.com', {limit: 5, properties: ['name', 'createdate']}).then(response => console.log(response)) 364 | * @property {number} [pagingProperties.limit] - The number of records to return in a single request. Supports values up to 100. 365 | * @property {number} [pagingProperties.offset=0] - Each response will include a hasMore value and an offset object. If hasMore is true, then you would use the offset object in the next request to get the next set of results. 366 | * @property {array} [pagingProperties.properties=["domain", "createdate", "name", "hs_lastmodifieddate"]] - An array of properties that will be included for the returned companies. By default, no properties will be included in the response, so you must specify any properties that you want. 367 | * @returns {Promise} 368 | */ 369 | byDomain, 370 | /** 371 | * Search for companies by ID. 372 | * @async 373 | * @memberof hs/company 374 | * @method byId 375 | * @param {int} id VID of company to search for 376 | * @example 377 | * const hs = new HubspotClient(props); 378 | * const companyInfo = await hs.company.byId(1234); 379 | * @returns {Promise} 380 | */ 381 | byId, 382 | /** 383 | * Get contacts at a company 384 | * @async 385 | * @memberof hs/company 386 | * @method getContacts 387 | * @param {int} id VID of company 388 | * @example 389 | * const hs = new HubspotClient(props); 390 | * const companyInfo = await hs.company.getContacts(1234); 391 | * @returns {Promise} 392 | */ 393 | getContacts 394 | }; 395 | } 396 | -------------------------------------------------------------------------------- /src/entities/contacts-lists.js: -------------------------------------------------------------------------------- 1 | import createRequest, { requiresAuthentication } from '../utilities'; 2 | import constants from '../constants'; 3 | 4 | const debug = require('debug')('hubspot-api:tests'); // eslint-disable-line 5 | let _baseOptions; 6 | 7 | const defaults = { 8 | propertyMode: 'value_only', 9 | formSubmissionMode: 'none' 10 | }; 11 | 12 | const getById = async listId => { 13 | try { 14 | requiresAuthentication(_baseOptions); 15 | const mergedProps = Object.assign({}, _baseOptions); 16 | const list = await createRequest( 17 | constants.api.contactsList.byId, 18 | { listId }, 19 | mergedProps 20 | ); 21 | return Promise.resolve(list); 22 | } catch (e) { 23 | return Promise.reject(e); 24 | } 25 | }; 26 | 27 | const getContactsInList = async (listId, opts = {}) => { 28 | try { 29 | requiresAuthentication(_baseOptions); 30 | const mergedProps = { ..._baseOptions, ...defaults, ...opts }; 31 | const contacts = await createRequest( 32 | constants.api.contactsList.contactsByListId, 33 | { listId }, 34 | mergedProps 35 | ); 36 | return Promise.resolve(contacts); 37 | } catch (e) { 38 | return Promise.reject(e); 39 | } 40 | }; 41 | 42 | const addContactsToList = async (listId, contacts = {}) => { 43 | try { 44 | requiresAuthentication(_baseOptions); 45 | const method = 'POST'; 46 | const body = contacts; 47 | const response = await createRequest( 48 | constants.api.contactsList.addContactsToList, 49 | { method, listId, body }, 50 | _baseOptions 51 | ); 52 | return Promise.resolve(response); 53 | } catch (e) { 54 | return Promise.reject(e); 55 | } 56 | }; 57 | 58 | export default function contactsListsApi(baseOptions) { 59 | _baseOptions = baseOptions; 60 | // API 61 | return { 62 | /** 63 | * Get contact list by ID 64 | * @async 65 | * @memberof hs/contactsLists 66 | * @method getById 67 | * @param {int} list The id of the list to retrieve 68 | * @example 69 | * const hs = new HubspotClient(props); 70 | * hs.contactsList.getById(123412313).then(response => console.log(response)) 71 | * @returns {Promise} 72 | */ 73 | getById, 74 | /** 75 | * Get contacts in list 76 | * @async 77 | * @memberof hs/contactsLists 78 | * @method getContactsInList 79 | * @param {int} list The id of the list to retrieve 80 | * @param {object} properties Optional extra properties to add to the request - see {@link https://developers.hubspot.com/docs/methods/lists/get_list_contacts|developer docs} 81 | * @example 82 | * const hs = new HubspotClient(props); 83 | * hs.contactsList.getContactsInList(123412313).then(response => console.log(response)) 84 | * @returns {Promise} 85 | */ 86 | getContactsInList, 87 | /** 88 | * Add contacts to list 89 | * @async 90 | * @memberof hs/contactsLists 91 | * @method addContactsToList 92 | * @param {int} list The id of the list to retrieve 93 | * @param {object} contacts the object where insert the email or id 94 | * @param {array} contacts.emails array of emails to add 95 | * @param {array} contacts.vids array of vids to add 96 | * @example 97 | * const hs = new HubspotClient(props); 98 | * hs.contactsList.addContactsToList(123412313,{email:["testingapis@hubspot.com"],vids:[3057124,5524274]}).then(response => console.log(response)) 99 | * @returns {Promise} 100 | */ 101 | addContactsToList 102 | }; 103 | } 104 | -------------------------------------------------------------------------------- /src/entities/contacts-properties.js: -------------------------------------------------------------------------------- 1 | import createRequest, { requiresAuthentication } from '../utilities'; 2 | import constants from '../constants'; 3 | 4 | const debug = require('debug')('hubspot-api:tests'); // eslint-disable-line 5 | 6 | const defaults = { 7 | }; 8 | let _baseOptions; 9 | 10 | const getAllContactsProperties = async() => { 11 | try { 12 | requiresAuthentication(_baseOptions); 13 | const mergedProps = Object.assign({}, defaults, _baseOptions); 14 | const contactsProperties = await createRequest( 15 | constants.api.contactsProperties.getAllContactsProperties, 16 | {}, 17 | mergedProps 18 | ); 19 | return Promise.resolve(contactsProperties); 20 | } catch (e) { 21 | return Promise.reject(e); 22 | } 23 | }; 24 | 25 | export default function contactsProperties(baseOptions) { 26 | _baseOptions = baseOptions; 27 | // API 28 | return { 29 | /** 30 | * Get all contact properties 31 | * @async 32 | * @memberof hs/contactsProperties 33 | * @method getAllContactsProperties 34 | * @example 35 | * const hs = new HubspotClient(props); 36 | * hs.contactsProperties.getAllContactsProperties().then(response => console.log(response)) 37 | * @returns {Promise} 38 | */ 39 | getAllContactsProperties 40 | }; 41 | } 42 | -------------------------------------------------------------------------------- /src/entities/contacts.js: -------------------------------------------------------------------------------- 1 | import createRequest, { requiresAuthentication } from '../utilities'; 2 | import constants from '../constants'; 3 | 4 | const debug = require('debug')('hubspot-api:tests'); // eslint-disable-line 5 | 6 | const defaults = { 7 | propertyMode: 'value_only', 8 | formSubmissionMode: 'none' 9 | }; 10 | let _baseOptions; 11 | 12 | const getById = async (vid, options = {}) => { 13 | try { 14 | requiresAuthentication(_baseOptions); 15 | const mergedProps = { ...defaults, ..._baseOptions, ...options }; 16 | const contact = await createRequest( 17 | constants.api.contacts.byId, 18 | { vid }, 19 | mergedProps 20 | ); 21 | return Promise.resolve(contact); 22 | } catch (e) { 23 | return Promise.reject(e); 24 | } 25 | }; 26 | 27 | const getByEmail = async (email, options) => { 28 | try { 29 | requiresAuthentication(_baseOptions); 30 | const mergedProps = { ...defaults, ..._baseOptions, ...options }; 31 | const contact = await createRequest( 32 | constants.api.contacts.byEmail, 33 | { email }, 34 | mergedProps 35 | ); 36 | return Promise.resolve(contact); 37 | } catch (e) { 38 | return Promise.reject(e); 39 | } 40 | }; 41 | 42 | const getByUtk = async (utk, options) => { 43 | try { 44 | requiresAuthentication(_baseOptions); 45 | const mergedProps = { ...defaults, ..._baseOptions, ...options }; 46 | const contact = await createRequest( 47 | constants.api.contacts.byUtk, 48 | { utk }, 49 | mergedProps 50 | ); 51 | return Promise.resolve(contact); 52 | } catch (e) { 53 | return Promise.reject(e); 54 | } 55 | }; 56 | 57 | // NOTE: Not recommended to use this, only for offline contacts. 58 | const createOrUpdateContact = async obj => { 59 | try { 60 | requiresAuthentication(_baseOptions); 61 | const method = 'POST'; 62 | const { email } = obj; 63 | if (!email) { 64 | throw new Error( 65 | 'Property "email" is required for creating contacts with this method.' 66 | ); 67 | } 68 | 69 | const body = { 70 | properties: Object.keys(obj).map(key => ({ 71 | property: key, 72 | value: obj[key] 73 | })) 74 | }; 75 | const response = await createRequest( 76 | constants.api.contacts.createContact, 77 | { method, body, email }, 78 | _baseOptions 79 | ); 80 | return Promise.resolve(response.data); 81 | } catch (e) { 82 | return Promise.reject(e); 83 | } 84 | }; 85 | 86 | const updateContactByVid = async (vid, properties) => { 87 | try { 88 | requiresAuthentication(_baseOptions); 89 | const method = 'POST'; 90 | 91 | if (!vid) { 92 | throw new Error('`vid` is a required field'); 93 | } 94 | 95 | const body = { 96 | properties: Object.keys(properties).map(key => ({ 97 | property: key, 98 | value: properties[key] 99 | })) 100 | }; 101 | debug('updateContactByVid', JSON.stringify(body)); 102 | 103 | await createRequest( 104 | constants.api.contacts.byId, 105 | { method, body, vid }, 106 | _baseOptions 107 | ); 108 | 109 | return { 110 | msg: `Successfully updated contact details for ${vid}` 111 | }; 112 | } catch (err) { 113 | return Promise.reject(err); 114 | } 115 | }; 116 | 117 | const batchUpdateContacts = async contactsToUpdate => { 118 | try { 119 | requiresAuthentication(_baseOptions); 120 | const method = 'POST'; 121 | const body = contactsToUpdate.map(contact => { 122 | const contactType = /@/i.test(contact.id) ? 'email' : 'vid'; 123 | const properties = Object.keys(contact.updates).map(i => ({ 124 | property: i, 125 | value: contact.updates[i] 126 | })); 127 | return { 128 | [`${contactType}`]: contact.id, 129 | properties 130 | }; 131 | }); 132 | await createRequest( 133 | constants.api.contacts.batchUpdateContacts, 134 | { method, body }, 135 | _baseOptions 136 | ); 137 | return Promise.resolve({ 138 | msg: 'Successfully updated contact properties' 139 | }); 140 | } catch (e) { 141 | return Promise.reject(e); 142 | } 143 | }; 144 | 145 | const deleteContact = async vid => { 146 | try { 147 | requiresAuthentication(_baseOptions); 148 | const method = 'DELETE'; 149 | await createRequest( 150 | constants.api.contacts.deleteById, 151 | { method, vid }, 152 | _baseOptions 153 | ); 154 | return Promise.resolve({ 155 | msg: `Successfully delete contact details for ${vid}` 156 | }); 157 | } catch (e) { 158 | return Promise.reject(e); 159 | } 160 | }; 161 | 162 | const getContacts = async options => { 163 | try { 164 | requiresAuthentication(_baseOptions); 165 | const mergedProps = { ...defaults, ..._baseOptions, ...options }; 166 | const allContacts = await createRequest( 167 | constants.api.contacts.getAll, 168 | {}, 169 | mergedProps 170 | ); 171 | return Promise.resolve(allContacts); 172 | } catch (e) { 173 | return Promise.reject(e); 174 | } 175 | }; 176 | 177 | const getRecentlyModified = async options => { 178 | try { 179 | requiresAuthentication(_baseOptions); 180 | const mergedProps = { ..._baseOptions, ...options }; 181 | const recentlyModifiedContacts = await createRequest( 182 | constants.api.contacts.getRecentlyModified, 183 | {}, 184 | mergedProps 185 | ); 186 | return Promise.resolve(recentlyModifiedContacts); 187 | } catch (e) { 188 | return Promise.reject(e.message); 189 | } 190 | }; 191 | 192 | const search = async (q, options) => { 193 | try { 194 | requiresAuthentication(_baseOptions); 195 | const mergedProps = { 196 | q, ...defaults, ..._baseOptions, ...options 197 | }; 198 | const searchResults = await createRequest( 199 | constants.api.contacts.search, 200 | {}, 201 | mergedProps 202 | ); 203 | return searchResults; 204 | } catch (e) { 205 | return null; 206 | } 207 | }; 208 | 209 | // 210 | // const mergeContacts = async (primary, secondary) => { 211 | // // FIXME: Implement this 212 | // }; 213 | 214 | export default function contacts(baseOptions) { 215 | _baseOptions = baseOptions; 216 | // API 217 | return { 218 | /** 219 | * Get contact by ID 220 | * @async 221 | * @memberof hs/contacts 222 | * @method getById 223 | * @param {int} vid The vid of the contact to retrieve 224 | * @param {object} properties Optional extra properties to add to the request - see {@link https://developers.hubspot.com/docs/methods/contacts/get_contact|developer docs} 225 | * @example 226 | * const hs = new HubspotClient(props); 227 | * hs.contacts.getById(123412313).then(response => console.log(response)) 228 | * @returns {Promise} 229 | */ 230 | getById, 231 | /** 232 | * Get contact by email 233 | * @async 234 | * @memberof hs/contacts 235 | * @method getByEmail 236 | * @param {string} email The email address of the contact 237 | * @param {object} properties Optional extra properties to add to the request - see {@link https://developers.hubspot.com/docs/methods/contacts/get_contact|developer docs} 238 | * @example 239 | * const hs = new HubspotClient(props); 240 | * hs.contacts.getByEmail('foo@bar.com').then(response => console.log(response)) 241 | * @returns {Promise} 242 | */ 243 | getByEmail, 244 | /** 245 | * Get contact by UTK (user token) 246 | * @async 247 | * @memberof hs/contacts 248 | * @method getByUtk 249 | * @param {string} utk The utk (User token) of the contact 250 | * @param {object} properties Optional extra properties to add to the request - see {@link https://developers.hubspot.com/docs/methods/contacts/get_contact|developer docs} 251 | * @example 252 | * const hs = new HubspotClient(props); 253 | * hs.contacts.getByUtk('jdalksjd82739jaskdksadjhkds').then(response => console.log(response)) 254 | * @returns {Promise} 255 | */ 256 | getByUtk, 257 | /** 258 | * Create or update a contact 259 | * @async 260 | * @memberof hs/contacts 261 | * @method createOrUpdateContact 262 | * @param {object} properties Key/value pair of properties to update. Note: `email` is a required key. 263 | * @example 264 | * const hs = new HubspotClient(props); 265 | * hs.contacts.createOrUpdateContact({ 266 | * email: 'foo@bar.com', 267 | * first_name: 'Foo', 268 | * last_name: 'Bar' 269 | * }).then(response => console.log(response)); 270 | * @returns {Promise} 271 | */ 272 | createOrUpdateContact, 273 | /** 274 | * Update contact properties, by VID 275 | * @async 276 | * @memberof hs/contacts 277 | * @method updateContactByVid 278 | * @param {number} vid VID of contact to update 279 | * @param {object} properties Key/value pair of properties to update. 280 | * @example 281 | * const hs = new HubspotClient(props); 282 | * hs.contacts.updateContactByVid(123456, { 283 | * first_name: 'Foo', 284 | * last_name: 'Bar' 285 | * }).then(response => console.log(response)); 286 | * @returns {Promise} 287 | */ 288 | updateContactByVid, 289 | /** 290 | * Batch update a set of contacts 291 | * @async 292 | * @memberof hs/contacts 293 | * @method batchUpdateContacts 294 | * @param {array} contactsToUpdate Array of contact updates, see example below 295 | * @example 296 | * const hs = new HubspotClient(props); 297 | * hs.contacts.batchUpdateContacts([{ 298 | id: 129838129313, 299 | updates: { 300 | email: 'sdjfhksdjf@boo.com', 301 | phone_no: '9128301982312' 302 | } 303 | }, 304 | { 305 | id: 2319082301823, 306 | updates: { 307 | email: 'skdjflkjsfdsfs@boo.com', 308 | phone_no: '1293801293801923' 309 | } 310 | }, 311 | { 312 | id: 271263871623, 313 | updates: { 314 | email: 'mxcxmncvmxc@boo.com', 315 | phone_no: '01823981023' 316 | } 317 | }, 318 | { 319 | id: 127361287312, 320 | updates: { 321 | email: 'yqeuyiqwuyeiquwey@boo.com', 322 | phone_no: '127398172398123' 323 | } 324 | } 325 | // ..... 326 | ]).then(response => console.log(response)) 327 | * @returns {Promise} 328 | */ 329 | batchUpdateContacts, 330 | /** 331 | * Remove a contact 332 | * @async 333 | * @memberof hs/contacts 334 | * @method deleteContact 335 | * @param {number} id Id of contact to remove 336 | * @example 337 | * const hs = new HubspotClient(props); 338 | * hs.contacts.deleteContact(82739182731).then(response => console.log(response)); 339 | * @returns {Promise} 340 | */ 341 | deleteContact, 342 | /** 343 | * Get all contacts 344 | * @async 345 | * @memberof hs/contacts 346 | * @method getContacts 347 | * @param {object} options Additional options & filters to apply 348 | * @example 349 | * const hs = new HubspotClient(props); 350 | * hs.contacts.getContacts({ limit: 25 }).then(response => console.log(response)); 351 | * @returns {Promise} 352 | */ 353 | getContacts, 354 | /** 355 | * Get recently modified contacts 356 | * @async 357 | * @memberof hs/contacts 358 | * @method getRecentlyModified 359 | * @param {object} options Additional options and paging criteria 360 | * @example 361 | * const hs = new HubspotClient(props); 362 | * hs.contacts.getRecentlyModified({count: 5}).then(response => console.log(response)) 363 | * @property {number} [options.count] - Specifies the number of contacts to be returned. 364 | * @property {number} [options.timeOffset] - This is used along with `vidOffset` to get the next page of results. Each request will return a `time-offset` and `vid-offset` in the response, and you'd use those offsets in the URL of your next request to get the next page of results. 365 | * @property {number} [options.vidOffset] - This is used along with `timeOffset` to get the next page of results. 366 | * @returns {Promise} 367 | */ 368 | getRecentlyModified, 369 | /** 370 | * Search contacts 371 | * @async 372 | * @memberof hs/contacts 373 | * @method search 374 | * @param {string} q The search term (see https://developers.hubspot.com/docs/methods/contacts/search_contacts) 375 | * @param {object} options Additional options and paging criteria 376 | * @example 377 | * const hs = new HubspotClient(props); 378 | * const contacts = await hs.contacts.search('john', { count: 5 }) 379 | * @property {number} [options.count] - Specifies the number of contacts to be returned. 380 | * @property {number} [options.offset] - This parameter is used to page through the results. Every call to this endpoint will return an offset value. This value is used in the offset= parameter of the next call to get the next page of contacts. 381 | * @property {array} [options.property] - The properties in the "contact" object in the returned data will only include the property or properties that you request. 382 | * @returns {Promise} 383 | */ 384 | search 385 | // mergeContacts // Unimplemented 386 | }; 387 | } 388 | -------------------------------------------------------------------------------- /src/entities/deals.js: -------------------------------------------------------------------------------- 1 | import createRequest, { requiresAuthentication } from '../utilities'; 2 | import constants from '../constants'; 3 | 4 | const defaults = {}; 5 | let _baseOptions; 6 | 7 | const getAll = async (opts = {}) => { 8 | try { 9 | requiresAuthentication(_baseOptions); 10 | const { limit, offset, properties, propertiesWithHistory } = opts; 11 | 12 | const allowedProps = { limit, offset, properties, propertiesWithHistory }; 13 | const mergedProps = Object.assign({}, defaults, _baseOptions, allowedProps); 14 | 15 | const allDeals = await createRequest( 16 | constants.api.deals.getAll, 17 | {}, 18 | mergedProps 19 | ); 20 | 21 | return Promise.resolve(allDeals); 22 | } catch (e) { 23 | return Promise.reject(e.message); 24 | } 25 | }; 26 | 27 | const getById = async (id, options = {}) => { 28 | try { 29 | requiresAuthentication(_baseOptions); 30 | const mergedProps = Object.assign({}, defaults, _baseOptions, options); 31 | const contact = await createRequest( 32 | constants.api.deals.byId, 33 | { id }, 34 | mergedProps 35 | ); 36 | return Promise.resolve(contact); 37 | } catch (e) { 38 | return Promise.reject(e); 39 | } 40 | }; 41 | 42 | const getRecentlyCreated = async (opts = {}) => { 43 | try { 44 | requiresAuthentication(_baseOptions); 45 | const { count, offset, since, includePropertyVersions } = opts; 46 | 47 | const allowedProps = { count, offset, since, includePropertyVersions }; 48 | const mergedProps = Object.assign({}, defaults, _baseOptions, allowedProps); 49 | const recentlyCreatedDeals = await createRequest( 50 | constants.api.deals.recentlyCreated, 51 | {}, 52 | mergedProps 53 | ); 54 | return Promise.resolve(recentlyCreatedDeals); 55 | } catch (e) { 56 | return Promise.reject(e.message); 57 | } 58 | }; 59 | 60 | const createOrUpdate = async (opts = {}) => { 61 | try { 62 | requiresAuthentication(_baseOptions); 63 | const mergedProps = Object.assign({}, defaults, _baseOptions); 64 | const { id, properties, associations } = opts; 65 | 66 | let method = 'POST'; 67 | let url = constants.api.deals.create; 68 | const body = { properties, associations }; 69 | const options = { method, body }; 70 | if (id) { 71 | method = 'PUT'; 72 | url = constants.api.deals.update; 73 | Object.assign(options, { method, id }); 74 | } 75 | const deal = await createRequest(url, options, mergedProps); 76 | return Promise.resolve(deal); 77 | } catch (e) { 78 | return Promise.reject(e.message); 79 | } 80 | }; 81 | 82 | const batchUpdate = async updates => { 83 | try { 84 | requiresAuthentication(_baseOptions); 85 | const mergedProps = Object.assign({}, defaults, _baseOptions); 86 | const method = 'POST'; 87 | const url = constants.api.deals.batchUpdate; 88 | await createRequest(url, { method, body: updates }, mergedProps); 89 | return Promise.resolve({ updated: true }); 90 | } catch (e) { 91 | return Promise.reject(e.message); 92 | } 93 | }; 94 | 95 | export default function deals(baseOptions) { 96 | _baseOptions = baseOptions; 97 | 98 | return { 99 | /** 100 | * Get deal by ID 101 | * @async 102 | * @memberof hs/deals 103 | * @method getById 104 | * @param {int} id The id of the deal to retrieve 105 | * @param {object} properties Optional extra properties to add to the request - see {@link https://developers.hubspot.com/docs/methods/deals/get_deal|developer docs} 106 | * @example 107 | * const hs = new HubspotClient(props); 108 | * hs.deals.getById(123412313).then(response => console.log(response)) 109 | * @returns {Promise} 110 | */ 111 | getById, 112 | /** 113 | * Get recently created deals 114 | * @async 115 | * @memberof hs/deals 116 | * @method getRecentlyCreated 117 | * @param {object} opts 118 | * @example 119 | * const hs = new HubspotClient(props); 120 | * hs.deals.getRecentlyCreated({ 121 | * count: 50, 122 | * offset: 5, 123 | * includePropertyVersions: true, 124 | * since: 1463680280365 125 | * }).then(response => console.log(response)); 126 | * @property {int} opts.count 127 | * @property {int} opts.offset 128 | * @property {int} opts.since 129 | * @property {boolean} opts.includePropertyVersions 130 | * @returns {Promise} 131 | */ 132 | getRecentlyCreated, 133 | /** 134 | * Get all deals 135 | * @async 136 | * @memberof hs/deals 137 | * @method getAll 138 | * @param {object} opts 139 | * @example 140 | * const hs = new HubspotClient(props); 141 | * hs.deals.getRecentlyCreated({ 142 | * limit: 2, 143 | * offset: 12356, 144 | * properties: ['dealname', 'pipeline'], 145 | * propertiesWithHistory: ['dealstage'] 146 | * }).then(response => console.log(response)); 147 | * @property {int} opts.limit 148 | * @property {int} opts.offset 149 | * @property {array} opts.properties 150 | * @property {array} opts.propertiesWithHistory 151 | * @returns {Promise} 152 | */ 153 | getAll, 154 | /** 155 | * Update a group of deals 156 | * @async 157 | * @memberof hs/deals 158 | * @method batchUpdate 159 | * @param {array} updates Array of objects. objectId corresponds with a dealId. See Example below. 160 | * @example 161 | * const hs = new HubspotClient(props); 162 | * const updates = [{ 163 | * "objectId": 93630457, 164 | * "properties": [ 165 | * { 166 | * "name": "dealname", 167 | * "value": "Updated Deal Name" 168 | * }, 169 | * { 170 | * "name": "dealname", 171 | * "value": "Updated Deal Name" 172 | * } 173 | * ]}, 174 | * { 175 | * "objectId": 26448234, 176 | * "properties": [ 177 | * { 178 | * "name": "amount", 179 | * "value": "2000" 180 | * } 181 | * ] 182 | * }]); 183 | * hs.deals.batchUpdate(updates).then(response => console.log(response)); 184 | * @returns {Promise} 185 | * If successful the promise will resolve with { updated: true }. Otherwise the promise will resolve with an error message. 186 | */ 187 | batchUpdate, 188 | /** 189 | * Create or update a deal 190 | * @async 191 | * @memberof hs/deals 192 | * @method createOrUpdate 193 | * @param {object} opts 194 | * @example 195 | * const hs = new HubspotClient(props); 196 | * const updatedDealOpts = { 197 | * id:93630457, 198 | * properties: [ 199 | * { 200 | * value: 'Update Deal Name', 201 | * name: 'dealname' 202 | * }, 203 | * { 204 | * value: '200000', 205 | * name: 'amount' 206 | * } 207 | * ]}; 208 | * hs.deals.createOrUpdate(updatedDealOpts).then(response => console.log(response)); 209 | * const newDealOpts = { 210 | * associations: { 211 | * associatedCompanyIds: 53333385 212 | * }, 213 | * properties: [ 214 | * { 215 | * value: 'Big Deal', 216 | * name: 'dealname' 217 | * }, 218 | * { 219 | * value: 'appointmentscheduled', 220 | * name: 'dealstage' 221 | * }, 222 | * { 223 | * value: 'default', 224 | * name: 'pipeline' 225 | * }, 226 | * { 227 | * value: 1409443200000, 228 | * name: 'closedate' 229 | * }, 230 | * { 231 | * value: '60000', 232 | * name: 'amount' 233 | * }, 234 | * { 235 | * value: 'newbusiness', 236 | * name: 'dealtype' 237 | * } 238 | * ] 239 | *}; 240 | * hs.deals.createOrUpdate(newDealOpts).then(response => console.log(response)); 241 | * @property {int} opts.id 242 | * @property {array} opts.properties 243 | * @property {array} opts.associations 244 | * @property {boolean} opts.includePropertyVersions 245 | * @returns {Promise} 246 | */ 247 | createOrUpdate 248 | }; 249 | } 250 | -------------------------------------------------------------------------------- /src/entities/domains.js: -------------------------------------------------------------------------------- 1 | import createRequest, { 2 | queryStringParamInterpolator, 3 | requiresAuthentication 4 | } from '../utilities'; 5 | import constants from '../constants'; 6 | 7 | const defaults = {}; 8 | let _baseOptions; 9 | 10 | const getDomains = async (opts = {}) => { 11 | try { 12 | requiresAuthentication(_baseOptions); 13 | const { 14 | limit, 15 | offset, 16 | id, 17 | domain, 18 | is_resolving, 19 | created, 20 | primary_site_page 21 | } = opts; 22 | let additionalOpts = { 23 | domain, 24 | limit, 25 | offset, 26 | is_resolving, 27 | primary_site_page, 28 | id 29 | }; 30 | // Extract additional dynamic querystring params and values. 31 | 32 | additionalOpts = queryStringParamInterpolator( 33 | { 34 | created 35 | }, 36 | additionalOpts 37 | ); 38 | 39 | const mergedProps = Object.assign( 40 | {}, 41 | defaults, 42 | _baseOptions, 43 | additionalOpts 44 | ); 45 | const domains = await createRequest( 46 | constants.api.domains.getAll, 47 | {}, 48 | mergedProps 49 | ); 50 | return Promise.resolve(domains); 51 | } catch (e) { 52 | return Promise.reject(e.message); 53 | } 54 | }; 55 | 56 | const getDomain = async id => { 57 | try { 58 | requiresAuthentication(_baseOptions); 59 | if (!id) { 60 | throw new Error('getDomain requires an `id` argument'); 61 | } 62 | const mergedProps = Object.assign({}, defaults, _baseOptions); 63 | const domainInfo = await createRequest( 64 | constants.api.domains.byId, 65 | { id }, 66 | mergedProps 67 | ); 68 | return Promise.resolve(domainInfo); 69 | } catch (e) { 70 | return Promise.reject(e.message); 71 | } 72 | }; 73 | 74 | export default function domainsApi(baseOptions) { 75 | _baseOptions = baseOptions; 76 | 77 | return { 78 | /** 79 | * Get all domains for a portal 80 | * @async 81 | * @memberof hs/domains 82 | * @method getDomains 83 | * @param {object} opts 84 | * @example 85 | * const hs = new HubspotClient(props); 86 | * hs.domains.getDomains(opts).then(response => console.log(response)); 87 | * @property {int} opts.limit 88 | * @property {int} opts.offset 89 | * @property {int} opts.id 90 | * @property {string} opts.domain 91 | * @property {boolean} opts.is_resolving 92 | * @property {int} opts.created 93 | * @property {string} opts.primary_site_page 94 | * @returns {Promise} 95 | */ 96 | getDomains, 97 | /** 98 | * Get domain by ID 99 | * @async 100 | * @memberof hs/domains 101 | * @method getDomain 102 | * @param {int} domainId 103 | * @example 104 | * const hs = new HubspotClient(props); 105 | * hs.domains.getDomain(domainId).then(response => console.log(response)); 106 | * @returns {Promise} 107 | */ 108 | getDomain 109 | }; 110 | } 111 | -------------------------------------------------------------------------------- /src/entities/email-events.js: -------------------------------------------------------------------------------- 1 | import createRequest, { requiresAuthentication } from '../utilities'; 2 | import constants from '../constants'; 3 | 4 | const defaults = { 5 | limit: 5 6 | }; 7 | let _baseOptions; 8 | 9 | const getCampaignsWithRecentActivity = async (opts = {}) => { 10 | try { 11 | requiresAuthentication(_baseOptions); 12 | const { offset, limit } = opts; 13 | const mergedProps = Object.assign({}, defaults, _baseOptions, { 14 | offset, 15 | limit 16 | }); 17 | const recentCampaigns = await createRequest( 18 | constants.api.emailEvents.campaignsWithRecentActivity, 19 | {}, 20 | mergedProps 21 | ); 22 | return Promise.resolve(recentCampaigns); 23 | } catch (e) { 24 | return Promise.reject(e.message); 25 | } 26 | }; 27 | 28 | const getCampaign = async (campaignId, appId) => { 29 | try { 30 | requiresAuthentication(_baseOptions); 31 | const mergedProps = Object.assign({}, defaults, _baseOptions, { appId }); 32 | const campaignInfo = await createRequest( 33 | constants.api.emailEvents.campaign, 34 | { 35 | campaignId 36 | }, 37 | mergedProps 38 | ); 39 | return Promise.resolve(campaignInfo); 40 | } catch (e) { 41 | return Promise.reject(e.message); 42 | } 43 | }; 44 | 45 | export default function emailEvents(baseOptions) { 46 | _baseOptions = baseOptions; 47 | 48 | return { 49 | /** 50 | * For a given portal, return all campaign IDs sorted by recent activity associated with the portal. 51 | * @async 52 | * @memberof hs/emailEvents 53 | * @method getCampaignsWithRecentActivity 54 | * @param {object} opts 55 | * @example 56 | * const hs = new HubspotClient(props); 57 | * hs.emailEvents.getCampaignsWithRecentActivity(opts).then(response => console.log(response)); 58 | * @property {int} opts.limit 59 | * @property {int} opts.offset 60 | * @returns {Promise} 61 | */ 62 | getCampaignsWithRecentActivity, 63 | /** 64 | * For a given campaign, return data associated with the campaign. 65 | * @async 66 | * @memberof hs/emailEvents 67 | * @method getCampaign 68 | * @param {int} campaignId Selected campaign id. 69 | * @param {int} appId The Application Id for the given email. Found in the get_campaigns endpoint. 70 | * @example 71 | * const hs = new HubspotClient(props); 72 | * hs.emailEvents.getCampaign(campaignId, appId).then(response => console.log(response)); 73 | * @returns {Promise} 74 | */ 75 | getCampaign 76 | }; 77 | } 78 | -------------------------------------------------------------------------------- /src/entities/email-subscriptions.js: -------------------------------------------------------------------------------- 1 | import createRequest, { requiresAuthentication } from '../utilities'; 2 | import constants from '../constants'; 3 | 4 | const defaults = {}; 5 | let _baseOptions; 6 | 7 | const updateEmailSubscription = async (email, body = {}) => { 8 | try { 9 | requiresAuthentication(_baseOptions); 10 | if (!email) { 11 | throw new Error('Email is a required field'); 12 | } 13 | 14 | const mergedProps = { ...defaults, ..._baseOptions }; 15 | const updateStatus = await createRequest( 16 | constants.api.emailSubscriptions.updateStatus, 17 | { body, method: 'PUT', email }, 18 | mergedProps 19 | ); 20 | return Promise.resolve(updateStatus); 21 | } catch (e) { 22 | return Promise.reject(e.message); 23 | } 24 | }; 25 | 26 | const getEmailSubscriptionStatus = async (email, portalId) => { 27 | try { 28 | requiresAuthentication(_baseOptions); 29 | 30 | if (!email || !portalId) { 31 | throw new Error('Email / Portal ID are required fields'); 32 | } 33 | 34 | const mergedProps = { ...defaults, ..._baseOptions }; 35 | const status = await createRequest( 36 | constants.api.emailSubscriptions.getStatus, 37 | { email, portalId }, 38 | mergedProps 39 | ); 40 | return Promise.resolve(status); 41 | } catch (e) { 42 | return Promise.reject(e.message); 43 | } 44 | }; 45 | 46 | export default function emailSubscriptions(baseOptions) { 47 | _baseOptions = baseOptions; 48 | 49 | return { 50 | /** 51 | * Update the email subscription status for an email address (https://developers.hubspot.com/docs/methods/email/update_status) 52 | * @async 53 | * @memberof hs/emailSubscriptions 54 | * @method updateEmailSubscription 55 | * @param {string} email 56 | * @param {object} opts 57 | * @example 58 | * const hs = new HubspotClient(props); 59 | * hs.emailSubscriptions.updateEmailSubscription(email, { unsubscribeFromAll: true}).then(response => console.log(response)); 60 | * @returns {Promise} 61 | */ 62 | updateEmailSubscription, 63 | /** 64 | * Get the email subscription status for an email address / portal ID combination (https://developers.hubspot.com/docs/methods/email/get_status) 65 | * @async 66 | * @memberof hs/emailSubscriptions 67 | * @method getEmailSubscriptionStatus 68 | * @param {string} email 69 | * @param {string} portalId 70 | * @example 71 | * const hs = new HubspotClient(props); 72 | * hs.emailSubscriptions.getEmailSubscriptionStatus(email, 198273).then(response => console.log(response)); 73 | * @returns {Promise} 74 | */ 75 | getEmailSubscriptionStatus 76 | }; 77 | } 78 | -------------------------------------------------------------------------------- /src/entities/engagements.js: -------------------------------------------------------------------------------- 1 | import createRequest, { requiresAuthentication } from '../utilities'; 2 | import constants from '../constants'; 3 | 4 | const defaults = {}; 5 | let _baseOptions; 6 | 7 | const create = async (opts = {}) => { 8 | try { 9 | requiresAuthentication(_baseOptions); 10 | const mergedProps = Object.assign({}, defaults, _baseOptions); 11 | const { engagement, associations, metadata } = opts; 12 | 13 | const method = 'POST'; 14 | const url = constants.api.engagements.create; 15 | const body = { engagement, associations, metadata }; 16 | const options = { method, body }; 17 | const result = await createRequest(url, options, mergedProps); 18 | return Promise.resolve(result); 19 | } catch (e) { 20 | return Promise.reject(e.message); 21 | } 22 | }; 23 | 24 | export default function engagements(baseOptions) { 25 | _baseOptions = baseOptions; 26 | 27 | return { 28 | /** 29 | * Create an engagement 30 | * @async 31 | * @memberof hs/engagements 32 | * @method create 33 | * @param {object} opts 34 | * @example 35 | * const hs = new HubspotClient(props); 36 | * const newEngagementOpts = { 37 | * engagement: { 38 | * type: 'NOTE', 39 | * active: true, 40 | * timestamp: new Date().getTime() 41 | * }, 42 | * associations: { 43 | * companyIds: [53333385] 44 | * }, 45 | * metadata: { 46 | * body: 'A note about robot' 47 | * } 48 | *}; 49 | * hs.engagements.create(newEngagementOpts).then(response => console.log(response)); 50 | * @property {object} opts.engagement 51 | * @property {object} opts.associations 52 | * @property {object} opts.metadata 53 | * @property {array} opts.attachments 54 | * @returns {Promise} 55 | */ 56 | create 57 | }; 58 | } 59 | -------------------------------------------------------------------------------- /src/entities/files.js: -------------------------------------------------------------------------------- 1 | import createRequest, { requiresAuthentication } from '../utilities'; 2 | import constants from '../constants'; 3 | 4 | const defaults = {}; 5 | let _baseOptions; 6 | 7 | const getFilesInFolder = async (folder_id, opts = {}) => { 8 | try { 9 | requiresAuthentication(_baseOptions); 10 | let { limit, offset } = opts; 11 | limit = limit || 100; 12 | offset = offset || 0; 13 | 14 | const mergedProps = Object.assign({}, defaults, _baseOptions, { 15 | folder_id, 16 | limit, 17 | offset 18 | }); 19 | 20 | const files = await createRequest( 21 | constants.api.files.getFilesInFolder, 22 | {}, 23 | mergedProps 24 | ); 25 | return Promise.resolve(files); 26 | } catch (e) { 27 | return Promise.reject(e.message); 28 | } 29 | }; 30 | 31 | const getFolders = async (parent_folder_id, opts = {}) => { 32 | try { 33 | requiresAuthentication(_baseOptions); 34 | let { limit, offset } = opts; 35 | limit = limit || 100; 36 | offset = offset || 0; 37 | 38 | const mergedProps = Object.assign({}, defaults, _baseOptions, { 39 | parent_folder_id, 40 | limit, 41 | offset 42 | }); 43 | 44 | const folders = await createRequest( 45 | constants.api.files.getFolders, 46 | {}, 47 | mergedProps 48 | ); 49 | return Promise.resolve(folders); 50 | } catch (e) { 51 | return Promise.reject(e.message); 52 | } 53 | }; 54 | 55 | export default function filesApi(baseOptions) { 56 | _baseOptions = baseOptions; 57 | 58 | return { 59 | /** 60 | * Retrieve all folder types 61 | * @async 62 | * @memberof hs/files 63 | * @method getFilesInFolder 64 | * 65 | * @param {number} folderId The ID for the specific folder in the file manager 66 | * @param {object} folderProperties An object containing folder properties to search for 67 | * @property {number} folderProperties.limit the number of results default 20 68 | * 69 | * @returns {Promise} 70 | * 71 | * @example 72 | * const hs = new HubspotClient(props); 73 | * hs.files.getFilesInFolder(123456789, folderProperties).then(response => console.log(response)) 74 | */ 75 | getFilesInFolder, 76 | /** 77 | * Retrieve all folder types 78 | * @async 79 | * @memberof hs/files 80 | * @method getFolders 81 | * 82 | * @param {number} parentFolderId The ID for the specific parent folder in the file manager 83 | * @param {object} folderProperties An object containing folder properties to search for 84 | * @property {number} folderProperties.limit the number of results default 20 85 | * 86 | * @returns {Promise} 87 | * 88 | * @example 89 | * const hs = new HubspotClient(props); 90 | * hs.files.getFolders(123456789, folderProperties).then(response => console.log(response)) 91 | */ 92 | getFolders 93 | }; 94 | } 95 | -------------------------------------------------------------------------------- /src/entities/forms.js: -------------------------------------------------------------------------------- 1 | 2 | import createRequest, { requiresAuthentication } from '../utilities'; 3 | import constants from '../constants'; 4 | 5 | const omit = require('lodash.omit'); 6 | 7 | const defaults = {}; 8 | let _baseOptions; 9 | 10 | const submitFormV3 = async (portalId, formId, opts = {}) => { 11 | try { 12 | const method = 'POST'; 13 | const response = await createRequest( 14 | constants.api.forms.submitFormV3, 15 | { 16 | formId, 17 | portalId, 18 | method, 19 | body: { 20 | ...opts, 21 | } 22 | } 23 | ); 24 | return Promise.resolve(response); 25 | } catch (e) { 26 | return Promise.reject(e); 27 | } 28 | }; 29 | 30 | const submitForm = async (portalId, formId, opts = {}) => { 31 | try { 32 | // hs-context params 33 | const { hutk, ipAddress, pageUrl, pageName, redirectUrl } = opts; 34 | 35 | const method = 'POST'; 36 | const hs_context = JSON.stringify({ 37 | hutk, 38 | ipAddress, 39 | pageUrl, 40 | pageName, 41 | redirectUrl 42 | }); 43 | 44 | const mergedProps = Object.assign( 45 | { 46 | hs_context 47 | }, 48 | defaults, 49 | _baseOptions, 50 | // Property values. This is essentially the entire payload minus the formId, portalId and hs_context params. 51 | omit(opts, ['hutk', 'ipAddress', 'pageUrl', 'pageName', 'redirectUrl']) 52 | ); 53 | 54 | // Remove the hapikey from these requests 55 | if (mergedProps.hapikey) { 56 | delete mergedProps.hapikey; 57 | } 58 | 59 | await createRequest( 60 | constants.api.forms.submitForm, 61 | { 62 | formId, 63 | portalId, 64 | method 65 | }, 66 | mergedProps 67 | ); 68 | 69 | return Promise.resolve({ submitted: true }); 70 | } catch (e) { 71 | return Promise.reject(e.message); 72 | } 73 | }; 74 | 75 | const getFormFields = async formId => { 76 | try { 77 | requiresAuthentication(_baseOptions); 78 | const mergedProps = Object.assign({}, defaults, _baseOptions); 79 | const formFields = await createRequest( 80 | constants.api.forms.formFields, 81 | { formId }, 82 | mergedProps 83 | ); 84 | return Promise.resolve(formFields); 85 | } catch (e) { 86 | return Promise.reject(e); 87 | } 88 | }; 89 | 90 | const getSubmissions = async (formId, opts = {}) => { 91 | try { 92 | requiresAuthentication(_baseOptions); 93 | const mergedProps = Object.assign({}, defaults, _baseOptions, opts); 94 | const submissions = await createRequest( 95 | constants.api.forms.submissions, 96 | { formId }, 97 | mergedProps 98 | ); 99 | return Promise.resolve(submissions); 100 | } catch (e) { 101 | return Promise.reject(e); 102 | } 103 | }; 104 | 105 | const getForm = async (formId, opts = {}) => { 106 | try { 107 | requiresAuthentication(_baseOptions); 108 | const mergedProps = Object.assign({}, defaults, _baseOptions, opts); 109 | const form = await createRequest( 110 | constants.api.forms.getForm, 111 | { formId }, 112 | mergedProps, 113 | ); 114 | return Promise.resolve(form); 115 | } catch (e) { 116 | return Promise.reject(e); 117 | } 118 | } 119 | 120 | export default function formsApi(baseOptions) { 121 | _baseOptions = baseOptions; 122 | 123 | return { 124 | /** 125 | * Submit a form with data See the {@link https://developers.hubspot.com/docs/methods/forms/submit_form|developer docs} for full spec. 126 | * @async 127 | * @memberof hs/forms 128 | * @method submitForm 129 | * @param {int} portalId Portal ID the form resides on 130 | * @param {string} formId ID of form to submit. 131 | * @param {object} formFields Key/value pairs of form fields. 132 | * @example 133 | * const hs = new HubspotClient(props); 134 | * hs.forms.submitForm(portalId, formId, formFields).then(response => console.log(response)); 135 | * @returns {Promise} 136 | */ 137 | submitForm, 138 | /** 139 | * Get Form, including fields, options and validation 140 | * @async 141 | * @memberof hs/forms 142 | * @method getForm 143 | * @param {string} formId 144 | */ 145 | getForm, 146 | /** 147 | * Get Form Fields for Specified Form 148 | * @async 149 | * @memberof hs/forms 150 | * @method getFormFields 151 | * @param {string} formId 152 | * @example 153 | * const hs = new HubSpotClient(props); 154 | * const formFields = await hs.forms.getFormFields(formId) 155 | */ 156 | getFormFields, 157 | /** 158 | * Get form submissions for specific form 159 | * @async 160 | * @memberof hs/forms 161 | * @method getSubmissions 162 | * @param {string} formId 163 | * @example 164 | * const hs = new HubSpotClient(props); 165 | * const submissions = await hs.forms.getSubmissions(formId) 166 | */ 167 | getSubmissions, 168 | /** 169 | * Submit a form with data See the {@link https://developers.hubspot.com/docs/methods/forms/submit_form_v3|developer docs} for full spec. 170 | * @async 171 | * @memberof hs/forms 172 | * @method submitFormV3 173 | * @param {int} portalId Portal ID the form resides on 174 | * @param {string} formId ID of form to submit. 175 | * @param {object} submitBody { fields, context, legalConsentOptions } see docs for full spec 176 | * @example 177 | * const hs = new HubspotClient(props); 178 | * hs.forms.submitFormV3(portalId, formId, submitBody).then(response => console.log(response)); 179 | * @returns {Promise} 180 | */ 181 | submitFormV3, 182 | }; 183 | } 184 | -------------------------------------------------------------------------------- /src/entities/hubdb.js: -------------------------------------------------------------------------------- 1 | import createRequest, { requiresAuthentication } from '../utilities'; 2 | import constants from '../constants'; 3 | 4 | const defaults = {}; 5 | let _baseOptions; 6 | 7 | const createTable = async (opts = {}) => { 8 | try { 9 | requiresAuthentication(_baseOptions); 10 | const { 11 | name, 12 | useForPages, 13 | columns, 14 | publishedAt 15 | } = opts; 16 | 17 | const body = { 18 | name, 19 | useForPages, 20 | columns, 21 | publishedAt 22 | }; 23 | 24 | const method = 'POST'; 25 | const url = constants.api.hubdb.tables; 26 | const options = { method, body }; 27 | 28 | const mergedProps = { ...defaults, ..._baseOptions }; 29 | const create = await createRequest(url, options, mergedProps); 30 | 31 | return Promise.resolve(create); 32 | } catch (e) { 33 | return Promise.reject(e.message); 34 | } 35 | }; 36 | 37 | const getTables = async () => { 38 | try { 39 | requiresAuthentication(_baseOptions); 40 | const mergedProps = { ...defaults, ..._baseOptions }; 41 | const tables = await createRequest( 42 | constants.api.hubdb.tables, 43 | {}, 44 | mergedProps 45 | ); 46 | 47 | return Promise.resolve(tables); 48 | } catch (e) { 49 | return Promise.reject(e.message); 50 | } 51 | }; 52 | 53 | const getTableRows = async (tableId, portalId, opts = {}) => { 54 | try { 55 | const additionalOpts = { portalId }; 56 | const mergedProps = { 57 | ...defaults, 58 | ..._baseOptions, 59 | ...opts, 60 | ...additionalOpts 61 | }; 62 | 63 | const rows = await createRequest( 64 | constants.api.hubdb.rows, 65 | { tableId }, 66 | mergedProps 67 | ); 68 | 69 | return Promise.resolve({ published: true, rows }); 70 | } catch (e) { 71 | return Promise.reject(e.message); 72 | } 73 | }; 74 | 75 | const publishTable = async tableId => { 76 | try { 77 | requiresAuthentication(_baseOptions); 78 | const mergedProps = { ...defaults, ..._baseOptions }; 79 | const method = 'PUT'; 80 | const table = await createRequest( 81 | constants.api.hubdb.publishTable, 82 | { tableId, method }, 83 | mergedProps 84 | ); 85 | 86 | return Promise.resolve(table); 87 | } catch (e) { 88 | return Promise.reject(e.message); 89 | } 90 | }; 91 | 92 | const getTableById = async (tableId, portalId, options = {}) => { 93 | try { 94 | const additionalOpts = { portalId }; 95 | const mergedProps = { 96 | ...defaults, 97 | ..._baseOptions, 98 | ...options, 99 | ...additionalOpts 100 | }; 101 | 102 | const table = await createRequest( 103 | constants.api.hubdb.table, 104 | { tableId }, 105 | mergedProps 106 | ); 107 | 108 | return Promise.resolve(table); 109 | } catch (e) { 110 | return Promise.reject(e.message); 111 | } 112 | }; 113 | 114 | const addTableRow = async (tableId, body = {}) => { 115 | try { 116 | requiresAuthentication(_baseOptions); 117 | const method = 'POST'; 118 | const url = constants.api.hubdb.rows; 119 | const options = { tableId, method, body }; 120 | 121 | const mergedProps = { ...defaults, ..._baseOptions }; 122 | const add = await createRequest(url, options, mergedProps); 123 | 124 | return Promise.resolve(add); 125 | } catch (e) { 126 | return Promise.reject(e.message); 127 | } 128 | }; 129 | 130 | const updateTableRow = async (tableId, rowId, body = {}) => { 131 | try { 132 | requiresAuthentication(_baseOptions); 133 | const method = 'PUT'; 134 | const url = constants.api.hubdb.row; 135 | const options = { 136 | tableId, 137 | id: rowId, 138 | method, 139 | body 140 | }; 141 | const mergedProps = { ...defaults, ..._baseOptions }; 142 | const update = await createRequest(url, options, mergedProps); 143 | 144 | return Promise.resolve(update); 145 | } catch (e) { 146 | return Promise.reject(e.message); 147 | } 148 | }; 149 | 150 | const updateTableRows = async (tableId, body = {}) => { 151 | try { 152 | requiresAuthentication(_baseOptions); 153 | const method = 'POST'; 154 | const url = constants.api.hubdb.rowsBatchUpdate; 155 | const options = { 156 | tableId, 157 | method, 158 | body 159 | }; 160 | const mergedProps = { ...defaults, ..._baseOptions }; 161 | const update = await createRequest(url, options, mergedProps); 162 | 163 | return Promise.resolve(update); 164 | } catch (e) { 165 | return Promise.reject(e.message); 166 | } 167 | }; 168 | 169 | const deleteTableRow = async (tableId, rowId, body = {}) => { 170 | try { 171 | requiresAuthentication(_baseOptions); 172 | const method = 'DELETE'; 173 | const url = constants.api.hubdb.row; 174 | const options = { 175 | tableId, 176 | id: rowId, 177 | method, 178 | body 179 | }; 180 | const mergedProps = { ...defaults, ..._baseOptions }; 181 | const update = await createRequest(url, options, mergedProps); 182 | 183 | return Promise.resolve(update); 184 | } catch (e) { 185 | return Promise.reject(e.message); 186 | } 187 | }; 188 | 189 | export default function hubdbApi(baseOptions) { 190 | _baseOptions = baseOptions; 191 | 192 | return { 193 | /** 194 | * Create a new HubDB table 195 | * @async 196 | * @memberof hs/hubdb 197 | * @method createTable 198 | * @param {object} opts 199 | * @example 200 | * const hs = new HubSpotClient(props); 201 | * hs.hubdb.createTable(opts).then(response => console.log(response)); 202 | * @property {string} opts.name 203 | * @property {boolean} opts.useForPages 204 | * @property {array} opts.columns 205 | * @property {int} opts.publishedAt 206 | * @returns {Promise} 207 | */ 208 | createTable, 209 | /** 210 | * Get a collection of HubDB tables 211 | * @async 212 | * @memberof hs/hubdb 213 | * @method getTables 214 | * @example 215 | * const hs = new HubSpotClient(props); 216 | * hs.hubdb.getTables(opts).then(response => console.log(response)); 217 | * @returns {Promise} 218 | */ 219 | getTables, 220 | /** 221 | * Get rows in a HubDB table 222 | * @async 223 | * @memberof hs/hubdb 224 | * @method getTableRows 225 | * @param {int} tableId 226 | * @param {int} portalId 227 | * @param {object} options 228 | * @example 229 | * const hs = new HubSpotClient(props); 230 | * hs.hubdb.getTableRows(tableId, portalId, options).then(response => console.log(response)) 231 | * @returns {Promise} 232 | */ 233 | getTableRows, 234 | /** 235 | * Retrieve HubDB table by ID 236 | * @async 237 | * @memberof hs/hubdb 238 | * @method getTableById 239 | * @param {int} tableId 240 | * @param {int} portalId 241 | * @param {object} options 242 | * @example 243 | * const hs = new HubSpotClient(props); 244 | * hs.hubdb.getTableById(tableId, portalId, options).then(response => console.log(response)) 245 | * @returns {Promise} 246 | */ 247 | getTableById, 248 | /** 249 | * Add row to a HubDB table 250 | * @async 251 | * @memberof hs/hubdb 252 | * @method addTableRow 253 | * @param {int} tableId 254 | * @param {object} options 255 | * @example 256 | * const hs = new HubSpotClient(props); 257 | * hs.hubdb.addTableRow(tableId, options).then(response => console.log(response)) 258 | * @returns {Promise} 259 | */ 260 | addTableRow, 261 | /** 262 | * Update row in a HubDB table 263 | * @async 264 | * @memberof hs/hubdb 265 | * @method updateTableRow 266 | * @param {int} tableId 267 | * @param {int} rowId 268 | * @param {object} options 269 | * @example 270 | * const hs = new HubSpotClient(props); 271 | * hs.hubdb.updateTableRow(tableId, rowId, options).then(response => console.log(response)) 272 | * @returns {Promise} 273 | */ 274 | updateTableRow, 275 | /** 276 | * Batch update in a HubDB table 277 | * @async 278 | * @memberof hs/hubdb 279 | * @method updateTableRows 280 | * @param {int} tableId 281 | * @param {object} options 282 | * @param {array} rows The rows to update 283 | * @example 284 | * const hs = new HubSpotClient(props); 285 | * const options = { 286 | * rows: [ 287 | * { 288 | id: 1234567, 289 | createdAt: 1000000000, 290 | path: 'test', 291 | name: 'Test', 292 | values: { 293 | '2': 'Some data', 294 | '3': 'None', 295 | } 296 | }, 297 | ...more rows 298 | ]} 299 | * hs.hubdb.updateTableRows(tableId, options).then(response => console.log(response)) 300 | * @returns {Promise} 301 | */ 302 | updateTableRows, 303 | /** 304 | * Delete row from a HubDB table 305 | * @async 306 | * @memberof hs/hubdb 307 | * @method deleteTableRow 308 | * @param {int} tableId 309 | * @param {int} rowId 310 | * @param {object} options 311 | * @example 312 | * const hs = new HubSpotClient(props); 313 | * hs.hubdb.deleteTableRow(tableId, rowId, options).then(response => console.log(response)) 314 | * @returns {Promise} 315 | */ 316 | deleteTableRow, 317 | /** 318 | * Publish a draft table 319 | * @async 320 | * @memberof hs/hubdb 321 | * @method publishTable 322 | * @param {int} tableId 323 | * @example 324 | * const hs = new HubSpotClient(props); 325 | * hs.hubdb.publishTable(tableId).then(response => console.log(response)) 326 | * @returns {Promise} 327 | */ 328 | publishTable 329 | }; 330 | } 331 | -------------------------------------------------------------------------------- /src/entities/layouts.js: -------------------------------------------------------------------------------- 1 | import createRequest, { 2 | queryStringParamInterpolator, 3 | requiresAuthentication 4 | } from '../utilities'; 5 | import constants from '../constants'; 6 | 7 | const defaults = {}; 8 | let _baseOptions; 9 | 10 | const getLayouts = async (opts = {}) => { 11 | try { 12 | requiresAuthentication(_baseOptions); 13 | const { 14 | limit, 15 | offset, 16 | category_id, 17 | created, 18 | deleted_at, 19 | id, 20 | label, 21 | path, 22 | custom_head, 23 | include_default_custom_css, 24 | enable_domain_stylesheet, 25 | attached_stylesheets 26 | } = opts; 27 | let additionalOpts = { 28 | limit, 29 | offset, 30 | category_id, 31 | created, 32 | deleted_at, 33 | id, 34 | label, 35 | path, 36 | custom_head, 37 | include_default_custom_css, 38 | enable_domain_stylesheet, 39 | attached_stylesheets 40 | }; 41 | // Extract additional dynamic querystring params and values. 42 | additionalOpts = queryStringParamInterpolator( 43 | { 44 | created 45 | }, 46 | additionalOpts 47 | ); 48 | 49 | const mergedProps = Object.assign( 50 | {}, 51 | defaults, 52 | _baseOptions, 53 | additionalOpts 54 | ); 55 | const layouts = await createRequest( 56 | constants.api.layouts.getAll, 57 | {}, 58 | mergedProps 59 | ); 60 | return Promise.resolve(layouts); 61 | } catch (e) { 62 | return Promise.reject(e.message); 63 | } 64 | }; 65 | 66 | const getLayout = async id => { 67 | try { 68 | requiresAuthentication(_baseOptions); 69 | if (!id) { 70 | throw new Error('getLayout requires an `id` argument'); 71 | } 72 | const mergedProps = Object.assign({}, defaults, _baseOptions); 73 | const layoutInfo = await createRequest( 74 | constants.api.layouts.byId, 75 | { id }, 76 | mergedProps 77 | ); 78 | return Promise.resolve(layoutInfo); 79 | } catch (e) { 80 | return Promise.reject(e.message); 81 | } 82 | }; 83 | 84 | const getLayoutBuffer = async id => { 85 | try { 86 | requiresAuthentication(_baseOptions); 87 | if (!id) { 88 | throw new Error('getLayoutBuffer requires an `id` argument'); 89 | } 90 | const mergedProps = Object.assign({}, defaults, _baseOptions); 91 | const layoutBuffer = await createRequest( 92 | constants.api.layouts.getBuffer, 93 | { id }, 94 | mergedProps 95 | ); 96 | return Promise.resolve(layoutBuffer); 97 | } catch (e) { 98 | return Promise.reject(e.message); 99 | } 100 | }; 101 | 102 | const hasBufferedChanges = async id => { 103 | try { 104 | requiresAuthentication(_baseOptions); 105 | if (!id) { 106 | throw new Error('hasBufferedChanges requires an `id` argument'); 107 | } 108 | const mergedProps = Object.assign({}, defaults, _baseOptions); 109 | const result = await createRequest( 110 | constants.api.layouts.hasBufferedChanges, 111 | { id }, 112 | mergedProps 113 | ); 114 | return Promise.resolve(result); 115 | } catch (e) { 116 | return Promise.reject(e.message); 117 | } 118 | }; 119 | 120 | const getPreviousLayoutVersions = async id => { 121 | try { 122 | requiresAuthentication(_baseOptions); 123 | if (!id) { 124 | throw new Error('getPreviousLayoutVersions requires an `id` argument'); 125 | } 126 | const mergedProps = Object.assign({}, defaults, _baseOptions); 127 | const previousVersions = await createRequest( 128 | constants.api.layouts.getPreviousVersions, 129 | { id }, 130 | mergedProps 131 | ); 132 | return Promise.resolve(previousVersions); 133 | } catch (e) { 134 | return Promise.reject(e.message); 135 | } 136 | }; 137 | 138 | const getPreviousLayoutVersion = async ({ id, versionId }) => { 139 | try { 140 | requiresAuthentication(_baseOptions); 141 | if (!id || !versionId) { 142 | throw new Error('getPreviousLayoutVersion requires the first argument to contain both keys for `id` & `versionId`'); 143 | } 144 | const mergedProps = Object.assign({}, defaults, _baseOptions); 145 | const previousVersions = await createRequest( 146 | constants.api.layouts.getPreviousVersions, 147 | { id, versionId }, 148 | mergedProps 149 | ); 150 | return Promise.resolve(previousVersions); 151 | } catch (e) { 152 | return Promise.reject(e.message); 153 | } 154 | }; 155 | 156 | export default function layoutsApi(baseOptions) { 157 | _baseOptions = baseOptions; 158 | 159 | return { 160 | /** 161 | * Get all layouts for a portal 162 | * @async 163 | * @memberof hs/layouts 164 | * @method getLayouts 165 | * @param {object} opts 166 | * @example 167 | * const hs = new HubspotClient(props); 168 | * hs.layouts.getLayouts(opts).then(response => console.log(response)); 169 | * @property {int} opts.limit 170 | * @property {int} opts.offset 171 | * @property {int} opts.category_id 172 | * @property {int} opts.created 173 | * @property {int} opts.deleted_at 174 | * @property {int} opts.id 175 | * @property {string} opts.label Find layouts matching this label. 176 | * @property {string} opts.path 177 | * @property {string} opts.custom_head 178 | * @property {boolean} opts.include_default_custom_css 179 | * @property {boolean} opts.enable_domain_stylesheet 180 | * @property {string} opts.attached_stylesheets 181 | * @returns {Promise} 182 | */ 183 | getLayouts, 184 | /** 185 | * Get layout info by ID 186 | * @async 187 | * @memberof hs/layouts 188 | * @method getLayout 189 | * @param {int} layoutId 190 | * @example 191 | * const hs = new HubspotClient(props); 192 | * hs.layouts.getLayout(layoutId).then(response => console.log(response)); 193 | * @returns {Promise} 194 | */ 195 | getLayout, 196 | /** 197 | * Gets the current contents of the auto-save buffer 198 | * @async 199 | * @memberof hs/layouts 200 | * @method getLayoutBuffer 201 | * @param {int} layoutId 202 | * @example 203 | * const hs = new HubspotClient(props); 204 | * hs.layout.getLayoutBuffer(layoutId).then(response => console.log(response)); 205 | * @return {Promise} 206 | */ 207 | getLayoutBuffer, 208 | /** 209 | * Returns a dictionary: {"has_changes": false/true} depending on if the buffer is different from the live object. 210 | * @async 211 | * @memberof hs/layouts 212 | * @method hasBufferedChanges 213 | * @param {int} layoutId 214 | * @example 215 | * const hs = new HubspotClient(props); 216 | * hs.layout.hasBufferedChanges(layoutId).then(response => console.log(response)); 217 | * @return {Promise} 218 | */ 219 | hasBufferedChanges, 220 | /** 221 | * Get the previous revisions for a specific layout, specified by ID. 222 | * @async 223 | * @memberof hs/layouts 224 | * @method getPreviousLayoutVersions 225 | * @param {int} layoutId 226 | * @example 227 | * const hs = new HubspotClient(props); 228 | * hs.layout.getPreviousLayoutVersions(layoutId).then(response => console.log(response)); 229 | * @return {Promise} 230 | */ 231 | getPreviousLayoutVersions, 232 | /** 233 | * Get a specific revision of a specific layout. Version id is the id of the version from the list previous versions endpoint 234 | * @async 235 | * @memberof hs/layouts 236 | * @method getPreviousLayoutVersion 237 | * @param {object} opts 238 | * @param {int} opts.id - layoutId 239 | * @param {int} opts.versionId - id of the versionm from the list previous versions endpoint 240 | * @example 241 | * const hs = new HubspotClient(props); 242 | * hs.layout.getPreviousLayoutVersion({ id , versionId }).then(response => console.log(response)); 243 | * @return {Promise} 244 | */ 245 | getPreviousLayoutVersion, 246 | }; 247 | } 248 | -------------------------------------------------------------------------------- /src/entities/oauth.js: -------------------------------------------------------------------------------- 1 | import createRequest from '../utilities'; 2 | import constants from '../constants'; 3 | 4 | const getTokenInfo = async token => { 5 | try { 6 | const info = await createRequest( 7 | constants.api.oauth.tokenInfo, 8 | { token } 9 | ); 10 | return Promise.resolve(info); 11 | } catch (e) { 12 | return Promise.reject(e.message); 13 | } 14 | }; 15 | 16 | export default function oauthApi() { 17 | return { 18 | /** 19 | * Get the meta data for an access token. This can be used to get the email address of the HubSpot * user that the token was created for. 20 | * @memberof hs/oauth 21 | * @method getTokenInfo 22 | * @param {string} token The access token that you want to get the information for. 23 | * @example 24 | * const hs = new HubspotClient(props); 25 | * hs.oauth.getTokenInfo(token).then(response => console.log(response)); 26 | * @returns {Promise} 27 | */ 28 | getTokenInfo 29 | }; 30 | } 31 | -------------------------------------------------------------------------------- /src/entities/social.js: -------------------------------------------------------------------------------- 1 | import createRequest, { requiresAuthentication } from '../utilities'; 2 | import constants from '../constants'; 3 | 4 | const defaults = {}; 5 | let _baseOptions; 6 | 7 | const getPublishingChannels = async () => { 8 | try { 9 | requiresAuthentication(_baseOptions); 10 | const mergedProps = Object.assign({}, defaults, _baseOptions); 11 | const publishingChannels = await createRequest( 12 | constants.api.social.channels, 13 | {}, 14 | mergedProps 15 | ); 16 | return Promise.resolve(publishingChannels); 17 | } catch (e) { 18 | return Promise.reject(e.message); 19 | } 20 | }; 21 | 22 | const createBroadcastMessage = async (opts = {}) => { 23 | try { 24 | requiresAuthentication(_baseOptions); 25 | const { channelGuid, status, triggerAt, body, photoUrl } = opts; 26 | 27 | const method = 'POST'; 28 | const requestBody = { 29 | channelGuid, 30 | triggerAt, 31 | content: { 32 | body, 33 | photoUrl 34 | }, 35 | status 36 | }; 37 | 38 | const mergedProps = Object.assign({}, defaults, _baseOptions); 39 | await createRequest( 40 | constants.api.social.createBroadcastMessage, 41 | { 42 | method, 43 | body: requestBody 44 | }, 45 | mergedProps 46 | ); 47 | return Promise.resolve({ status, scheduled: true }); 48 | } catch (e) { 49 | return Promise.reject(e.message); 50 | } 51 | }; 52 | 53 | export default function socialApi(baseOptions) { 54 | _baseOptions = baseOptions; 55 | 56 | return { 57 | /** 58 | * Get publishing channels for selected portal 59 | * @async 60 | * @memberof hs/social 61 | * @method getPublishingChannels 62 | * @example 63 | * const hs = new HubspotClient(props); 64 | * hs.social.getPublishingChannels().then(response => console.log(response)); 65 | * @returns {Promise} 66 | */ 67 | getPublishingChannels, 68 | /** 69 | * Create a broadcast message 70 | * @async 71 | * @memberof hs/social 72 | * @method createBroadcastMessage 73 | * @param {object} opts 74 | * @example 75 | * const hs = new HubspotClient(props); 76 | * hs.social.createBroadcastMessage(opts).then(response => console.log(response)); 77 | * @property {string} opts.channelGuid 78 | * @property {string} opts.status 79 | * @property {int} opts.triggerAt 80 | * @property {string} opts.body 81 | * @property {string} opts.photoUrl 82 | * @returns {Promise} 83 | */ 84 | createBroadcastMessage 85 | }; 86 | } 87 | -------------------------------------------------------------------------------- /src/entities/templates.js: -------------------------------------------------------------------------------- 1 | import createRequest, { requiresAuthentication } from '../utilities'; 2 | import constants from '../constants'; 3 | 4 | const defaults = {}; 5 | let _baseOptions; 6 | 7 | const updateTemplate = async (templateId, body) => { 8 | try { 9 | requiresAuthentication(_baseOptions); 10 | const method = 'PUT'; 11 | const options = { method, body, templateId }; 12 | const mergedProps = { ...defaults, ..._baseOptions }; 13 | const update = await createRequest( 14 | constants.api.templates.byId, 15 | options, 16 | mergedProps 17 | ); 18 | return Promise.resolve(update); 19 | } catch (e) { 20 | return Promise.reject(e.message); 21 | } 22 | }; 23 | 24 | const createTemplate = async body => { 25 | try { 26 | requiresAuthentication(_baseOptions); 27 | const method = 'POST'; 28 | const options = { method, body }; 29 | const mergedProps = { ...defaults, ..._baseOptions }; 30 | const update = await createRequest( 31 | constants.api.templates.base, 32 | options, 33 | mergedProps 34 | ); 35 | return Promise.resolve(update); 36 | } catch (e) { 37 | return Promise.reject(e.message); 38 | } 39 | }; 40 | 41 | const deleteTemplate = async templateId => { 42 | try { 43 | requiresAuthentication(_baseOptions); 44 | const method = 'DELETE'; 45 | const options = { method, templateId }; 46 | const mergedProps = { ...defaults, ..._baseOptions }; 47 | await createRequest(constants.api.templates.byId, options, mergedProps); 48 | 49 | return Promise.resolve({ deleted: true }); 50 | } catch (e) { 51 | return Promise.reject(e.message); 52 | } 53 | }; 54 | 55 | const getTemplate = async templateId => { 56 | try { 57 | requiresAuthentication(_baseOptions); 58 | const method = 'GET'; 59 | const options = { method, templateId }; 60 | const mergedProps = { ...defaults, ..._baseOptions }; 61 | const template = await createRequest( 62 | constants.api.templates.byId, 63 | options, 64 | mergedProps 65 | ); 66 | return Promise.resolve(template); 67 | } catch (e) { 68 | return Promise.reject(e.message); 69 | } 70 | }; 71 | 72 | const getTemplatePages = async templateId => { 73 | try { 74 | requiresAuthentication(_baseOptions); 75 | const method = 'GET'; 76 | const options = { method, templateId }; 77 | const mergedProps = { ...defaults, ..._baseOptions }; 78 | const update = await createRequest( 79 | constants.api.templates.pages, 80 | options, 81 | mergedProps 82 | ); 83 | return Promise.resolve(update); 84 | } catch (e) { 85 | return Promise.reject(e.message); 86 | } 87 | }; 88 | 89 | const getTemplates = async (opts = {}) => { 90 | try { 91 | requiresAuthentication(_baseOptions); 92 | const { 93 | limit, 94 | offset, 95 | deleted_at, 96 | id, 97 | is_available_for_new_content, 98 | label, 99 | path 100 | } = opts; 101 | 102 | const additionalOpts = { 103 | limit, 104 | offset, 105 | deleted_at, 106 | id, 107 | is_available_for_new_content, 108 | label, 109 | path 110 | }; 111 | 112 | const mergedProps = { 113 | ...defaults, 114 | ..._baseOptions, 115 | ...additionalOpts 116 | }; 117 | const method = 'GET'; 118 | const options = { method }; 119 | const templates = await createRequest( 120 | constants.api.templates.base, 121 | options, 122 | mergedProps 123 | ); 124 | return Promise.resolve(templates); 125 | } catch (e) { 126 | return Promise.reject(e); 127 | } 128 | }; 129 | 130 | const updateAutosaveBuffer = async (templateId, body) => { 131 | try { 132 | requiresAuthentication(_baseOptions); 133 | const method = 'PUT'; 134 | const options = { method, templateId, body }; 135 | const mergedProps = { ...defaults, ..._baseOptions }; 136 | const updatedBuffer = await createRequest( 137 | constants.api.templates.buffer, 138 | options, 139 | mergedProps 140 | ); 141 | return Promise.resolve(updatedBuffer); 142 | } catch (e) { 143 | return Promise.reject(e.message); 144 | } 145 | }; 146 | 147 | const getUpdatedAutosaveBuffer = async templateId => { 148 | try { 149 | requiresAuthentication(_baseOptions); 150 | const options = { templateId }; 151 | const mergedProps = { ...defaults, ..._baseOptions }; 152 | const updatedBuffer = await createRequest( 153 | constants.api.templates.buffer, 154 | options, 155 | mergedProps 156 | ); 157 | return Promise.resolve(updatedBuffer); 158 | } catch (e) { 159 | return Promise.reject(e.message); 160 | } 161 | }; 162 | 163 | const hasBufferedChanges = async templateId => { 164 | try { 165 | requiresAuthentication(_baseOptions); 166 | const options = { templateId }; 167 | const mergedProps = { ...defaults, ..._baseOptions }; 168 | const updatedBuffer = await createRequest( 169 | constants.api.templates.hasBufferedChanges, 170 | options, 171 | mergedProps 172 | ); 173 | 174 | return updatedBuffer.has_changes; 175 | } catch (e) { 176 | return Promise.reject(e.message); 177 | } 178 | }; 179 | 180 | const pushBufferedChangesLive = async templateId => { 181 | try { 182 | requiresAuthentication(_baseOptions); 183 | const options = { method: 'POST', templateId }; 184 | const mergedProps = { ...defaults, ..._baseOptions }; 185 | await createRequest( 186 | constants.api.templates.pushBufferLive, 187 | options, 188 | mergedProps 189 | ); 190 | return true; 191 | } catch (e) { 192 | return Promise.reject(e.message); 193 | } 194 | }; 195 | 196 | const getVersions = async templateId => { 197 | try { 198 | requiresAuthentication(_baseOptions); 199 | const options = { templateId }; 200 | const mergedProps = { ...defaults, ..._baseOptions }; 201 | const versions = await createRequest( 202 | constants.api.templates.versions, 203 | options, 204 | mergedProps 205 | ); 206 | return versions; 207 | } catch (e) { 208 | return Promise.reject(e.message); 209 | } 210 | }; 211 | 212 | const getVersion = async (templateId, versionId) => { 213 | try { 214 | requiresAuthentication(_baseOptions); 215 | const options = { templateId, versionId }; 216 | const mergedProps = { ...defaults, ..._baseOptions }; 217 | await createRequest(constants.api.templates.version, options, mergedProps); 218 | return true; 219 | } catch (e) { 220 | return Promise.reject(e.message); 221 | } 222 | }; 223 | 224 | const restoreVersion = async (templateId, version_id) => { 225 | try { 226 | requiresAuthentication(_baseOptions); 227 | const options = { method: 'POST', templateId, body: { version_id } }; 228 | const mergedProps = { ...defaults, ..._baseOptions }; 229 | const response = await createRequest(constants.api.templates.restore, options, mergedProps); 230 | return response; 231 | } catch (e) { 232 | return Promise.reject(e.message); 233 | } 234 | }; 235 | 236 | export default function templatesApi(baseOptions) { 237 | _baseOptions = baseOptions; 238 | return { 239 | /** 240 | * Update a template for a portal 241 | * @async 242 | * @memberof hs/templates 243 | * @method updateTemplate 244 | * @param {object} opts 245 | * @example 246 | * const hs = new HubSpotClient(props); 247 | * hs.templates.updateTemplate(opts).then(response => console.log(response)); 248 | * @property {string} opts.template_id 249 | * @property {string} opts.source 250 | */ 251 | updateTemplate, 252 | /** 253 | * Create a template for a portal (see https://developers.hubspot.com/docs/methods/templates/post_templates) 254 | * @async 255 | * @memberof hs/templates 256 | * @method createTemplate 257 | * @param {object} opts 258 | * @example 259 | * const hs = new HubSpotClient(props); 260 | * hs.templates.createTemplate(opts).then(response => console.log(response)); 261 | * @property {number} opts.category_id 262 | * @property {string} opts.folder 263 | * @property {number} opts.template_type 264 | * @property {string} opts.path 265 | * @property {string} opts.source 266 | * @property {string} opts.is_available_for_new_content 267 | */ 268 | createTemplate, 269 | /** 270 | * Delete a template by id (see https://developers.hubspot.com/docs/methods/templates/delete_templates_template_id) 271 | * @async 272 | * @memberof hs/templates 273 | * @method deleteTemplate 274 | * @param {number} id 275 | * @example 276 | * const hs = new HubSpotClient(props); 277 | * hs.templates.deleteTemplate(id).then(response => console.log(response)); 278 | */ 279 | deleteTemplate, 280 | /** 281 | * Get a template by id (see https://developers.hubspot.com/docs/methods/templates/get_templates_template_id) 282 | * @async 283 | * @memberof hs/templates 284 | * @method getTemplate 285 | * @param {number} id 286 | * @example 287 | * const hs = new HubSpotClient(props); 288 | * hs.templates.getTemplate(id).then(response => console.log(response)); 289 | */ 290 | getTemplate, 291 | /** 292 | * Get the list of pages from a template by id 293 | * @async 294 | * @memberof hs/templates 295 | * @method getTemplatePages 296 | * @param {number} id 297 | * @example 298 | * const hs = new HubSpotClient(props); 299 | * hs.templates.getTemplatePages(id).then(response => console.log(response)); 300 | */ 301 | getTemplatePages, 302 | /** 303 | * Get the list of templates 304 | * @async 305 | * @memberof hs/templates 306 | * @method getTemplates 307 | * @param {object} opts 308 | * @example 309 | * const hs = new HubSpotClient(props); 310 | * const opts = { limit: 10 }; 311 | * // Get the first 10 templates 312 | * hs.templates.getTemplates(opts).then(response => console.log(response)); 313 | * @property {string} opts.limit 314 | * @property {string} opts.offset 315 | * @property {string} opts.deleted_at 316 | * @property {int} opts.id 317 | * @property {boolean} opts.is_available_for_new_content 318 | * @property {string} opts.label 319 | * @property {string} opts.path 320 | */ 321 | getTemplates, 322 | /** 323 | * Update the autosave buffer for a template (see https://developers.hubspot.com/docs/methods/templates/put_templates_template_id_buffer) 324 | * @async 325 | * @memberof hs/templates 326 | * @method updateAutosaveBuffer 327 | * @param {number} id 328 | * @param {object} body 329 | * @example 330 | * const hs = new HubSpotClient(props); 331 | * hs.templates.updateAutosaveBuffer(id, body).then(response => console.log(response)); 332 | */ 333 | updateAutosaveBuffer, 334 | /** 335 | * Get the autosave buffer for a template (see https://developers.hubspot.com/docs/methods/templates/put_templates_template_id_buffer) 336 | * @async 337 | * @memberof hs/templates 338 | * @method getUpdatedAutosaveBuffer 339 | * @param {number} id 340 | * @param {object} body 341 | * @example 342 | * const hs = new HubSpotClient(props); 343 | * hs.templates.getUpdatedAutosaveBuffer(id).then(response => console.log(response)); 344 | * @property {number} id 345 | */ 346 | getUpdatedAutosaveBuffer, 347 | /** 348 | * Determine if the auto-save buffer differs from the live Template (see https://developers.hubspot.com/docs/methods/templates/get_templates_template_id_has_buffered_changes) 349 | * @async 350 | * @memberof hs/templates 351 | * @method hasBufferedChanges 352 | * @param {number} id 353 | * @returns {boolean} 354 | * @example 355 | * const hs = new HubSpotClient(props); 356 | * hs.templates.hasBufferedChanges(id).then(response => console.log(response)); 357 | */ 358 | hasBufferedChanges, 359 | /** 360 | * Copies the contents of the auto-save buffer into the live Template (see https://developers.hubspot.com/docs/methods/templates/post_templates_template_id_push_buffer_live) 361 | * @async 362 | * @memberof hs/templates 363 | * @method pushBufferedChangesLive 364 | * @param {number} id 365 | * @example 366 | * const hs = new HubSpotClient(props); 367 | * hs.templates.pushBufferedChangesLive(id).then(response => console.log(response)); 368 | */ 369 | pushBufferedChangesLive, 370 | /** 371 | * List previous versions of the Template (see https://developers.hubspot.com/docs/methods/templates/get_templates_template_id_versions) 372 | * @async 373 | * @memberof hs/templates 374 | * @method getVersions 375 | * @param {number} id 376 | * @returns {array} 377 | * @example 378 | * const hs = new HubSpotClient(props); 379 | * hs.templates.getVersions(id).then(response => console.log(response)); 380 | */ 381 | getVersions, 382 | /** 383 | * Get the previous version of the Template(see https://developers.hubspot.com/docs/methods/templates/get_templates_template_id_versions_version_id) 384 | * @async 385 | * @memberof hs/templates 386 | * @method getVersion 387 | * @param {number} templateId 388 | * @param {number} versionId 389 | * @returns {object} 390 | * @example 391 | * const hs = new HubSpotClient(props); 392 | * hs.templates.getVersion(templateId, versionId).then(response => console.log(response)); 393 | */ 394 | getVersion, 395 | /** 396 | * Restore a previous version of the Template (see https://developers.hubspot.com/docs/methods/templates/post_templates_template_id_versions_restore) 397 | * @async 398 | * @memberof hs/templates 399 | * @method restoreVersion 400 | * @param {number} templateId 401 | * @param {number} versionId 402 | * @returns {object} 403 | * @example 404 | * const hs = new HubSpotClient(props); 405 | * hs.templates.restoreVersion(templateId, versionId).then(response => console.log(response)); 406 | */ 407 | restoreVersion 408 | }; 409 | } 410 | -------------------------------------------------------------------------------- /src/entities/transactional-emails.js: -------------------------------------------------------------------------------- 1 | 2 | import createRequest, { requiresAuthentication } from '../utilities'; 3 | import constants from '../constants'; 4 | 5 | const defaults = {}; 6 | let _baseOptions; 7 | 8 | const singleSend = async (emailId, message, contactProperties, customProperties) => { 9 | try { 10 | requiresAuthentication(_baseOptions); 11 | const method = 'POST'; 12 | const mergedProps = {...defaults, ..._baseOptions}; 13 | 14 | const response = await createRequest( 15 | constants.api.transactionalEmail.singleSend, 16 | { 17 | method, 18 | body: { 19 | emailId, 20 | message, 21 | contactProperties, 22 | customProperties 23 | } 24 | }, 25 | mergedProps 26 | ); 27 | return Promise.resolve(response); 28 | } catch (e) { 29 | return Promise.reject(e); 30 | } 31 | }; 32 | 33 | export default function transactionalEmails(baseOptions) { 34 | _baseOptions = baseOptions; 35 | 36 | return { 37 | /** 38 | * Send an email designed and maintained in the HubSpot marketing Email Tool. 39 | * See the {@link https://developers.hubspot.com/docs/methods/email/transactional_email/single-send-overview|developer docs} for full spec. 40 | * @async 41 | * @memberof hs/transactionalEmails 42 | * @method singleSend 43 | * @param {int} emailId The ID of the email to send, from the Email Tool when viewing a transactional email. 44 | * @param {string} message A JSON object holding the recipient (in its to field) as well as other customizations that can be made on the fly. 45 | * @param {object} contactProperties A list of JSON objects representing contact property values to set when sending the email. 46 | * @param {object} customProperties A list of JSON objects representing property values to set when sending the email. 47 | * @example 48 | * const hs = new HubspotClient(props); 49 | * hs.transactionalEmails.singleSend(emailId, message, contactProperties, customProperties).then(response => console.log(response)); 50 | * @returns {Promise} 51 | */ 52 | singleSend 53 | }; 54 | } 55 | -------------------------------------------------------------------------------- /src/entities/url-mappings.js: -------------------------------------------------------------------------------- 1 | import createRequest, { 2 | requiresAuthentication 3 | } from '../utilities'; 4 | import constants from '../constants'; 5 | 6 | const defaults = {}; 7 | let _baseOptions; 8 | 9 | const createOrUpdateUrlMapping = async (opts = {}) => { 10 | try { 11 | requiresAuthentication(_baseOptions); 12 | const { 13 | routePrefix, 14 | destination, 15 | redirectStyle, 16 | isMatchFullUrl, 17 | isMatchQueryString, 18 | isOnlyAfterNotFound, 19 | isPattern, 20 | precedence 21 | } = opts; 22 | 23 | const body = { 24 | routePrefix, 25 | destination, 26 | redirectStyle, 27 | isMatchFullUrl, 28 | isMatchQueryString, 29 | isOnlyAfterNotFound, 30 | isPattern, 31 | precedence 32 | }; 33 | 34 | let method = 'POST'; 35 | let url = constants.api.urlMappings.create; 36 | 37 | const options = { method, body }; 38 | if (id) { 39 | method = 'PUT'; 40 | url = constants.api.urlMappings.byId; 41 | Object.assign(options, { method, id }); 42 | } 43 | 44 | const mergedProps = Object.assign({}, defaults, _baseOptions); 45 | const update = await createRequest(url, options, mergedProps); 46 | 47 | return Promise.resolve(update); 48 | } catch (e) { 49 | return Promise.reject(e.message); 50 | } 51 | }; 52 | 53 | const getUrlMappings = async (opts = {}) => { 54 | try { 55 | requiresAuthentication(_baseOptions); 56 | const { 57 | limit, 58 | offset, 59 | id, 60 | routePrefix, 61 | destination 62 | } = opts; 63 | let additionalOpts = { 64 | limit, 65 | offset, 66 | id, 67 | routePrefix, 68 | destination 69 | }; 70 | 71 | const mergedProps = Object.assign( 72 | {}, 73 | defaults, 74 | _baseOptions, 75 | additionalOpts 76 | ); 77 | const urlMappings = await createRequest( 78 | constants.api.urlMappings.getAll, 79 | {}, 80 | mergedProps 81 | ); 82 | 83 | return Promise.resolve(urlMappings); 84 | } catch (e) { 85 | return Promise.reject(e.message); 86 | } 87 | }; 88 | 89 | const getUrlMappingById = async id => { 90 | try { 91 | requiresAuthentication(_baseOptions); 92 | if (!id) { 93 | throw new Error('getUrlMappingById requires an `id` argument'); 94 | } 95 | const mergedProps = Object.assign({}, defaults, _baseOptions); 96 | const urlMapping = await createRequest( 97 | constants.api.urlMappings.byId, 98 | { id }, 99 | mergedProps 100 | ); 101 | return Promise.resolve(urlMapping); 102 | } catch (e) { 103 | return Promise.reject(e.message); 104 | } 105 | }; 106 | 107 | const deleteUrlMapping = async id => { 108 | try { 109 | requiresAuthentication(_baseOptions); 110 | const mergedProps = Object.assign({}, defaults, _baseOptions); 111 | await createRequest( 112 | constants.api.pages.byId, 113 | { url_mapping_id, method: 'DELETE' }, 114 | mergedProps 115 | ); 116 | return Promise.resolve({ deleted: true }); 117 | } catch (e) { 118 | return Promise.reject(e.message); 119 | } 120 | }; 121 | 122 | export default function urlMappingApi(baseOptions) { 123 | _baseOptions = baseOptions; 124 | 125 | return { 126 | /** 127 | * Create a new url mapping or update an existing url mapping 128 | * @memberof hs/urlMappings 129 | * @method createOrUpdateUrlMapping 130 | * @param {object} opts 131 | * @example 132 | * const hs = new HubspotClient(props); 133 | * hs.pages.createOrUpdateUrlMapping(opts).then(response => console.log(response)); 134 | * @property {int} opts.id If set, this will update the page with the corresponding ID. 135 | * @property {int} opts.routePrefix, 136 | * @property {int} opts.destination, 137 | * @property {int} opts.redirectStyle, 138 | * @property {int} opts.isMatchFullUrl, 139 | * @property {int} opts.isMatchQueryString, 140 | * @property {int} opts.isOnlyAfterNotFound, 141 | * @property {int} opts.isPattern, 142 | * @property {int} opts.precedence 143 | * @returns {Promise} 144 | */ 145 | createOrUpdateUrlMapping, 146 | /** 147 | * Get URL Mappings for a portal 148 | * @async 149 | * @memberof hs/urlMappings 150 | * @method getUrlMappings 151 | * @param {object} opts 152 | * @example 153 | * const hs = new HubspotClient(props); 154 | * @property {int} opts.limit, 155 | * @property {int} opts.offset, 156 | * @property {int} opts.id, 157 | * @property {int} opts.routePrefix, 158 | * @property {int} opts.destination 159 | * @returns {Promise} 160 | */ 161 | getUrlMappings, 162 | /** 163 | * Get URL Mapping by ID 164 | * @async 165 | * @memberof hs/urlMappings 166 | * @method getUrlMapping 167 | * @param {int} UrlMappingId 168 | * @example 169 | * const hs = new HubspotClient(props); 170 | * hs.urlMappings.getUrlMapping(UrlMappingId).then(response => console.log(response)); 171 | * @returns {Promise} 172 | */ 173 | getUrlMappingById, 174 | /** 175 | * Retrieve page info by ID 176 | * @async 177 | * @memberof hs/urlMappings 178 | * @method deleteUrlMapping 179 | * @param {int} id 180 | * @example 181 | * const hs = new HubspotClient(props); 182 | * hs.pages.deleteUrlMapping(id).then(response => console.log(response)) 183 | * @returns {Promise} 184 | */ 185 | deleteUrlMapping 186 | }; 187 | } -------------------------------------------------------------------------------- /src/entities/workflows.js: -------------------------------------------------------------------------------- 1 | import createRequest, { requiresAuthentication } from '../utilities'; 2 | import constants from '../constants'; 3 | 4 | const defaults = {}; 5 | let _baseOptions; 6 | 7 | const createWorkflow = async (opts = {}) => { 8 | try { 9 | requiresAuthentication(_baseOptions); 10 | const { 11 | type, 12 | name, 13 | actions, 14 | description, 15 | enabled, 16 | portalId, 17 | isSegmentBased, 18 | listening, 19 | nurtureTimeRange, 20 | onlyExecOnBizDays, 21 | insertedAt, 22 | updatedAt, 23 | recurringSetting, 24 | enrollOnCriteriaUpdate, 25 | onlyEnrollsManually, 26 | creationSource, 27 | updateSource, 28 | allowContactToTriggerMultipleTimes, 29 | unenrollmentSetting, 30 | segmentCriteria, 31 | goalCriteria, 32 | reEnrollmentTriggerSets, 33 | triggerSets, 34 | suppressionListIds, 35 | lastUpdatedBy, 36 | metaData 37 | } = opts; 38 | const body = { 39 | type, 40 | name, 41 | actions, 42 | description, 43 | enabled, 44 | portalId, 45 | isSegmentBased, 46 | listening, 47 | nurtureTimeRange, 48 | onlyExecOnBizDays, 49 | insertedAt, 50 | updatedAt, 51 | recurringSetting, 52 | enrollOnCriteriaUpdate, 53 | onlyEnrollsManually, 54 | creationSource, 55 | updateSource, 56 | allowContactToTriggerMultipleTimes, 57 | unenrollmentSetting, 58 | segmentCriteria, 59 | goalCriteria, 60 | reEnrollmentTriggerSets, 61 | triggerSets, 62 | suppressionListIds, 63 | lastUpdatedBy, 64 | metaData 65 | }; 66 | 67 | const mergedProps = Object.assign({}, defaults, _baseOptions); 68 | const method = 'POST'; 69 | 70 | const workflowInfo = await createRequest( 71 | constants.api.workflows.create, 72 | { 73 | method, 74 | body 75 | }, 76 | mergedProps 77 | ); 78 | return Promise.resolve(workflowInfo); 79 | } catch (e) { 80 | return Promise.reject(e.message); 81 | } 82 | }; 83 | 84 | const getWorkflow = async id => { 85 | try { 86 | requiresAuthentication(_baseOptions); 87 | const mergedProps = Object.assign({}, defaults, _baseOptions); 88 | if (!id) { 89 | throw new Error('getWorkflow requires an `id` argument'); 90 | } 91 | const workflowInfo = await createRequest( 92 | constants.api.workflows.byId, 93 | { 94 | id 95 | }, 96 | mergedProps 97 | ); 98 | return Promise.resolve(workflowInfo); 99 | } catch (e) { 100 | return Promise.reject(e.message); 101 | } 102 | }; 103 | 104 | const deleteWorkflow = async id => { 105 | try { 106 | requiresAuthentication(_baseOptions); 107 | const mergedProps = Object.assign({}, defaults, _baseOptions); 108 | if (!id) { 109 | throw new Error('deleteWorkflow requires an `id` argument'); 110 | } 111 | await createRequest( 112 | constants.api.workflows.byId, 113 | { 114 | method: 'DELETE', 115 | id 116 | }, 117 | mergedProps 118 | ); 119 | return Promise.resolve({ deleted: true }); 120 | } catch (e) { 121 | return Promise.reject(e.message); 122 | } 123 | }; 124 | 125 | const updateWorkflow = async (opts = {}) => { 126 | try { 127 | requiresAuthentication(_baseOptions); 128 | const mergedProps = Object.assign({}, defaults, _baseOptions); 129 | const body = Object.assign({}, opts); 130 | const method = 'PUT'; 131 | const { id, portalId } = opts; 132 | if (!id || !portalId) { 133 | throw new Error( 134 | 'Workflow payload requires an `id` and `portalId` property' 135 | ); 136 | } 137 | 138 | const workflowInfo = await createRequest( 139 | constants.api.workflows.byId, 140 | { 141 | method, 142 | body, 143 | id 144 | }, 145 | mergedProps 146 | ); 147 | return Promise.resolve(workflowInfo); 148 | } catch (e) { 149 | return Promise.reject(e.message); 150 | } 151 | }; 152 | 153 | const getAll = async () => { 154 | try { 155 | requiresAuthentication(_baseOptions); 156 | const mergedProps = Object.assign({}, defaults, _baseOptions); 157 | const allWorkflows = await createRequest( 158 | constants.api.workflows.getAll, 159 | {}, 160 | mergedProps 161 | ); 162 | return Promise.resolve(allWorkflows); 163 | } catch (e) { 164 | return Promise.reject(e.message); 165 | } 166 | }; 167 | 168 | const enrollContact = async (opts = {}) => { 169 | try { 170 | requiresAuthentication(_baseOptions); 171 | const { workflowId, email } = opts; 172 | const method = 'POST'; 173 | const mergedProps = Object.assign({}, defaults, _baseOptions); 174 | await createRequest( 175 | constants.api.workflows.enrollContact, 176 | { 177 | method, 178 | workflowId, 179 | email 180 | }, 181 | mergedProps 182 | ); 183 | return Promise.resolve({ enrolled: true }); 184 | } catch (e) { 185 | return Promise.reject(e.message); 186 | } 187 | }; 188 | 189 | const unenrollContact = async (opts = {}) => { 190 | try { 191 | requiresAuthentication(_baseOptions); 192 | const { workflowId, email } = opts; 193 | const method = 'DELETE'; 194 | const mergedProps = Object.assign({}, defaults, _baseOptions); 195 | await createRequest( 196 | constants.api.workflows.enrollContact, 197 | { 198 | method, 199 | workflowId, 200 | email 201 | }, 202 | mergedProps 203 | ); 204 | return Promise.resolve({ unenrolled: true }); 205 | } catch (e) { 206 | return Promise.reject(e.message); 207 | } 208 | }; 209 | 210 | const getEnrollments = async id => { 211 | try { 212 | requiresAuthentication(_baseOptions); 213 | const mergedProps = Object.assign({}, defaults, _baseOptions); 214 | const enrollments = await createRequest( 215 | constants.api.workflows.enrollments, 216 | { 217 | id 218 | }, 219 | mergedProps 220 | ); 221 | return Promise.resolve(enrollments); 222 | } catch (e) { 223 | return Promise.reject(e.message); 224 | } 225 | }; 226 | 227 | const getWorkflowEventLog = async (opts = {}) => { 228 | try { 229 | requiresAuthentication(_baseOptions); 230 | const { vid, types, workflowId } = opts; 231 | const body = { 232 | vid, 233 | types 234 | }; 235 | const method = 'PUT'; 236 | const mergedProps = Object.assign({}, defaults, _baseOptions); 237 | const eventLogs = await createRequest( 238 | constants.api.workflows.eventLogs, 239 | { 240 | method, 241 | body, 242 | workflowId 243 | }, 244 | mergedProps 245 | ); 246 | return Promise.resolve(eventLogs); 247 | } catch (e) { 248 | return Promise.reject(e.message); 249 | } 250 | }; 251 | 252 | export default function workflows(baseOptions) { 253 | _baseOptions = baseOptions; 254 | 255 | return { 256 | /** 257 | * Get workflow by ID 258 | * @async 259 | * @memberof hs/workflows 260 | * @method getWorkflow 261 | * @param {int} workflowId 262 | * @example 263 | * const hs = new HubspotClient(props); 264 | * hs.workflows.getWorkflow(workflowId).then(response => console.log(response)); 265 | * @returns {Promise} 266 | */ 267 | getWorkflow, 268 | /** 269 | * Create a new workflow 270 | * @async 271 | * @memberof hs/workflows 272 | * @method createWorkflow 273 | * @param {object} workflowBody See {@link https://developers.hubspot.com/docs/methods/workflows/v3/create_workflow|developer docs} for examples of workflow JSON 274 | * @example 275 | * const hs = new HubspotClient(props); 276 | * hs.workflows.createWorkflow(workflowBody).then(response => console.log(response)); 277 | * @returns {Promise} 278 | */ 279 | createWorkflow, 280 | /** 281 | * Update an existing workflow 282 | * @async 283 | * @memberof hs/workflows 284 | * @method updateWorkflow 285 | * @param {object} workflowBody See {@link https://developers.hubspot.com/docs/methods/workflows/v3/create_workflow|developer docs} for examples of workflow JSON 286 | * @example 287 | * const hs = new HubspotClient(props); 288 | * hs.workflows.updateWorkflow(workflowBody).then(response => console.log(response)); 289 | * @property {int} opts.id The ID of the workflow you want to update. 290 | * @property {int} opts.portalId The ID of the portal that the workflow resides on 291 | * @returns {Promise} 292 | */ 293 | updateWorkflow, 294 | /** 295 | * Delete an existing workflow 296 | * @async 297 | * @memberof hs/workflows 298 | * @method deleteWorkflow 299 | * @param {int} id The ID of the workflow you wish to delete 300 | * @example 301 | * const hs = new HubspotClient(props); 302 | * hs.workflows.deleteWorkflow(workflowBody).then(response => console.log(response)); 303 | * @returns {Promise} 304 | */ 305 | deleteWorkflow, 306 | /** 307 | * Get all workflows 308 | * @async 309 | * @memberof hs/workflows 310 | * @method getAll 311 | * @example 312 | * const hs = new HubspotClient(props); 313 | * hs.workflows.getAll().then(response => console.log(response)); 314 | * @returns {Promise} 315 | */ 316 | getAll, 317 | /** 318 | * Enroll a contact in a workflow 319 | * @async 320 | * @memberof hs/workflows 321 | * @method enrollContact 322 | * @param {object} opts Contact & workflow options 323 | * @example 324 | * const hs = new HubspotClient(props); 325 | * hs.workflows.enrollContact(opts).then(response => console.log(response)); 326 | * @property {int} opts.workflowId The ID of the workflow you want to enroll the contact to. 327 | * @property {int} opts.email The email address of the contact you wish to enroll. 328 | * @returns {Promise} 329 | */ 330 | enrollContact, 331 | /** 332 | * Unenroll a contact from a workflow 333 | * @async 334 | * @memberof hs/workflows 335 | * @method unenrollContact 336 | * @param {object} opts Contact & workflow options 337 | * @example 338 | * const hs = new HubspotClient(props); 339 | * hs.workflows.unenrollContact(opts).then(response => console.log(response)); 340 | * @property {int} opts.workflowId The ID of the workflow you want to unenroll the contact from. 341 | * @property {int} opts.email The email address of the contact you wish to unenroll. 342 | * @returns {Promise} 343 | */ 344 | unenrollContact, 345 | /** 346 | * Get workflow enrollments for the specified contact ID 347 | * @async 348 | * @memberof hs/workflows 349 | * @method getEnrollments 350 | * @param {int} id Contact id to retrieve enrollments for 351 | * @example 352 | * const hs = new HubspotClient(props); 353 | * hs.workflows.getEnrollments(id).then(response => console.log(response)); 354 | * @returns {Promise} 355 | */ 356 | getEnrollments, 357 | /** 358 | * Get a list of log events for a contact by workflow. For more information, checkout the {@link https://developers.hubspot.com/docs/methods/workflows/log_events|developer docs}. 359 | * @async 360 | * @memberof hs/workflows 361 | * @method getWorkflowEventLog 362 | * @param {object} opts Filtering options 363 | * @example 364 | * const hs = new HubspotClient(props); 365 | * hs.workflows.getWorkflowEventLog({ 366 | vid: 1283719823 367 | workflowId: 123239681612, 368 | types: ['ENROLLED'] 369 | }).then(response => console.log(response)); 370 | * @property {int} opts.vid The contact ID to filter on 371 | * @property {int} opts.workflowId The ID of the workflow you want to get log events for 372 | * @property {int} opts.types An array of event types 373 | * @returns {Promise} 374 | */ 375 | getWorkflowEventLog 376 | }; 377 | } 378 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import accountApi from './entities/account'; 2 | import contactsApi from './entities/contacts'; 3 | import contactsPropertiesApi from './entities/contacts-properties'; 4 | import companyApi from './entities/company'; 5 | import calendarApi from './entities/calendar'; 6 | import blogPostsApi from './entities/blog'; 7 | import workflowsApi from './entities/workflows'; 8 | import filesApi from './entities/files'; 9 | import domainsApi from './entities/domains'; 10 | import layoutsApi from './entities/layouts'; 11 | import templatesApi from './entities/templates'; 12 | import formsApi from './entities/forms'; 13 | import socialApi from './entities/social'; 14 | import emailEventsApi from './entities/email-events'; 15 | import dealsApi from './entities/deals'; 16 | import pagesApi from './entities/pages'; 17 | import hubdbApi from './entities/hubdb'; 18 | import engagementsApi from './entities/engagements'; 19 | import oauthApi from './entities/oauth'; 20 | import contactsListsApi from './entities/contacts-lists'; 21 | import emailSubscriptionsApi from './entities/email-subscriptions'; 22 | import transactionalEmailsApi from './entities/transactional-emails'; 23 | 24 | /** 25 | * HubSpotClient class 26 | * @example 27 | const HubSpotClient = require('hubspot-api'); 28 | const hs = new HubSpotClient({ accessToken: 'i82739813ksjksf' }); 29 | // or 30 | const hs = new HubSpotClient({ hapikey: '76128312asa7s8761823761' }); 31 | */ 32 | 33 | class HubSpotClient { 34 | /** 35 | * @param {object} props Constructor props. 1 of hapikey / accessToken is required for authenticated requests. No properties required for public methods (eg HubDB, forms) 36 | * @param {string} props.hapikey - hapikey 37 | * @param {string} props.accessToken - accessToken 38 | * @returns {object} 39 | */ 40 | constructor(props) { 41 | Object.assign(this, { props }); 42 | } 43 | 44 | /** 45 | * A collection of methods related to the Account API 46 | * @namespace hs/account 47 | */ 48 | get account() { 49 | return accountApi(this.props); 50 | } 51 | 52 | /** 53 | * A collection of methods related to the Calendar API 54 | * @namespace hs/calendar 55 | */ 56 | get calendar() { 57 | return calendarApi(this.props); 58 | } 59 | 60 | /** 61 | * A collection of methods related to the Contacts API 62 | * @namespace hs/contacts 63 | */ 64 | get contacts() { 65 | return contactsApi(this.props); 66 | } 67 | 68 | /** 69 | * A collection of methods related to the Contacts Properties API 70 | * @namespace hs/contactsProperties 71 | */ 72 | get contactsProperties() { 73 | return contactsPropertiesApi(this.props); 74 | } 75 | 76 | /** 77 | * A collection of methods related to the Comapny API 78 | * @namespace hs/company 79 | */ 80 | get company() { 81 | return companyApi(this.props); 82 | } 83 | 84 | /** 85 | * A collection of methods related to the Blog API / Blog Posts API / Blog Authors API 86 | * @namespace hs/blog 87 | */ 88 | get blog() { 89 | return blogPostsApi(this.props); 90 | } 91 | 92 | /** 93 | * A collection of methods related to the Workflows API 94 | * @namespace hs/workflows 95 | */ 96 | get workflows() { 97 | return workflowsApi(this.props); 98 | } 99 | 100 | /** 101 | * A collection of methods related to the COS Files API 102 | * @namespace hs/files 103 | */ 104 | get files() { 105 | return filesApi(this.props); 106 | } 107 | 108 | /** 109 | * A collection of methods related to the Domains API 110 | * @namespace hs/domains 111 | */ 112 | get domains() { 113 | return domainsApi(this.props); 114 | } 115 | 116 | /** 117 | * A collection of methods related to the Layouts API 118 | * @namespace hs/layouts 119 | */ 120 | get layouts() { 121 | return layoutsApi(this.props); 122 | } 123 | 124 | /** 125 | * A collection of methods related to the Templates API 126 | * @namespace hs/templates 127 | */ 128 | get templates() { 129 | return templatesApi(this.props); 130 | } 131 | 132 | /** 133 | * A collection of methods related to the Forms API 134 | * @namespace hs/forms 135 | */ 136 | get forms() { 137 | return formsApi(this.props); 138 | } 139 | 140 | /** 141 | * A collection of methods related to the Social API 142 | * @namespace hs/social 143 | */ 144 | get social() { 145 | return socialApi(this.props); 146 | } 147 | 148 | /** 149 | * A collection of methods related to the Email Events API 150 | * @namespace hs/emailEvents 151 | */ 152 | get emailEvents() { 153 | return emailEventsApi(this.props); 154 | } 155 | 156 | /** 157 | * A collection of methods related to the Deals API 158 | * @namespace hs/deals 159 | */ 160 | get deals() { 161 | return dealsApi(this.props); 162 | } 163 | 164 | /** 165 | * A collection of methods related to the Page Publishing API 166 | * @namespace hs/pages 167 | */ 168 | get pages() { 169 | return pagesApi(this.props); 170 | } 171 | 172 | /** 173 | * A collection of methods related to the HubDB Tables API 174 | * @namespace hs/hubdb 175 | */ 176 | get hubdb() { 177 | return hubdbApi(this.props); 178 | } 179 | 180 | /** 181 | * A collection of methods related to the Engagements API 182 | * @namespace hs/engagements 183 | */ 184 | get engagements() { 185 | return engagementsApi(this.props); 186 | } 187 | 188 | /** 189 | * A collection of methods related to the OAuth API 190 | * @namespace hs/oauth 191 | */ 192 | get oauth() { 193 | return oauthApi(this.props); 194 | } 195 | 196 | /** 197 | * A collection of methods related to the ContactsList API 198 | * @namespace hs/contactsLists 199 | */ 200 | get contactsLists() { 201 | return contactsListsApi(this.props); 202 | } 203 | 204 | /** 205 | * A collection of methods related to the Email Subscriptions API 206 | * @namespace hs/emailSubscriptions 207 | */ 208 | get emailSubscriptions() { 209 | return emailSubscriptionsApi(this.props); 210 | } 211 | 212 | /** 213 | * A collection of methods related to the Transactional Emails API 214 | * @namespace hs/transactionalEmails 215 | */ 216 | get transactionalEmails() { 217 | return transactionalEmailsApi(this.props); 218 | } 219 | } 220 | 221 | export default HubSpotClient; 222 | -------------------------------------------------------------------------------- /src/utilities.js: -------------------------------------------------------------------------------- 1 | const qs = require('querystring') 2 | const axios = require('axios'); 3 | const debugApp = require('debug')('hubspot-api:utilities'); 4 | 5 | export const requiresAuthentication = ({ hapikey, accessToken }) => { 6 | if (!hapikey && !accessToken) { 7 | throw new Error('This method requires hapikey/accessToken authentication'); 8 | } 9 | }; 10 | 11 | export const interpolate = (template, data, opts = {}) => { 12 | // For escaping strings to go in regex 13 | const regexEscape = /([$^\\/()|?+*[\]{}.-])/g; 14 | const delimiter = opts.delimiter || '{}'; 15 | const delLen = delimiter.length; 16 | const lDelLen = Math.ceil(delLen / 2); 17 | // escape delimiters for regex 18 | const lDel = delimiter.substr(0, lDelLen).replace(regexEscape, '\\$1'); 19 | const rDel = 20 | delimiter.substr(lDelLen, delLen).replace(regexEscape, '\\$1') || lDel; 21 | 22 | // construct the new regex 23 | const regex = new RegExp(`${lDel}[^${lDel}${rDel}]+${rDel}`, 'g'); 24 | 25 | return template.replace(regex, placeholder => { 26 | const key = placeholder.slice(lDelLen, -lDelLen); 27 | const keyParts = key.split('.'); 28 | let val; 29 | let i = 0; 30 | const len = keyParts.length; 31 | 32 | if (key in data) { 33 | // need to be backwards compatible with "flattened" data. 34 | val = data[key]; 35 | } else { 36 | // look up the chain 37 | val = data; 38 | for (; i < len; i++) { 39 | if (keyParts[i] in val) { 40 | val = val[keyParts[i]]; 41 | } else { 42 | return placeholder; 43 | } 44 | } 45 | } 46 | return val; 47 | }); 48 | }; 49 | 50 | export default async function createRequest(uri, options, props = {}) { 51 | try { 52 | const properties = Object.keys(props).reduce((acc, curr) => { 53 | if (typeof props[curr] !== 'undefined') { 54 | acc[curr] = props[curr]; 55 | } 56 | return acc; 57 | }, {}); 58 | // Prevent this from being appended to URL. 59 | delete properties.accessToken; 60 | 61 | const url = `${interpolate(uri, options)}?${qs.stringify(properties)}`; 62 | debugApp(`url: ${url}`); 63 | const method = options.method || 'GET'; 64 | debugApp(`${method}: ${url}`); 65 | const headers = {}; 66 | const timeout = 30000; 67 | const data = options.body || {}; 68 | if (props.accessToken) { 69 | headers.Authorization = `Bearer ${props.accessToken}`; 70 | } 71 | 72 | return axios({ url, method, headers, timeout, data }).then( 73 | response => response.data 74 | ); 75 | } catch (e) { 76 | return Promise.reject(e); 77 | } 78 | } 79 | 80 | export const queryStringParamInterpolator = (objs, original) => { 81 | const response = Object.keys(objs) 82 | .map(key => { 83 | if (key && objs[key]) { 84 | // Remove from the original object 85 | delete original[key]; 86 | const innerResp = Object.keys(objs[key]).reduce((acc, curr) => { 87 | acc[`${key}__${curr}`] = objs[key][curr]; 88 | return acc; 89 | }, {}); 90 | return innerResp; 91 | } 92 | return undefined; 93 | }) 94 | .reduce((acc, curr) => { 95 | Object.assign(acc, curr); 96 | return acc; 97 | }, {}); 98 | 99 | return Object.assign(original, response); 100 | }; 101 | 102 | export const sanitizeObject = obj => JSON.parse(JSON.stringify(obj)); 103 | -------------------------------------------------------------------------------- /test/account.spec.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const HubSpotClient = require('../src/index').default; 3 | const expect = require('chai').expect; 4 | const debug = require('debug')('hubspot-api:tests'); //eslint-disable-line 5 | const { schemaDailyLimit, validate } = require('./schemas/account'); 6 | 7 | const { E2E_TESTS_HAPI_KEY: hapikey } = process.env; 8 | const hs = new HubSpotClient({ hapikey }); 9 | 10 | describe('Get Daily Limit', async () => { 11 | it('returns a valid daily limit response', async () => { 12 | const dailyLimit = await hs.account.getDailyLimit(); 13 | expect(validate(dailyLimit[0], schemaDailyLimit).error).to.be.a('null'); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /test/blog.spec.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const HubSpotClient = require('../src/index').default; 3 | const expect = require('chai').expect; 4 | const debug = require('debug')('hubspot-api:tests'); //eslint-disable-line 5 | const { 6 | schemaPosts, 7 | schemaPost, 8 | schemaAuthors, 9 | validate 10 | } = require('./schemas/blog'); 11 | 12 | const { 13 | E2E_TESTS_HAPI_KEY: hapikey, 14 | E2E_TESTS_BLOG_ID: content_group_id 15 | } = process.env; 16 | const hs = new HubSpotClient({ hapikey }); 17 | 18 | xdescribe('Get Blog Post List', async () => { 19 | // FIXME: Improve this test. 20 | it('returns a valid blog post list response with no query', async () => { 21 | const posts = await hs.blog.getPosts(); 22 | expect(validate(posts, schemaPosts).error).to.be.a('null'); 23 | return Promise.resolve(); 24 | }); 25 | 26 | // FIXME: Improve this test. 27 | it('returns a valid blog post list response with a limit set', async () => { 28 | const limit = 5; 29 | const posts = await hs.blog.getPosts({ limit }); 30 | expect(validate(posts, schemaPosts).error).to.be.a('null'); 31 | const { objects } = posts; 32 | expect(objects) 33 | .to.be.an('array') 34 | .and.has.lengthOf(limit); 35 | return Promise.resolve(); 36 | }); 37 | 38 | // FIXME: Improve this test. 39 | it('returns a valid blog post list response with only archived posts', async () => { 40 | const posts = await hs.blog.getPosts({ archived: true }); 41 | const { objects } = posts; 42 | expect(validate(posts, schemaPosts).error).to.be.a('null'); 43 | const allArchivedPosts = objects.every(obj => obj.archived); 44 | expect(allArchivedPosts).to.equal(true); 45 | return Promise.resolve(); 46 | }); 47 | }); 48 | 49 | xdescribe('Creating Blog Posts', async () => { 50 | it('creates a blog post and returns a valid response', async () => { 51 | const createdPostResponse = await hs.blog.createOrUpdatePost({ 52 | name: `A test post created by testing at ${new Date().getTime()}`, 53 | post_body: 54 | 'This blog post was created by an E2E test as part of the hubspot-api library, you can safely delete it.', 55 | content_group_id 56 | }); 57 | expect(validate(createdPostResponse, schemaPost).error).to.be.a('null'); 58 | return Promise.resolve(); 59 | }); 60 | }); 61 | 62 | describe('Get Blog Authors List', async () => { 63 | it('returns a valid blog author response', async () => { 64 | const authors = await hs.blog.getAuthors(); 65 | expect(validate(authors, schemaAuthors).error).to.be.a('null'); 66 | }); 67 | }); 68 | -------------------------------------------------------------------------------- /test/calendar.spec.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const HubSpotClient = require('../src/index').default; 3 | const expect = require('chai').expect; 4 | const debug = require('debug')('hubspot-api:tests'); //eslint-disable-line 5 | const { schemaEvent, schemaEvents, validate } = require('./schemas/calendar'); 6 | const { tomorrow } = require('./utilities'); 7 | 8 | const { 9 | E2E_TESTS_HAPI_KEY: hapikey, 10 | E2E_TESTS_BLOG_ID: contentGroupId, 11 | E2E_TEST_CREATED_BY_ID: createdBy 12 | } = process.env; 13 | 14 | const hs = new HubSpotClient({ hapikey }); 15 | 16 | describe('Get Calendar Event List', async () => { 17 | it('returns a valid calendar event list response with no query', async () => { 18 | const events = await hs.calendar.taskEvents({ 19 | startDate: new Date().getTime(), 20 | endDate: tomorrow() 21 | }); 22 | expect(validate(events, schemaEvents).error).to.be.a('null'); 23 | return Promise.resolve(); 24 | }); 25 | }); 26 | 27 | describe('Creating Calendar Event', async () => { 28 | it('creates an event and returns a valid response', async () => { 29 | const createdEventResponse = await hs.calendar.createTask({ 30 | eventDate: tomorrow(), 31 | category: 'BLOG_POST', 32 | state: 'TODO', 33 | name: 'Test Blog Task With topics 3', 34 | description: 'Cool Post with Topics', 35 | contentGroupId, 36 | createdBy: createdBy + "" 37 | }); 38 | 39 | expect(validate(createdEventResponse, schemaEvent).error).to.be.a('null'); 40 | return Promise.resolve(); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /test/contacts-properties.spec.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const HubSpotClient = require('../src/index').default; 3 | const expect = require('chai').expect; 4 | const debug = require('debug')('hubspot-api:tests'); //eslint-disable-line 5 | const { schemaContactsProperties, validate } = require('./schemas/contactsProperties'); 6 | 7 | const { E2E_TESTS_HAPI_KEY: hapikey } = process.env; 8 | const hs = new HubSpotClient({ hapikey }); 9 | 10 | describe('Get Contacts Properties List', async () => { 11 | it('returns a valid contacts properties response', async () => { 12 | const contactsProperties = await hs.contactsProperties.getAllContactsProperties(); 13 | expect(validate(contactsProperties, schemaContactsProperties).error).to.be.a('null'); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /test/contacts.spec.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const HubSpotClient = require('../src/index').default; 3 | const expect = require('chai').expect; 4 | const debug = require('debug')('hubspot-api:tests'); //eslint-disable-line 5 | const { schemaContacts, validate } = require('./schemas/contacts'); 6 | 7 | const { E2E_TESTS_HAPI_KEY: hapikey } = process.env; 8 | const hs = new HubSpotClient({ hapikey }); 9 | 10 | describe('Get Contact List', async () => { 11 | it('returns a valid contact response', async () => { 12 | const contacts = await hs.contacts.getContacts(); 13 | expect(validate(contacts, schemaContacts).error).to.be.a('null'); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /test/deals.spec.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const HubSpotClient = require('../src/index').default; 3 | const expect = require('chai').expect; 4 | const debug = require('debug')('hubspot-api:tests'); //eslint-disable-line 5 | const { 6 | schemaDeal, 7 | schemaDeals, 8 | schemaRecentDeals, 9 | validate 10 | } = require('./schemas/deal'); 11 | 12 | const { 13 | E2E_TESTS_HAPI_KEY: hapikey, 14 | } = process.env; 15 | 16 | const newDealData = { 17 | properties: [ 18 | { 19 | value: 'Corp Deal', 20 | name: 'dealname' 21 | }, 22 | { 23 | value: 'appointmentscheduled', 24 | name: 'dealstage' 25 | } 26 | ] 27 | }; 28 | 29 | const hs = new HubSpotClient({ hapikey }); 30 | 31 | describe('Get all deals', async () => { 32 | it('returns a valid deals response with no options', async () => { 33 | const deals = await hs.deals.getAll(); 34 | expect(validate(deals, schemaDeals).error).to.be.a('null'); 35 | return Promise.resolve(); 36 | }); 37 | 38 | it('returns a valid deal response with a limit set', async () => { 39 | const limit = 3; 40 | const response = await hs.deals.getAll({ limit }); 41 | expect(validate(response, schemaDeals).error).to.be.a('null'); 42 | expect(response.deals).to.be.an('array').and.has.lengthOf(limit); 43 | return Promise.resolve(); 44 | }); 45 | }); 46 | 47 | xdescribe('Get recently created deals', async () => { 48 | // FIXME: Failing because the endpoint is not always accessible. 49 | it('returns a valid deals response with some deals', async () => { 50 | const deals = await hs.deals.getRecentlyCreated(); 51 | expect(validate(deals, schemaRecentDeals).error).to.be.a('null'); 52 | return Promise.resolve(); 53 | }); 54 | 55 | // FIXME: Failing because the test portal doesnt have 3 recently created deals at any/all times. 56 | it('returns a valid deal response with total equal to count', async () => { 57 | const count = 3; 58 | const response = await hs.deals.getRecentlyCreated({ count }); 59 | expect(validate(response, schemaRecentDeals).error).to.be.a('null'); 60 | expect(response.results).to.be.an('array').and.has.lengthOf(count); 61 | return Promise.resolve(); 62 | }); 63 | }); 64 | 65 | describe('Create a new deal', async () => { 66 | it('creates a deal and returns a valid response', async () => { 67 | const deals = await hs.deals.createOrUpdate(newDealData); 68 | expect(validate(deals, schemaDeal).error).to.be.a('null'); 69 | return Promise.resolve(); 70 | }); 71 | }); 72 | -------------------------------------------------------------------------------- /test/engagements.spec.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const HubSpotClient = require('../src/index').default; 3 | const expect = require('chai').expect; 4 | const { 5 | schemaEngagement, 6 | validate 7 | } = require('./schemas/engagement'); 8 | 9 | const { 10 | E2E_TESTS_HAPI_KEY: hapikey, 11 | E2E_TESTS_CONTACT_ID: contactId 12 | } = process.env; 13 | 14 | const newEngagementData = { 15 | engagement: { 16 | type: 'NOTE', 17 | active: true, 18 | timestamp: new Date().getTime() 19 | }, 20 | associations: { 21 | contactIds: [contactId] 22 | }, 23 | metadata: { 24 | body: 'A note about robot' 25 | } 26 | }; 27 | 28 | const hs = new HubSpotClient({ hapikey }); 29 | 30 | describe('Create a new engagement', async () => { 31 | it('creates an engagement and returns a valid response', async () => { 32 | const engagement = await hs.engagements.create(newEngagementData); 33 | expect(validate(engagement, schemaEngagement).error).to.be.a('null'); 34 | return Promise.resolve(); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /test/files.spec.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const { expect } = require('chai'); 3 | const debug = require('debug')('hubspot-api:tests'); //eslint-disable-line 4 | const HubSpotClient = require('../src/index').default; 5 | const { schemaFiles, validate } = require('./schemas/file'); 6 | 7 | const { E2E_TESTS_FOLDER_ID: folderId, E2E_TESTS_HAPI_KEY: hapikey } = process.env; 8 | 9 | const hs = new HubSpotClient({ hapikey }); 10 | 11 | describe('Entity:files Get files in Folder', async () => { 12 | it('gets file given folderId', async () => { 13 | const response = await hs.files.getFilesInFolder(folderId); 14 | const valid = validate(response, schemaFiles); 15 | expect(valid.error).to.be.a('null'); 16 | return Promise.resolve(); 17 | }); 18 | }); 19 | 20 | describe('Entity:files Get folders in parent folder with 4 subdirectories', async () => { 21 | it('gets sub folders', async () => { 22 | const { total_count } = await hs.files.getFolders(folderId); 23 | expect(total_count).to.equal(4); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /test/forms.spec.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const HubSpotClient = require('../src/index').default; 3 | const expect = require('chai').expect; 4 | const debug = require('debug')('hubspot-api:tests'); //eslint-disable-line 5 | 6 | const { E2E_TESTS_FORM_ID: formId, E2E_TESTS_PORTAL_ID: portalId, E2E_TESTS_HAPI_KEY: hapikey } = process.env; 7 | 8 | const hs = new HubSpotClient({ hapikey }); 9 | 10 | describe('Get Form', async () => { 11 | it('gets form given formId', async () => { 12 | const form = await hs.forms.getForm(formId); 13 | expect(form).to.exist; 14 | }); 15 | }) 16 | 17 | describe('Form Submit', async () => { 18 | it('successfully submits a form', async () => { 19 | const body = { 20 | fields: [ 21 | { 22 | name: 'email', 23 | value: `apitesting-${Date.now()}@gmail.com` 24 | }, 25 | { 26 | name: 'firstname', 27 | value: 'Jeff' 28 | } 29 | ], 30 | context: { 31 | pageUri: 'www.example.com/page', 32 | pageName: 'Example page' 33 | }, 34 | legalConsentOptions: { 35 | consent: { // Include this object when GDPR options are enabled 36 | consentToProcess: true, 37 | text: 'I agree to allow Example Company to store and process my personal data.', 38 | communications: [ 39 | { 40 | value: true, 41 | subscriptionTypeId: 999, 42 | text: 'I agree to receive marketing communications from Example Company.' 43 | } 44 | ] 45 | } 46 | } 47 | }; 48 | 49 | const result = await hs.forms.submitFormV3(portalId, formId, { ...body }); 50 | expect(result.errors).to.be.an('undefined'); 51 | }); 52 | }); 53 | -------------------------------------------------------------------------------- /test/hubdb.spec.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const HubSpotClient = require('../src/index').default; 3 | const expect = require('chai').expect; 4 | const debug = require('debug')('hubspot-api:tests'); //eslint-disable-line 5 | const { 6 | schemaTables, 7 | schemaRow, 8 | validate 9 | } = require('./schemas/hubdb'); 10 | 11 | const { 12 | E2E_TESTS_HUBDB_TABLE_ID: tableId, 13 | E2E_TESTS_HUBDB_PORTAL_ID: portalId, 14 | E2E_TESTS_HAPI_KEY: hapikey 15 | } = process.env; 16 | 17 | const hs = new HubSpotClient({ hapikey }); 18 | 19 | describe('Get Tables List', async () => { 20 | it('returns a valid list of tables', async () => { 21 | const tables = await hs.hubdb.getTables(); 22 | expect(validate(tables, schemaTables).error).to.be.a('null'); 23 | return Promise.resolve(); 24 | }); 25 | }); 26 | 27 | describe('Create Table', async () => { 28 | it('creates a new table and gets a valid response', async () => { 29 | const createTableResponse = await hs.hubdb.createTable({ 30 | name: 'A test table' 31 | }); 32 | expect(createTableResponse.id).to.be.a('number'); 33 | expect(createTableResponse.name).to.be.a('string'); 34 | return Promise.resolve(); 35 | }); 36 | }); 37 | 38 | // FIXME: Figure out why test is failing 39 | describe('Get table by ID', async () => { 40 | it('returns a valid table response', async () => { 41 | const getTableResponse = await hs.hubdb.getTableById(tableId, portalId); 42 | expect(getTableResponse.id).to.be.a('number'); 43 | expect(getTableResponse.name).to.be.a('string'); 44 | return Promise.resolve(); 45 | }); 46 | }); 47 | 48 | // FIXME: Figure out why test is failing 49 | describe('Get table rows', async () => { 50 | it('returns a valid table rows response', async () => { 51 | const getRowsResponse = await hs.hubdb.getTableRows(tableId, portalId); 52 | // eslint-disable-next-line no-unused-expressions 53 | expect(getRowsResponse.rows.objects).to.be.an('array').that.is.not.empty; 54 | return Promise.resolve(); 55 | }); 56 | }); 57 | 58 | describe('Update table rows', async () => { 59 | it('returns a valid table rows response', async () => { 60 | const getRowsResponse = await hs.hubdb.getTableRows(tableId, portalId); 61 | const rows = getRowsResponse.rows.objects; 62 | const getUpdateResponse = await hs.hubdb.updateTableRows(tableId, rows); 63 | // eslint-disable-next-line no-unused-expressions 64 | expect(getUpdateResponse).to.be.an('array').that.is.not.empty; 65 | return Promise.resolve(); 66 | }); 67 | }); 68 | 69 | describe('Add a table row', async () => { 70 | it('returns a valid table row response', async () => { 71 | const ms = new Date().getTime(); 72 | const addRowResponse = await hs.hubdb.addTableRow(tableId, { 73 | name: `Test row ${ms}`, 74 | path: `my-test-row-${ms}` 75 | }); 76 | expect(validate(addRowResponse, schemaRow).error).to.be.a('null'); 77 | return Promise.resolve(); 78 | }); 79 | }); 80 | -------------------------------------------------------------------------------- /test/layouts.spec.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const HubSpotClient = require('../src/index').default; 3 | const expect = require('chai').expect; 4 | 5 | const { 6 | E2E_TESTS_HAPI_KEY: hapikey, 7 | E2E_TESTS_LAYOUT_ID: layoutId, 8 | E2E_TESTS_LAYOUT_VERSION_ID: versionId, 9 | } = process.env; 10 | 11 | const hs = new HubSpotClient({ hapikey }); 12 | 13 | describe('List Layouts', async () => { 14 | it('gets all layouts for an account', async () => { 15 | const layouts = await hs.layouts.getLayouts(); 16 | expect(layouts.limit).to.equal(100); 17 | return Promise.resolve(); 18 | }); 19 | }); 20 | 21 | describe('Get layout by id', () => { 22 | it('get the details for a specific layout, by ID.', async () => { 23 | const layout = await hs.layouts.getLayoutBuffer(layoutId); 24 | expect(layout).to.be.an('object'); 25 | expect(layout.id.toString()).to.equal(layoutId); 26 | return Promise.resolve(); 27 | }); 28 | }); 29 | 30 | describe('Get Layout Buffer', async () => { 31 | it('gets the current contents of the auto save buffer', async () => { 32 | const layout = await hs.layouts.getLayoutBuffer(layoutId); 33 | expect(layout).to.be.an('object'); 34 | expect(layout.id.toString()).to.equal(layoutId); 35 | return Promise.resolve(); 36 | }); 37 | }); 38 | 39 | describe('Determine if the auto-save buffer differs from he live layout', () => { 40 | it('returns an object with the key `has_changes`', async () => { 41 | const hasBufferedChanges = await hs.layouts.hasBufferedChanges(layoutId); 42 | expect(hasBufferedChanges).to.be.an('object'); 43 | expect(hasBufferedChanges.has_changes).to.be.a('boolean'); 44 | return Promise.resolve(); 45 | }); 46 | }); 47 | 48 | describe('Get previous versions of the layout', () => { 49 | it('returns a array of previous versions', async () => { 50 | const previousVersions = await hs.layouts.getPreviousLayoutVersions(layoutId); 51 | expect(previousVersions).to.be.an('array'); 52 | return Promise.resolve(); 53 | }); 54 | }); 55 | 56 | describe('Get Previous Version of the layout', () => { 57 | it('returns the previous version specified in the id', async () => { 58 | const previousVersion = await hs.layouts.getPreviousLayoutVersion({ id: layoutId, versionId }); 59 | expect(previousVersion).to.be.an('array').with.lengthOf(3); 60 | return Promise.resolve(); 61 | }); 62 | }); 63 | 64 | -------------------------------------------------------------------------------- /test/mocha.opts: -------------------------------------------------------------------------------- 1 | --timeout 30000 2 | --watch-extensions ts 3 | -------------------------------------------------------------------------------- /test/page.spec.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const HubSpotClient = require('../src/index').default; 3 | const expect = require('chai').expect; 4 | const debug = require('debug')('hubspot-api:tests'); //eslint-disable-line 5 | const { schemaPages, schemaPage, validate } = require('./schemas/page'); 6 | 7 | const { E2E_TESTS_HAPI_KEY: hapikey, E2E_TESTS_PAGE_ID: id, E2E_TESTS_TEMPLATE_PATH: template_path } = process.env; 8 | 9 | const hs = new HubSpotClient({ hapikey }); 10 | 11 | // FIXME: Improve these tests. 12 | describe('Get Pages List', async () => { 13 | it('returns a valid page list', async () => { 14 | const pages = await hs.pages.getPages(); 15 | expect(validate(pages, schemaPages).error).to.be.a('null'); 16 | return Promise.resolve(); 17 | }); 18 | 19 | it('returns a valid page list with filters applied', async () => { 20 | const pages = await hs.pages.getPages({ 21 | limit: 2, 22 | name: { icontains: 'About' } 23 | }); 24 | expect(validate(pages, schemaPages).error).to.be.a('null'); 25 | return Promise.resolve(); 26 | }); 27 | }); 28 | 29 | xdescribe('Create or Update Page', async () => { 30 | it('creates a new page or updates page and gets a valid response', async () => { 31 | const createPageResponse = await hs.pages.createOrUpdatePage({ 32 | name: 'A test page', 33 | subcategory: 'site_page', 34 | }); 35 | expect(validate(createPageResponse, schemaPage).error).to.be.a('null'); 36 | return Promise.resolve(); 37 | }); 38 | }); 39 | 40 | // FIXME: This test is failing 41 | xdescribe('Update Page Template', async () => { 42 | it('updates page template and gets a valid response', async () => { 43 | const createPageResponse = await hs.pages.createOrUpdatePage({ 44 | id, 45 | template_path, 46 | template_path_for_render: template_path, 47 | }); 48 | expect(validate(createPageResponse, schemaPage).error).to.be.a('null'); 49 | return Promise.resolve(); 50 | }); 51 | }); 52 | -------------------------------------------------------------------------------- /test/schemas/account.js: -------------------------------------------------------------------------------- 1 | const joi = require('joi'); 2 | 3 | const schemaDailyLimit = joi.object().keys({ 4 | name: joi.string(), 5 | usageLimit: joi.number(), 6 | currentUsage: joi.number(), 7 | collectedAt: joi.number(), 8 | fetchStatus: joi.string(), 9 | resetsAt: joi.number(), 10 | }); 11 | 12 | module.exports = { 13 | schemaDailyLimit, 14 | validate: joi.validate 15 | }; 16 | -------------------------------------------------------------------------------- /test/schemas/blog.js: -------------------------------------------------------------------------------- 1 | const joi = require('joi'); 2 | 3 | const schemaPost = joi.object().keys({ 4 | ab: joi.boolean(), 5 | ab_variation: joi.boolean(), 6 | absolute_url: joi.string(), 7 | allowed_slug_conflict: joi.boolean(), 8 | analytics_page_id: joi.string(), 9 | analytics_page_type: joi.string(), 10 | archived: joi.boolean(), 11 | are_comments_allowed: joi.boolean(), 12 | attached_stylesheets: joi.array(), 13 | author: joi.string(), 14 | author_at: joi.number(), 15 | author_email: joi.string().email(), 16 | author_name: joi.string(), 17 | author_user_id: joi.number(), 18 | author_username: joi.string(), 19 | blog_author: joi.object(), 20 | blog_author_id: joi.number(), 21 | blog_post_author: joi.object(), 22 | blog_post_schedule_task_uid: joi.string(), 23 | blog_publish_instant_email_retry_count: joi.number(), 24 | blueprint_type_id: joi.number(), 25 | campaign: joi.string().allow(''), 26 | category: joi.number(), 27 | category_id: joi.number(), 28 | comment_count: joi.number(), 29 | composition_id: joi.number(), 30 | content_group: joi.number(), 31 | content_group_id: joi.number(), 32 | created: joi.number(), 33 | created_time: joi.number(), 34 | css: joi.object(), 35 | css_text: joi.string().allow(''), 36 | ctas: joi 37 | .object() 38 | .optional() 39 | .allow(null), 40 | current_state: joi.string(), 41 | currently_published: joi.boolean(), 42 | deleted_at: joi.number(), 43 | domain: joi.string().allow(''), 44 | enable_google_amp_output_override: joi.boolean(), 45 | featured_image: joi.string().allow(''), 46 | featured_image_alt_text: joi.string().allow(''), 47 | featured_image_height: joi.number(), 48 | featured_image_length: joi.number(), 49 | featured_image_width: joi.number(), 50 | flex_areas: joi.object(), 51 | footer_html: joi.string().allow(''), 52 | freeze_date: joi.number(), 53 | has_user_changes: joi.boolean(), 54 | head_html: joi.string().allow(''), 55 | html_title: joi.string(), 56 | id: joi.number(), 57 | is_captcha_required: joi.boolean(), 58 | is_draft: joi.boolean(), 59 | is_instant_email_enabled: joi.boolean(), 60 | is_published: joi.boolean(), 61 | is_social_publishing_enabled: joi.boolean(), 62 | keywords: joi.array(), 63 | label: joi.string().allow(''), 64 | layout_sections: joi.object().allow(null), 65 | link_rel_canonical_url: joi.string().allow(''), 66 | list_template: joi.string().allow(''), 67 | live_domain: joi.string().allow(''), 68 | meta: joi.object(), 69 | meta_description: joi.string().allow(''), 70 | name: joi.string().allow(''), 71 | page_redirected: joi.boolean(), 72 | page_title: joi.string().allow(''), 73 | parent_blog: joi.object(), 74 | performable_variation_letter: joi.string().allow(''), 75 | personas: joi.array(), 76 | placement_guids: joi.array(), 77 | portal_id: joi.number(), 78 | post_body: joi.string().allow(''), 79 | post_body_rss: joi.string().allow(''), 80 | post_email_content: joi.string().allow(''), 81 | post_featured_image_if_enabled: joi.string().allow(''), 82 | post_list_content: joi.string().allow(''), 83 | post_list_summary_featured_image: joi.string().allow(''), 84 | post_rss_content: joi.string().allow(''), 85 | post_rss_summary_featured_image: joi.string().allow(''), 86 | post_summary: joi.string().allow(''), 87 | post_summary_rss: joi.string().allow(''), 88 | post_template: joi.string().allow(''), 89 | preview_key: joi.string().allow(''), 90 | processing_status: joi.string().allow(''), 91 | public_access_rules: joi.array(), 92 | public_access_rules_enabled: joi.boolean(), 93 | publish_date: joi.number(), 94 | publish_date_local_time: joi.number(), 95 | publish_date_localized: joi.object(), 96 | publish_immediately: joi.boolean(), 97 | published_url: joi.string().allow(''), 98 | resolved_domain: joi.string().allow(''), 99 | rss_body: joi.string().allow(''), 100 | rss_summary: joi.string().allow(''), 101 | rss_summary_featured_image: joi.string().allow(''), 102 | slug: joi.string().allow(''), 103 | state: joi.string().allow(''), 104 | subcategory: joi.string().allow(''), 105 | synced_with_blog_root: joi.boolean(), 106 | tag_ids: joi.array(), 107 | tag_list: joi.array(), 108 | tag_names: joi.array(), 109 | template_path: joi.string().allow(''), 110 | template_path_for_render: joi.string().allow(''), 111 | title: joi.string().allow(''), 112 | topic_ids: joi.array(), 113 | topic_list: joi.array(), 114 | topic_names: joi.array(), 115 | topics: joi.array(), 116 | translated_content: joi.object(), 117 | tweet_immediately: joi.boolean(), 118 | unpublished_at: joi.number(), 119 | updated: joi.number(), 120 | upsize_featured_image: joi.boolean(), 121 | url: joi.string().allow(''), 122 | use_featured_image: joi.boolean(), 123 | views: joi.number(), 124 | widget_containers: joi.object(), 125 | widgetcontainers: joi.object(), 126 | widgets: joi.object() 127 | }); 128 | 129 | const limit = joi 130 | .number() 131 | .integer() 132 | .min(0) 133 | .required(); 134 | const offset = joi 135 | .number() 136 | .integer() 137 | .min(0) 138 | .required(); 139 | const total = joi 140 | .number() 141 | .integer() 142 | .min(0) 143 | .required(); 144 | const total_count = joi 145 | .number() 146 | .integer() 147 | .min(0) 148 | .required(); 149 | 150 | const totalCount = total_count; 151 | 152 | const schemaPosts = joi.object().keys({ 153 | limit, 154 | offset, 155 | total, 156 | total_count, 157 | objects: joi.array().items(schemaPost) 158 | }); 159 | 160 | const schemaAuthors = joi.object().keys({ 161 | limit, 162 | offset, 163 | total, 164 | totalCount, 165 | objects: joi.array() 166 | }); 167 | 168 | module.exports = { 169 | schemaPosts, 170 | schemaPost, 171 | schemaAuthors, 172 | validate: joi.validate 173 | }; 174 | -------------------------------------------------------------------------------- /test/schemas/calendar.js: -------------------------------------------------------------------------------- 1 | const joi = require('joi'); 2 | 3 | const schemaEvent = joi.object(); 4 | const schemaEvents = joi.array().items(schemaEvent); 5 | 6 | module.exports = { 7 | schemaEvents, 8 | schemaEvent, 9 | validate: joi.validate 10 | }; 11 | -------------------------------------------------------------------------------- /test/schemas/contacts.js: -------------------------------------------------------------------------------- 1 | const joi = require('joi'); 2 | 3 | const schemaContact = joi.object().keys({ 4 | addedAt: joi.number(), 5 | vid: joi.number(), 6 | 'canonical-vid': joi.number(), 7 | 'merged-vids': joi.array(), 8 | 'portal-id': joi.number(), 9 | 'is-contact': joi.boolean(), 10 | 'profile-token': joi.string(), 11 | 'profile-url': joi.string(), 12 | properties: joi.object(), 13 | 'form-submissions': joi.array(), 14 | 'identity-profiles': joi.array(), 15 | 'merge-audits': joi.array() 16 | }); 17 | 18 | const schemaContacts = joi.object().keys({ 19 | 'has-more': joi.boolean(), 20 | 'vid-offset': joi.number(), 21 | contacts: joi.array().items(schemaContact) 22 | }); 23 | 24 | module.exports = { 25 | schemaContacts, 26 | schemaContact, 27 | validate: joi.validate 28 | }; 29 | -------------------------------------------------------------------------------- /test/schemas/contactsProperties.js: -------------------------------------------------------------------------------- 1 | const joi = require('joi'); 2 | 3 | const schemaContactsProperty = joi.object().allow(null); 4 | // .keys({ 5 | // name: joi.string(), 6 | // label: joi 7 | // .string() 8 | // .allow(null) 9 | // .allow(''), 10 | // description: joi.string().allow(''), 11 | // groupName: joi.string(), 12 | // type: joi.string(), 13 | // fieldType: joi.string(), 14 | // createdAt: joi.number().allow(null), 15 | // readOnlyDefinition: joi.boolean(), 16 | // updatedAt: joi.number().allow(null), 17 | // formField: joi.boolean(), 18 | // displayOrder: joi.number(), 19 | // readOnlyValue: joi.boolean(), 20 | // hidden: joi.boolean(), 21 | // mutableDefinitionNotDeletable: joi.boolean(), 22 | // favorited: joi.boolean(), 23 | // favoritedOrder: joi.number(), 24 | // calculated: joi.boolean(), 25 | // externalOptions: joi.boolean(), 26 | // hubspotDefined: joi.boolean().allow(null), 27 | // displayMode: joi.string(), 28 | // showCurrencySymbol: joi.boolean().allow(null), 29 | // searchTextAnalysisMode: joi.string().allow(null), 30 | // optionSortStrategy: joi.string().allow(null), 31 | // createdUserId: joi.number().allow(null), 32 | // textDisplayHint: joi.string().allow(null), 33 | // numberDisplayHint: joi.string().allow(null), 34 | // optionsAreMutable: joi.boolean().allow(null), 35 | // referencedObjectType: joi.string().allow(null), 36 | // isCustomizedDefault: joi.boolean(), 37 | // searchableInGlobalSearch: joi.boolean(), 38 | // currencyPropertyName: joi.string().allow(null), 39 | // hasUniqueValue: joi.boolean(), 40 | // deleted: joi.boolean().allow(null), 41 | // updatedUserId: joi.number().allow(null), 42 | // options: joi.array() 43 | 44 | const schemaContactsProperties = joi.array().items(schemaContactsProperty); 45 | 46 | module.exports = { 47 | schemaContactsProperties, 48 | schemaContactsProperty, 49 | validate: joi.validate 50 | }; 51 | -------------------------------------------------------------------------------- /test/schemas/deal.js: -------------------------------------------------------------------------------- 1 | const joi = require('joi'); 2 | 3 | const schemaDeal = joi.object().keys({ 4 | associations: joi.object().allow(null), 5 | dealId: joi.number(), 6 | imports: joi.array(), 7 | isDeleted: joi.boolean(), 8 | portalId: joi.number(), 9 | properties: joi.object(), 10 | stateChanges: joi.array(), 11 | }); 12 | 13 | const joiPositiveInteger = 14 | joi 15 | .number() 16 | .integer() 17 | .min(0); 18 | 19 | const limit = joiPositiveInteger; 20 | const offset = joiPositiveInteger; 21 | const count = joiPositiveInteger; 22 | const total = joiPositiveInteger; 23 | const hasMore = joi.boolean(); 24 | 25 | const schemaDeals = joi.object().keys({ 26 | limit, 27 | offset, 28 | hasMore, 29 | count, 30 | deals: joi.array().items(schemaDeal) 31 | }); 32 | 33 | const schemaRecentDeals = joi.object().keys({ 34 | offset, 35 | hasMore, 36 | total, 37 | results: joi.array().items(schemaDeal) 38 | }); 39 | 40 | module.exports = { 41 | schemaDeal, 42 | schemaDeals, 43 | schemaRecentDeals, 44 | validate: joi.validate 45 | }; 46 | -------------------------------------------------------------------------------- /test/schemas/engagement.js: -------------------------------------------------------------------------------- 1 | const joi = require('joi'); 2 | 3 | const schemaEngagement = joi.object().allow('null'); 4 | // .keys({ 5 | // associations: joi.object().allow(null), 6 | // engagement: joi.object(), 7 | // attachments: joi.array(), 8 | // metadata: joi.object() 9 | // }); 10 | 11 | module.exports = { 12 | schemaEngagement, 13 | validate: joi.validate 14 | }; 15 | -------------------------------------------------------------------------------- /test/schemas/file.js: -------------------------------------------------------------------------------- 1 | const joi = require('joi'); 2 | 3 | const schemaFile = joi.object().keys({ 4 | name: joi.string(), 5 | id: joi.number(), 6 | }).unknown(); 7 | 8 | const limit = joi 9 | .number() 10 | .integer() 11 | .min(0) 12 | .required(); 13 | const offset = joi 14 | .number() 15 | .integer() 16 | .min(0) 17 | .required(); 18 | const total_count = joi 19 | .number() 20 | .integer() 21 | .min(0) 22 | .required(); 23 | 24 | const schemaFiles = joi.object().keys({ 25 | limit, 26 | offset, 27 | total_count, 28 | objects: joi 29 | .array() 30 | .items(schemaFile) 31 | .min(1) 32 | }); 33 | 34 | module.exports = { 35 | schemaFiles, 36 | schemaFile, 37 | validate: joi.validate 38 | }; 39 | -------------------------------------------------------------------------------- /test/schemas/hubdb.js: -------------------------------------------------------------------------------- 1 | const joi = require('joi'); 2 | 3 | const schemaRow = joi.object().keys({ 4 | id: joi.number(), 5 | createdAt: joi.number(), 6 | name: joi.string().allow(null), 7 | path: joi.string().allow(null), 8 | hs_path: joi.string().optional(), 9 | hs_name: joi.string().optional(), 10 | values: joi.object(), 11 | isSoftEditable: joi.boolean(), 12 | childTableId: joi.number().optional() 13 | }); 14 | 15 | const limit = joi 16 | .number() 17 | .integer() 18 | .min(0) 19 | .required(); 20 | const offset = joi 21 | .number() 22 | .integer() 23 | .min(0) 24 | .required(); 25 | const total = joi 26 | .number() 27 | .integer() 28 | .min(0) 29 | .required(); 30 | const message = joi 31 | .string() 32 | .required() 33 | .allow(null); 34 | const totalCount = joi 35 | .number() 36 | .integer() 37 | .min(0) 38 | .required(); 39 | 40 | const schemaTables = joi.object().keys({ 41 | limit, 42 | offset, 43 | total, 44 | totalCount, 45 | message, 46 | objects: joi 47 | .array() 48 | .items(joi.object()) 49 | .min(1) 50 | }); 51 | 52 | module.exports = { 53 | schemaTables, 54 | schemaRow, 55 | validate: joi.validate 56 | }; 57 | -------------------------------------------------------------------------------- /test/schemas/page.js: -------------------------------------------------------------------------------- 1 | const joi = require('joi'); 2 | 3 | const schemaPage = joi.object(); 4 | 5 | // Removing the granularity on this as this API is constantly changing. 6 | // .keys({ 7 | // ab: joi.boolean(), 8 | // ab_variation: joi.boolean(), 9 | // absolute_url: joi.string().allow(''), 10 | // allowed_slug_conflict: joi.boolean(), 11 | // analytics_page_id: joi.string().allow(''), 12 | // analytics_page_type: joi.string().allow(''), 13 | // archived: joi.boolean(), 14 | // are_comments_allowed: joi.boolean(), 15 | // attached_stylesheets: joi.array(), 16 | // author: joi.string().allow(''), 17 | // author_at: joi.number(), 18 | // author_email: joi.string().email(), 19 | // author_name: joi.string().allow(''), 20 | // author_user_id: joi.number(), 21 | // author_username: joi.string().allow(''), 22 | // blueprint_type_id: joi.number(), 23 | // campaign: joi.string().allow(''), 24 | // campaign_name: joi.string().allow(''), 25 | // category: joi.number(), 26 | // category_id: joi.number(), 27 | // created: joi.number(), 28 | // created_by_id: joi.number().optional(), 29 | // created_time: joi.number(), 30 | // css: joi.object(), 31 | // css_text: joi.string().allow(''), 32 | // ctas: joi 33 | // .object() 34 | // .optional() 35 | // .allow(null), 36 | // current_state: joi.string().allow(''), 37 | // currently_published: joi.boolean(), 38 | // deleted_at: joi.number(), 39 | // domain: joi.string().allow(''), 40 | // enable_domain_stylesheets: joi.boolean(), 41 | // featured_image: joi.string().allow(''), 42 | // featured_image_alt_text: joi.string().allow(''), 43 | // featured_image_height: joi.number(), 44 | // featured_image_length: joi.number(), 45 | // featured_image_width: joi.number(), 46 | // flex_areas: joi.object(), 47 | // freeze_date: joi.number(), 48 | // has_user_changes: joi.boolean(), 49 | // head_html: joi.string().allow(''), 50 | // html_title: joi.string().allow(''), 51 | // id: joi.number(), 52 | // include_default_custom_css: joi.boolean(), 53 | // is_draft: joi.boolean(), 54 | // is_published: joi.boolean(), 55 | // is_social_publishing_enabled: joi.boolean(), 56 | // keywords: joi.array(), 57 | // label: joi.string().allow(''), 58 | // layout_sections: joi 59 | // .object() 60 | // .optional() 61 | // .allow(null), 62 | // link_rel_canonical_url: joi.string().allow(''), 63 | // live_domain: joi.string().allow(''), 64 | // meta: joi.object(), 65 | // meta_description: joi.string().allow(''), 66 | // name: joi.string().allow(''), 67 | // page_expiry_enabled: joi.boolean(), 68 | // page_redirected: joi.boolean(), 69 | // page_title: joi.string().allow(''), 70 | // password: joi.string().optional(), 71 | // performable_guid: joi.string().allow(''), 72 | // personas: joi.array(), 73 | // placement_guids: joi.array(), 74 | // portal_id: joi.number(), 75 | // preview_key: joi.string().allow(''), 76 | // processing_status: joi.string().allow(''), 77 | // public_access_rules: joi.array(), 78 | // public_access_rules_enabled: joi.boolean(), 79 | // publish_date: joi.number(), 80 | // publish_date_local_time: joi.number(), 81 | // publish_date_localized: joi.object(), 82 | // publish_immediately: joi.boolean(), 83 | // published_url: joi.string().allow(''), 84 | // resolved_domain: joi.string().allow(''), 85 | // screenshot_preview_taken_at: joi.number(), 86 | // screenshot_preview_url: joi.string().allow(''), 87 | // site_id: joi.number(), 88 | // slug: joi.string().allow(''), 89 | // state: joi.string().allow(''), 90 | // subcategory: joi.string().allow(''), 91 | // team_perms: joi.array().allow(null), 92 | // template_path: joi.string().allow(''), 93 | // template_path_for_render: joi.string().allow(''), 94 | // title: joi.string().allow(''), 95 | // translated_content: joi.object(), 96 | // tweet_immediately: joi.boolean(), 97 | // unpublished_at: joi.number(), 98 | // updated: joi.number(), 99 | // updated_by_id: joi.number().optional(), 100 | // upsize_featured_image: joi.boolean(), 101 | // url: joi.string().allow(''), 102 | // use_featured_image: joi.boolean(), 103 | // user_perms: joi.array().allow(null), 104 | // views: joi.number(), 105 | // widget_containers: joi.object(), 106 | // widgetcontainers: joi.object(), 107 | // widgets: joi.object() 108 | // }); 109 | 110 | const limit = joi 111 | .number() 112 | .integer() 113 | .min(0) 114 | .required(); 115 | const offset = joi 116 | .number() 117 | .integer() 118 | .min(0) 119 | .required(); 120 | const total = joi 121 | .number() 122 | .integer() 123 | .min(0) 124 | .required(); 125 | 126 | const schemaPages = joi.object().keys({ 127 | limit, 128 | offset, 129 | total, 130 | objects: joi 131 | .array() 132 | .items(schemaPage) 133 | .min(1) 134 | }); 135 | 136 | module.exports = { 137 | schemaPages, 138 | schemaPage, 139 | validate: joi.validate 140 | }; 141 | -------------------------------------------------------------------------------- /test/schemas/template.js: -------------------------------------------------------------------------------- 1 | const joi = require('joi'); 2 | 3 | const schemaTemplate = joi.object(); 4 | module.exports = { 5 | schemaTemplate, 6 | validate: joi.validate 7 | }; 8 | -------------------------------------------------------------------------------- /test/schemas/workflows.js: -------------------------------------------------------------------------------- 1 | const joi = require('joi'); 2 | const pick = require('lodash.pick'); 3 | 4 | const baseKeys = { 5 | type: joi.string().allow('').allow(null), 6 | name: joi.string().allow('').allow(null), 7 | actions: joi.array().allow(null), 8 | id: joi.number(), 9 | description: joi.string().allow('').allow(null), 10 | enabled: joi.boolean(), 11 | portalId: joi.number(), 12 | isSegmentBased: joi.boolean(), 13 | listening: joi.boolean(), 14 | nurtureTimeRange: joi.object().allow('').allow(null), 15 | onlyExecOnBizDays: joi.boolean(), 16 | insertedAt: joi.number(), 17 | updatedAt: joi.number(), 18 | recurringSetting: joi.object().allow('').allow(null), 19 | enrollOnCriteriaUpdate: joi.boolean(), 20 | onlyEnrollsManually: joi.boolean(), 21 | creationSource: joi.object().allow(null), 22 | updateSource: joi.object().allow(null), 23 | allowContactToTriggerMultipleTimes: joi.boolean(), 24 | unenrollmentSetting: joi.object().allow('').allow(null), 25 | segmentCriteria: joi.array(), 26 | goalCriteria: joi.array(), 27 | reEnrollmentTriggerSets: joi.array(), 28 | triggerSets: joi.array(), 29 | suppressionListIds: joi.array(), 30 | lastUpdatedBy: joi.string().allow('').allow(null), 31 | metaData: joi.object().allow('').allow(null), 32 | migrationStatus: joi.object().allow('').allow(null) 33 | }; 34 | 35 | const keysRequiredInCollection = Object.assign({ 36 | lastUpdatedByUserId: joi.number(), 37 | personaTagIds: joi.array().allow(null), 38 | contactListIds: joi.object(), 39 | contactCounts: joi.object(), 40 | originalAuthorUserId: joi.number() 41 | }, pick(baseKeys, [ 42 | 'type', 43 | 'name', 44 | 'id', 45 | 'description', 46 | 'enabled', 47 | 'portalId', 48 | 'insertedAt', 49 | 'updatedAt', 50 | 'creationSource', 51 | 'updateSource', 52 | 'migrationStatus' 53 | ])); 54 | 55 | 56 | const schemaWorkflow = joi.object().keys(baseKeys); 57 | const schemaWorkflows = joi.object().keys({ 58 | workflows: joi.array().items(joi.object().keys(keysRequiredInCollection)) 59 | }); 60 | 61 | module.exports = { 62 | schemaWorkflows, 63 | schemaWorkflow, 64 | validate: joi.validate 65 | }; 66 | -------------------------------------------------------------------------------- /test/templates.spec.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const HubSpotClient = require('../src/index').default; 3 | const expect = require('chai').expect; 4 | const uuid = require('node-uuid'); 5 | const { schemaTemplate, validate } = require('./schemas/template'); 6 | 7 | const { E2E_TESTS_HAPI_KEY: hapikey } = process.env; 8 | 9 | const hs = new HubSpotClient({ hapikey }); 10 | 11 | const embeddedTemplateDefaultContent = '\n'; 12 | 13 | // FIXME: This test is failing. 14 | describe('Templates', () => { 15 | describe('Working with templates', async () => { 16 | const guid = uuid.v4(); 17 | const path = `/Coded Files/Custom/page/Web_Team/E2E_TESTING/${guid}.html`; 18 | const folder = 'Web_Team/E2E_TESTING'; 19 | let source = `

Test ${guid}.

`; 20 | const template_type = 4; 21 | let id; 22 | 23 | it('creates a template in a portal and returns an id', async () => { 24 | const payload = { 25 | source, 26 | path, 27 | folder, 28 | template_type 29 | }; 30 | 31 | const template = await hs.templates.createTemplate(payload); 32 | ({ id } = template); 33 | expect(id.toString()).to.not.equal(''); 34 | return Promise.resolve(); 35 | }); 36 | 37 | it('retrieves a template by id', async () => { 38 | const template = await hs.templates.getTemplate(id); 39 | expect(validate(template, schemaTemplate).error).to.be.a('null'); 40 | return Promise.resolve(); 41 | }); 42 | 43 | it('updates a template in a portal', async () => { 44 | try { 45 | source = '

Test updated

'; 46 | const { source: newSource } = await hs.templates.updateTemplate(id, { 47 | source 48 | }); 49 | expect(newSource).to.equal(embeddedTemplateDefaultContent + source); 50 | return Promise.resolve(); 51 | } catch (err) { 52 | return Promise.reject(err); 53 | } 54 | }); 55 | 56 | it('updates the autosave buffer for a template', async () => { 57 | try { 58 | source = '

Test updating autosave buffer

'; 59 | 60 | // Update the autosave buffer with this content 61 | await hs.templates.updateAutosaveBuffer(id, { 62 | source 63 | }); 64 | 65 | // Get the autosave buffer and determine if it is equal to the updated value 66 | const { 67 | source: updatedBufferSource 68 | } = await hs.templates.getUpdatedAutosaveBuffer(id); 69 | 70 | expect(updatedBufferSource).to.equal(embeddedTemplateDefaultContent + source); 71 | return Promise.resolve(); 72 | } catch (err) { 73 | return Promise.reject(err); 74 | } 75 | }); 76 | 77 | it('returns true if the buffer contains changes', async () => { 78 | try { 79 | source = '

Test updating autosave buffer for a second time

'; 80 | 81 | // Update the autosave buffer with this content 82 | await hs.templates.updateAutosaveBuffer(id, { 83 | source 84 | }); 85 | 86 | // Get the autosave buffer and determine if it is equal to the updated value 87 | const hasChanges = await hs.templates.hasBufferedChanges(id); 88 | 89 | // eslint-disable-next-line no-unused-expressions 90 | expect(hasChanges).to.be.true; 91 | 92 | return Promise.resolve(); 93 | } catch (err) { 94 | return Promise.reject(err); 95 | } 96 | }); 97 | 98 | it('pushes buffered changes live', async () => { 99 | try { 100 | source = '

Test pushing buffered changes live

'; 101 | 102 | // Update the autosave buffer with this content 103 | await hs.templates.updateAutosaveBuffer(id, { 104 | source 105 | }); 106 | 107 | // Publish the autosave buffer 108 | await hs.templates.pushBufferedChangesLive(id); 109 | 110 | // Get the live content and determine if it is equal to the updated value 111 | const { source: liveSource } = await hs.templates.getTemplate(id); 112 | 113 | // eslint-disable-next-line no-unused-expressions 114 | expect(liveSource).to.equal(embeddedTemplateDefaultContent + source); 115 | 116 | return Promise.resolve(); 117 | } catch (err) { 118 | return Promise.reject(err); 119 | } 120 | }); 121 | 122 | it('gets all versions of a template', async () => { 123 | try { 124 | const versions = await hs.templates.getVersions(id); 125 | // Should have 3 versions (based on tests above) 126 | expect(versions).to.be.an('array'); 127 | expect(versions).to.have.lengthOf(3); 128 | 129 | return Promise.resolve(); 130 | } catch (err) { 131 | return Promise.reject(err); 132 | } 133 | }); 134 | 135 | it('deletes a previously created template by id', async () => { 136 | try { 137 | const response = await hs.templates.deleteTemplate(id); 138 | // eslint-disable-next-line no-unused-expressions 139 | expect(response.deleted).to.be.true; 140 | return Promise.resolve(); 141 | } catch (err) { 142 | return Promise.reject(err); 143 | } 144 | }); 145 | }); 146 | }); 147 | -------------------------------------------------------------------------------- /test/transactional-emails.spec.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const { expect } = require('chai'); 3 | const HubSpotClient = require('../src/index').default; 4 | 5 | const { 6 | E2E_TESTS_HAPI_KEY: hapikey, 7 | E2E_TESTS_CONTACT_EMAIL, 8 | E2E_TESTS_EMAIL_ID, 9 | } = process.env; 10 | 11 | const hs = new HubSpotClient({ hapikey }); 12 | 13 | // FIXME: Returns a 403 because of 14 | // "This app hasn't been granted all required scopes to make this call. Read more about required scopes here: https://developers.hubspot.com/scopes." 15 | xdescribe('Transactional Emails', () => { 16 | describe('Single Send API', () => { 17 | it('Sends an email', async () => { 18 | const emailId = E2E_TESTS_EMAIL_ID; 19 | const message = { 20 | to: E2E_TESTS_CONTACT_EMAIL 21 | }; 22 | 23 | const { 24 | sendResult, 25 | message: responseMessage, 26 | eventId 27 | } = await hs.transactionalEmails.singleSend(emailId, message); 28 | 29 | expect(sendResult).not.to.be(''); 30 | expect(responseMessage).not.to.be(''); 31 | if (sendResult === 'SENT') { 32 | expect(eventId).not.to.be(''); 33 | } 34 | 35 | return Promise.resolve(); 36 | }); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /test/utilities.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | tomorrow() { 3 | const currentDate = new Date(); 4 | currentDate.setDate(currentDate.getDate() + 1); 5 | return currentDate.getTime(); 6 | } 7 | }; 8 | -------------------------------------------------------------------------------- /test/workflow.spec.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const HubSpotClient = require('../src/index').default; 3 | const expect = require('chai').expect; 4 | const debug = require('debug')('hubspot-api:tests'); //eslint-disable-line 5 | const { 6 | schemaWorkflows, 7 | schemaWorkflow, 8 | validate 9 | } = require('./schemas/workflows'); 10 | 11 | const { 12 | E2E_TESTS_HAPI_KEY: hapikey, 13 | E2E_TESTS_WORKFLOW_ID, 14 | E2E_TESTS_CONTACT_EMAIL: email, 15 | E2E_TESTS_CONTACT_ID: vid 16 | } = process.env; 17 | 18 | const hs = new HubSpotClient({ hapikey }); 19 | 20 | // Set up some test data 21 | const name = `Workflow created by API integration testing scripts at ${new Date().getTime()}`; 22 | const type = 'DRIP_DELAY'; 23 | const workflowPayload = { 24 | name, 25 | type, 26 | enabled: true, 27 | onlyEnrollsManually: true, 28 | actions: [ 29 | { 30 | type: 'DELAY', 31 | delayMillis: 3600000 32 | }, 33 | { 34 | newValue: 'HubSpot', 35 | propertyName: 'company', 36 | type: 'SET_CONTACT_PROPERTY' 37 | }, 38 | { 39 | type: 'WEBHOOK', 40 | url: 'https://www.myintegration.com/webhook.php', 41 | method: 'POST', 42 | authCreds: { 43 | user: 'user', 44 | password: 'password' 45 | } 46 | } 47 | ] 48 | }; 49 | 50 | // For global storage 51 | const createdWorkflow = {}; 52 | 53 | describe('Workflows', async () => { 54 | describe('Retrieving Workflows', async () => { 55 | it('returns a valid workflow list', async () => { 56 | const workflows = await hs.workflows.getAll(); 57 | expect(validate(workflows, schemaWorkflows).error).to.be.a('null'); 58 | return Promise.resolve(); 59 | }); 60 | 61 | it('returns a valid workflow', async () => { 62 | const workflowById = await hs.workflows.getWorkflow( 63 | E2E_TESTS_WORKFLOW_ID 64 | ); 65 | expect(validate(workflowById, schemaWorkflow).error).to.be.a('null'); 66 | return Promise.resolve(); 67 | }); 68 | }); 69 | 70 | describe('Mutating Workflows', async () => { 71 | it('creates a new workflow', async () => { 72 | const newWorkflow = await hs.workflows.createWorkflow(workflowPayload); 73 | 74 | // Store this globally, so we can update/delete the same one. 75 | Object.assign(createdWorkflow, newWorkflow); 76 | 77 | expect(newWorkflow.name) 78 | .to.be.a('string') 79 | .and.to.equal(name); 80 | expect(newWorkflow.type) 81 | .to.be.a('string') 82 | .and.to.equal(type); 83 | expect(newWorkflow.actions) 84 | .to.be.an('array') 85 | .and.to.have.lengthOf(3); 86 | return Promise.resolve(); 87 | }); 88 | 89 | // NOTE: This test was removed, as API support for updating workflows programmatically is not officially supported in v3 90 | // https://developers.hubspot.com/docs/methods/workflows/v3/create_workflow 91 | }); 92 | 93 | describe('Enrolling/unenrolling contacts in workflows', async () => { 94 | it('enrolls a contact in to an existing workflow', async () => { 95 | const enrollment = await hs.workflows.enrollContact({ 96 | workflowId: createdWorkflow.id, 97 | email 98 | }); 99 | expect(enrollment).to.be.a('object'); 100 | expect(enrollment.enrolled).to.equal(true); 101 | return Promise.resolve(); 102 | }); 103 | 104 | it('gets a list of enrollments for a specified contact and should have at least one (1) enrollment', async () => { 105 | const enrollments = await hs.workflows.getEnrollments(vid); 106 | expect(enrollments) 107 | .to.be.an('array', 'Enrollments is not an array') 108 | .and.to.have.lengthOf.at.least(1, 'Must have at least 1 enrollment'); 109 | return Promise.resolve(); 110 | }); 111 | 112 | it('unenrolls a contact from a pre-existing workflow', async () => { 113 | const enrollment = await hs.workflows.unenrollContact({ 114 | workflowId: createdWorkflow.id, 115 | email 116 | }); 117 | expect(enrollment).to.be.a('object'); 118 | expect(enrollment.unenrolled).to.equal(true); 119 | return Promise.resolve(); 120 | }); 121 | }); 122 | 123 | describe('Retrieving event logs', async () => { 124 | // FIXME: Fix this test 125 | xit('retrieves an event log for a specific workflowId', async () => { 126 | const eventLogs = await hs.workflows.getWorkflowEventLog({ 127 | workflowId: createdWorkflow.id 128 | }); 129 | debug(eventLogs); 130 | 131 | expect(eventLogs.objects).to.be.an('array'); 132 | return Promise.resolve(); 133 | }); 134 | }); 135 | 136 | describe('Deleting Workflows', async () => { 137 | it('deletes a workflow', async () => { 138 | const deletedWorkflow = await hs.workflows.deleteWorkflow( 139 | createdWorkflow.id 140 | ); 141 | expect(deletedWorkflow).to.be.an('object'); 142 | expect(deletedWorkflow.deleted).to.equal(true); 143 | return Promise.resolve(); 144 | }); 145 | }); 146 | }); 147 | -------------------------------------------------------------------------------- /types/main.d.ts: -------------------------------------------------------------------------------- 1 | export = HubSpotClient; 2 | 3 | declare class HubSpotClient { 4 | constructor(props: HubSpotClient.IHubSpotClientProps); 5 | account: IAccountApi; 6 | calendar: any // ICalendarApi; 7 | contacts: any // IContactsApi; 8 | contactsLists: any // IContactsListsApi; 9 | contactsProperties: any // IContactsPropertiesApi 10 | company: any // ICompanyApi; 11 | blog: any // IBlogApi; 12 | workflows: any // IWorkflowsApi; 13 | files: any // IFilesApi; 14 | domains: any // IDomainsApi; 15 | layouts: ILayoutsApi; 16 | forms: any // IFormsApi; 17 | social: any // ISocialApi; 18 | emailEvents: any // IEmailEventsApi; 19 | deals: any // IDealsApi; 20 | pages: any // pagesApi; 21 | hubdb: any // hubdbApi; 22 | engagements: any // IEngagementsApi; 23 | templates: any 24 | } 25 | 26 | declare namespace HubSpotClient { 27 | export interface IHubSpotClientProps { 28 | hapikey?: string; 29 | accessToken?: string; 30 | } 31 | } 32 | 33 | interface IAccountApi { 34 | getAccountDetails: () => Promise; 35 | getDailyLimit: () => Promise; 36 | } 37 | 38 | declare namespace IAccountApi { 39 | interface IAccountDetails { 40 | portalId: number; 41 | timeZone: string; 42 | currency: string; 43 | utcOffsetMilliseconds: string; 44 | utcOffset: string; 45 | } 46 | 47 | interface IDailyLimit { 48 | name: string; 49 | usageLimit: number; 50 | currentUsage: number; 51 | collectedAt: number; 52 | fetchStatus: string; 53 | resetsAt: number; 54 | } 55 | } 56 | 57 | interface IBlogApi { 58 | 59 | } 60 | 61 | interface ICalendarApi { 62 | 63 | } 64 | 65 | interface IContactsApi { 66 | 67 | } 68 | 69 | interface ICompanyApi { 70 | 71 | } 72 | 73 | interface IWorkflowsApi { 74 | 75 | } 76 | 77 | interface IFilesApi { 78 | getFilesInFolder: (folderId: number, opts?: any) => Promise<{ limit: number; objects: any[]; }>; 79 | getFolders: (parentFolderId: number, opts?: any) => Promise<{ limit: number; objects: any[]; }>; 80 | } 81 | 82 | interface IDomainsApi { 83 | 84 | } 85 | 86 | interface ILayoutsApi { 87 | getLayouts: (opts?: ILayoutsApi.IGetLayoutOpts) => Promise<{ limit: number; objects: ILayoutsApi.ILayoutEntity[]; }>; 88 | getLayout: (layoutId: number) => Promise; 89 | getLayoutBuffer: (layoutId: number) => Promise; 90 | hasBufferedChanges: (layoutId: number) => Promise<{ has_changes: boolean }>; 91 | getPreviousLayoutVersions: (layoutId: number) => Promise<{ [index: string]: any}[]> 92 | getPreviousLayoutVersion: (opts: { id: number, versionId: number }) => Promise<{ [index: string]: any}[]> 93 | } 94 | 95 | declare namespace ILayoutsApi { 96 | interface IGetLayoutOpts { 97 | limit?: number; 98 | offset?: number; 99 | category_id?: number; 100 | created?: number; 101 | deleted_at?: number; 102 | id?: number; 103 | label?: string; 104 | path?: string; 105 | custom_head?: string; 106 | include_default_custom_css?: boolean; 107 | enable_domain_stylesheet?: boolean; 108 | attatched_stylesheets?: string; 109 | } 110 | 111 | interface ILayoutEntity { 112 | body_class: string; 113 | body_class_id: string; 114 | category_id: string; 115 | created: number; 116 | deleted_at: number; 117 | id: string; 118 | label: string; 119 | layout_data: string; 120 | path: string; 121 | updated: string; 122 | [index: string]: any; 123 | } 124 | } 125 | 126 | interface IFormsApi { 127 | 128 | } 129 | 130 | interface ISocialApi { 131 | 132 | } 133 | 134 | interface IEmailEventsApi { 135 | 136 | } 137 | 138 | interface IDealsApi { 139 | 140 | } 141 | 142 | interface pagesApi { 143 | 144 | } 145 | 146 | interface hubdbApi { 147 | 148 | } 149 | 150 | interface IEngagementsApi { 151 | 152 | } 153 | --------------------------------------------------------------------------------