npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

desk.js

v0.1.4

Published

A wrapper for the desk.com API.

Downloads

6

Readme

desk.js Build Status Coverage Status Dependency Status

This is a node.js wrapper for the desk.com API (it might also work in the browser but only with either a proxy or from a desk.com case template). It'll only support APIv2 which is currently in heavy development, changes happen nearly twice to three times a week. We'll try to keep up with all the changes but things might break unexpectedly.

Now for the fun part ...

Example

This is a basic example of how to create a client and establish a connection. It shows the four request methods supported by the desk.com API (GET, POST, PATCH and DELETE).

var desk = require('desk.js')
  , client = desk.createClient({
  subdomain: 'devel',
  // optional include only if you use a custom domain - see below for details
  endpoint: 'https://support.example.com',
  // use it with basic auth
  username: '[email protected]',
  password: '12345',
  // use it with oauth
  consumerKey: 'this-is-my-consumer-key',
  consumerSecret: 'and-my-consumer-secret',
  token: 'my-access-token',
  tokenSecret: 'my-token-secret'
  // add a request logger
  logger: console,
  // allow retry
  retry: true
});

client.get('/api/v2/cases', function(err, json, response) {
  if (err) throw err;
  // the json is the response body already parsed
});

client.post('/api/v2/cases', myNewCaseHash, function(err, json, response) {
  if (err) throw err;
  // the json is the response body already parsed
});

client.patch('/api/v2/cases/1', { subject: 'New Subject' }, function(err, json, response) {
  if (err) throw err;
  // the json is the response body already parsed
});

// for resources like articles we also allow delete
client.delete('/api/v2/articles/1', function(err, response) {
  if (err) throw err;
  // response.statusCode === 201
});

Working with Resources and Collections

This wrapper also allows you to work with collections and resources, follow associations and more ...

Finders

// find a resource by url
client.cases().byUrl('/api/v2/cases/1', function(err, myCase) {
  if (err) throw err;
  // use myCase
});

// find a resource by id
client.cases().byId(1, function(err, myCase) {
  if (err) throw err;
  // use myCase
});

Pagination

// fetch the first page of the users collection
client.users(function(err, users) {
  // the result is an array with additional functions
  users.last(function(err, users) {
    // users is now pointed to the last page
    users.first(function(err, users) {
      // back to the first page
    });
  });
});

// you can also specify the pagination directly
client.users().perPage(10).page(5).exec(function(err, users) {
  // users would now be an array of user 51 - 60
  users.next(function(err, users) {
    // now it would be user 61 - 70
    users.previous(function(err, users) {
      // and we are back
    });
  });
});

We also support indicators now, so on any page you are you can check if that page has a next/previous page.

// fetch the first page of the cases collection
client.cases(function(err, cases) {
  // if we have more than 30 (default page size) cases
  // a check for a next page will be true
  cases.hasNextPage() === true;
  // however the first page will never have a previous page
  cases.hasPreviousPage() === false;
  // this turns on the last page
  cases.last(function(err, cases) {
    // we won't have a next page
    cases.hasNextPage() === false;
    // but we'll have a previous page
    cases.hasPreviousPage() === true;
  });
});

Pagination is pretty obvious but the cool part about pagination or rather resources is the auto-linking. As soon as the resource has a link defined, it'll be navigatable:

{
  "_links": {
    "self": {
      "href": "/api/v2/cases/1",
      "class": "case"
    },
    "message": {
      "href": "/api/v2/cases/1/message",
      "class": "message"
    },
    "customer": {
      "href": "/api/v2/customers/1",
      "class": "customer"
    },
    "assigned_user": {
      "href": "/api/v2/users/2",
      "class": "user"
    },
    "assigned_group": {
      "href": "/api/v2/groups/1",
      "class": "group"
    },
    "locked_by": null
  }
}
client.cases(function(err, cases) {
  cases[0].customer(function(err, customer) {
    // now you can use the customer
    // you could do an update
    customer.update({ first_name: 'John', last_name: 'Doe' }, function(err, customer) {
      console.log(customer.getFirstName());
      // => John
    });
  });
});

Create, Update and Delete

desk.js supports create, update and delete methods on resources. However if the API doesn't allow for example deletion, it won't be a method on the resource.

/**
 * Create
 */
client.articles().create({
  subject: 'Some Subject',
  body: 'Some Body',
  _links: {
    topic: {
      href: '/api/v2/topics/1',
      'class': 'topic'
    }
  }
}, function(err, article) {
  article.getSubject().should.equal('Some Subject');
  article.getBody().should.equal('Some Body');
});

/**
 * Update
 */
client.articles(function(err, articles) {
  articles[0].update({
    subject: 'Some other Subject'
  }, function(err, article) {
    // use article
  });
});

/**
 * Delete
 */
client.articles(function(err, articles) {
  articles[0].delete(function(err) {
    if (err) throw err;
    continue;
  });
});

/**
 * ATTENTION: Case doesn't allow deletion.
 */
client.cases().byId(25, function(err, myCase) {
  myCase.delete();
  // => Error: No method delete defined on Object Case.
});

Getters & Setters

For each field on the resource it'll automatically create getters and setters:

client.customers().byId(1, function(err, customer) {
  console.log(customer.getFirstName());
  console.log(customer.getLastName());
  console.log(customer.getTitle());
  // ...

  // for updates you can either use the setters
  customer.setFirstName('John');
  customer.update(function(err, customer) {})
  // or a hash
  customer.update({ first_name: 'John' }, function(err, customer) {})
});

Custom Domains

If your desk.com site uses a custom domain, you should use that URL as the basis for your api calls. If you set the endpoint property when creating your desk.com client, it will use this domain instead of the subdomain value to construct the URL. The URL should be a fully qualified path to your site (e.g. https://yoursupportsite.com)

var desk = require('desk')
  , client = desk.createClient({
  // Specify to use a custom domain
  endpoint: 'https://support.example.com',
  // use it with basic auth
  username: '[email protected]',
  password: '12345',
  // use it with oauth
  consumerKey: 'this-is-my-consumer-key',
  consumerSecret: 'and-my-consumer-secret',
  token: 'my-access-token',
  tokenSecret: 'my-token-secret'
  // add a request logger
  logger: console,
  // allow retry
  retry: true
});

API Errors

Sometimes the API is going to return errors, eg. Validation Error. In these cases we bubble the API error through to the callback so you can deal with it however you please.

client.cases().create({
  external_id: null,
  subject: 'Some Subject',
  priority: 5,
  description: 'Some Description',
  status: 'new',
  type: 'email',
  labels: ['ignore', 'spam'],
  language: null,
  custom_fields: {},
  message: {
    direction: 'in',
    status: 'received',
    body: 'Some Body',
    subject: 'Some Subject'
  },
  _links: {
    customer: {
      href: '/api/v2/customers/34290812',
      'class': 'customer'
    }
  }
}, function(err, myCase) {
  console.log(err.message);
  // => Message requires to, cc or bcc fields to be set to a non-blank value
});

License

Copyright (c) 2013-2014, Salesforce.com, Inc. All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  • Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

  • Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

  • Neither the name of Salesforce.com nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.