Git Product home page Git Product logo

node-hubspot's Introduction

node-hubspot

This project is deprecated. We recommand using instead the official Node.js library.

Installing

npm install hubspot

Instantiate client

const Hubspot = require('hubspot')
const hubspot = new Hubspot({
  apiKey: 'abc',
  checkLimit: false // (Optional) Specify whether to check the API limit on each call. Default: true
})

You can also authenticate via token:

const hubspot = new Hubspot({ accessToken: YOUR_ACCESS_TOKEN })

To change the base url:

const hubspot = new Hubspot({ accessToken: YOUR_ACCESS_TOKEN, baseUrl: 'https://some-url' })

If you're an app developer, you can also instantiate a client with your app details and a refresh_token and obtain a new accessToken:

const hubspot = new Hubspot({
  clientId: ...,
  clientSecret: ...,
  redirectUri: ...,
  refreshToken: ...
})
return hubspot.refreshAccessToken()
  .then(results => {
    console.log(results.access_token)

    // this assigns the new accessToken to the client, so your client is ready
    // to use
    console.log(hubspot.accessToken)
    return hubspot.contacts.get()
  })

Changing rate limiter options

Bottleneck is used for rate limiting. To override the default settings, pass a limiter object when instantiating the client. Bottleneck options can be found here.

const hubspot = new Hubspot({
  apiKey: YOUR_API_KEY,
  limiter: {
    maxConcurrent: 2,
    minTime: 1000 / 9,
  }
})

Usage

All methods return a promise. The success case includes the returned object from the response. Use the API method via:

hubspot.contacts
  .get(options)
  .then(results => {
    console.log(results)
  })
  .catch(err => {
    console.error(err)
  })

Samples

Please see repository with samples applications with common cases.

{EXAMPLE} Create Contact

const contactObj = {
  "properties": [
    { "property": "firstname","value": yourvalue },
    { "property": "lastname", "value": yourvalue }
  ]
};

const hubspot = new Hubspot({ apiKey: YOUR_API_KEY });
const hubspotContact = await hubspot.contacts.create(contactObj);

{EXAMPLE} If you need to insert multiple values

Each value must have a semi colon after each value

{ "property": "foo", "value": "value1;value2;value3;value4" }

API limits

HubSpot has relatively stringent API limits (40,000 per day by default). To prevent from consuming it all-at-once, this library checks API quotas regularly and will fail requests if the total is too close to the max. By default

Available Methods

Companies

hubspot.companies.get(opts)
hubspot.companies.getById(id)
hubspot.companies.getRecentlyCreated(opts)
hubspot.companies.getRecentlyModified(opts)
hubspot.companies.getByDomain(domain)
hubspot.companies.create(data)
hubspot.companies.addContactToCompany(data)
// data = { companyId: 123, contactVid: 123 }
hubspot.companies.getContactIds(id, options)
hubspot.companies.getContacts(id, options)
hubspot.companies.update(id, data)
hubspot.companies.updateBatch(data)
// data = [{ objectId: 123, properties: [] }]
hubspot.companies.delete(id)

Company properties

hubspot.companies.properties.get(query) // query is optional
hubspot.companies.properties.getByName(name)
hubspot.companies.properties.create(data)
hubspot.companies.properties.update(name, data)
hubspot.companies.properties.upsert(data)
// not an official API, wrapper doing two API calls. Callbacks not supported
// at this time.

Company properties groups

hubspot.companies.properties.groups.get(query) // query is optional
hubspot.companies.properties.groups.create(data)
hubspot.companies.properties.groups.update(name, data)
hubspot.companies.properties.groups.upsert(data)
// not an official API, wrapper doing two API calls. Callbacks not supported at
// this time

Contacts

hubspot.contacts.get(opts)
hubspot.contacts.getAll(opts)
hubspot.contacts.getByEmail(email)
hubspot.contacts.getByEmailBatch(emails)
hubspot.contacts.getById(id)
hubspot.contacts.getByIdBatch(ids)
hubspot.contacts.getByToken(utk)
hubspot.contacts.update(id, data)
hubspot.contacts.create(data)
hubspot.contacts.createOrUpdateBatch(data)
// data = [{ vid/email: '', properties: [] }]
hubspot.contacts.search(query)
hubspot.contacts.getRecentlyCreated()
hubspot.contacts.getRecentlyModified()
hubspot.contacts.createOrUpdate(email, data)
hubspot.contacts.updateByEmail(email, data)
hubspot.contacts.delete(id)
hubspot.contacts.merge(primaryId, secondaryId)

// Add a secondary email address to a contact
hubspot.contacts.addSecondaryEmail(vid, secondaryEmail)

Contact properties

hubspot.contacts.properties.get()
hubspot.contacts.properties.getByName(name)
hubspot.contacts.properties.create(data)
hubspot.contacts.properties.update(name, data)
hubspot.contacts.properties.upsert(data)
// not an official API, wrapper doing two API calls.
// Callbacks not supported at this time
hubspot.contacts.properties.getGroups()
// => [ {name: '...', displayName: '...'}, ...]
hubspot.contacts.properties.createGroup({ name, displayName })
hubspot.contacts.properties.updateGroup(name, { displayName })
hubspot.contacts.properties.deleteGroup(name)
hubspot.contacts.properties.delete(name)

CRM associations

hubspot.crm.associations.create(data)
hubspot.crm.associations.createBatch(data)
hubspot.crm.associations.delete(data)
hubspot.crm.associations.deleteBatch(data)
// not an official API, wrapper doing two API calls. Callbacks not supported at
// this time

Pages

// more opts can be found at https://developers.hubspot.com/docs/methods/pages/get_pages
hubspot.pages.get(opts) // eg: opts = {is_draft: false}

Deals

hubspot.deals.get(opts)
hubspot.deals.getRecentlyModified(opts)
hubspot.deals.getRecentlyCreated(opts)
hubspot.deals.getById(id)
hubspot.deals.getAssociated(objectType, objectId, opts)
hubspot.deals.deleteById(id)
hubspot.deals.updateById(id, data)
hubspot.deals.updateBatch(data)
hubspot.deals.create(data)
hubspot.deals.associate(id, objectType, associatedObjectId)
hubspot.deals.removeAssociation(id, objectType, associatedObjectId)

Deals properties

hubspot.deals.properties.get(query) // query is optional
hubspot.deals.properties.getByName(name)
hubspot.deals.properties.create(data)
hubspot.deals.properties.update(name, data)
hubspot.deals.properties.upsert(data)
// not an official API, wrapper doing two API calls. Callbacks not supported at
// this time

Deals properties groups

hubspot.deals.properties.groups.get(query) // query is optional
hubspot.deals.properties.groups.create(data)
hubspot.deals.properties.groups.update(name, data)
hubspot.deals.properties.groups.upsert(data)
hubspot.deals.properties.groups.delete(name)
// not an official API, wrapper doing two API calls. Callbacks not supported at
// this time

Engagements

hubspot.engagements.create(data)
hubspot.engagements.get(opts)
hubspot.engagements.update(engagementId, data)
hubspot.engagements.getRecentlyModified(opts)
hubspot.engagements.getAssociated(objectType, objectId, opts)
hubspot.engagements.getCallDispositions()

Owners

hubspot.owners.get(opts)

Pipelines

hubspot.pipelines.get(opts)

Lists

hubspot.lists.get(opts)
hubspot.lists.getOne(id)
hubspot.lists.getByIdBatch(ids)
hubspot.lists.create(data)
hubspot.lists.delete(id)
hubspot.lists.getContacts(id, opts)
hubspot.lists.getRecentContacts(id, opts)
hubspot.lists.getRecentUpdates(opts)
hubspot.lists.addContacts(id, contactBody)
hubspot.lists.removeContacts(id, contactBody)

Files

hubspot.files.get()
hubspot.files.getOne(id)
hubspot.files.upload(fileDetails, overwrite, hidden)
hubspot.files.uploadByUrl(fileDetails, overwrite, hidden)

Forms

hubspot.forms.get(opts)
hubspot.forms.getById(id)
hubspot.forms.getSingleField(guid, fieldname)
hubspot.forms.getSubmissions(guid, opts)
hubspot.forms.create(data)
hubspot.forms.update(id, data)
hubspot.forms.delete(id)

hubspot.forms.submit(portalId, formId, data)

hubspot.forms.getUploadedFileByUrl(url)

Email

hubspot.subscriptions.get(opts)
hubspot.subscriptions.subscribeToAll(email)
hubspot.subscriptions.unsubscribe(email)

Email Events

hubspot.campaigns.getById()
hubspot.campaigns.get(opts)
hubspot.campaigns.getOne(id)
hubspot.campaigns.events(opts)

Marketing Email

hubspot.marketingEmail.get(opts)
hubspot.marketingEmail.getById(id)
hubspot.marketingEmail.create(data)
hubspot.marketingEmail.update(id, data)
hubspot.marketingEmail.clone(id, data)
hubspot.marketingEmail.delete(id)
hubspot.marketingEmail.versions(id)
hubspot.marketingEmail.restore(id)
hubspot.marketingEmail.hasBufferedChanges(id)
hubspot.marketingEmail.statistics(opts)
hubspot.marketingEmail.statisticsById(id)

Social Media

hubspot.broadcasts.get(opts)

Timelines

// setup for timeline events
hubspot.timelines.createEventType(applicationId, userId, data)
hubspot.timelines.updateEventType(applicationId, eventTypeId, data)
hubspot.timelines.createEventTypeProperty(
  applicationId,
  eventTypeId,
  userId,
  data,
)
hubspot.timelines.updateEventTypeProperty(
  applicationId,
  eventTypeId,
  propertyId,
  data,
)
// creating timeline events
hubspot.timelines.createTimelineEvent(applicationId, eventTypeId, data)

NOTE: From the documentation for createTimelineEvent:

Returns a 204 response on success. Otherwise, you'll receive a 4xx error, with more details about the specific error in the body of the response.

So on success the body is empty or undefined and you will not get a result from the resolved promise.

Transactional Emails

hubspot.emails.sendTransactionalEmail(data)

Workflows

hubspot.workflows.getAll()
hubspot.workflows.get(workflowId)
hubspot.workflows.create(data)
hubspot.workflows.delete(workflowId)
hubspot.workflows.enroll(workflowId, email)
hubspot.workflows.unenroll(workflowId, email)
hubspot.workflows.current(contactId)

OAuth

hubspot.oauth.getAuthorizationUrl(opts)
hubspot.oauth.getAccessToken(data)
hubspot.oauth.refreshAccessToken()
hubspot.oauth.getPortalInfo(token)

Obtain your authorization url

const params = {
  client_id: 'your_client_id',
  scope: 'some scopes',
  redirect_uri: 'take_me_to_the_ballpark',
}
const uri = hubspot.oauth.getAuthorizationUrl(params)

Obtain an access token from an authorization_code

const hubspot = new Hubspot({
  clientId: '',
  clientSecret: '',
  redirectUri: ''
})
return hubspot.oauth.getAccessToken({
  code: 'abc' // the code you received from the oauth flow
}).then(...)

You can also pass the constructor directly as parameters (although with a slightly awkward case change)

const params = {
  code: 'abc' // the code you received from the oauth flow
  client_id: '',
  client_secret: '',
  redirect_uri: ''
}
const hubspot = new Hubspot(params)
return hubspot.oauth.getAccessToken(params).then(...)

Tickets

const data = [
  {
    name: 'subject',
    value: 'This is an example ticket'
  },
  {
    name: 'content',
    value: 'Here are the details of the ticket.'
  },
  {
    name: 'hs_pipeline',
    value: '0'
  },
  {
    name: 'hs_pipeline_stage',
    value: '1'
  }
];
const ids = [176606, 177919];
const properties = ['subject', 'content', 'hs_pipeline'];
const newDataId = [
    {
      objectId: 176606,
      properties: [
        {
          name: 'subject',
          value: 'SUBJECT 001'
        },
        {
          name: 'content',
          value: 'TICKET 001'
        }
      ]
    },
    {
      objectId: 177919,
      properties: [
        {
          name: 'subject',
          value: 'SUBJECT 002'
        },
        {
          name: 'content',
          value: 'TICKET 002'
        }
      ]
    }
  ];

hubspot.tickets.create(data);
hubspot.tickets.createBatch(data);
hubspot.tickets.getAll();
hubspot.tickets.getAll(properties);
hubspot.tickets.getById(id);
hubspot.tickets.getById(id, properties);
hubspot.tickets.getBatchById(ids);
hubspot.tickets.getBatchById(ids, properties);
hubspot.tickets.delete(id);
hubspot.tickets.deleteBatch(ids);
hubspot.tickets.update(id, newData);
hubspot.tickets.updateBatch(newDataId);

Not wrapped endpoint(s)

It is possible to access the hubspot request method directly, it could be handy if wrapper doesn't have implementation for some endpoint yet. Using of exposed request method benefits by the bottleneck throttling, auth and request parsing and formatting already in place

hubspot.apiRequest({
            method: 'PUT',
            path: '/some/api/not/wrapped/yet',
            body: { key: 'value' },
          })

Also it is possible to overlap hubspot base API URL using overlapUrl parameter

hubspot.apiRequest({
            method: 'GET',
            overlapUrl: 'https://api.hubspot.com/some/alternative/api',
          })

Typescript

You may use this library in your Typescript project via:

import Hubspot from 'hubspot';
const hubspot = new Hubspot({ apiKey: YOUR_API_KEY });

License

MIT

Contributing

See our contributing guidelines

Development Workflow

After cloning this repo, run npm install to fetch dependencies. Then run the test suite with npm test. From master, this should be green. Our tests mock out requests to HubSpot's api using nock. We then recommend running the test suite without mocking api requests with NOCK_OFF=true npm run mocha. This should fail as you'll need some environment variables for real requests to pass.

If you haven't already, create a developer account on hubspot. You'll want to create an app and a test account as well. Then, create a new file, .env in the root of the repo. Inside you'll need to add an app id, a HubSpot user id, and an oauth access token, also you will need to provide api key and set some workflow id;

NOTE: Your HubSpot user ID; This can be found in the same place as your Developer HAPIkey in your Developer portal.

APPLICATION_ID=111111
USER_ID=2222222
ACCESS_TOKEN=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA_BBB_CCCC_____-D_EEEEEEEEEEEEEEEEEEEEEEE_fffffff-gggggg"
HUBSPOT_API_KEY=1111-2222-3333-4444-5555
TEST_WORKFLOW_ID=333

To get an access token, you should follow the instructions here after cloning the oauth-quickstart-nodejs project. Make sure to modify index.js to include all the required scopes as shown in this pull-request.

Once you have a green test suite with mocking turned off (run NOCK_OFF=true npm run mocha to confirm) you can write a test for the new feature or bug fix hitting the live API. Once that test is passing try mocking out the endpoint using the fakeHubspotApi test helper.

Push to your fork. Write a good commit message. Submit a pull request.

Others will give constructive feedback. This is a time for discussion and improvements, and making the necessary changes will be required before we can merge the contribution.

Thank you to all our contributors.

node-hubspot's People

Contributors

austinleegordon avatar brainflake avatar carterharrison avatar chiragrajk avatar ctataru avatar davidmfoley avatar dependabot[bot] avatar durchanek avatar et avatar fabioprod avatar gorgekara avatar iameap avatar jimbeam2019 avatar kr1sp1n avatar ksvirkou-hubspot avatar lokosama avatar markitecht avatar matt-forster avatar mattmsumner avatar mattscamp avatar morrislaptop avatar mpuittinen avatar mswagner avatar pcothenet avatar samlevan avatar tejasmanohar avatar timisbusy avatar trs avatar twisterking avatar vkhomiv-hubspot avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

node-hubspot's Issues

best way to handle paged results

when doing a query against the contacts api or any other api that can returned paged results, is there a built in method to get all the results? Or do we need to create our own iterator to do it? A quick example in the README.md would be awesome!

Deal's associate for multiple objects at once

Hey, could be handy to let the users associate/removeAssociation of many contacts or companies at once, the hubspot API supports it and it could save some requests, maybe create a new function like associateMany or tweak a little the current one without breaking the current behavior, I could open a PR for that. WDYT?

Documentation for owners.get not consistent with implementation

Given the following code

'use strict';

const Hubspot = require('hubspot');
const hubspot = new Hubspot({ apiKey: 'demo' });

const opts = {};

return hubspot.owners.get(opts)
.then(results => {
  console.log("Successful get request of owners")
  console.log(results);
  return results;
}).catch(err => {
  console.log("Failed get request of owners")
  console.log(err);
});

I get the following error message:

Failed get request of owners
TypeError: cb is not a function
    at limiter.schedule.then.catch.err ((path omitted)/node_modules/hubspot/lib/client.js:120:13)
    at <anonymous>

This is because Owner.get currently only takes an argument of cb, rather than opts, cb as stated in the documentation, where it said hubspot.owners.get(opts, cb)

I haven't looked into whether the documentation should be changed to get rid of opts, or whether the implementation should be changed to utilize supplied options.

Contacts.createOrUpdate data object issue

This may be a typscript specific issue. When i try to use the contacts.createOrUpdate method i get an error. It requires two inputs into it. a string which would be the email and a data object. All the other methods like create require a JSON object but when i try to use it like this:
const contact_data = { 'properties': [ { "property": "email", "value": postData.email }, { "property": "firstname", "value": postData.f_name }, { "property": "lastname", "value": postData.l_name }, { "property": "company", "value": postData.company }, { "property": "phone", "value": postData.phone }, { "property": "state", "value": postData.state } ] };

it gives me this error:
[ts] Argument of type '{ 'properties': { "property": string; "value": any; }[]; }' is not assignable to parameter of type '{}[]'. Property 'length' is missing in type '{ 'properties': { "property": string; "value": any; }[]; }'.

i make the call like this:
return hubspot.contacts.createOrUpdate(postData.email, contact_data)

Set expiration on bottleneck jobs

When using Bottleneck in clustering mode, it is strongly recommended to set expiration on every job.

See https://github.com/SGrondin/bottleneck#important-considerations-when-clustering

It would be great if hubspot would pass an expiration to calls to this.limiter.schedule, e.g.

return this.checkApiLimit(params).then(() => {
      this.emit('apiCall', params)
      return this.limiter.schedule({ expiration: this.apiTimeout }, () =>
        request(params)
          .then(res => {
            this.updateApiLimit(res)
            return res
          })
          .then(res => res.body)
      ) // limit the number of concurrent requests
    })

then if I setup clustering mode for hubspot's bottleneck instance, it will apply this recommendation.

Contacts getById should accept optional parameters

contacts.getById only takes id, but per Hub Spot spec there are more options that can go into the URL query string (as it is a GET)

https://developers.hubspot.com/docs/methods/contacts/get_contact

string[]? property
string? propertyMode // "value_only", "value_and_history"
string? formSubmissionMode // "all", "none", "newest", "oldest"
boolean? showListMemberships

Solution:

Add an options param which accepts these values and adds them to qs


The signature is slightly different to the equivalent company/deal versions documented

Move to Typescript

@AustinLeeGordon looks like we were able to move past the prettier issue but now it is telling me there is another error...which i believe is another prettier issue

Detailed stack trace: /user_code/node_modules/hubspot/lib/oauth.js:12
...data,
^^^

SyntaxError: Unexpected token ...
at createScript (vm.js:56:10)

Originally posted by @PrimalIan in #154 (comment)

Plan for Form support

Hi guys

Thanks for what is available so far.
I was wondering wether you had in mind to support the Forms?

Cheers
Sam

decode error object

I'm trying to decode error object message

     async createContact(data) {
  
        try {
            let contact = await this.hubspot.contacts.create(data)
            return contact;
        }catch(e){
            debug('error object',e)
            throw new Error(e)
        }
      


   
}

I get this print in terminal
Error: StatusCodeError: 409 - {"status":"error","message":"Contact already exists","correlationId":"3c5db087-36d0-415c-94ef-039de03b848a","identityProfile":{"vid":2751,"identity":[{"value":"[email protected]","type":"EMAIL","timestamp":1545572760708,"isPrimary":true},{"value":"a389c82e-60b3-4ef0-9c7c-4f19e432c0d3","type":"LEAD_GUID","timestamp":1545572760719}],"linkedVid":[],"isContact":true,"savedAtTimestamp":1545572760723},"error":"CONTACT_EXISTS","requestId":"71e6085bfec2c5d13046f6b344beed0e"}
but when I can't print the error message, it's not object.

Automatically retry on error 429

It would be nice to automatically retry if we get error 429 back from the server, indicating we have sent too many requests.

This is relatively easy to implement using bottleneck, see https://github.com/SGrondin/bottleneck#retries

If retries are not wanted as a default, perhaps the hubspot client object could pass-through the relevant events from bottleneck so the user of hubspot can listen to them, and provide an example in the docs how to add appropriate error handlers to retry.

To workaround the issue, we can add our own event handler on the limiter, but it requires accessing the undocumented internals of the hubspot client:

const hubspotClient = new Hubspot({
  apiKey,
  limiter: {
    id: 'hubspot',
    maxConcurrent: 2,
    minTime: 150,
    // Clustering options
    datastore: 'ioredis',
    clearDatastore: false,
    clientOptions: redisClientOptions,
    timeout: 60000,
  },
});

const errorMaxRetries = (error: any) => {
  switch (error.statusCode) {
    case 502:
    case 503:
    case 427: {
      return 10;
    }
    case 500: {
      return 1;
    }
  }
  switch (error.code) {
    case 'ECONNRESET':
    case 'ESOCKETTIMEDOUT':
    case 'ETIMEDOUT':
      return 10;
  }
  return 0;
};
((hubspotClient as any).limiter as Bottleneck).on(
  'failed',
  async (error: any, jobInfo: any) => {
    if (jobInfo.retryCount < errorMaxRetries(error)) {
      log.warn(error, 'Retrying HubSpot request.');
      return 500;
    }
  },
);

createEventType needs UserId

When i use this method i got an error: "UserId not provided in request". I can see that some other methods use it, but not this one.

Passing callbacks as the first argument should be removed

There are some functions that take a callback as the second argument but we have code allowing one to pass it as the first. This seems to happen inconsistently and not be documented anywhere.

I think it's worth removing callbacks as the first argument. It may also be an option to remove callbacks entirely and document how to use promises and then to achieve the same functionality.

AccessToken expires_in

After the Oauth flow the hubspot object does not contain the accessToken "expires_in" data.
Such data is useful for setting the local cache lifetime properly

NPM outdated

commit log

It appears you haven't published to NPM since request was added as a dependency to this package. Would you please bump the version and do so? Till then, I'll use a GitHub user/repo path in my package.json.

cannot authenticate via oAUTH

not sure it is appropriate to ask a question here, but I have been struggling with initiating oAUTH with this Library. When I get authenticated I plan on adding timeline create functionality. I just have no idea what I am doing wrong. I used a sample app so I know I am on the right track with my variables. Here is my code, I desperately need a bump in the right direction. Maybe we could do a screen share and willing to pay as I have been battling this for 2 days. Guessing it is something simple I am missing.
I keep getting BAD_AUTH_CODE, "missing or unknown auth code". I could get data via APIkey but the timeline calls require oAUTH. My email is [email protected]

const chai = require('chai')
const expect = chai.expect

const Hubspot = require('hubspot')

class hubspotapi {
authorize(params) {
return new Promise((resolve, reject) => {
let body = '';
if(params.client_id) {
const pams = {
code: 'codecodecode',
clientId: 'xxxxx',
clientSecret: 'yyyyyyy',
redirectUri: 'https://plainsmobile.com'
//
}
const hubspot = new Hubspot(pams)
return hubspot.oauth.getAccessToken({
pams // the code you received from the oauth flow
}).then(data => {
expect(res).to.have.a.property('access_token')
})
return hubspot.contacts.get().then(data => {
expect(data).to.be.a('object')
expect(data.contacts).to.be.a('array')
expect(data.contacts[0]).to.be.an('object')
})
//return hubspot.refreshAccessToken()
// .then(results => {
// console.log(results.access_token)
// console.log(hubspot.accessToken) // this assigns the new accessToken to the client, so your client is ready to use
// return hubspot.contacts.get()
// })

			resolve(response);
			
		} else {
			resolve();
		}
	});
}

}
module.exports=hubspotapi;
hubspotapi.txt

Allow Bottleneck limiter options to be overridden

Would like to be able to override the Bottleneck limiter options (or at least fix the default options) to allow for requests to be processed concurrently.

In the current version the Bottleneck limiter has minTime set to 1000, delaying each request by 1s. I think it was intended to allow for 9 concurrent requests with maxConcurrent set to 9, but it will almost always only run one at a time since requests should resolve before the 1s delay.

const Bottleneck = require('bottleneck')
const limiter = new Bottleneck({
maxConcurrent: 9,
minTime: 1000,
})

Changing minTime to something like 1000 / 9 will keep it under the 10 requests per second limit.

const Bottleneck = require('bottleneck')
const limiter = new Bottleneck({
  maxConcurrent: 9,
  minTime: 1000 / 9,
})

Might need some tweaking to account for errors. In one application where I tested it, some of the emails being uploaded would bounce back errors and cause some of the following requests to hit the limit.

I'd be happy to work on a PR for this, but I wanted to make sure there weren't any other gotchas that I needed to watch out for.

Unexpected token ) in company.js file

I am trying to deploy a project to cloud functions. i switched from TS to JS to try to get better usability with this and other packages. But every time I add const Hubspot = require('hubspot'); I get an error on deployment that halts everything.

Is there a syntax error in your code?
Detailed stack trace: /user_code/node_modules/hubspot/lib/company.js:89
)
^

SyntaxError: Unexpected token )
at createScript (vm.js:56:10)
at Object.runInThisContext (vm.js:97:10)
at Module._compile (module.js:549:28)
at Object.Module._extensions..js (module.js:586:10)
at Module.load (module.js:494:32)
at tryModuleLoad (module.js:453:12)
at Function.Module._load (module.js:445:3)
at Module.require (module.js:504:17)
at require (internal/module.js:20:19)
at Object. (/user_code/node_modules/hubspot/lib/client.js:3:17)
at Module._compile (module.js:577:32)
at Object.Module._extensions..js (module.js:586:10)
at Module.load (module.js:494:32)
at tryModuleLoad (module.js:453:12)
at Function.Module._load (module.js:445:3)
at Module.require (module.js:504:17)

Use ResolveWithFullResponse for request-promise calls

Some of the HubSpot API Calls don't return any messages (like the update call - https://developers.hubspot.com/docs/methods/contacts/update_contact), and the status code needs to be validated to determine the success of the calls. Currently, the tests for node-hubspot wrapper validates the test as a success if the message returned is undefined, but that is not necessarily true. As per the HubSpot API documentation, all of the following cases will return an undefined message

  • 204 when a contact is updated
  • 401 when an unauthorized request is made, such as an expired access token of wrong API key.
  • 404 when there is no existing contact with the specified vid.
  • 500 when an internal server error occurs. Please alert us in the API Forum if you receive an HTTP 500 error.

useKey function bug

The useKey function requires a callback function for errors, but doesn't call it on success, which means that it can't be used properly.

function useKey (key, cb) {
    if (!key || typeof key !== 'string') {
      return cb(new Error("You must provide a key."));
    }

    self.key = key;
  }

It should probably be:

function useKey (key, cb) {
    if (!key || typeof key !== 'string') {
      return cb(new Error("You must provide a key."));
    }
    else {
        self.key = key;
        return cb(null);
    }
  }

refreshToken example does not work

const Hubspot = require('hubspot');
const hubspot = new Hubspot({
  clientId: process.env.HUBSPOT_CLIENT_ID,
  clientSecret: process.env.HUBSPOT_CLIENT_SECRET,
  redirectUri: `http://localhost:${PORT}`,
  refreshToken: "someStringHere"
})
return hubspot.refreshAccessToken()
  .then(results => {
    console.log(results.access_token)
    console.log(hubspot.accessToken) // this assigns the new accessToken to the client, so your client is ready to use
    return hubspot.contacts.get()
  })
  .catch((err)=> console.log(err))

This method does not work. I'm getting a 400 BAD_REFRESH_TOKEN error message.

I've diagnosed the problem as a poor refreshToken when instantiating the client which throws an error before I can get to the refreshToken() method.. I searched the website and forums for what should be in the refresh token property... can't find anything.

Anyone know where I can find what should go here?

Endpoints status

@brainflake What endpoints are missing from the library? Can we create a list of them and add so we could start to work on adding it and creating test so maybe next major release has all endpoints?

Also they just release the new timeline and webhooks api's maybe it will be good to go ahead and start to think how to include there.

Fix flaky tests

Build is currently failing because some tests (workflows for example) rely on objects that are not guaranteed to exist in the sandbox.

client.contacts.createOrUpdate is missing from README

This function is missing from README:

client.contacts.createOrUpdate(email, data, cb)

See line 89 of index.js for definition.

I can confirm this function works, as I have used it in my own code.

I tried to make fork and PR, but hit some SSH issues that I didn't have time to fix.

'clientId' does not exist in type 'ApiOptions | AccessTokenOptions'.

This bug is about the new Hubspot constructor's signature being incorrect according to the TypeScript engine when taking an example from the documentation.

The steps to reproduce the bug are:

  1. Create a folder named node-hubspot-issue
  2. Open a terminal inside this folder and install the typescript & ts-node packages.
$ yarn add --dev typescript ts-node
  1. Install the node-hubspot package as well.
$ yarn add --dev node-hubspot
  1. Copy/paste the following content to a file named index.ts at the root of the node-hubspot-issue folder.
import Hubspot from 'hubspot';

const hubspot = new Hubspot({
  clientId: '...',
  clientSecret: '...',
  redirectUri: '...',
  refreshToken: '...'
});
  1. Inside the opened terminal, run the script.
$ yarn ts-node index.ts
  1. Observe the exception thrown in the terminal.

The expected behavior was to not have any errors thrown when using the constructor's signature from the documentation.

Here are some screenshots.
image
image

Environment:

  • ArchLinux: 241.7-2-arch
  • Docker: 18.09.2-ce, build 62479626f2
  • TypeScript: 3.3.3333
  • TS Node: 8.0.2
  • Hubspot: 2.1.1

Check API limit does not handle 10 requests / second limit

AS title suggests, if you're running a bunch of API calls, they'll start failing and you'll get the following error:

{“status”:“error”,“message”:“You have reached your secondly limit.”,“errorType”:“RATE_LIMIT”,“correlationId”:“6828d000-3d2f-4701-8d90-b21e53b0d005”,“policyName”:“SECONDLY”,“requestId”:“ce6a6a91-ac17-492f-bbdb-7d72477c319c”}

The API checker currently hits the APILimit end point, but that endpoint only handles the daily limit.

Not sure the best way to handle it, but a quick fix might be to use the .delay chain function on the request-promise object

feature request: workflows API

Hi,

I'm working on a project require the workflows feature. Any chance that supports this feature in the future?

Thanks.
Luke

Better management of api limit

Added a support for checking API limits:

But couple issues:

  • should probably be optional (makes a couple extra api calls + complicate the code)
  • doesn't seem to work with all scopes on OAuth (at least not with the ones I have)

Will try to do a cleaner implementation soon.

Broadcast.js throws syntax error in 1.3.4 due to comma after cb

Hi team

Just a minor suggestion - v1.3.4 updated automatically for us from NPM, and then we got a series of errors (copy below). I think it's the trailing comma @ https://github.com/MadKudu/node-hubspot/blob/1.3.4/lib/broadcast.js#L18

Suggest maybe unpublishing v1.3.4 rather than keep it live? Just to avoid others having same problem.

/var/app/current/node_modules/hubspot/lib/broadcast.js:19
)
^
SyntaxError: Unexpected token )
at Object.exports.runInThisContext (vm.js:73:16)
at Module._compile (module.js:543:28)
at Object.Module._extensions..js (module.js:580:10)
at Module.load (module.js:488:32)
at tryModuleLoad (module.js:447:12)
at Function.Module._load (module.js:439:3)
at Module.require (module.js:498:17)
at Module.require (/var/app/current/node_modules/require-in-the-middle/index.js:43:24)
at require (internal/module.js:20:19)
at Object. (/var/app/current/node_modules/hubspot/lib/client.js:1:81)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.