├── .gemtest ├── .rspec ├── spec ├── fixtures │ ├── article_destroy.json │ ├── macro_destroy.json │ ├── topic_destroy.json │ ├── macro.json │ ├── group.json │ ├── topic.json │ ├── macro_action.json │ ├── macro_action_update.json │ ├── macro_create.json │ ├── macro_update.json │ ├── topic_update.json │ ├── topic_create.json │ ├── user.json │ ├── customer_create_email.json │ ├── customer_update_email.json │ ├── groups.json │ ├── macros.json │ ├── users.json │ ├── topics.json │ ├── customer.json │ ├── customer_update.json │ ├── article.json │ ├── macro_actions.json │ ├── customer_create.json │ ├── case.json │ ├── case_update.json │ ├── article_create.json │ ├── article_update.json │ ├── articles.json │ ├── customers.json │ ├── interaction_create.json │ ├── cases.json │ └── interactions.json ├── desk │ ├── client_spec.rb │ ├── client │ │ ├── user_spec.rb │ │ ├── group_spec.rb │ │ ├── case_spec.rb │ │ ├── topic_spec.rb │ │ ├── article_spec.rb │ │ ├── customer_spec.rb │ │ ├── macro_spec.rb │ │ └── interaction_spec.rb │ └── api_spec.rb ├── faraday │ └── response_spec.rb ├── helper.rb └── desk_spec.rb ├── lib ├── desk │ ├── version.rb │ ├── authentication.rb │ ├── api.rb │ ├── client.rb │ ├── client │ │ ├── group.rb │ │ ├── user.rb │ │ ├── case.rb │ │ ├── topic.rb │ │ ├── interaction.rb │ │ ├── article.rb │ │ ├── macro.rb │ │ └── customer.rb │ ├── connection.rb │ ├── request.rb │ ├── error.rb │ └── configuration.rb ├── faraday │ ├── request │ │ ├── oauth.rb │ │ └── multipart_with_file.rb │ └── response │ │ ├── raise_http_5xx.rb │ │ └── raise_http_4xx.rb └── desk.rb ├── .gitignore ├── .yardopts ├── Gemfile ├── Rakefile ├── LICENSE.mkd ├── HISTORY.mkd ├── desk.gemspec └── README.mkd /.gemtest: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --format=nested 3 | --backtrace 4 | -------------------------------------------------------------------------------- /spec/fixtures/article_destroy.json: -------------------------------------------------------------------------------- 1 | { 2 | "success":true 3 | } -------------------------------------------------------------------------------- /spec/fixtures/macro_destroy.json: -------------------------------------------------------------------------------- 1 | { 2 | "success":true 3 | } -------------------------------------------------------------------------------- /spec/fixtures/topic_destroy.json: -------------------------------------------------------------------------------- 1 | { 2 | "success":true 3 | } -------------------------------------------------------------------------------- /lib/desk/version.rb: -------------------------------------------------------------------------------- 1 | module Desk 2 | # The version of the gem 3 | VERSION = '0.3.2'.freeze unless defined?(::Desk::VERSION) 4 | end 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | .DS_Store 4 | .bundle 5 | .rvmrc 6 | .yardoc 7 | Gemfile.lock 8 | coverage/* 9 | doc/* 10 | log/* 11 | pkg/* 12 | -------------------------------------------------------------------------------- /spec/fixtures/macro.json: -------------------------------------------------------------------------------- 1 | { 2 | "macro": 3 | { 4 | "id":13, 5 | "name":"API Macro", 6 | "enabled":true 7 | } 8 | } -------------------------------------------------------------------------------- /spec/fixtures/group.json: -------------------------------------------------------------------------------- 1 | { 2 | "group": { 3 | "id": 1, 4 | "name": "Sales", 5 | "created_at": "2013-01-21T09:47:23Z", 6 | "updated_at": "2013-01-21T09:47:23Z" 7 | } 8 | } -------------------------------------------------------------------------------- /.yardopts: -------------------------------------------------------------------------------- 1 | --no-private 2 | --protected 3 | --tag format:"Supported formats" 4 | --tag authenticated:"Requires Authentication" 5 | --tag rate_limited:"Rate Limited" 6 | --markup markdown 7 | - 8 | HISTORY.mkd 9 | LICENSE.mkd 10 | -------------------------------------------------------------------------------- /spec/fixtures/topic.json: -------------------------------------------------------------------------------- 1 | { 2 | "topic": 3 | { 4 | "id":1, 5 | "name":"General", 6 | "description":"Anything goes here", 7 | "show_in_portal":true 8 | } 9 | } -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "http://rubygems.org" 2 | 3 | gemspec 4 | 5 | 6 | group :development do 7 | #gem "ruby-debug19" 8 | gem 'json', '~> 1.7.7' 9 | end 10 | 11 | group :test do 12 | #gem "ruby-debug19" 13 | gem 'json', '~> 1.7.7' 14 | end -------------------------------------------------------------------------------- /spec/fixtures/macro_action.json: -------------------------------------------------------------------------------- 1 | { 2 | "action": 3 | { 4 | "id":201, 5 | "slug":"set-case-description", 6 | "value":"Description to be applied", 7 | "enabled":true 8 | } 9 | } -------------------------------------------------------------------------------- /spec/desk/client_spec.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | 3 | describe Desk::Client do 4 | it "should connect using the endpoint configuration" do 5 | client = Desk::Client.new 6 | endpoint = URI.parse(client.api_endpoint) 7 | connection = client.send(:connection).build_url("./").to_s.strip 8 | connection.should == endpoint.to_s 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /spec/fixtures/macro_action_update.json: -------------------------------------------------------------------------------- 1 | { 2 | "success":true, 3 | "results": 4 | { 5 | "action": 6 | { 7 | "slug":"set-case-description", 8 | "value":"Description to be applied", 9 | "enabled":true 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /spec/fixtures/macro_create.json: -------------------------------------------------------------------------------- 1 | { 2 | "success":true, 3 | "results": 4 | { 5 | "macro": 6 | { 7 | "id":12, 8 | "name":"API Macro", 9 | "enabled":true, 10 | "labels":"Escalated,Some Other Label" 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /spec/fixtures/macro_update.json: -------------------------------------------------------------------------------- 1 | { 2 | "success":true, 3 | "results": 4 | { 5 | "macro": 6 | { 7 | "id":13, 8 | "name":"Updated", 9 | "enabled":true, 10 | "labels":"Escalated,Some Other Label" 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /spec/fixtures/topic_update.json: -------------------------------------------------------------------------------- 1 | { 2 | "success":true, 3 | "results": 4 | { 5 | "topic": 6 | { 7 | "id":13, 8 | "name":"Updated", 9 | "description":"Updated Description", 10 | "show_in_portal":true 11 | } 12 | 13 | } 14 | } -------------------------------------------------------------------------------- /spec/fixtures/topic_create.json: -------------------------------------------------------------------------------- 1 | { 2 | "success":true, 3 | "results": 4 | { 5 | "topic": 6 | { 7 | "id":9, 8 | "name":"General", 9 | "description":"Everything belongs here", 10 | "show_in_portal":true 11 | } 12 | 13 | } 14 | } -------------------------------------------------------------------------------- /spec/fixtures/user.json: -------------------------------------------------------------------------------- 1 | { 2 | "user": 3 | { 4 | "id":1, 5 | "name":"Chris Warren", 6 | "name_public":"Chris", 7 | "email":"chris@zencoder.com", 8 | "created_at":"2009-11-25T14:58:39Z", 9 | "updated_at":"2010-07-19T02:36:21Z", 10 | "user_level":"sysadmin", 11 | "login_count":561, 12 | "time_zone":"Central Time (US & Canada)", 13 | "last_login_at":"2010-07-18T02:06:08Z" 14 | } 15 | } -------------------------------------------------------------------------------- /spec/fixtures/customer_create_email.json: -------------------------------------------------------------------------------- 1 | { 2 | "success":true, 3 | "results": 4 | { 5 | "email": 6 | { 7 | "created_at":"2011-02-09T17:06:22Z", 8 | "customer_contact_type":"work", 9 | "email":"api@example.com", 10 | "id":18, 11 | "updated_at":"2011-02-09T17:06:22Z", 12 | "verified_at":null 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /spec/fixtures/customer_update_email.json: -------------------------------------------------------------------------------- 1 | { 2 | "success":true, 3 | "results": 4 | { 5 | "email": 6 | { 7 | "created_at":"2011-02-09T17:06:22Z", 8 | "customer_contact_type":"work", 9 | "email":"api@example.com", 10 | "id":18, 11 | "updated_at":"2011-02-09T17:06:22Z", 12 | "verified_at":null 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /lib/desk/authentication.rb: -------------------------------------------------------------------------------- 1 | module Desk 2 | # @private 3 | module Authentication 4 | private 5 | 6 | # Authentication hash 7 | # 8 | # @return [Hash] 9 | def authentication 10 | { 11 | :consumer_key => consumer_key, 12 | :consumer_secret => consumer_secret, 13 | :token => oauth_token, 14 | :token_secret => oauth_token_secret 15 | } 16 | end 17 | 18 | # Check whether user is authenticated 19 | # 20 | # @return [Boolean] 21 | def authenticated? 22 | authentication.values.all? 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /spec/fixtures/groups.json: -------------------------------------------------------------------------------- 1 | { 2 | "results": [ 3 | { 4 | "group": { 5 | "id": 1, 6 | "name": "Sales", 7 | "created_at": "2013-01-21T09:47:23Z", 8 | "updated_at": "2013-01-21T09:47:23Z" 9 | } 10 | }, 11 | { 12 | "group": { 13 | "id": 2, 14 | "name": "Administrators", 15 | "created_at": "2013-01-21T09:47:23Z", 16 | "updated_at": "2013-01-21T09:47:23Z" 17 | } 18 | } 19 | ], 20 | "page": 1, 21 | "count": 20, 22 | "total": 2 23 | } -------------------------------------------------------------------------------- /lib/faraday/request/oauth.rb: -------------------------------------------------------------------------------- 1 | require 'faraday' 2 | 3 | module Faraday 4 | class Request::OAuth < Faraday::Middleware 5 | dependency 'simple_oauth' 6 | 7 | def call(env) 8 | params = env[:body] || {} 9 | 10 | signature_params = params.reject{ |k,v| v.respond_to?(:content_type) || (env[:method] == :put) } 11 | 12 | header = SimpleOAuth::Header.new(env[:method], env[:url], signature_params, @options) 13 | 14 | env[:request_headers]['Authorization'] = header.to_s 15 | 16 | @app.call(env) 17 | end 18 | 19 | def initialize(app, options) 20 | @app, @options = app, options 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'bundler' 2 | Bundler::GemHelper.install_tasks 3 | 4 | require 'rspec/core/rake_task' 5 | RSpec::Core::RakeTask.new(:spec) 6 | 7 | task :test => :spec 8 | task :default => :spec 9 | 10 | namespace :doc do 11 | require 'yard' 12 | YARD::Rake::YardocTask.new do |task| 13 | task.files = ['HISTORY.mkd', 'LICENSE.mkd', 'lib/**/*.rb'] 14 | task.options = [ 15 | '--protected', 16 | '--output-dir', 'doc/yard', 17 | '--tag', 'format:Supported formats', 18 | '--tag', 'authenticated:Requires Authentication', 19 | '--tag', 'rate_limited:Rate Limited', 20 | '--markup', 'markdown', 21 | ] 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /spec/fixtures/macros.json: -------------------------------------------------------------------------------- 1 | { 2 | "results": 3 | [ 4 | { 5 | "macro": 6 | { 7 | "id":11, 8 | "name":"Test Macro", 9 | "enabled":false 10 | } 11 | }, 12 | { 13 | "macro": 14 | { 15 | "id":12, 16 | "name":"API Macro", 17 | "enabled":true 18 | } 19 | } 20 | ], 21 | "page":1, 22 | "count":20, 23 | "total":2 24 | } -------------------------------------------------------------------------------- /lib/desk.rb: -------------------------------------------------------------------------------- 1 | require 'desk/error' 2 | require 'desk/configuration' 3 | require 'desk/api' 4 | require 'desk/client' 5 | require 'pony' 6 | 7 | module Desk 8 | extend Configuration 9 | @counter = 0 10 | @minute = Time.now.min 11 | 12 | class << self 13 | attr_accessor :counter, :minute 14 | 15 | # Alias for Desk::Client.new 16 | # 17 | # @return [Desk::Client] 18 | def client(options={}) 19 | Desk::Client.new(options) 20 | end 21 | 22 | # Delegate to Desk::Client 23 | def method_missing(method, *args, &block) 24 | return super unless client.respond_to?(method) 25 | client.send(method, *args, &block) 26 | end 27 | 28 | def respond_to?(method) 29 | client.respond_to?(method) || super 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /lib/faraday/request/multipart_with_file.rb: -------------------------------------------------------------------------------- 1 | require 'faraday' 2 | 3 | # @private 4 | module Faraday 5 | # @private 6 | class Request::MultipartWithFile < Faraday::Middleware 7 | def call(env) 8 | if env[:body].is_a?(Hash) 9 | env[:body].each do |key, value| 10 | if value.is_a?(File) 11 | env[:body][key] = Faraday::UploadIO.new(value, mime_type(value), value.path) 12 | end 13 | end 14 | end 15 | 16 | @app.call(env) 17 | end 18 | 19 | private 20 | 21 | def mime_type(file) 22 | case file.path 23 | when /\.jpe?g/i then 'image/jpeg' 24 | when /\.gif$/i then 'image/gif' 25 | when /\.png$/i then 'image/png' 26 | else 'application/octet-stream' 27 | end 28 | end 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /lib/desk/api.rb: -------------------------------------------------------------------------------- 1 | require 'desk/connection' 2 | require 'desk/request' 3 | require 'desk/authentication' 4 | 5 | module Desk 6 | # @private 7 | class API 8 | # @private 9 | attr_accessor *Configuration::VALID_OPTIONS_KEYS 10 | 11 | # Creates a new API 12 | def initialize(options={}) 13 | options = Desk.options.merge(options) 14 | 15 | Configuration::VALID_OPTIONS_KEYS.each do |key| 16 | send("#{key}=", options[key]) 17 | end 18 | end 19 | 20 | def endpoint 21 | if self.fulldomain.blank? 22 | return "https://#{self.subdomain}.desk.com/api/#{self.version}/" 23 | else 24 | return "#{self.fulldomain}/api/#{self.version}/" 25 | end 26 | end 27 | 28 | include Connection 29 | include Request 30 | include Authentication 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /lib/faraday/response/raise_http_5xx.rb: -------------------------------------------------------------------------------- 1 | require 'faraday' 2 | 3 | # @private 4 | module Faraday 5 | # @private 6 | class Response::RaiseHttp5xx < Response::Middleware 7 | def on_complete(env) 8 | case env[:status].to_i 9 | when 500 10 | raise Desk::InternalServerError.new(error_message(env, "Something is technically wrong."), env[:response_headers]) 11 | when 502 12 | raise Desk::BadGateway.new(error_message(env, "Desk.com is down or being upgraded."), env[:response_headers]) 13 | when 503 14 | raise Desk::ServiceUnavailable.new(error_message(env, "(__-){ Desk.com is over capacity."), env[:response_headers]) 15 | end 16 | end 17 | 18 | private 19 | 20 | def error_message(env, body=nil) 21 | "#{env[:method].to_s.upcase} #{env[:url].to_s}: #{[env[:status].to_s + ':', body].compact.join(' ')} Check http://desk.com/ for updates on the status of the Desk.com service." 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /spec/fixtures/users.json: -------------------------------------------------------------------------------- 1 | { 2 | "results": 3 | [ 4 | { 5 | "user": 6 | { 7 | "id":1, 8 | "name":"Test User", 9 | "name_public":"Test User", 10 | "email":"testuser@desk.com", 11 | "created_at":"2011-01-28T16:30:16Z", 12 | "updated_at":"2011-01-28T16:31:09Z", 13 | "user_level":"siteadmin_billing", 14 | "login_count":1, 15 | "time_zone":"Pacific Time (US & Canada)", 16 | "last_login_at":null, 17 | "current_login_at":"2011-01-28T16:30:16Z" 18 | } 19 | } 20 | ], 21 | "page":1, 22 | "count":20, 23 | "total":1 24 | } -------------------------------------------------------------------------------- /spec/faraday/response_spec.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | 3 | describe Faraday::Response do 4 | before do 5 | @client = Desk::Client.new 6 | end 7 | 8 | { 9 | 400 => Desk::BadRequest, 10 | 401 => Desk::Unauthorized, 11 | 403 => Desk::Forbidden, 12 | 404 => Desk::NotFound, 13 | 406 => Desk::NotAcceptable, 14 | 420 => Desk::EnhanceYourCalm, 15 | 500 => Desk::InternalServerError, 16 | 502 => Desk::BadGateway, 17 | 503 => Desk::ServiceUnavailable, 18 | }.each do |status, exception| 19 | context "when HTTP status is #{status}" do 20 | 21 | before do 22 | stub_get('users/1.json'). 23 | with(:headers => {'Accept'=>'application/json', 'User-Agent'=>Desk::Configuration::DEFAULT_USER_AGENT}). 24 | to_return(:status => status) 25 | end 26 | 27 | it "should raise #{exception.name} error" do 28 | lambda do 29 | @client.user(1) 30 | end.should raise_error(exception) 31 | end 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /LICENSE.mkd: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011 Chris Warren 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /lib/desk/client.rb: -------------------------------------------------------------------------------- 1 | module Desk 2 | # Wrapper for the Desk.com REST API 3 | # 4 | # @note All methods have been separated into modules and follow the same grouping used in {http://dev.desk.com/doc the Desk.com API Documentation}. 5 | # @see http://dev.desk.com/pages/every_developer 6 | class Client < API 7 | # Require client method modules after initializing the Client class in 8 | # order to avoid a superclass mismatch error, allowing those modules to be 9 | # Client-namespaced. 10 | require 'desk/client/user' 11 | require 'desk/client/group' 12 | require 'desk/client/interaction' 13 | require 'desk/client/case' 14 | require 'desk/client/customer' 15 | require 'desk/client/topic' 16 | require 'desk/client/article' 17 | require 'desk/client/macro' 18 | 19 | alias :api_endpoint :endpoint 20 | 21 | include Desk::Client::User 22 | include Desk::Client::Group 23 | include Desk::Client::Interaction 24 | include Desk::Client::Case 25 | include Desk::Client::Customer 26 | include Desk::Client::Topic 27 | include Desk::Client::Article 28 | include Desk::Client::Macro 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /lib/desk/client/group.rb: -------------------------------------------------------------------------------- 1 | module Desk 2 | class Client 3 | # Defines methods related to groups 4 | module Group 5 | # Returns extended information of groups 6 | # 7 | # @option options [Boolean, String, Integer] 8 | # @example Return extended information for 12345 9 | # Desk.groups 10 | # Desk.groups(:count => 5) 11 | # Desk.groups(:count => 5, :page => 3) 12 | # @format :json 13 | # @authenticated true 14 | # @see http://dev.desk.com/docs/api/groups 15 | def groups(*args) 16 | options = args.last.is_a?(Hash) ? args.pop : {} 17 | response = get("groups",options) 18 | response 19 | end 20 | 21 | # Returns extended information on a single group 22 | # 23 | # @param id [Integer] a group ID 24 | # @option options [Hash] 25 | # @example Return extended information for 12345 26 | # Desk.group(12345) 27 | # @format :json 28 | # @authenticated true 29 | # @see http://dev.desk.com/docs/api/groups/show 30 | def group(id) 31 | response = get("groups/#{id}") 32 | response.group 33 | end 34 | 35 | end 36 | end 37 | end -------------------------------------------------------------------------------- /spec/helper.rb: -------------------------------------------------------------------------------- 1 | require 'simplecov' 2 | SimpleCov.start do 3 | add_group 'Desk', 'lib/desk' 4 | add_group 'Faraday Middleware', 'lib/faraday' 5 | add_group 'Specs', 'spec' 6 | end 7 | require 'desk' 8 | require 'pony' 9 | require 'rspec' 10 | require 'webmock/rspec' 11 | require 'email_spec' 12 | #require 'ruby-debug' 13 | RSpec.configure do |config| 14 | config.include WebMock::API 15 | end 16 | 17 | def a_delete(path) 18 | a_request(:delete, Desk.endpoint + path) 19 | end 20 | 21 | def a_get(path) 22 | a_request(:get, Desk.endpoint + path) 23 | end 24 | 25 | def a_post(path) 26 | a_request(:post, Desk.endpoint + path) 27 | end 28 | 29 | def a_put(path) 30 | a_request(:put, Desk.endpoint + path) 31 | end 32 | 33 | def stub_delete(path) 34 | stub_request(:delete, Desk.endpoint + path) 35 | end 36 | 37 | def stub_get(path) 38 | stub_request(:get, Desk.endpoint + path) 39 | end 40 | 41 | def stub_post(path) 42 | stub_request(:post, Desk.endpoint + path) 43 | end 44 | 45 | def stub_put(path) 46 | stub_request(:put, Desk.endpoint + path) 47 | end 48 | 49 | def fixture_path 50 | File.expand_path("../fixtures", __FILE__) 51 | end 52 | 53 | def fixture(file) 54 | File.new(fixture_path + '/' + file) 55 | end 56 | -------------------------------------------------------------------------------- /lib/desk/connection.rb: -------------------------------------------------------------------------------- 1 | require 'faraday_middleware' 2 | require 'faraday/request/multipart_with_file' 3 | require 'faraday/response/raise_http_4xx' 4 | require 'faraday/response/raise_http_5xx' 5 | 6 | module Desk 7 | # @private 8 | module Connection 9 | private 10 | 11 | def connection(raw=false) 12 | options = { 13 | :headers => {'Accept' => "application/#{format}", 'User-Agent' => user_agent}, 14 | :proxy => proxy, 15 | :ssl => {:verify => false}, 16 | :url => api_endpoint, 17 | } 18 | 19 | Faraday.new(options) do |builder| 20 | builder.use Faraday::Request::MultipartWithFile 21 | builder.use Faraday::Request::OAuth, authentication if authenticated? 22 | builder.use Faraday::Request::Multipart 23 | builder.use Faraday::Request::UrlEncoded 24 | builder.use Faraday::Response::RaiseHttp4xx 25 | builder.use Faraday::Response::Rashify unless raw 26 | unless raw 27 | case format.to_s.downcase 28 | when 'json' 29 | builder.use Faraday::Response::ParseJson 30 | when 'xml' 31 | builder.use Faraday::Response::ParseXml 32 | end 33 | end 34 | builder.use Faraday::Response::RaiseHttp5xx 35 | builder.adapter(adapter) 36 | end 37 | end 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /spec/fixtures/topics.json: -------------------------------------------------------------------------------- 1 | { 2 | "results": 3 | [ 4 | { 5 | "topic": 6 | { 7 | "id":1, 8 | "name":"Sample :: General", 9 | "description":"Sample Topic Description :: Information about our company", 10 | "show_in_portal":true 11 | } 12 | }, 13 | { 14 | "topic": 15 | { 16 | "id":2, 17 | "name":"Canned Responses", 18 | "description":"Internal responses to common questions", 19 | "show_in_portal":false 20 | } 21 | }, 22 | { 23 | "topic": 24 | { 25 | "id":3, 26 | "name":"Sample :: Products", 27 | "description":"Sample Topic Description :: Information about our products", 28 | "show_in_portal":true 29 | } 30 | } 31 | ], 32 | "page":1, 33 | "count":20, 34 | "total":3 35 | } -------------------------------------------------------------------------------- /lib/faraday/response/raise_http_4xx.rb: -------------------------------------------------------------------------------- 1 | require 'faraday' 2 | 3 | # @private 4 | module Faraday 5 | # @private 6 | class Response::RaiseHttp4xx < Response::Middleware 7 | def on_complete(env) 8 | case env[:status].to_i 9 | when 400 10 | raise Desk::BadRequest.new(error_message(env), env[:response_headers]) 11 | when 401 12 | raise Desk::Unauthorized.new(error_message(env), env[:response_headers]) 13 | when 403 14 | raise Desk::Forbidden.new(error_message(env), env[:response_headers]) 15 | when 404 16 | raise Desk::NotFound.new(error_message(env), env[:response_headers]) 17 | when 406 18 | raise Desk::NotAcceptable.new(error_message(env), env[:response_headers]) 19 | when 420 20 | raise Desk::EnhanceYourCalm.new(error_message(env), env[:response_headers]) 21 | end 22 | end 23 | 24 | private 25 | 26 | def error_message(env) 27 | "#{env[:method].to_s.upcase} #{env[:url].to_s}: #{env[:status]}#{error_body(env[:body])}" 28 | end 29 | 30 | def error_body(body) 31 | if body.nil? 32 | nil 33 | elsif body['error'] 34 | ": #{body['error']}" 35 | elsif body['errors'] 36 | first = body['errors'].to_a.first 37 | if first.kind_of? Hash 38 | ": #{first['message'].chomp}" 39 | else 40 | ": #{first.chomp}" 41 | end 42 | end 43 | end 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /lib/desk/client/user.rb: -------------------------------------------------------------------------------- 1 | module Desk 2 | class Client 3 | # Defines methods related to users 4 | module User 5 | # Returns extended information of a given user 6 | # 7 | # @overload user(user, options={}) 8 | # @param user [Integer] An Assitely user ID 9 | # @option options [Boolean, String, Integer] :include_entities Include {http://dev.twitter.com/pages/tweet_entities Tweet Entities} when set to true, 't' or 1. 10 | # @return [Hashie::Mash] The requested user. 11 | # @example Return extended information for 12345 12 | # Desk.user(12345) 13 | # @format :json, :xml 14 | # @authenticated true 15 | # @see http://dev.desk.com/docs/api/users/show 16 | def user(id,*args) 17 | options = args.last.is_a?(Hash) ? args.pop : {} 18 | response = get("users/#{id}",options) 19 | response.user 20 | end 21 | 22 | # Returns extended information for up to 100 users 23 | # 24 | # @format :json, :xml 25 | # @authenticated true 26 | # @rate_limited true 27 | # @return [Array] The requested users. 28 | # @see http://dev.desk.com/docs/api/users 29 | # @example Return extended information account users 30 | # Desk.users 31 | def users(*args) 32 | options = args.last.is_a?(Hash) ? args.pop : {} 33 | response = get('users', options) 34 | response 35 | end 36 | end 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /lib/desk/request.rb: -------------------------------------------------------------------------------- 1 | module Desk 2 | # Defines HTTP request methods 3 | module Request 4 | # Perform an HTTP GET request 5 | def get(path, options={}, raw=false) 6 | request(:get, path, options, raw) 7 | end 8 | 9 | # Perform an HTTP POST request 10 | def post(path, options={}, raw=false) 11 | request(:post, path, options, raw) 12 | end 13 | 14 | # Perform an HTTP PUT request 15 | def put(path, options={}, raw=false) 16 | request(:put, path, options, raw) 17 | end 18 | 19 | # Perform an HTTP DELETE request 20 | def delete(path, options={}, raw=false) 21 | request(:delete, path, options, raw) 22 | end 23 | 24 | private 25 | 26 | def before_request 27 | if Desk.minute != Time.now.min 28 | Desk.minute = Time.now.min 29 | Desk.counter = 0 30 | end 31 | 32 | Desk.counter += 1 33 | if Desk.use_max_requests 34 | if Desk.counter > Desk.max_requests 35 | raise Desk::TooManyRequests 36 | end 37 | end 38 | end 39 | 40 | # Perform an HTTP request 41 | def request(method, path, options, raw=false) 42 | before_request 43 | response = connection(raw).send(method) do |request| 44 | case method 45 | when :get, :delete 46 | request.url(formatted_path(path), options) 47 | when :post, :put 48 | request.path = formatted_path(path) 49 | request.body = options unless options.empty? 50 | end 51 | end 52 | raw ? response : response.body 53 | end 54 | 55 | def formatted_path(path) 56 | [path, format].compact.join('.') 57 | end 58 | end 59 | end 60 | -------------------------------------------------------------------------------- /HISTORY.mkd: -------------------------------------------------------------------------------- 1 | 0.3.2 February 25, 2013 2 | ------------------ 3 | Support for groups (Thanks to @tstachl) 4 | Support for max_requests (Thanks to @tstachl) 5 | New multi_json and multi_xml version 6 | 7 | 0.3.0 July 15, 2012 8 | ------------------ 9 | Renamed from Assistly to Desk 10 | Updated Faraday and Hashie versions 11 | 12 | 0.2.6 October 3, 2011 13 | ------------------ 14 | Newer Faraday and multixml versions 15 | 16 | 0.2.5 August 17, 2011 17 | ------------------ 18 | Newer multijson version for Rails 3.1 compatibility 19 | 20 | 0.2.3 April 18, 2011 21 | ------------------ 22 | Removed deep_merge because it conflicts with rails. Handling the merge inside the create_outbound_interaction method only on headers for now, until we need it elsewhere. 23 | 24 | 0.2.2 - April 18, 2011 25 | ------------------ 26 | Added deep_merge! support so that we keep any custom email headers when creating outbound interactions. 27 | Required pony in the gem so you don't have to include it in your app. 28 | 29 | 0.2.1 - April 18, 2011 30 | ------------------ 31 | Stopped returning only the 'results' array when listing things, because this would cause us to not have access to page numbers, total counts, etc. 32 | 33 | 0.2.0 - April 16, 2011 34 | ------------------------- 35 | * Support for Topics, Articles, and Macros added 36 | * Update Readme 37 | 38 | 0.1.5 - April 15, 2011 39 | ------------------------- 40 | * Support for creating outbound interactions via email 41 | 42 | 0.1.1 - 0.1.4 - April 14, 2011 43 | ------------------------- 44 | * Miscellaneous bug fixes found when actually using this in production. 45 | * Moved to Zencoder Github account 46 | 47 | 0.1 - April 12, 2011 48 | ------------------------- 49 | * Initial release 50 | * Support for Case, Customer, Interaction, and User APIs. 51 | -------------------------------------------------------------------------------- /desk.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | require File.expand_path('../lib/desk/version', __FILE__) 3 | 4 | Gem::Specification.new do |s| 5 | #s.add_development_dependency('json', '~> 1.7') 6 | s.add_development_dependency('nokogiri', '~> 1.4') 7 | s.add_development_dependency('maruku', '~> 0.6') 8 | s.add_development_dependency('rake', '~> 0.8') 9 | s.add_development_dependency('rspec', '~> 2.5') 10 | s.add_development_dependency('email_spec', '~> 1.1.1') 11 | s.add_development_dependency('simplecov', '~> 0.4') 12 | s.add_development_dependency('webmock', '~> 1.6') 13 | s.add_development_dependency('yard', '~> 0.6') 14 | s.add_runtime_dependency('json', '~> 1.7') if RUBY_VERSION < '1.9' 15 | s.add_runtime_dependency('hashie', '~> 1.2.0') 16 | s.add_runtime_dependency('faraday', '~> 0.8.0') 17 | s.add_runtime_dependency('faraday_middleware', '~> 0.9.0') 18 | s.add_runtime_dependency('jruby-openssl', '~> 0.7.2') if RUBY_PLATFORM == 'java' 19 | s.add_runtime_dependency('multi_json', '~> 1.5') 20 | s.add_runtime_dependency('multi_xml', '~> 0.5') 21 | s.add_runtime_dependency('rash', '~> 0.3.1') 22 | s.add_runtime_dependency('simple_oauth', '~> 0.2.0') 23 | s.add_runtime_dependency('pony', '~> 1.1') 24 | s.authors = ["Chris Warren"] 25 | s.description = %q{A Ruby wrapper for the Desk.com REST API} 26 | s.email = ['chris@zencoder.com'] 27 | s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } 28 | s.files = `git ls-files`.split("\n") 29 | s.homepage = 'https://github.com/zencoder/desk' 30 | s.name = 'desk' 31 | s.platform = Gem::Platform::RUBY 32 | s.require_paths = ['lib'] 33 | s.required_rubygems_version = Gem::Requirement.new('>= 1.3.6') if s.respond_to? :required_rubygems_version= 34 | s.rubyforge_project = s.name 35 | s.summary = %q{Ruby wrapper for the Desk.com API} 36 | s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") 37 | s.version = Desk::VERSION.dup 38 | end 39 | -------------------------------------------------------------------------------- /spec/desk/client/user_spec.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | 3 | describe Desk::Client do 4 | Desk::Configuration::VALID_FORMATS.each do |format| 5 | context ".new(:format => '#{format}')" do 6 | before do 7 | @client = Desk::Client.new(:subdomain => "example", :format => format, :consumer_key => 'CK', :consumer_secret => 'CS', :oauth_token => 'OT', :oauth_token_secret => 'OS') 8 | end 9 | 10 | describe ".user" do 11 | 12 | context "with id passed" do 13 | 14 | before do 15 | stub_get("users/1.#{format}"). 16 | to_return(:body => fixture("user.#{format}"), :headers => {:content_type => "application/#{format}; charset=utf-8"}) 17 | end 18 | 19 | it "should get the correct resource" do 20 | @client.user(1) 21 | a_get("users/1.#{format}"). 22 | should have_been_made 23 | end 24 | 25 | it "should return extended information of a given user" do 26 | user = @client.user(1) 27 | user.name.should == "Chris Warren" 28 | end 29 | 30 | end 31 | end 32 | 33 | describe ".users" do 34 | 35 | context "lookup" do 36 | 37 | before do 38 | stub_get("users.#{format}"). 39 | to_return(:body => fixture("users.#{format}"), :headers => {:content_type => "application/#{format}; charset=utf-8"}) 40 | end 41 | 42 | it "should get the correct resource" do 43 | @client.users 44 | a_get("users.#{format}"). 45 | should have_been_made 46 | end 47 | 48 | it "should return up to 100 users worth of extended information" do 49 | users = @client.users 50 | users.results.should be_a Array 51 | users.results.first.user.name.should == "Test User" 52 | end 53 | 54 | end 55 | end 56 | end 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /spec/desk/client/group_spec.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | 3 | describe Desk::Client do 4 | Desk::Configuration::VALID_FORMATS.each do |format| 5 | context ".new(:format => '#{format}')" do 6 | before do 7 | @client = Desk::Client.new(:subdomain => "example", :format => format, :consumer_key => 'CK', :consumer_secret => 'CS', :oauth_token => 'OT', :oauth_token_secret => 'OS') 8 | end 9 | 10 | describe ".groups" do 11 | context "lookup" do 12 | before do 13 | stub_get("groups.#{format}"). 14 | to_return(:body => fixture("groups.#{format}"), :headers => {:content_type => "application/#{format}; charset=utf-8"}) 15 | end 16 | 17 | it "should get the correct resource" do 18 | @client.groups 19 | a_get("groups.#{format}"). 20 | should have_been_made 21 | end 22 | 23 | it "should return up to 100 groups worth of extended information" do 24 | groups = @client.groups 25 | groups.results.should be_a Array 26 | groups.results.last.group.id.should == 2 27 | groups.results.last.group.name.should == "Administrators" 28 | end 29 | end 30 | end 31 | 32 | describe ".group" do 33 | context "lookup" do 34 | before do 35 | stub_get("groups/1.#{format}"). 36 | to_return(:body => fixture("group.#{format}"), :headers => {:content_type => "application/#{format}; charset=utf-8"}) 37 | end 38 | 39 | it "should get the correct resource" do 40 | @client.group(1) 41 | a_get("groups/1.#{format}"). 42 | should have_been_made 43 | end 44 | 45 | it "should return up to 100 cases worth of extended information" do 46 | group = @client.group(1) 47 | group.id.should == 1 48 | group.name.should == "Sales" 49 | end 50 | end 51 | end 52 | end 53 | end 54 | end -------------------------------------------------------------------------------- /lib/desk/client/case.rb: -------------------------------------------------------------------------------- 1 | module Desk 2 | class Client 3 | # Defines methods related to cases 4 | module Case 5 | # Returns extended information of cases 6 | # 7 | # @option options [Boolean, String, Integer] 8 | # @example Return extended information for 12345 9 | # Desk.cases(:case_id => 12345) 10 | # Desk.cases(:email => "customer@example.com", :count => 5) 11 | # Desk.cases(:since_id => 12345) 12 | # @format :json 13 | # @authenticated true 14 | # @see http://dev.desk.com/docs/api/cases/show 15 | def cases(*args) 16 | options = args.last.is_a?(Hash) ? args.pop : {} 17 | response = get("cases",options) 18 | response 19 | end 20 | 21 | # Returns extended information on a single case 22 | # 23 | # @option options [String] 24 | # @example Return extended information for 12345 25 | # Desk.case(12345) 26 | # Desk.case(12345, :by => "external_id") 27 | # @format :json 28 | # @authenticated true 29 | # @see http://dev.desk.com/docs/api/cases/show 30 | def case(id, *args) 31 | options = args.last.is_a?(Hash) ? args.pop : {} 32 | response = get("cases/#{id}",options) 33 | response.case 34 | end 35 | 36 | # Updates a single case 37 | # 38 | # @option options [String] 39 | # @example Return extended information for 12345 40 | # Desk.update_case(12345, :subject => "New Subject") 41 | # @format :json 42 | # @authenticated true 43 | # @see http://dev.desk.com/docs/api/cases/update 44 | def update_case(id, *args) 45 | options = args.last.is_a?(Hash) ? args.pop : {} 46 | response = put("cases/#{id}",options) 47 | response.case 48 | end 49 | 50 | #todo: this needs to support custom subdomains 51 | def case_url(id) 52 | "https://#{subdomain}.desk.com/agent/case/#{id}" 53 | #"https://#{fulldomain}/agent/case/#{id}" 54 | end 55 | end 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /spec/desk/api_spec.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | 3 | describe Desk::API do 4 | before do 5 | @keys = Desk::Configuration::VALID_OPTIONS_KEYS 6 | end 7 | 8 | context "with module configuration" do 9 | 10 | before do 11 | Desk.configure do |config| 12 | @keys.each do |key| 13 | config.send("#{key}=", key) 14 | end 15 | end 16 | end 17 | 18 | after do 19 | Desk.reset 20 | end 21 | 22 | it "should inherit module configuration" do 23 | api = Desk::API.new 24 | @keys.each do |key| 25 | api.send(key).should == key 26 | end 27 | end 28 | 29 | context "with class configuration" do 30 | 31 | before do 32 | @configuration = { 33 | :consumer_key => 'CK', 34 | :consumer_secret => 'CS', 35 | :oauth_token => 'OT', 36 | :oauth_token_secret => 'OS', 37 | :adapter => :typhoeus, 38 | :format => :xml, 39 | :max_requests => 50, 40 | :proxy => 'http://erik:sekret@proxy.example.com:8080', 41 | :subdomain => 'zencoder', 42 | :fulldomain => 'https://zencoder.desk.com', 43 | :support_email => 'help@zencoder.com', 44 | :use_max_requests => true, 45 | :user_agent => 'Custom User Agent', 46 | :version => "amazing" 47 | } 48 | end 49 | 50 | context "during initialization" 51 | 52 | it "should override module configuration" do 53 | api = Desk::API.new(@configuration) 54 | @keys.each do |key| 55 | api.send(key).should == @configuration[key] 56 | end 57 | end 58 | 59 | context "after initilization" do 60 | 61 | it "should override module configuration after initialization" do 62 | api = Desk::API.new 63 | @configuration.each do |key, value| 64 | api.send("#{key}=", value) 65 | end 66 | @keys.each do |key| 67 | api.send(key).should == @configuration[key] 68 | end 69 | end 70 | end 71 | end 72 | end 73 | end 74 | -------------------------------------------------------------------------------- /spec/fixtures/customer.json: -------------------------------------------------------------------------------- 1 | { 2 | "customer": { 3 | "custom_test": null, 4 | "first_name": "Jeremy", 5 | "last_name": "Suriel", 6 | "phones": [ 7 | 8 | ], 9 | "custom_order": "4", 10 | "custom_t2": "", 11 | "custom_i": "", 12 | "emails": [ 13 | { 14 | "email": { 15 | "verified_at": "2009-11-27T21:41:11-05:00", 16 | "created_at": "2009-11-27T21:40:55-05:00", 17 | "updated_at": "2009-11-27T21:41:11-05:00", 18 | "customer_contact_type": "home", 19 | "email": "jeremysuriel@...com" 20 | } 21 | } 22 | ], 23 | "custom_t3": null, 24 | "custom_t": "", 25 | "addresses": [ 26 | { 27 | "address": { 28 | "region": "NY", 29 | "city": "Commack", 30 | "location": "67 Harned Road, Commack, NY 11725, USA", 31 | "created_at": "2009-12-22T16:21:23-05:00", 32 | "street_2": null, 33 | "country": "US", 34 | "updated_at": "2009-12-22T16:32:37-05:00", 35 | "postalcode": "11730", 36 | "street": "67 Harned Road", 37 | "lng": "-73.196225", 38 | "customer_contact_type": "home", 39 | "lat": "40.716894" 40 | } 41 | } 42 | ], 43 | "twitters": [ 44 | { 45 | "twitter": { 46 | "profile_image_url": "http://a3.twimg.com...", 47 | "created_at": "2009-11-25T10:35:56-05:00", 48 | "updated_at": "2010-05-29T22:41:55-04:00", 49 | "twitter_user_id": 12267802, 50 | "followers_count": 93, 51 | "verified": false, 52 | "login": "jrmey" 53 | } 54 | } 55 | ], 56 | "id": 8 57 | } 58 | } -------------------------------------------------------------------------------- /spec/fixtures/customer_update.json: -------------------------------------------------------------------------------- 1 | { 2 | "success":true, 3 | "results": 4 | { 5 | "customer": 6 | { 7 | "id":1, 8 | "first_name":"Joslyn", 9 | "last_name":"Esser", 10 | "emails": 11 | [ 12 | { 13 | "email": 14 | { 15 | "created_at":"2011-02-09T17:06:22Z", 16 | "customer_contact_type":"work", 17 | "email":"support@desk.com", 18 | "id":1, 19 | "updated_at":"2011-02-09T17:06:22Z", 20 | "verified_at":null 21 | } 22 | } 23 | ], 24 | "phones": 25 | [ 26 | ], 27 | "addresses": 28 | [ 29 | ], 30 | "twitters": 31 | [ 32 | { 33 | "twitter": 34 | { 35 | "created_at":"2011-02-09T17:06:23Z", 36 | "followers_count":896, 37 | "login":"Desk", 38 | "profile_image_url":"http://a2.twimg.com/profile_images/1231458746/Desk_Twitter_Profile_normal.png", 39 | "twitter_user_id":66799471, 40 | "updated_at":"2011-02-09T17:06:23Z", 41 | "verified":true 42 | } 43 | } 44 | ] 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /spec/fixtures/article.json: -------------------------------------------------------------------------------- 1 | { 2 | "article": 3 | { 4 | "id":13, 5 | "subject":"API Tips", 6 | "show_in_portal":true, 7 | "main_content":"Tips on using our API", 8 | "agent_content":null, 9 | "email":"Tips on using our API", 10 | "chat":"Tips on using our API", 11 | "twitter":"Tips on using our API", 12 | "question_answer":"Tips on using our API", 13 | "phone":"Tips on using our API", 14 | "quickcode":null, 15 | "created_by": 16 | { 17 | "user": 18 | { 19 | "id":1, 20 | "name":"API User", 21 | "name_public":"API User", 22 | "email":"apiuser@yoursite.com", 23 | "created_at":"2011-01-13T22:41:42Z", 24 | "updated_at":"2011-01-19T22:04:33Z", 25 | "user_level":"admin", 26 | "login_count":9, 27 | "time_zone":"Pacific Time (US & Canada)", 28 | "last_login_at":"2011-01-19T18:21:52Z", 29 | "current_login_at":"2011-01-19T22:04:33Z" 30 | } 31 | }, 32 | "updated_by": 33 | { 34 | "user": 35 | { 36 | "id":1, 37 | "name":"API User", 38 | "name_public":"API User", 39 | "email":"apiuser@yoursite.com", 40 | "created_at":"2011-01-13T22:41:42Z", 41 | "updated_at":"2011-01-19T22:04:33Z", 42 | "user_level":"admin", 43 | "login_count":9, 44 | "time_zone":"Pacific Time (US & Canada)", 45 | "last_login_at":"2011-01-19T18:21:52Z", 46 | "current_login_at":"2011-01-19T22:04:33Z" 47 | } 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /lib/desk/error.rb: -------------------------------------------------------------------------------- 1 | module Desk 2 | # Custom error class for rescuing from all Desk.com errors 3 | class Error < StandardError 4 | attr_reader :http_headers 5 | 6 | def initialize(message, http_headers) 7 | http_headers ||= {} 8 | @http_headers = Hash[http_headers] 9 | super message 10 | end 11 | 12 | def ratelimit_reset 13 | Time.at(@http_headers.values_at('x-ratelimit-reset', 'X-RateLimit-Reset').detect {|value| value }.to_i) 14 | end 15 | 16 | def ratelimit_limit 17 | @http_headers.values_at('x-ratelimit-limit', 'X-RateLimit-Limit').detect {|value| value }.to_i 18 | end 19 | 20 | def ratelimit_remaining 21 | @http_headers.values_at('x-ratelimit-limit', 'X-RateLimit-Limit').detect {|value| value }.to_i 22 | end 23 | 24 | def retry_after 25 | [(ratelimit_reset - Time.now).ceil, 0].max 26 | end 27 | end 28 | 29 | # Raised when Desk returns the HTTP status code 400 30 | class BadRequest < Error; end 31 | 32 | # Raised when Desk returns the HTTP status code 401 33 | class Unauthorized < Error; end 34 | 35 | # Raised when Desk returns the HTTP status code 403 36 | class Forbidden < Error; end 37 | 38 | # Raised when Desk returns the HTTP status code 404 39 | class NotFound < Error; end 40 | 41 | # Raised when Desk returns the HTTP status code 406 42 | class NotAcceptable < Error; end 43 | 44 | # Raised when Desk returns the HTTP status code 420 45 | class EnhanceYourCalm < Error 46 | # The number of seconds your application should wait before requesting date from the Search API again 47 | # 48 | # @see http://dev.desk.com/pages/rate-limiting 49 | def retry_after 50 | @http_headers.values_at('retry-after', 'Retry-After').detect {|value| value }.to_i 51 | end 52 | end 53 | 54 | # Raised when Desk max_requests is reached and use_max_requests is set to true 55 | class TooManyRequests < StandardError; end 56 | 57 | # Raised when Desk returns the HTTP status code 500 58 | class InternalServerError < Error; end 59 | 60 | # Raised when Desk returns the HTTP status code 502 61 | class BadGateway < Error; end 62 | 63 | # Raised when Desk returns the HTTP status code 503 64 | class ServiceUnavailable < Error; end 65 | 66 | # Gem Specific Errors 67 | class DeskError < StandardError; end 68 | 69 | class SupportEmailNotSet < DeskError; end 70 | end 71 | -------------------------------------------------------------------------------- /spec/fixtures/macro_actions.json: -------------------------------------------------------------------------------- 1 | { 2 | "results": 3 | [ 4 | { 5 | "action": 6 | { 7 | "slug":"set-case-description", 8 | "value":"Description to be applied", 9 | "enabled":true 10 | } 11 | }, 12 | { 13 | "action": 14 | { 15 | "slug":"set-case-priority", 16 | "value":"10", 17 | "enabled":true 18 | } 19 | }, 20 | { 21 | "action": 22 | { 23 | "slug":"set-case-status", 24 | "value":"", 25 | "enabled":false 26 | } 27 | }, 28 | { 29 | "action": 30 | { 31 | "slug":"set-case-group", 32 | "value":"", 33 | "enabled":false 34 | } 35 | }, 36 | { 37 | "action": 38 | { 39 | "slug":"set-case-user", 40 | "value":"", 41 | "enabled":false 42 | } 43 | }, 44 | { 45 | "action": 46 | { 47 | "slug":"set-case-quick-reply", 48 | "value":"", 49 | "enabled":false 50 | } 51 | }, 52 | { 53 | "action": 54 | { 55 | "slug":"append-case-article", 56 | "value":"", 57 | "enabled":false 58 | } 59 | }, 60 | { 61 | "action": 62 | { 63 | "slug":"set-case-outbound-email-subject", 64 | "value":"", 65 | "enabled":false 66 | } 67 | } 68 | ] 69 | } -------------------------------------------------------------------------------- /spec/fixtures/customer_create.json: -------------------------------------------------------------------------------- 1 | { 2 | "success":true, 3 | "results": 4 | { 5 | "customer": 6 | { 7 | "id":61, 8 | "first_name":"John", 9 | "last_name":"Smith", 10 | "emails": 11 | [ 12 | { 13 | "email": 14 | { 15 | "created_at":"2011-01-04T21:35:41Z", 16 | "customer_contact_type":"home", 17 | "email":"john.smith@desk.com", 18 | "updated_at":"2011-01-04T21:35:41Z", 19 | "verified_at":null 20 | } 21 | } 22 | ], 23 | "phones": 24 | [ 25 | { 26 | "phone": 27 | { 28 | "created_at":"2011-01-04T21:35:41Z", 29 | "customer_contact_type":"home", 30 | "phone":"123-456-7890", 31 | "updated_at":"2011-01-04T21:35:41Z" 32 | } 33 | } 34 | ], 35 | "addresses": 36 | [ 37 | ], 38 | "twitters": 39 | [ 40 | { 41 | "twitter": 42 | { 43 | "created_at":"2011-01-04T21:35:41Z", 44 | "followers_count":89, 45 | "login":"johnsmith", 46 | "profile_image_url":"http://a3.twimg.com/profile_images/58296943/eye_normal.jpg", 47 | "twitter_user_id":745073, 48 | "updated_at":"2011-01-04T21:35:41Z", 49 | "verified":false 50 | } 51 | } 52 | ], 53 | "custom_tier":"vip" 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /spec/fixtures/case.json: -------------------------------------------------------------------------------- 1 | { 2 | "case": 3 | { 4 | "id":1, 5 | "external_id":"123", 6 | "last_available_at":"2011-02-22T22:30:09Z", 7 | "created_at":"2011-02-09T17:06:24Z", 8 | "active_at":"2011-02-22T20:27:31Z", 9 | "route_at":"2011-02-09T17:06:24Z", 10 | "first_resolved_at":null, 11 | "active_user":null, 12 | "updated_at":"2011-02-22T22:30:09Z", 13 | "case_status_at":"2011-02-14T17:28:31Z", 14 | "priority":4, 15 | "last_saved_by_id":null, 16 | "interaction_in_at":"2011-02-09T09:06:27Z", 17 | "assigned_at":"2011-02-09T17:06:24Z", 18 | "subject":"Welcome to Desk.com", 19 | "routed_at":null, 20 | "group": 21 | { 22 | "id":1, 23 | "name":"General", 24 | "created_at":"2011-02-09T17:06:04Z", 25 | "updated_at":"2011-02-09T17:06:04Z" 26 | }, 27 | "user": 28 | { 29 | "id":1, 30 | "name":"Joslyn Esser", 31 | "name_public":"Joslyn Esser", 32 | "email":"joslyn@example.com", 33 | "created_at":"2011-02-09T17:02:46Z", 34 | "updated_at":"2011-02-16T19:25:10Z", 35 | "user_level":"agent", 36 | "login_count":15, 37 | "time_zone":"Pacific Time (US & Canada)", 38 | "last_login_at":"2011-02-15T23:32:51Z", 39 | "current_login_at":"2011-02-16T19:25:10Z" 40 | }, 41 | "first_opened_at":"2011-02-14T17:28:31Z", 42 | "channel":"email", 43 | "resolved_at":null, 44 | "description":null, 45 | "customer_id":1, 46 | "closed_at":null, 47 | "changed_at":"2011-02-14T17:28:31Z", 48 | "case_status_type":"open", 49 | "labels":["Example"], 50 | "pending_at":null, 51 | "opened_at":"2011-02-14T17:28:31Z", 52 | "route_status":"available", 53 | "thread_count":1, 54 | "note_count":0, 55 | "preview":"Thanks for trying Desk.com. We hope your trial goes well and you decide to use Desk.com to wow your customers. Please let us know if there is anything we can do to make your experience better. In the meantime see the tips below for getting started", 56 | "macros":null, 57 | "articles":null 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /spec/fixtures/case_update.json: -------------------------------------------------------------------------------- 1 | { 2 | "case": 3 | { 4 | "id":1, 5 | "external_id":"123", 6 | "last_available_at":"2011-02-22T22:30:09Z", 7 | "created_at":"2011-02-09T17:06:24Z", 8 | "active_at":"2011-02-22T20:27:31Z", 9 | "route_at":"2011-02-09T17:06:24Z", 10 | "first_resolved_at":null, 11 | "active_user":null, 12 | "updated_at":"2011-02-22T22:30:09Z", 13 | "case_status_at":"2011-02-14T17:28:31Z", 14 | "priority":4, 15 | "last_saved_by_id":null, 16 | "interaction_in_at":"2011-02-09T09:06:27Z", 17 | "assigned_at":"2011-02-09T17:06:24Z", 18 | "subject":"Welcome to Desk.com", 19 | "routed_at":null, 20 | "group": 21 | { 22 | "id":1, 23 | "name":"General", 24 | "created_at":"2011-02-09T17:06:04Z", 25 | "updated_at":"2011-02-09T17:06:04Z" 26 | }, 27 | "user": 28 | { 29 | "id":1, 30 | "name":"Joslyn Esser", 31 | "name_public":"Joslyn Esser", 32 | "email":"joslyn@example.com", 33 | "created_at":"2011-02-09T17:02:46Z", 34 | "updated_at":"2011-02-16T19:25:10Z", 35 | "user_level":"agent", 36 | "login_count":15, 37 | "time_zone":"Pacific Time (US & Canada)", 38 | "last_login_at":"2011-02-15T23:32:51Z", 39 | "current_login_at":"2011-02-16T19:25:10Z" 40 | }, 41 | "first_opened_at":"2011-02-14T17:28:31Z", 42 | "channel":"email", 43 | "resolved_at":null, 44 | "description":null, 45 | "customer_id":1, 46 | "closed_at":null, 47 | "changed_at":"2011-02-14T17:28:31Z", 48 | "case_status_type":"open", 49 | "labels":["Example"], 50 | "pending_at":null, 51 | "opened_at":"2011-02-14T17:28:31Z", 52 | "route_status":"available", 53 | "thread_count":1, 54 | "note_count":0, 55 | "preview":"Thanks for trying Desk.com. We hope your trial goes well and you decide to use Desk.com to wow your customers. Please let us know if there is anything we can do to make your experience better. In the meantime see the tips below for getting started", 56 | "macros":null, 57 | "articles":null 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /spec/fixtures/article_create.json: -------------------------------------------------------------------------------- 1 | { 2 | "success":true, 3 | "results": 4 | { 5 | "article": 6 | { 7 | "id":13, 8 | "subject":"API Tips", 9 | "show_in_portal":true, 10 | "main_content":"Tips on using our API", 11 | "agent_content":null, 12 | "email":"Tips on using our API", 13 | "chat":"Tips on using our API", 14 | "twitter":"Tips on using our API", 15 | "question_answer":"Tips on using our API", 16 | "phone":"Tips on using our API", 17 | "quickcode":null, 18 | "created_by": 19 | { 20 | "user": 21 | { 22 | "id":1, 23 | "name":"API User", 24 | "name_public":"API User", 25 | "email":"apiuser@yoursite.com", 26 | "created_at":"2011-01-13T22:41:42Z", 27 | "updated_at":"2011-01-19T22:04:33Z", 28 | "user_level":"admin", 29 | "login_count":9, 30 | "time_zone":"Pacific Time (US & Canada)", 31 | "last_login_at":"2011-01-19T18:21:52Z", 32 | "current_login_at":"2011-01-19T22:04:33Z" 33 | } 34 | }, 35 | "updated_by": 36 | { 37 | "user": 38 | { 39 | "id":1, 40 | "name":"API User", 41 | "name_public":"API User", 42 | "email":"apiuser@yoursite.com", 43 | "created_at":"2011-01-13T22:41:42Z", 44 | "updated_at":"2011-01-19T22:04:33Z", 45 | "user_level":"admin", 46 | "login_count":9, 47 | "time_zone":"Pacific Time (US & Canada)", 48 | "last_login_at":"2011-01-19T18:21:52Z", 49 | "current_login_at":"2011-01-19T22:04:33Z" 50 | } 51 | } 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /spec/fixtures/article_update.json: -------------------------------------------------------------------------------- 1 | { 2 | "success":true, 3 | "results": 4 | { 5 | "article": 6 | { 7 | "id":13, 8 | "subject":"API Tips", 9 | "show_in_portal":true, 10 | "main_content":"Tips on using our API", 11 | "agent_content":null, 12 | "email":"Tips on using our API", 13 | "chat":"Tips on using our API", 14 | "twitter":"Tips on using our API", 15 | "question_answer":"Tips on using our API", 16 | "phone":"Tips on using our API", 17 | "quickcode":null, 18 | "created_by": 19 | { 20 | "user": 21 | { 22 | "id":1, 23 | "name":"API User", 24 | "name_public":"API User", 25 | "email":"apiuser@yoursite.com", 26 | "created_at":"2011-01-13T22:41:42Z", 27 | "updated_at":"2011-01-19T22:04:33Z", 28 | "user_level":"admin", 29 | "login_count":9, 30 | "time_zone":"Pacific Time (US & Canada)", 31 | "last_login_at":"2011-01-19T18:21:52Z", 32 | "current_login_at":"2011-01-19T22:04:33Z" 33 | } 34 | }, 35 | "updated_by": 36 | { 37 | "user": 38 | { 39 | "id":1, 40 | "name":"API User", 41 | "name_public":"API User", 42 | "email":"apiuser@yoursite.com", 43 | "created_at":"2011-01-13T22:41:42Z", 44 | "updated_at":"2011-01-19T22:04:33Z", 45 | "user_level":"admin", 46 | "login_count":9, 47 | "time_zone":"Pacific Time (US & Canada)", 48 | "last_login_at":"2011-01-19T18:21:52Z", 49 | "current_login_at":"2011-01-19T22:04:33Z" 50 | } 51 | } 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /lib/desk/client/topic.rb: -------------------------------------------------------------------------------- 1 | module Desk 2 | class Client 3 | # Defines methods related to topics 4 | module Topic 5 | # Returns extended information of topics 6 | # 7 | # @option options [Boolean, String, Integer] 8 | # @example Return extended information for 12345 9 | # Desk.topics 10 | # Desk.topics(:count => 5) 11 | # Desk.topics(:count => 5, :page => 3) 12 | # @format :json 13 | # @authenticated true 14 | # @see http://dev.desk.com/docs/api/topics/show 15 | def topics(*args) 16 | options = args.last.is_a?(Hash) ? args.pop : {} 17 | response = get("topics",options) 18 | response 19 | end 20 | 21 | # Returns extended information on a single topic 22 | # 23 | # @param id [Integer] a topic ID 24 | # @option options [Hash] 25 | # @example Return extended information for 12345 26 | # Desk.topic(12345) 27 | # Desk.topic(12345, :by => "external_id") 28 | # @format :json 29 | # @authenticated true 30 | # @see http://dev.desk.com/docs/api/topics/show 31 | def topic(id, *args) 32 | options = args.last.is_a?(Hash) ? args.pop : {} 33 | response = get("topics/#{id}",options) 34 | response.topic 35 | end 36 | 37 | # Creates a new topic 38 | # 39 | # @param name [String] A topic name 40 | # @option options [Hash] 41 | # @example Creates a new topic 42 | # Desk.create_topic("name") 43 | # Desk.create_topic("name", :description => "description") 44 | # @format :json 45 | # @authenticated true 46 | # @see http://dev.desk.com/docs/api/topics/create 47 | def create_topic(name, *args) 48 | options = args.last.is_a?(Hash) ? args.pop : {} 49 | response = post("topics",options) 50 | if response['success'] 51 | return response['results']['topic'] 52 | else 53 | return response 54 | end 55 | end 56 | 57 | # Updates a single topic 58 | # 59 | # @param id [Integer] a topic ID 60 | # @option options [String] 61 | # @example Updates information for topic 12345 62 | # Desk.update_topic(12345, :subject => "New Subject") 63 | # @format :json 64 | # @authenticated true 65 | # @see http://dev.desk.com/docs/api/topics/update 66 | def update_topic(id, *args) 67 | options = args.last.is_a?(Hash) ? args.pop : {} 68 | response = put("topics/#{id}",options) 69 | if response['success'] 70 | return response['results']['topic'] 71 | else 72 | return response 73 | end 74 | end 75 | 76 | # Deletes a single topic 77 | # 78 | # @param id [Integer] a topic ID 79 | # @example Deletes topic 12345 80 | # Desk.update_topic(12345, :subject => "New Subject") 81 | # @format :json 82 | # @authenticated true 83 | # @see http://dev.desk.com/docs/api/topics/update 84 | def delete_topic(id) 85 | response = delete("topics/#{id}") 86 | response 87 | end 88 | end 89 | end 90 | end 91 | -------------------------------------------------------------------------------- /lib/desk/client/interaction.rb: -------------------------------------------------------------------------------- 1 | module Desk 2 | class Client 3 | # Defines methods related to interactions 4 | module Interaction 5 | 6 | # Returns extended information of up to 100 interactions 7 | # 8 | # @option options [Boolean, String, Integer] 9 | # @example Return extended information for 12345 10 | # Desk.interactions(:since_id => 12345) 11 | # Desk.interactions(:since_id => 12345, :count => 5) 12 | # @format :json 13 | # @authenticated true 14 | # @see http://dev.desk.com/docs/api/interactions 15 | def interactions(*args) 16 | options = args.last.is_a?(Hash) ? args.pop : {} 17 | response = get("interactions",options) 18 | response 19 | end 20 | 21 | def create_interaction(*args) 22 | options = args.last.is_a?(Hash) ? args.pop : {} 23 | if options[:direction].to_s == "outbound" 24 | options.delete(:direction) 25 | to = options.delete(:customer_email) 26 | subject = options.delete(:interaction_subject) 27 | body = options.delete(:interaction_body) 28 | 29 | create_outbound_interaction(to, subject, body, options) 30 | else 31 | create_inbound_interaction(options) 32 | end 33 | end 34 | 35 | # Creates an interaction from a customer 36 | # 37 | # @format :json 38 | # @authenticated true 39 | # @rate_limited true 40 | # @return [Array] The requested users. 41 | # @see http://dev.desk.com/docs/api/interactions/create 42 | # @example Create a new interaction 43 | # Desk.create_interaction(:interaction_subject => "this is an api test", :customer_email => "foo@example.com") 44 | def create_inbound_interaction(*args) 45 | options = args.last.is_a?(Hash) ? args.pop : {} 46 | response = post('interactions', options) 47 | if response['success'] 48 | return response['results'] 49 | else 50 | return response 51 | end 52 | end 53 | 54 | # Create an interaction from an agent 55 | # 56 | # Desk's API doesn't support creating a new case/interaction initiated by an agent 57 | # so we'll use send an email to the customer directly that is BCC'd to the support email address 58 | # which will create the ticket 59 | # 60 | # @see http://support.desk.com/customer/portal/articles/4180 61 | # @see http://support.desk.com/customer/portal/articles/6728 62 | def create_outbound_interaction(to, subject, body, *args) 63 | raise Desk::SupportEmailNotSet if support_email.blank? 64 | options = args.last.is_a?(Hash) ? args.pop : {} 65 | options.merge!(:to => to, :subject => subject, :body => body, :from => support_email, :bcc => support_email) 66 | headers = { "x-assistly-customer-email" => to, 67 | "x-assistly-interaction-direction" => "out", 68 | "x-assistly-case-status" => options[:status]||"open"} 69 | headers.merge!(options[:headers]) if options[:headers] 70 | options.merge!(:headers => headers) 71 | Pony.mail(options) 72 | end 73 | end 74 | end 75 | end 76 | -------------------------------------------------------------------------------- /spec/fixtures/articles.json: -------------------------------------------------------------------------------- 1 | { 2 | "results": 3 | [ 4 | { 5 | "article": 6 | { 7 | "id":13, 8 | "subject":"API Tips", 9 | "show_in_portal":true, 10 | "main_content":"Tips on using our API", 11 | "agent_content":null, 12 | "email":"Tips on using our API", 13 | "chat":"Tips on using our API", 14 | "twitter":"Tips on using our API", 15 | "question_answer":"Tips on using our API", 16 | "phone":"Tips on using our API", 17 | "quickcode":null, 18 | "created_by": 19 | { 20 | "user": 21 | { 22 | "id":1, 23 | "name":"API User", 24 | "name_public":"API User", 25 | "email":"apiuser@yoursite.com", 26 | "created_at":"2011-01-13T22:41:42Z", 27 | "updated_at":"2011-01-19T22:04:33Z", 28 | "user_level":"admin", 29 | "login_count":9, 30 | "time_zone":"Pacific Time (US & Canada)", 31 | "last_login_at":"2011-01-19T18:21:52Z", 32 | "current_login_at":"2011-01-19T22:04:33Z" 33 | } 34 | }, 35 | "updated_by": 36 | { 37 | "user": 38 | { 39 | "id":1, 40 | "name":"API User", 41 | "name_public":"API User", 42 | "email":"apiuser@yoursite.com", 43 | "created_at":"2011-01-13T22:41:42Z", 44 | "updated_at":"2011-01-19T22:04:33Z", 45 | "user_level":"admin", 46 | "login_count":9, 47 | "time_zone":"Pacific Time (US & Canada)", 48 | "last_login_at":"2011-01-19T18:21:52Z", 49 | "current_login_at":"2011-01-19T22:04:33Z" 50 | } 51 | } 52 | } 53 | } 54 | ], 55 | "page":1, 56 | "count":20, 57 | "total":1 58 | } -------------------------------------------------------------------------------- /spec/desk/client/case_spec.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | 3 | describe Desk::Client do 4 | Desk::Configuration::VALID_FORMATS.each do |format| 5 | context ".new(:format => '#{format}')" do 6 | before do 7 | @client = Desk::Client.new(:subdomain => "example", :format => format, :consumer_key => 'CK', :consumer_secret => 'CS', :oauth_token => 'OT', :oauth_token_secret => 'OS') 8 | end 9 | 10 | describe ".cases" do 11 | 12 | context "lookup" do 13 | 14 | before do 15 | stub_get("cases.#{format}"). 16 | to_return(:body => fixture("cases.#{format}"), :headers => {:content_type => "application/#{format}; charset=utf-8"}) 17 | end 18 | 19 | it "should get the correct resource" do 20 | @client.cases 21 | a_get("cases.#{format}"). 22 | should have_been_made 23 | end 24 | 25 | it "should return up to 100 cases worth of extended information" do 26 | cases = @client.cases 27 | 28 | cases.results.should be_a Array 29 | cases.results.first.case.id.should == 1 30 | cases.results.first.case.user.name.should == "Jeremy Suriel" 31 | end 32 | 33 | end 34 | end 35 | 36 | describe ".case" do 37 | 38 | context "lookup" do 39 | 40 | before do 41 | stub_get("cases/1.#{format}"). 42 | to_return(:body => fixture("case.#{format}"), :headers => {:content_type => "application/#{format}; charset=utf-8"}) 43 | end 44 | 45 | it "should get the correct resource" do 46 | @client.case(1) 47 | a_get("cases/1.#{format}"). 48 | should have_been_made 49 | end 50 | 51 | it "should return up to 100 cases worth of extended information" do 52 | a_case = @client.case(1) 53 | 54 | a_case.id.should == 1 55 | a_case.external_id.should == "123" 56 | a_case.subject.should == "Welcome to Desk.com" 57 | end 58 | 59 | end 60 | end 61 | 62 | describe ".update_case" do 63 | 64 | context "update" do 65 | 66 | before do 67 | stub_put("cases/1.#{format}"). 68 | to_return(:body => fixture("case_update.#{format}"), :headers => {:content_type => "application/#{format}; charset=utf-8"}) 69 | end 70 | 71 | it "should get the correct resource" do 72 | @client.update_case(1, :subject => "Welcome to Desk") 73 | a_put("cases/1.#{format}"). 74 | should have_been_made 75 | end 76 | 77 | it "should return up to 100 cases worth of extended information" do 78 | a_case = @client.update_case(1, :subject => "Welcome to Desk.com") 79 | 80 | a_case.id.should == 1 81 | a_case.subject.should == "Welcome to Desk.com" 82 | end 83 | 84 | end 85 | end 86 | 87 | describe ".case_url" do 88 | 89 | context "generating a case url" do 90 | 91 | it "should make a correct url for the case" do 92 | @client.case_url(123).should == "https://example.desk.com/agent/case/123" 93 | end 94 | 95 | end 96 | end 97 | end 98 | end 99 | end 100 | -------------------------------------------------------------------------------- /lib/desk/client/article.rb: -------------------------------------------------------------------------------- 1 | module Desk 2 | class Client 3 | # Defines methods related to articles 4 | module Article 5 | # Returns extended information of articles for a topic 6 | # 7 | # @param id [Integer] a article ID 8 | # @option options [Boolean, String, Integer] 9 | # @example Return extended information for 12345 10 | # Desk.articles 11 | # Desk.articles(:count => 5) 12 | # Desk.articles(:count => 5, :page => 3) 13 | # @format :json 14 | # @authenticated true 15 | # @see http://dev.desk.com/docs/api/topics/articles 16 | def articles(id, *args) 17 | options = args.last.is_a?(Hash) ? args.pop : {} 18 | response = get("topics/#{id}/articles",options) 19 | response 20 | end 21 | 22 | # Returns extended information on a single article 23 | # 24 | # @param id [Integer] a article ID 25 | # @option options [Hash] 26 | # @example Return extended information for 12345 27 | # Desk.article(12345) 28 | # Desk.article(12345, :by => "external_id") 29 | # @format :json 30 | # @authenticated true 31 | # @see http://dev.desk.com/docs/api/articles/show 32 | def article(id, *args) 33 | options = args.last.is_a?(Hash) ? args.pop : {} 34 | response = get("articles/#{id}",options) 35 | response.article 36 | end 37 | 38 | # Creates a new article 39 | # 40 | # @param id [Integer] a article ID 41 | # @param id [Integer] a article ID 42 | # @param id [Integer] a article ID 43 | # @option options [Hash] 44 | # @example Creates a new article 45 | # Desk.create_article(1, :subject => "API Tips", :main_content => "Tips on using our API") 46 | # @format :json 47 | # @authenticated true 48 | # @see http://dev.desk.com/docs/api/articles/create 49 | def create_article(topic_id, *args) 50 | options = args.last.is_a?(Hash) ? args.pop : {} 51 | response = post("topics/#{topic_id}/articles",options) 52 | if response['success'] 53 | return response['results']['article'] 54 | else 55 | return response 56 | end 57 | end 58 | 59 | # Updates a single article 60 | # 61 | # @param id [Integer] a article ID 62 | # @option options [String] 63 | # @example Updates information for article 12345 64 | # Desk.update_article(12345, :subject => "New Subject") 65 | # @format :json 66 | # @authenticated true 67 | # @see http://dev.desk.com/docs/api/articles/update 68 | def update_article(id, *args) 69 | options = args.last.is_a?(Hash) ? args.pop : {} 70 | response = put("articles/#{id}",options) 71 | if response['success'] 72 | return response['results']['article'] 73 | else 74 | return response 75 | end 76 | end 77 | 78 | # Deletes a single article 79 | # 80 | # @param id [Integer] a article ID 81 | # @example Deletes article 12345 82 | # Desk.update_article(12345, :subject => "New Subject") 83 | # @format :json 84 | # @authenticated true 85 | # @see http://dev.desk.com/docs/api/articles/update 86 | def delete_article(id) 87 | response = delete("articles/#{id}") 88 | response 89 | end 90 | end 91 | end 92 | end 93 | -------------------------------------------------------------------------------- /spec/fixtures/customers.json: -------------------------------------------------------------------------------- 1 | { 2 | "page": 1, 3 | "count": 20, 4 | "total": 541, 5 | "results": [ 6 | { 7 | "customer": { 8 | "custom_test": null, 9 | "addresses": [ 10 | { 11 | "address": { 12 | "region": "NY", 13 | "city": "Commack", 14 | "location": "67 Harned Road, Commack, NY 11725, USA", 15 | "created_at": "2009-12-22T16:21:23-05:00", 16 | "street_2": null, 17 | "country": "US", 18 | "updated_at": "2009-12-22T16:32:37-05:00", 19 | "postalcode": "11725", 20 | "street": "67 Harned Road", 21 | "lng": "-73.196225", 22 | "customer_contact_type": "home", 23 | "lat": "40.716894" 24 | } 25 | } 26 | ], 27 | "phones": [ 28 | 29 | ], 30 | "last_name": "Suriel", 31 | "custom_order": "4", 32 | "first_name": "Jeremy", 33 | "custom_t2": "", 34 | "custom_i": "", 35 | "custom_t3": null, 36 | "custom_t": "", 37 | "emails": [ 38 | { 39 | "email": { 40 | "verified_at": "2009-11-27T21:41:11-05:00", 41 | "created_at": "2009-11-27T21:40:55-05:00", 42 | "updated_at": "2009-11-27T21:41:11-05:00", 43 | "customer_contact_type": "home", 44 | "email": "jeremysuriel+twitter@gmail.com" 45 | } 46 | } 47 | ], 48 | "id": 8, 49 | "twitters": [ 50 | { 51 | "twitter": { 52 | "profile_image_url": "http://a3.twimg.com...", 53 | "created_at": "2009-11-25T10:35:56-05:00", 54 | "updated_at": "2010-05-29T22:41:55-04:00", 55 | "twitter_user_id": 12267802, 56 | "followers_count": 93, 57 | "verified": false, 58 | "login": "jrmey" 59 | } 60 | } 61 | ] 62 | } 63 | }, 64 | { 65 | "customer": { 66 | "custom_test": null, 67 | "addresses": [ 68 | 69 | ], 70 | "phones": [ 71 | 72 | ], 73 | "last_name": "", 74 | "custom_order": null, 75 | "first_name": "jeremy@example.com", 76 | "custom_t2": null, 77 | "custom_i": null, 78 | "custom_t3": null, 79 | "custom_t": null, 80 | "emails": [ 81 | { 82 | "email": { 83 | "verified_at": null, 84 | "created_at": "2009-12-05T20:39:00-05:00", 85 | "updated_at": "2009-12-05T20:39:00-05:00", 86 | "customer_contact_type": "home", 87 | "email": "jeremy@example.com" 88 | } 89 | } 90 | ], 91 | "id": 27, 92 | "twitters": [ 93 | null 94 | ] 95 | } 96 | } 97 | ] 98 | } -------------------------------------------------------------------------------- /lib/desk/configuration.rb: -------------------------------------------------------------------------------- 1 | require 'faraday' 2 | require 'desk/version' 3 | 4 | module Desk 5 | # Defines constants and methods related to configuration 6 | module Configuration 7 | # An array of valid keys in the options hash when configuring a {Twitter::API} 8 | VALID_OPTIONS_KEYS = [ 9 | :adapter, 10 | :consumer_key, 11 | :consumer_secret, 12 | :format, 13 | :max_requests, 14 | :oauth_token, 15 | :oauth_token_secret, 16 | :proxy, 17 | :subdomain, 18 | :fulldomain, 19 | :support_email, 20 | :use_max_requests, 21 | :user_agent, 22 | :version].freeze 23 | 24 | # An array of valid request/response formats 25 | # 26 | # @note Not all methods support the XML format. 27 | VALID_FORMATS = [ 28 | :json].freeze 29 | 30 | # The adapter that will be used to connect if none is set 31 | # 32 | # @note The default faraday adapter is Net::HTTP. 33 | DEFAULT_ADAPTER = Faraday.default_adapter 34 | 35 | # By default, don't set an application key 36 | DEFAULT_CONSUMER_KEY = nil 37 | 38 | # By default, don't set an application secret 39 | DEFAULT_CONSUMER_SECRET = nil 40 | 41 | # The response format appended to the path and sent in the 'Accept' header if none is set 42 | # 43 | # @note JSON is preferred over XML because it is more concise and faster to parse. 44 | DEFAULT_FORMAT = :json 45 | 46 | # By default, set the max requests to 60 per minute 47 | DEFAULT_MAX_REQUESTS = 60 48 | 49 | # By default, don't use the max request feature 50 | DEFAULT_USE_MAX_REQUESTS = false 51 | 52 | # By default, don't set a user oauth token 53 | DEFAULT_OAUTH_TOKEN = nil 54 | 55 | # By default, don't set a user oauth secret 56 | DEFAULT_OAUTH_TOKEN_SECRET = nil 57 | 58 | # By default, don't use a proxy server 59 | DEFAULT_PROXY = nil 60 | 61 | # By default use example 62 | DEFAULT_SUBDOMAIN = "example" 63 | 64 | # Option for vanity domains with custom ssl certificates 65 | DEFAULT_FULLDOMAIN = "" 66 | 67 | # The user agent that will be sent to the API endpoint if none is set 68 | DEFAULT_USER_AGENT = "Desk.com Ruby Gem #{Desk::VERSION}".freeze 69 | 70 | # The user agent that will be sent to the API endpoint if none is set 71 | DEFAULT_VERSION = "v1".freeze 72 | 73 | # By default, don't set a support email address 74 | DEFAULT_SUPPORT_EMAIL = nil 75 | 76 | # @private 77 | attr_accessor *VALID_OPTIONS_KEYS 78 | 79 | # When this module is extended, set all configuration options to their default values 80 | def self.extended(base) 81 | base.reset 82 | end 83 | 84 | # Convenience method to allow configuration options to be set in a block 85 | def configure 86 | yield self 87 | end 88 | 89 | # Create a hash of options and their values 90 | def options 91 | Hash[VALID_OPTIONS_KEYS.map {|key| [key, send(key)] }] 92 | end 93 | 94 | # Reset all configuration options to defaults 95 | def reset 96 | self.adapter = DEFAULT_ADAPTER 97 | self.consumer_key = DEFAULT_CONSUMER_KEY 98 | self.consumer_secret = DEFAULT_CONSUMER_SECRET 99 | self.format = DEFAULT_FORMAT 100 | self.max_requests = DEFAULT_MAX_REQUESTS 101 | self.oauth_token = DEFAULT_OAUTH_TOKEN 102 | self.oauth_token_secret = DEFAULT_OAUTH_TOKEN_SECRET 103 | self.proxy = DEFAULT_PROXY 104 | self.subdomain = DEFAULT_SUBDOMAIN 105 | self.fulldomain = DEFAULT_FULLDOMAIN 106 | self.support_email = DEFAULT_SUPPORT_EMAIL 107 | self.use_max_requests = DEFAULT_USE_MAX_REQUESTS 108 | self.user_agent = DEFAULT_USER_AGENT 109 | self.version = DEFAULT_VERSION 110 | self 111 | end 112 | end 113 | end 114 | -------------------------------------------------------------------------------- /spec/fixtures/interaction_create.json: -------------------------------------------------------------------------------- 1 | { 2 | "success": true, 3 | "results": { 4 | "customer": { 5 | "id": 665, 6 | "first_name": "API", 7 | "last_name": "Customer", 8 | "emails": [ 9 | { 10 | "email": { 11 | "created_at": "2010-09-07T20:38:05Z", 12 | "customer_contact_type": "home", 13 | "email": "customer@zencoder.com", 14 | "updated_at": "2010-09-07T20:38:05Z", 15 | "verified_at": null 16 | } 17 | } 18 | ], 19 | "phones": [ 20 | 21 | ], 22 | "addresses": [ 23 | 24 | ], 25 | "twitters": [ 26 | null 27 | ], 28 | "custom_test": null 29 | }, 30 | "case": { 31 | "id": 1835, 32 | "last_available_at": null, 33 | "created_at": "2010-09-07T20:40:16Z", 34 | "active_at": null, 35 | "route_at": "2010-09-07T20:40:15Z", 36 | "first_resolved_at": null, 37 | "active_user": null, 38 | "updated_at": "2010-09-07T20:40:16Z", 39 | "case_status_at": "2010-09-07T20:40:15Z", 40 | "priority": 4, 41 | "last_saved_by_id": null, 42 | "interaction_in_at": null, 43 | "assigned_at": null, 44 | "subject": "this is an api test", 45 | "routed_at": null, 46 | "group": null, 47 | "user": null, 48 | "first_opened_at": null, 49 | "channel": "email", 50 | "resolved_at": null, 51 | "description": null, 52 | "customer_id": 665, 53 | "closed_at": null, 54 | "changed_at": "2010-09-07T20:40:16Z", 55 | "case_status_type": "new", 56 | "labels": [ 57 | "apitest" 58 | ], 59 | "pending_at": null, 60 | "opened_at": null, 61 | "route_status": "added", 62 | "thread_count": null, 63 | "note_count": null, 64 | "preview": null, 65 | "custom_order": null 66 | }, 67 | "interaction": { 68 | "accept_langs": null, 69 | "browser": null, 70 | "browser_version": null, 71 | "channel": "email", 72 | "city": null, 73 | "country": null, 74 | "country_code": null, 75 | "created_at": "2010-09-07T20:40:16Z", 76 | "email": null, 77 | "engine": null, 78 | "engine_version": null, 79 | "id": 2955, 80 | "basis": null, 81 | "direction": "in", 82 | "out_status": null, 83 | "ip_address": null, 84 | "lat": null, 85 | "lng": null, 86 | "name": null, 87 | "orig_route_at": "2010-09-07T20:40:16Z", 88 | "os": null, 89 | "os_version": null, 90 | "preferred_lang": null, 91 | "region": null, 92 | "group": null, 93 | "user": null, 94 | "sent_at": null, 95 | "case_id": 1835, 96 | "updated_at": "2010-09-07T20:40:16Z", 97 | "user_agent": null, 98 | "interactionable": { 99 | "email": { 100 | "bcc": null, 101 | "cc": null, 102 | "to": null, 103 | "from": "testapi@desk.com", 104 | "reply_to": null, 105 | "in_reply_to": null, 106 | "sender": null, 107 | "message_id": null, 108 | "uid": null, 109 | "subject": "this is an api test", 110 | "body_text": null, 111 | "body_html": null, 112 | "body": null, 113 | "header": null, 114 | "sent_at": null, 115 | "created_at": "2010-09-07T20:40:16Z", 116 | "updated_at": "2010-09-07T20:40:16Z", 117 | "x_mailer": null, 118 | "delivered_to": null, 119 | "interaction_direction": "in", 120 | "hidden_at": null, 121 | "hidden_by_id": null 122 | } 123 | } 124 | } 125 | } 126 | } -------------------------------------------------------------------------------- /spec/desk/client/topic_spec.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | 3 | describe Desk::Client do 4 | Desk::Configuration::VALID_FORMATS.each do |format| 5 | context ".new(:format => '#{format}')" do 6 | before do 7 | @client = Desk::Client.new(:subdomain => "example", :format => format, :consumer_key => 'CK', :consumer_secret => 'CS', :oauth_token => 'OT', :oauth_token_secret => 'OS') 8 | end 9 | 10 | describe ".topics" do 11 | 12 | context "lookup" do 13 | 14 | before do 15 | stub_get("topics.#{format}"). 16 | to_return(:body => fixture("topics.#{format}"), :headers => {:content_type => "application/#{format}; charset=utf-8"}) 17 | end 18 | 19 | it "should get the correct resource" do 20 | @client.topics 21 | a_get("topics.#{format}"). 22 | should have_been_made 23 | end 24 | 25 | it "should return up to 100 topics worth of extended information" do 26 | topics = @client.topics 27 | 28 | topics.results.should be_a Array 29 | topics.results.first.topic.id.should == 1 30 | end 31 | 32 | end 33 | end 34 | 35 | describe ".topic" do 36 | 37 | context "lookup" do 38 | 39 | before do 40 | stub_get("topics/1.#{format}"). 41 | to_return(:body => fixture("topic.#{format}"), :headers => {:content_type => "application/#{format}; charset=utf-8"}) 42 | end 43 | 44 | it "should get the correct resource" do 45 | @client.topic(1) 46 | a_get("topics/1.#{format}"). 47 | should have_been_made 48 | end 49 | 50 | it "should return up to 100 cases worth of extended information" do 51 | topic = @client.topic(1) 52 | 53 | topic.id.should == 1 54 | topic.name.should == "General" 55 | end 56 | 57 | end 58 | end 59 | 60 | describe ".create_topic" do 61 | 62 | context "create" do 63 | 64 | before do 65 | stub_post("topics.#{format}"). 66 | to_return(:body => fixture("topic_create.#{format}"), :headers => {:content_type => "application/#{format}; charset=utf-8"}) 67 | end 68 | 69 | it "should post to the correct resource" do 70 | @client.create_topic("General", :description => "Everything belongs here") 71 | a_post("topics.#{format}"). 72 | should have_been_made 73 | end 74 | 75 | it "should return the new topic" do 76 | topic = @client.create_topic("General", :description => "Everything belongs here") 77 | 78 | topic.id.should == 9 79 | topic.name.should == "General" 80 | topic.description.should == "Everything belongs here" 81 | end 82 | 83 | end 84 | end 85 | 86 | describe ".update_topic" do 87 | 88 | context "update" do 89 | 90 | before do 91 | stub_put("topics/1.#{format}"). 92 | to_return(:body => fixture("topic_update.#{format}"), :headers => {:content_type => "application/#{format}; charset=utf-8"}) 93 | end 94 | 95 | it "should post to the correct resource" do 96 | @client.update_topic(1, :name => "Updated", :description => "Updated Description") 97 | a_put("topics/1.#{format}"). 98 | should have_been_made 99 | end 100 | 101 | it "should return the new topic" do 102 | topic = @client.update_topic(1, :name => "Updated", :description => "Updated Description") 103 | 104 | topic.name.should == "Updated" 105 | topic.description.should == "Updated Description" 106 | end 107 | 108 | end 109 | end 110 | 111 | describe ".delete_topic" do 112 | 113 | context "delete" do 114 | 115 | before do 116 | stub_delete("topics/1.#{format}"). 117 | to_return(:body => fixture("topic_destroy.#{format}"), :headers => {:content_type => "application/#{format}; charset=utf-8"}) 118 | end 119 | 120 | it "should post to the correct resource" do 121 | @client.delete_topic(1) 122 | a_delete("topics/1.#{format}"). 123 | should have_been_made 124 | end 125 | 126 | it "should return a successful response" do 127 | topic = @client.delete_topic(1) 128 | topic.success.should == true 129 | end 130 | 131 | end 132 | end 133 | end 134 | end 135 | end 136 | -------------------------------------------------------------------------------- /spec/desk/client/article_spec.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | 3 | describe Desk::Client do 4 | Desk::Configuration::VALID_FORMATS.each do |format| 5 | context ".new(:format => '#{format}')" do 6 | before do 7 | @client = Desk::Client.new(:subdomain => "example", :format => format, :consumer_key => 'CK', :consumer_secret => 'CS', :oauth_token => 'OT', :oauth_token_secret => 'OS') 8 | end 9 | 10 | describe ".articles" do 11 | 12 | context "lookup" do 13 | 14 | before do 15 | stub_get("topics/1/articles.#{format}"). 16 | to_return(:body => fixture("articles.#{format}"), :headers => {:content_type => "application/#{format}; charset=utf-8"}) 17 | end 18 | 19 | it "should post to the correct resource" do 20 | @client.articles(1) 21 | a_get("topics/1/articles.#{format}"). 22 | should have_been_made 23 | end 24 | 25 | it "should return the articles" do 26 | articles = @client.articles(1) 27 | 28 | articles.results.should be_a Array 29 | articles.results.first.article.id.should == 13 30 | end 31 | 32 | end 33 | end 34 | 35 | describe ".article" do 36 | 37 | context "lookup" do 38 | 39 | before do 40 | stub_get("articles/13.#{format}"). 41 | to_return(:body => fixture("article.#{format}"), :headers => {:content_type => "application/#{format}; charset=utf-8"}) 42 | end 43 | 44 | it "should get the correct resource" do 45 | @client.article(13) 46 | a_get("articles/13.#{format}"). 47 | should have_been_made 48 | end 49 | 50 | it "should return up to 100 cases worth of extended information" do 51 | article = @client.article(13) 52 | 53 | article.id.should == 13 54 | article.subject.should == "API Tips" 55 | end 56 | 57 | end 58 | end 59 | 60 | describe ".create_article" do 61 | 62 | context "create" do 63 | 64 | before do 65 | stub_post("topics/1/articles.#{format}"). 66 | to_return(:body => fixture("article_create.#{format}"), :headers => {:content_type => "application/#{format}; charset=utf-8"}) 67 | end 68 | 69 | it "should post to the correct resource" do 70 | @client.create_article(1, :subject => "API Tips", :main_content => "Tips on using our API") 71 | a_post("topics/1/articles.#{format}"). 72 | should have_been_made 73 | end 74 | 75 | it "should return the articles" do 76 | article = @client.create_article(1, :subject => "API Tips", :main_content => "Tips on using our API") 77 | 78 | article.id.should == 13 79 | end 80 | 81 | end 82 | end 83 | 84 | describe ".update_article" do 85 | 86 | context "update" do 87 | 88 | before do 89 | stub_put("articles/1.#{format}"). 90 | to_return(:body => fixture("article_update.#{format}"), :headers => {:content_type => "application/#{format}; charset=utf-8"}) 91 | end 92 | 93 | it "should post to the correct resource" do 94 | @client.update_article(1, :subject => "API Tips", :main_content => "Tips on using our API") 95 | a_put("articles/1.#{format}"). 96 | should have_been_made 97 | end 98 | 99 | it "should return the new topic" do 100 | topic = @client.update_article(1, :subject => "API Tips", :main_content => "Tips on using our API") 101 | 102 | topic.subject.should == "API Tips" 103 | topic.main_content.should == "Tips on using our API" 104 | end 105 | 106 | end 107 | end 108 | 109 | describe ".delete_article" do 110 | 111 | context "delete" do 112 | 113 | before do 114 | stub_delete("articles/1.#{format}"). 115 | to_return(:body => fixture("article_destroy.#{format}"), :headers => {:content_type => "application/#{format}; charset=utf-8"}) 116 | end 117 | 118 | it "should post to the correct resource" do 119 | @client.delete_article(1) 120 | a_delete("articles/1.#{format}"). 121 | should have_been_made 122 | end 123 | 124 | it "should return a successful response" do 125 | topic = @client.delete_article(1) 126 | topic.success.should == true 127 | end 128 | 129 | end 130 | end 131 | 132 | end 133 | end 134 | end 135 | -------------------------------------------------------------------------------- /lib/desk/client/macro.rb: -------------------------------------------------------------------------------- 1 | module Desk 2 | class Client 3 | # Defines methods related to macros 4 | module Macro 5 | # Returns extended information of macros 6 | # 7 | # @option options [Boolean, String, Integer] 8 | # @example Return extended information for 12345 9 | # Desk.macros 10 | # Desk.macros(:count => 5) 11 | # Desk.macros(:count => 5, :page => 3) 12 | # @format :json 13 | # @authenticated true 14 | # @see http://dev.desk.com/docs/api/macros 15 | def macros(*args) 16 | options = args.last.is_a?(Hash) ? args.pop : {} 17 | response = get("macros",options) 18 | response 19 | end 20 | 21 | # Returns extended information on a single macro 22 | # 23 | # @param id [Integer] a macro ID 24 | # @option options [Hash] 25 | # @example Return extended information for 12345 26 | # Desk.macro(12345) 27 | # @format :json 28 | # @authenticated true 29 | # @see http://dev.desk.com/docs/api/macros/show 30 | def macro(id) 31 | response = get("macros/#{id}") 32 | response.macro 33 | end 34 | 35 | # Creates a new macro 36 | # 37 | # @param name [String] A macro name 38 | # @option options [Hash] 39 | # @example Creates a new macro 40 | # Desk.create_macro("name") 41 | # Desk.create_macro("name") 42 | # @format :json 43 | # @authenticated true 44 | # @see http://dev.desk.com/docs/api/macros/create 45 | def create_macro(name, *args) 46 | options = args.last.is_a?(Hash) ? args.pop : {} 47 | response = post("macros",options) 48 | if response['success'] 49 | return response['results']['macro'] 50 | else 51 | return response 52 | end 53 | end 54 | 55 | # Updates a single macro 56 | # 57 | # @param id [Integer] a macro ID 58 | # @option options [String] 59 | # @example Updates information for macro 12345 60 | # Desk.update_macro(12345, :subject => "New Subject") 61 | # @format :json 62 | # @authenticated true 63 | # @see http://dev.desk.com/docs/api/macros/update 64 | def update_macro(id, *args) 65 | options = args.last.is_a?(Hash) ? args.pop : {} 66 | response = put("macros/#{id}",options) 67 | if response['success'] 68 | return response['results']['macro'] 69 | else 70 | return response 71 | end 72 | end 73 | 74 | # Deletes a single macro 75 | # 76 | # @param id [Integer] a macro ID 77 | # @example Deletes macro 12345 78 | # Desk.update_macro(12345, :subject => "New Subject") 79 | # @format :json 80 | # @authenticated true 81 | # @see http://dev.desk.com/docs/api/macros/update 82 | def delete_macro(id) 83 | response = delete("macros/#{id}") 84 | response 85 | end 86 | 87 | ########## 88 | # Macro Actions 89 | ########## 90 | 91 | # Returns extended information of macros 92 | # 93 | # @option options [Boolean, String, Integer] 94 | # @example Return extended information for 12345 95 | # Desk.macro_actions(1) 96 | # Desk.macro_actions(1, :count => 5) 97 | # Desk.macro_actions(1, :count => 5, :page => 3) 98 | # @format :json 99 | # @authenticated true 100 | # @see http://dev.desk.com/docs/api/macros/actions 101 | def macro_actions(id, *args) 102 | options = args.last.is_a?(Hash) ? args.pop : {} 103 | response = get("macros/#{id}/actions",options) 104 | response['results'] 105 | end 106 | 107 | # Returns extended information on a single macro 108 | # 109 | # @param id [Integer] a macro ID 110 | # @option options [Hash] 111 | # @example Return extended information for 12345 112 | # Desk.macro_action(12345, "set-case-description") 113 | # @format :json 114 | # @authenticated true 115 | # @see http://dev.desk.com/docs/api/macros/actions/show 116 | def macro_action(id, slug) 117 | response = get("macros/#{id}/actions/#{slug}") 118 | response['action'] 119 | end 120 | 121 | # Updates a single macro action 122 | # 123 | # @param id [Integer] a macro ID 124 | # @option options [String] 125 | # @example Updates information for macro 12345 126 | # Desk.update_macro_action(12345, "set-case-description", :value => "New Subject") 127 | # @format :json 128 | # @authenticated true 129 | # @see http://dev.desk.com/docs/api/macros/actions/update 130 | def update_macro_action(id, slug, *args) 131 | options = args.last.is_a?(Hash) ? args.pop : {} 132 | response = put("macros/#{id}/actions/#{slug}",options) 133 | if response['success'] 134 | return response['results']['action'] 135 | else 136 | return response 137 | end 138 | end 139 | 140 | end 141 | end 142 | end 143 | -------------------------------------------------------------------------------- /lib/desk/client/customer.rb: -------------------------------------------------------------------------------- 1 | module Desk 2 | class Client 3 | # Defines methods related to customers 4 | module Customer 5 | # Returns extended information of customers 6 | # 7 | # @option options [Boolean, String, Integer] 8 | # @example Return extended information for customers 9 | # Desk.customers 10 | # Desk.customers(:since_id => 12345, :count => 5) 11 | # @format :json 12 | # @authenticated true 13 | # @see http://dev.desk.com/docs/api/customers 14 | def customers(*args) 15 | options = args.last.is_a?(Hash) ? args.pop : {} 16 | response = get("customers",options) 17 | response 18 | end 19 | 20 | # Returns extended information on a single customer 21 | # 22 | # @option options [String] 23 | # @example Return extended information for customer 12345 24 | # Desk.customer(12345) 25 | # @format :json 26 | # @authenticated true 27 | # @see http://dev.desk.com/docs/api/customers/show 28 | def customer(id) 29 | response = get("customers/#{id}") 30 | response.customer 31 | end 32 | 33 | # Create a new customer 34 | # 35 | # @option options [String] 36 | # @example Return extended information for 12345 37 | # Desk.create_customer(:name => "Chris Warren", :twitter => "cdwarren") 38 | # @format :json 39 | # @authenticated true 40 | # @see http://dev.desk.com/docs/api/customers/create 41 | def create_customer(*args) 42 | options = args.last.is_a?(Hash) ? args.pop : {} 43 | response = post("customers",options) 44 | if response['success'] 45 | return response['results']['customer'] 46 | else 47 | return response 48 | end 49 | end 50 | 51 | # Update a customer 52 | # 53 | # @option options [String] 54 | # @example Return extended information for 12345 55 | # Desk.update_customer(12345, :name => "Christopher Warren") 56 | # @format :json 57 | # @authenticated true 58 | # @see http://dev.desk.com/docs/api/customers/update 59 | def update_customer(id, *args) 60 | options = args.last.is_a?(Hash) ? args.pop : {} 61 | response = put("customers/#{id}",options) 62 | if response['success'] 63 | return response['results']['customer'] 64 | else 65 | return response 66 | end 67 | end 68 | 69 | # Create a new customer email 70 | # 71 | # @option options [String] 72 | # @example Return extended information for 12345 73 | # Desk.create_customer_email(12345, "foo@example.com") 74 | # @format :json 75 | # @authenticated true 76 | # @see http://dev.desk.com/docs/api/customers/emails/create 77 | def create_customer_email(id, email, *args) 78 | options = args.last.is_a?(Hash) ? args.pop : {} 79 | options.merge!({:email => email}) 80 | response = post("customers/#{id}/emails",options) 81 | if response['success'] 82 | return response['results']['email'] 83 | else 84 | return response 85 | end 86 | end 87 | 88 | # Update a customer's email 89 | # 90 | # @option options [String] 91 | # @example Return extended information for 12345 92 | # Desk.update_customer_email(12345, 12345, :email => "foo@example.com") 93 | # Desk.update_customer_email(12345, 12345, :customer_contact_type => "work") 94 | # @format :json 95 | # @authenticated true 96 | # @see http://dev.desk.com/docs/api/customers/emails/update 97 | def update_customer_email(id, email_id, *args) 98 | options = args.last.is_a?(Hash) ? args.pop : {} 99 | response = put("customers/#{id}/emails/#{email_id}",options) 100 | if response['success'] 101 | return response['results']['email'] 102 | else 103 | return response 104 | end 105 | end 106 | 107 | # Create a new customer phone number 108 | # 109 | # @option options [String] 110 | # @example Return extended information for 12345 111 | # Desk.create_customer_phone(12345, "555-368-7147") 112 | # @format :json 113 | # @authenticated true 114 | # @see http://dev.desk.com/docs/api/customers/phones/create 115 | def create_customer_phone(id, phone, *args) 116 | options = args.last.is_a?(Hash) ? args.pop : {} 117 | options.merge!({:phone => phone}) 118 | response = post("customers/#{id}/phones",options) 119 | if response['success'] 120 | return response['results']['phone'] 121 | else 122 | return response 123 | end 124 | end 125 | 126 | # Update a customer's phone number 127 | # 128 | # @option options [String] 129 | # @example Return extended information for 12345 130 | # Desk.update_customer_phone(12345, 12345, :phone => "555-368-7147") 131 | # Desk.update_customer_phone(12345, 12345, :customer_contact_type => "work") 132 | # @format :json 133 | # @authenticated true 134 | # @see http://dev.desk.com/docs/api/customers/phones/update 135 | def update_customer_phone(id, phone_id, *args) 136 | options = args.last.is_a?(Hash) ? args.pop : {} 137 | response = put("customers/#{id}/phones/#{phone_id}",options) 138 | if response['success'] 139 | return response['results']['phone'] 140 | else 141 | return response 142 | end 143 | end 144 | end 145 | end 146 | end 147 | -------------------------------------------------------------------------------- /spec/desk/client/customer_spec.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | 3 | describe Desk::Client do 4 | Desk::Configuration::VALID_FORMATS.each do |format| 5 | context ".new(:format => '#{format}')" do 6 | before do 7 | @client = Desk::Client.new(:subdomain => "example", :format => format, :consumer_key => 'CK', :consumer_secret => 'CS', :oauth_token => 'OT', :oauth_token_secret => 'OS') 8 | end 9 | 10 | describe ".customers" do 11 | 12 | context "lookup" do 13 | 14 | before do 15 | stub_get("customers.#{format}"). 16 | to_return(:body => fixture("customers.#{format}"), :headers => {:content_type => "application/#{format}; charset=utf-8"}) 17 | end 18 | 19 | it "should get the correct resource" do 20 | @client.customers 21 | a_get("customers.#{format}"). 22 | should have_been_made 23 | end 24 | 25 | it "should return up to 100 customers worth of extended information" do 26 | customers = @client.customers 27 | 28 | customers.results.should be_a Array 29 | customers.results.first.customer.first_name.should == "Jeremy" 30 | end 31 | 32 | end 33 | end 34 | 35 | describe ".customer" do 36 | 37 | context "lookup" do 38 | 39 | before do 40 | stub_get("customers/1.#{format}"). 41 | to_return(:body => fixture("customer.#{format}"), :headers => {:content_type => "application/#{format}; charset=utf-8"}) 42 | end 43 | 44 | it "should get the correct resource" do 45 | @client.customer(1) 46 | a_get("customers/1.#{format}"). 47 | should have_been_made 48 | end 49 | 50 | it "should return up to 100 customers worth of extended information" do 51 | customer = @client.customer(1) 52 | 53 | customer.first_name.should == "Jeremy" 54 | customer.addresses.first.address.city.should == "Commack" 55 | end 56 | 57 | end 58 | end 59 | 60 | describe ".create_customer" do 61 | 62 | context "create" do 63 | 64 | before do 65 | stub_post("customers.#{format}"). 66 | to_return(:body => fixture("customer_create.#{format}"), :headers => {:content_type => "application/#{format}; charset=utf-8"}) 67 | end 68 | 69 | it "should get the correct resource" do 70 | @client.create_customer(:name => "Chris Warren", :twitter => "cdwarren") 71 | a_post("customers.#{format}"). 72 | should have_been_made 73 | end 74 | 75 | it "should return the information about this user" do 76 | customer = @client.create_customer(:name => "John Smith", :twitter => "cdwarren") 77 | 78 | customer.first_name.should == "John" 79 | customer.phones.first.phone.phone.should == "123-456-7890" 80 | end 81 | 82 | end 83 | end 84 | 85 | describe ".update_customer" do 86 | 87 | context "update" do 88 | 89 | before do 90 | stub_put("customers/1.#{format}"). 91 | to_return(:body => fixture("customer_update.#{format}"), :headers => {:content_type => "application/#{format}; charset=utf-8"}) 92 | end 93 | 94 | it "should get the correct resource" do 95 | @client.update_customer(1, :name => "Chris Warren", :twitter => "cdwarren") 96 | a_put("customers/1.#{format}"). 97 | should have_been_made 98 | end 99 | 100 | it "should return the information about this user" do 101 | customer = @client.update_customer(1, :name => "Joslyn Esser") 102 | 103 | customer.first_name.should == "Joslyn" 104 | end 105 | 106 | end 107 | end 108 | 109 | describe ".create_customer_email" do 110 | 111 | context "create" do 112 | 113 | before do 114 | stub_post("customers/1/emails.#{format}"). 115 | to_return(:body => fixture("customer_create_email.#{format}"), :headers => {:content_type => "application/#{format}; charset=utf-8"}) 116 | end 117 | 118 | it "should get the correct resource" do 119 | @client.create_customer_email(1, :email => "foo@example.com") 120 | a_post("customers/1/emails.#{format}"). 121 | should have_been_made 122 | end 123 | 124 | it "should return the information about this user" do 125 | email = @client.create_customer_email(1, :email => "api@example.com") 126 | 127 | email.email.should == "api@example.com" 128 | end 129 | 130 | end 131 | end 132 | 133 | describe ".update_customer_email" do 134 | 135 | context "update" do 136 | 137 | before do 138 | stub_put("customers/1/emails/2.#{format}"). 139 | to_return(:body => fixture("customer_update_email.#{format}"), :headers => {:content_type => "application/#{format}; charset=utf-8"}) 140 | end 141 | 142 | it "should get the correct resource" do 143 | @client.update_customer_email(1, 2, :email => "foo@example.com") 144 | a_put("customers/1/emails/2.#{format}"). 145 | should have_been_made 146 | end 147 | 148 | it "should return the information about this user" do 149 | email = @client.update_customer_email(1, 2, :email => "api@example.com") 150 | 151 | email.email.should == "api@example.com" 152 | end 153 | 154 | end 155 | end 156 | end 157 | end 158 | end 159 | -------------------------------------------------------------------------------- /spec/desk_spec.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | 3 | describe Desk do 4 | after do 5 | Desk.reset 6 | end 7 | 8 | # context "when delegating to a client" do 9 | # 10 | # before do 11 | # stub_get("statuses/user_timeline.json"). 12 | # with(:query => {:screen_name => "sferik"}). 13 | # to_return(:body => fixture("statuses.json"), :headers => {:content_type => "application/json; charset=utf-8"}) 14 | # end 15 | # 16 | # it "should get the correct resource" do 17 | # Twitter.user_timeline('sferik') 18 | # a_get("statuses/user_timeline.json"). 19 | # with(:query => {:screen_name => "sferik"}). 20 | # should have_been_made 21 | # end 22 | # 23 | # it "should return the same results as a client" do 24 | # Desk.user_timeline('sferik').should == Twitter::Client.new.user_timeline('sferik') 25 | # end 26 | # 27 | # end 28 | 29 | describe ".client" do 30 | it "should be an Desk::Client" do 31 | Desk.client.should be_a Desk::Client 32 | end 33 | end 34 | 35 | describe ".adapter" do 36 | it "should return the default adapter" do 37 | Desk.adapter.should == Desk::Configuration::DEFAULT_ADAPTER 38 | end 39 | end 40 | 41 | describe ".adapter=" do 42 | it "should set the adapter" do 43 | Desk.adapter = :typhoeus 44 | Desk.adapter.should == :typhoeus 45 | end 46 | end 47 | 48 | describe ".subdomain=" do 49 | before do 50 | Desk.subdomain = "zencoder" 51 | end 52 | 53 | it "should set the subdomain" do 54 | Desk.subdomain.should == "zencoder" 55 | end 56 | 57 | it "should change the endpoint" do 58 | Desk.endpoint.should == "https://zencoder.desk.com/api/#{Desk::Configuration::DEFAULT_VERSION}/" 59 | end 60 | end 61 | 62 | describe ".fulldomain=" do 63 | before do 64 | Desk.fulldomain = "https://help.zencoder.com" 65 | end 66 | 67 | it "should set the subdomain" do 68 | Desk.fulldomain.should == "https://help.zencoder.com" 69 | end 70 | 71 | it "should change the endpoint" do 72 | Desk.endpoint.should == "https://help.zencoder.com/api/#{Desk::Configuration::DEFAULT_VERSION}/" 73 | end 74 | end 75 | 76 | describe ".support_email" do 77 | it "should return the default support_email" do 78 | Desk.support_email.should == Desk::Configuration::DEFAULT_SUPPORT_EMAIL 79 | end 80 | end 81 | 82 | describe ".support_email=" do 83 | it "should set the support_email" do 84 | Desk.support_email = "help@example.com" 85 | Desk.support_email.should == "help@example.com" 86 | end 87 | end 88 | 89 | describe ".version=" do 90 | before do 91 | Desk.version = "v4" 92 | end 93 | 94 | it "should set the subdomain" do 95 | Desk.version.should == "v4" 96 | end 97 | 98 | it "should change the endpoint" do 99 | Desk.endpoint.should == "https://#{Desk::Configuration::DEFAULT_SUBDOMAIN}.desk.com/api/v4/" 100 | end 101 | end 102 | 103 | describe ".format" do 104 | it "should return the default format" do 105 | Desk.format.should == Desk::Configuration::DEFAULT_FORMAT 106 | end 107 | end 108 | 109 | describe ".format=" do 110 | it "should set the format" do 111 | Desk.format = 'xml' 112 | Desk.format.should == 'xml' 113 | end 114 | end 115 | 116 | describe ".max_requests" do 117 | it "should return the default max requests" do 118 | Desk.max_requests.should == Desk::Configuration::DEFAULT_MAX_REQUESTS 119 | end 120 | end 121 | 122 | describe ".max_requests=" do 123 | it "should set the max_requests" do 124 | Desk.max_requests = 50 125 | Desk.max_requests.should == 50 126 | end 127 | end 128 | 129 | describe ".use_max_requests" do 130 | it "should return the default max requests flag" do 131 | Desk.use_max_requests.should == Desk::Configuration::DEFAULT_USE_MAX_REQUESTS 132 | end 133 | end 134 | 135 | describe ".use_max_requests=" do 136 | it "should set the use_max_requests flag" do 137 | Desk.max_requests = true 138 | Desk.max_requests.should == true 139 | end 140 | end 141 | 142 | describe ".user_agent" do 143 | it "should return the default user agent" do 144 | Desk.user_agent.should == Desk::Configuration::DEFAULT_USER_AGENT 145 | end 146 | end 147 | 148 | describe ".user_agent=" do 149 | it "should set the user_agent" do 150 | Desk.user_agent = 'Custom User Agent' 151 | Desk.user_agent.should == 'Custom User Agent' 152 | end 153 | end 154 | 155 | describe ".configure" do 156 | 157 | Desk::Configuration::VALID_OPTIONS_KEYS.each do |key| 158 | 159 | it "should set the #{key}" do 160 | Desk.configure do |config| 161 | config.send("#{key}=", key) 162 | Desk.send(key).should == key 163 | end 164 | end 165 | end 166 | end 167 | 168 | describe ".counter" do 169 | before do 170 | Desk.counter = 0 171 | stub_get("cases.json"). 172 | to_return(:body => fixture("cases.json"), :headers => {:content_type => "application/json; charset=utf-8"}) 173 | end 174 | 175 | it "should be 0 in the beginning" do 176 | Desk.counter.should == 0 177 | end 178 | 179 | it "should count the requests" do 180 | 5.times { 181 | Desk.cases 182 | } 183 | Desk.counter.should == 5 184 | end 185 | 186 | context "max requests enabled" do 187 | before do 188 | Desk.use_max_requests = true 189 | end 190 | 191 | it "should only allow 60 requests" do 192 | expect { 193 | 70.times { 194 | Desk.cases 195 | } 196 | }.to raise_error 197 | end 198 | 199 | it "should only allow defined requests" do 200 | Desk.max_requests = 50 201 | expect { 202 | 55.times { 203 | Desk.cases 204 | } 205 | }.to raise_error 206 | end 207 | 208 | def make_request 209 | Desk.cases 210 | rescue Desk::TooManyRequests 211 | sleep(5) 212 | make_request 213 | end 214 | 215 | xit "should allow more requests after minute has passed" do 216 | 70.times { 217 | make_request 218 | } 219 | Desk.counter.should == 10 220 | end 221 | end 222 | end 223 | 224 | describe ".minute" do 225 | before do 226 | Desk.minute = Time.now.min 227 | end 228 | 229 | it "should be the current minute" do 230 | Desk.minute.should == Time.now.min 231 | end 232 | end 233 | end 234 | -------------------------------------------------------------------------------- /spec/desk/client/macro_spec.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | 3 | describe Desk::Client do 4 | Desk::Configuration::VALID_FORMATS.each do |format| 5 | context ".new(:format => '#{format}')" do 6 | before do 7 | @client = Desk::Client.new(:subdomain => "example", :format => format, :consumer_key => 'CK', :consumer_secret => 'CS', :oauth_token => 'OT', :oauth_token_secret => 'OS') 8 | end 9 | 10 | describe ".macros" do 11 | 12 | context "lookup" do 13 | 14 | before do 15 | stub_get("macros.#{format}"). 16 | to_return(:body => fixture("macros.#{format}"), :headers => {:content_type => "application/#{format}; charset=utf-8"}) 17 | end 18 | 19 | it "should get the correct resource" do 20 | @client.macros 21 | a_get("macros.#{format}"). 22 | should have_been_made 23 | end 24 | 25 | it "should return up to 100 macros worth of extended information" do 26 | macros = @client.macros 27 | 28 | macros.results.should be_a Array 29 | macros.results.first.macro.id.should == 11 30 | end 31 | 32 | end 33 | end 34 | 35 | describe ".macro" do 36 | 37 | context "lookup" do 38 | 39 | before do 40 | stub_get("macros/13.#{format}"). 41 | to_return(:body => fixture("macro.#{format}"), :headers => {:content_type => "application/#{format}; charset=utf-8"}) 42 | end 43 | 44 | it "should get the correct resource" do 45 | @client.macro(13) 46 | a_get("macros/13.#{format}"). 47 | should have_been_made 48 | end 49 | 50 | it "should return up to 100 cases worth of extended information" do 51 | macro = @client.macro(13) 52 | 53 | macro.id.should == 13 54 | macro.name.should == "API Macro" 55 | end 56 | 57 | end 58 | end 59 | 60 | describe ".create_macro" do 61 | 62 | context "create" do 63 | 64 | before do 65 | stub_post("macros.#{format}"). 66 | to_return(:body => fixture("macro_create.#{format}"), :headers => {:content_type => "application/#{format}; charset=utf-8"}) 67 | end 68 | 69 | it "should post to the correct resource" do 70 | @client.create_macro("API Macro", :description => "Everything belongs here") 71 | a_post("macros.#{format}"). 72 | should have_been_made 73 | end 74 | 75 | it "should return the new macro" do 76 | macro = @client.create_macro("API Macro", :description => "Everything belongs here") 77 | 78 | macro.id.should == 12 79 | macro.name.should == "API Macro" 80 | end 81 | 82 | end 83 | end 84 | 85 | describe ".update_macro" do 86 | 87 | context "update" do 88 | 89 | before do 90 | stub_put("macros/13.#{format}"). 91 | to_return(:body => fixture("macro_update.#{format}"), :headers => {:content_type => "application/#{format}; charset=utf-8"}) 92 | end 93 | 94 | it "should post to the correct resource" do 95 | @client.update_macro(13, :name => "Updated") 96 | a_put("macros/13.#{format}"). 97 | should have_been_made 98 | end 99 | 100 | it "should return the new macro" do 101 | macro = @client.update_macro(13, :name => "Updated") 102 | 103 | macro.name.should == "Updated" 104 | end 105 | 106 | end 107 | end 108 | 109 | describe ".delete_macro" do 110 | 111 | context "delete" do 112 | 113 | before do 114 | stub_delete("macros/1.#{format}"). 115 | to_return(:body => fixture("macro_destroy.#{format}"), :headers => {:content_type => "application/#{format}; charset=utf-8"}) 116 | end 117 | 118 | it "should post to the correct resource" do 119 | @client.delete_macro(1) 120 | a_delete("macros/1.#{format}"). 121 | should have_been_made 122 | end 123 | 124 | it "should return a successful response" do 125 | macro = @client.delete_macro(1) 126 | macro.success.should == true 127 | end 128 | 129 | end 130 | end 131 | 132 | describe ".macro_actions" do 133 | 134 | context "lookup" do 135 | 136 | before do 137 | stub_get("macros/1/actions.#{format}"). 138 | to_return(:body => fixture("macro_actions.#{format}"), :headers => {:content_type => "application/#{format}; charset=utf-8"}) 139 | end 140 | 141 | it "should get the correct resource" do 142 | @client.macro_actions(1) 143 | a_get("macros/1/actions.#{format}"). 144 | should have_been_made 145 | end 146 | 147 | it "should return up to 100 macro actions worth of extended information" do 148 | macro_actions = @client.macro_actions(1) 149 | 150 | macro_actions.should be_a Array 151 | macro_actions.first.action.slug.should == "set-case-description" 152 | end 153 | 154 | end 155 | end 156 | 157 | describe ".macro_action" do 158 | 159 | context "lookup" do 160 | 161 | before do 162 | stub_get("macros/1/actions/set-case-description.#{format}"). 163 | to_return(:body => fixture("macro_action.#{format}"), :headers => {:content_type => "application/#{format}; charset=utf-8"}) 164 | end 165 | 166 | it "should get the correct resource" do 167 | @client.macro_action(1,"set-case-description") 168 | a_get("macros/1/actions/set-case-description.#{format}"). 169 | should have_been_made 170 | end 171 | 172 | it "should return up to 100 macro actions worth of extended information" do 173 | macro_action = @client.macro_action(1,"set-case-description") 174 | macro_action.slug.should == "set-case-description" 175 | end 176 | 177 | end 178 | end 179 | 180 | describe ".update_macro_action" do 181 | 182 | context "update" do 183 | 184 | before do 185 | stub_put("macros/1/actions/set-case-description.#{format}"). 186 | to_return(:body => fixture("macro_action_update.#{format}"), :headers => {:content_type => "application/#{format}; charset=utf-8"}) 187 | end 188 | 189 | it "should post to the correct resource" do 190 | @client.update_macro_action(1, "set-case-description", :value => "This is my case description") 191 | a_put("macros/1/actions/set-case-description.#{format}"). 192 | should have_been_made 193 | end 194 | 195 | it "should return the new macro" do 196 | macro_action = @client.update_macro_action(1, "set-case-description", :value => "This is my case description") 197 | macro_action.value.should == "Description to be applied" 198 | end 199 | 200 | end 201 | end 202 | end 203 | end 204 | end 205 | -------------------------------------------------------------------------------- /spec/fixtures/cases.json: -------------------------------------------------------------------------------- 1 | { 2 | "results": [ 3 | { 4 | "case": 5 | { 6 | "id":1, 7 | "last_available_at":"2010-05-11T18:44:39Z", 8 | "created_at":"2009-11-25T15:00:41Z", 9 | "active_at":"2010-05-11T18:42:53Z", 10 | "route_at":"2009-11-25T15:00:41Z", 11 | "first_resolved_at":"2010-03-10T16:31:21Z", 12 | "active_user":null, 13 | "updated_at":"2010-05-11T19:35:57Z", 14 | "case_status_at":"2010-05-11T19:35:57Z", 15 | "priority":9, 16 | "last_saved_by_id":1, 17 | "interaction_in_at":"2009-11-25T15:00:41Z", 18 | "assigned_at":"2010-03-12T20:01:43Z", 19 | "subject":"Andr\u00e9 M\u00fcller test", 20 | "routed_at":null, 21 | "group":null, 22 | "user": { 23 | "id":1, 24 | "name":"Jeremy Suriel", 25 | "name_public":"Jeremy Suriel", 26 | "email":"jeremysuriel+agent1@gmail.com", 27 | "created_at":"2009-11-25T14:58:39Z", 28 | "updated_at":"2010-07-19T02:36:21Z", 29 | "user_level":"sysadmin", 30 | "login_count":561, 31 | "time_zone":"Eastern Time (US & Canada)", 32 | "last_login_at":"2010-07-18T02:06:08Z" 33 | }, 34 | "first_opened_at":"2010-03-09T15:27:00Z", 35 | "channel":"chat", 36 | "resolved_at":"2010-03-10T16:31:21Z", 37 | "description":"test", 38 | "customer_id":2, 39 | "closed_at":"2010-05-11T19:35:57Z", 40 | "changed_at":"2010-05-11T19:35:57Z", 41 | "case_status_type":"closed", 42 | "labels":"test, reload", 43 | "pending_at":null, 44 | "opened_at":"2010-03-09T15:27:00Z", 45 | "route_status":"available", 46 | "thread_count":1, 47 | "note_count":1, 48 | "preview":null, 49 | "macros":["Example Macros"], 50 | "articles":["Example Kb Article"], 51 | "custom_c":"", 52 | "custom_order":"" 53 | } 54 | }, 55 | { 56 | "case": 57 | { 58 | "id":2, 59 | "last_available_at":"2010-03-09T16:08:51Z", 60 | "created_at":"2009-11-25T15:35:57Z", 61 | "active_at":"2010-03-09T16:08:43Z", 62 | "route_at":"2009-11-25T15:35:57Z", 63 | "first_resolved_at":null, 64 | "active_user":null, 65 | "updated_at":"2010-03-18T15:45:34Z", 66 | "case_status_at":"2010-03-09T16:08:43Z", 67 | "priority":5, 68 | "last_saved_by_id":null, 69 | "interaction_in_at":"2009-11-25T15:35:57Z", 70 | "assigned_at":null, 71 | "subject":"test2", 72 | "routed_at":null, 73 | "group":null, 74 | "user":null, 75 | "first_opened_at":"2010-03-09T16:08:43Z", 76 | "channel":"twitter", 77 | "resolved_at":null, 78 | "description":"test2", 79 | "customer_id":8, 80 | "closed_at":null, 81 | "changed_at":"2010-03-18T15:45:34Z", 82 | "case_status_type":"open", 83 | "labels":"", 84 | "pending_at":null, 85 | "opened_at":"2010-03-09T16:08:43Z", 86 | "route_status":"available", 87 | "thread_count":1, 88 | "note_count":1, 89 | "preview":null, 90 | "macros":null, 91 | "articles":null, 92 | "custom_c":null, 93 | "custom_order":null 94 | } 95 | }, 96 | { 97 | "case": 98 | { 99 | "id":3, 100 | "last_available_at":null, 101 | "created_at":"2009-11-25T15:35:59Z", 102 | "active_at":null, 103 | "route_at":"2009-11-25T15:35:59Z", 104 | "first_resolved_at":null, 105 | "active_user":null, 106 | "updated_at":"2010-03-18T15:45:34Z", 107 | "case_status_at":"2010-02-23T01:26:59Z", 108 | "priority":5, 109 | "last_saved_by_id":1, 110 | "interaction_in_at":"2009-11-25T15:35:59Z", 111 | "assigned_at":null, 112 | "subject":"yo", 113 | "routed_at":null, 114 | "group":null, 115 | "user":null, 116 | "first_opened_at":"2009-11-26T15:09:01Z", 117 | "channel":"twitter", 118 | "resolved_at":null, 119 | "description":"yo", 120 | "customer_id":8, 121 | "closed_at":null, 122 | "changed_at":"2010-03-18T15:45:34Z", 123 | "case_status_type":"open", 124 | "labels":"", 125 | "pending_at":null, 126 | "opened_at":"2009-11-26T15:09:01Z", 127 | "route_status":"available", 128 | "thread_count":3, 129 | "note_count":1, 130 | "preview":"yo", 131 | "macros":null, 132 | "articles":null, 133 | "custom_c":null, 134 | "custom_order":null 135 | } 136 | }, 137 | { 138 | "case": 139 | { 140 | "id":4, 141 | "last_available_at":null, 142 | "created_at":"2009-11-25T15:36:01Z", 143 | "active_at":null, 144 | "route_at":"2009-11-25T15:36:01Z", 145 | "first_resolved_at":null, 146 | "active_user":null, 147 | "updated_at":"2010-03-18T15:45:34Z", 148 | "case_status_at":"2010-02-23T01:26:59Z", 149 | "priority":5, 150 | "last_saved_by_id":1, 151 | "interaction_in_at":"2009-11-25T15:36:01Z", 152 | "assigned_at":null, 153 | "subject":"Yo yo", 154 | "routed_at":null, 155 | "group":null, 156 | "user":null, 157 | "first_opened_at":"2009-11-27T17:44:29Z", 158 | "channel":"twitter", 159 | "resolved_at":null, 160 | "description":"Yo yo", 161 | "customer_id":8, 162 | "closed_at":null, 163 | "changed_at":"2010-03-18T15:45:34Z", 164 | "case_status_type":"open", 165 | "labels":"", 166 | "pending_at":null, 167 | "opened_at":"2009-11-27T17:44:29Z", 168 | "route_status":"available", 169 | "thread_count":1, 170 | "note_count":1, 171 | "preview":null, 172 | "macros":null, 173 | "articles":null, 174 | "custom_c":null, 175 | "custom_order":null 176 | } 177 | } 178 | ], 179 | "page":1, 180 | "count":4, 181 | "total":150 182 | } -------------------------------------------------------------------------------- /spec/desk/client/interaction_spec.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | 3 | describe Desk::Client do 4 | include EmailSpec::Helpers 5 | include EmailSpec::Matchers 6 | 7 | Desk::Configuration::VALID_FORMATS.each do |format| 8 | context ".new(:format => '#{format}')" do 9 | before do 10 | @client = Desk::Client.new(:subdomain => "example", :format => format, :consumer_key => 'CK', :consumer_secret => 'CS', :oauth_token => 'OT', :oauth_token_secret => 'OS', :support_email => "help@example.com") 11 | end 12 | 13 | describe ".create_interaction" do 14 | context "create a new interaction without specifying direction should default to inbound" do 15 | before do 16 | stub_post("interactions.#{format}"). 17 | to_return(:body => fixture("interaction_create.#{format}"), :headers => {:content_type => "application/#{format}; charset=utf-8"}) 18 | end 19 | 20 | it "should get the correct resource" do 21 | @client.create_interaction(:interaction_subject => "this is an api test", :customer_email => "foo@example.com") 22 | a_post("interactions.#{format}"). 23 | should have_been_made 24 | end 25 | 26 | it "should create an interaction" do 27 | interaction = @client.create_interaction(:interaction_subject => "this is an api test", :customer_email => "foo@example.com") 28 | 29 | interaction.customer.emails.first.email.email.should == "customer@zencoder.com" 30 | interaction.interaction.interactionable.email.subject.should == "this is an api test" 31 | end 32 | end 33 | 34 | context "create a new interaction and specify inbound" do 35 | before do 36 | stub_post("interactions.#{format}"). 37 | to_return(:body => fixture("interaction_create.#{format}"), :headers => {:content_type => "application/#{format}; charset=utf-8"}) 38 | end 39 | 40 | it "should get the correct resource" do 41 | @client.create_interaction(:interaction_subject => "this is an api test", :customer_email => "foo@example.com", :direction => "in") 42 | a_post("interactions.#{format}"). 43 | should have_been_made 44 | end 45 | 46 | it "should create an interaction" do 47 | interaction = @client.create_interaction(:interaction_subject => "this is an api test", :customer_email => "foo@example.com", :direction => "in") 48 | 49 | interaction.customer.emails.first.email.email.should == "customer@zencoder.com" 50 | interaction.interaction.interactionable.email.subject.should == "this is an api test" 51 | end 52 | end 53 | 54 | context "create a new interaction and specify outbound" do 55 | before do 56 | @email = @client.create_interaction(:customer_email => "customer@example.com", :interaction_subject => "Need help?", :interaction_body => "Sorry we missed you in chat today.", :direction => "outbound") 57 | end 58 | 59 | it "should deliver the email to the customer" do 60 | @email.last.should deliver_to("customer@example.com") 61 | end 62 | 63 | it "should be from the support email" do 64 | @email.last.should deliver_from(@client.support_email) 65 | end 66 | 67 | it "should contain the message in the mail body" do 68 | @email.last.should have_body_text(/Sorry we missed you in chat today/) 69 | end 70 | 71 | it "should bcc to the support email" do 72 | @email.last.should bcc_to(@client.support_email) 73 | end 74 | 75 | it "should set the Desk headers" do 76 | @email.last.should have_header("x-assistly-customer-email","customer@example.com") 77 | @email.last.should have_header("x-assistly-interaction-direction","out") 78 | @email.last.should have_header("x-assistly-case-status","open") 79 | end 80 | end 81 | end 82 | 83 | describe ".create_inbound_interaction" do 84 | context "create a new interaction" do 85 | before do 86 | stub_post("interactions.#{format}"). 87 | to_return(:body => fixture("interaction_create.#{format}"), :headers => {:content_type => "application/#{format}; charset=utf-8"}) 88 | end 89 | 90 | it "should get the correct resource" do 91 | @client.create_inbound_interaction(:interaction_subject => "this is an api test", :customer_email => "foo@example.com") 92 | a_post("interactions.#{format}"). 93 | should have_been_made 94 | end 95 | 96 | it "should create an interaction" do 97 | interaction = @client.create_inbound_interaction(:interaction_subject => "this is an api test", :customer_email => "foo@example.com") 98 | 99 | interaction.customer.emails.first.email.email.should == "customer@zencoder.com" 100 | interaction.interaction.interactionable.email.subject.should == "this is an api test" 101 | end 102 | end 103 | end 104 | 105 | describe ".create_outbound_interaction" do 106 | context "create" do 107 | 108 | before do 109 | @email = @client.create_outbound_interaction("customer@example.com", "Need help?", "Sorry we missed you in chat today.") 110 | end 111 | 112 | it "should deliver the email to the customer" do 113 | @email.last.should deliver_to("customer@example.com") 114 | end 115 | 116 | it "should be from the support email" do 117 | @email.last.should deliver_from(@client.support_email) 118 | end 119 | 120 | it "should contain the message in the mail body" do 121 | @email.last.should have_body_text(/Sorry we missed you in chat today/) 122 | end 123 | 124 | it "should bcc to the support email" do 125 | @email.last.should bcc_to(@client.support_email) 126 | end 127 | 128 | it "should set the Assitly headers" do 129 | @email.last.should have_header("x-assistly-customer-email","customer@example.com") 130 | @email.last.should have_header("x-assistly-interaction-direction","out") 131 | @email.last.should have_header("x-assistly-case-status","open") 132 | end 133 | 134 | end 135 | 136 | context "without support_email defined" do 137 | 138 | before do 139 | @client_without_support_email = Desk::Client.new(:subdomain => "example", :format => format, :consumer_key => 'CK', :consumer_secret => 'CS', :oauth_token => 'OT', :oauth_token_secret => 'OS') 140 | end 141 | 142 | it "should raise an error" do 143 | lambda do 144 | @client_without_support_email.create_outbound_interaction("customer@example.com", "Need help?", "Sorry we missed you in chat today.") 145 | end.should raise_error(Desk::SupportEmailNotSet) 146 | end 147 | 148 | end 149 | 150 | context "with customer headers set" do 151 | before do 152 | @custom_email = @client.create_outbound_interaction("customer@example.com", "Need help?", "Sorry we missed you in chat today.", :headers => { "x-assistly-interaction-user-agent" => "12345"}) 153 | end 154 | 155 | it "should merge the custom headers" do 156 | @custom_email.last.should have_header("x-assistly-interaction-user-agent","12345") 157 | end 158 | 159 | it "should preserve the existing headers" do 160 | @custom_email.last.should have_header("x-assistly-customer-email","customer@example.com") 161 | end 162 | end 163 | end 164 | 165 | describe ".interactions" do 166 | 167 | context "lookup" do 168 | 169 | before do 170 | stub_get("interactions.#{format}"). 171 | to_return(:body => fixture("interactions.#{format}"), :headers => {:content_type => "application/#{format}; charset=utf-8"}) 172 | end 173 | 174 | it "should get the correct resource" do 175 | @client.interactions 176 | a_get("interactions.#{format}"). 177 | should have_been_made 178 | end 179 | 180 | it "should return up to 100 users worth of extended information" do 181 | interactions = @client.interactions 182 | 183 | interactions.results.should be_a Array 184 | interactions.results.last.interaction.user.name.should == "Agent Jeremy" 185 | end 186 | 187 | end 188 | end 189 | end 190 | end 191 | end 192 | -------------------------------------------------------------------------------- /spec/fixtures/interactions.json: -------------------------------------------------------------------------------- 1 | { 2 | "results": [ 3 | { 4 | "interaction": { 5 | "accept_langs": null, 6 | "browser": null, 7 | "browser_version": null, 8 | "channel": "email", 9 | "city": null, 10 | "country": null, 11 | "country_code": null, 12 | "created_at": "2010-08-31T15:44:04Z", 13 | "email": null, 14 | "engine": null, 15 | "engine_version": null, 16 | "id": 2912, 17 | "basis": "original", 18 | "direction": "in", 19 | "out_status": null, 20 | "ip_address": null, 21 | "lat": null, 22 | "lng": null, 23 | "name": null, 24 | "orig_route_at": "2010-08-31T15:44:04Z", 25 | "os": null, 26 | "os_version": null, 27 | "preferred_lang": null, 28 | "region": null, 29 | "group": null, 30 | "user": null, 31 | "sent_at": null, 32 | "case_id": 1811, 33 | "updated_at": "2010-08-31T15:44:04Z", 34 | "user_agent": null, 35 | "interactionable": { 36 | "email": { 37 | "bcc": null, 38 | "cc": null, 39 | "to": "JTest 01 ", 40 | "from": "Jeremy Suriel ", 41 | "reply_to": null, 42 | "in_reply_to": null, 43 | "sender": null, 44 | "message_id": "", 45 | "uid": 2479, 46 | "subject": "Please help me", 47 | "body_text": "What color is the sky?\n\n\nJeremy Suriel \nChief Architect \u2022 Assist.ly\ntwitter.com/jrmey\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n", 48 | "body_html": "", 49 | "body": "What color is the sky?\n\n\nJeremy Suriel \nChief Architect \u2022 Assist.ly\ntwitter.com/jrmey\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n", 50 | "header": "Delivered-To: jtest01@desk.com\r\nReceived: by 10.204.62.68 with SMTP id w4cs129345bkh; Tue, 31 Aug 2010\r\n 08:43:56 -0700 (PDT)\r\nReceived: by 10.114.13.14 with SMTP id 14mr7016661wam.157.1283269433994; Tue, \r\n 31 Aug 2010 08:43:53 -0700 (PDT)\r\nReturn-Path: \r\nReceived: from mail-px0-f170.google.com (mail-px0-f170.google.com\r\n [209.85.212.170 ]", 51 | "sent_at": "2010-08-31T15:43:49Z", 52 | "created_at": "2010-08-31T15:44:04Z", 53 | "updated_at": "2010-08-31T15:44:04Z", 54 | "x_mailer": "Apple Mail (2.1081)", 55 | "delivered_to": "JTest 01 ", 56 | "interaction_direction": "in", 57 | "hidden_at": null, 58 | "hidden_by_id": null 59 | } 60 | } 61 | } 62 | }, 63 | { 64 | "interaction": { 65 | "accept_langs": null, 66 | "browser": null, 67 | "browser_version": null, 68 | "channel": "email", 69 | "city": null, 70 | "country": null, 71 | "country_code": null, 72 | "created_at": "2010-08-31T15:44:50Z", 73 | "email": null, 74 | "engine": null, 75 | "engine_version": null, 76 | "id": 2914, 77 | "basis": "reply", 78 | "direction": "out", 79 | "out_status": "sent", 80 | "ip_address": null, 81 | "lat": null, 82 | "lng": null, 83 | "name": null, 84 | "orig_route_at": "2010-08-31T15:44:50Z", 85 | "os": null, 86 | "os_version": null, 87 | "preferred_lang": null, 88 | "region": null, 89 | "group": null, 90 | "user": { 91 | "id": 1, 92 | "name": "Agent Jeremy", 93 | "name_public": "Agent Jeremy", 94 | "email": "jeremy+agent1@desk.com", 95 | "created_at": "2009-11-25T14:58:39Z", 96 | "updated_at": "2010-08-31T15:44:59Z", 97 | "user_level": "sysadmin", 98 | "login_count": 579, 99 | "time_zone": "Eastern Time (US & Canada)", 100 | "last_login_at": "2010-08-27T13:45:38Z" 101 | }, 102 | "sent_at": "2010-08-31T15:45:17Z", 103 | "case_id": 1811, 104 | "updated_at": "2010-08-31T15:45:18Z", 105 | "user_agent": null, 106 | "interactionable": { 107 | "email": { 108 | "bcc": "", 109 | "cc": "", 110 | "to": "Jeremy Suriel ", 111 | "from": "JTest 01 ", 112 | "reply_to": null, 113 | "in_reply_to": "", 114 | "sender": null, 115 | "message_id": "<4c7d237242eb3_160d867bebe4121@jeremy.desk.local>", 116 | "uid": null, 117 | "subject": "Re: Please help me", 118 | "body_text": "\n\n__________________________________\nPlease type your reply at the top of the email...\n------------------------------------------------------\n \n \n\nAgent Jeremy | AUG 31,2010 03:44PM UTC\n\n\nBlue!\n\n\n------------------------------------------------------\n\n\n \n\n\nSales | AUG 31,2010 03:44PM UTC | Original message \n \n\n\nWhat color is the sky?\n\n\nJeremy Suriel \nChief Architect \u2022 Assist.ly\ntwitter.com/jrmey\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n------------------------------------------------------\nFor your reference this is Case #: 1811\n------------------------------------------------------\n\nSupport powered by Assistly (http://www.desk.com/)\n\n [ [ cc11a56264793ec471701942ec80bd4e9e7194a5-1975 ] ]", 119 | "body_html": "
__________________________________
\n\t
\n\t\tPlease type your reply at the top of the email...\n\t
\n\t
\n\t\n\t\n\t\n\t
\n\t\t
\n\t\t\tAgent Jeremy\n\t\t
\n\t\t
\n\t\t\tAUG 31, 2010  |  11:44AM EDT
\t\t\n\t\t
\n\t\t\tBlue!\n\t\t
\n\t
\n\t
\n\t
\n\t
\n\n\n\t\n\t\n\t\n\t
\n\t\t
\n\t\t\t\n\t\t\tSales\n\t\t\n\t\t
\n\t\t
\n\t\t\tAUG 31, 2010  |  03:44PM UTC\n\t\t\t\n\t\t\t
\n\t\t\tOriginal message\n\t\t\t\n\t\t
\n\n\n\t\t\t\n\t\t
\n\t\t\n\t\t\tWhat color is the sky?


Jeremy Suriel
Chief Architect \u2022 Assist.ly
twitter.com/jrmey














\n\t\t
\n\t
\n\t
\n\t
\n\t\n\t\t\t\n\t
\n\t\tFor your reference this is Case #: 1811\n\t
\n\t
\n\t
\n\t\tSupport powered by Assistly\n\t
\n
", 120 | "body": "Blue!", 121 | "header": null, 122 | "sent_at": "2010-08-31T15:45:17Z", 123 | "created_at": "2010-08-31T15:44:50Z", 124 | "updated_at": "2010-08-31T15:45:17Z", 125 | "x_mailer": null, 126 | "delivered_to": null, 127 | "interaction_direction": "out", 128 | "hidden_at": null, 129 | "hidden_by_id": null, 130 | "out_status": "sent" 131 | } 132 | } 133 | } 134 | } 135 | ], 136 | "page": 1, 137 | "count": 20, 138 | "total": 2 139 | } 140 | -------------------------------------------------------------------------------- /README.mkd: -------------------------------------------------------------------------------- 1 | The Desk Ruby Gem 2 | ==================== 3 | A Ruby wrapper for the [Desk.com API](http://dev.desk.com/) 4 | 5 | Installation 6 | ------------ 7 | gem install desk 8 | 9 | What's new in 0.3.0? 10 | ------------------ 11 | Renamed to Desk 12 | Newer faraday, faraday_middleware and hashie 13 | 14 | What's new in 0.2.6? 15 | ------------------ 16 | Newer faraday, faraday_middleware and multixml versions 17 | 18 | What's new in 0.2.5? 19 | ------------------ 20 | Newer multijson version for Rails 3.1 compatibility 21 | 22 | What's new in 0.2.4? 23 | ------------------ 24 | Resolved an issue that was breaking PUT requests. 25 | 26 | What's new in 0.2.3? 27 | ------------------ 28 | Removed deep_merge because it conflicts with rails. Handling the merge inside the create_outbound_interaction method only on headers for now, until we need it elsewhere. 29 | 30 | What's new in 0.2.2? 31 | ------------------ 32 | Added deep_merge support so that we keep any custom email headers when creating outbound interactions. 33 | Required pony in the gem so you don't have to include it in your app. 34 | 35 | What's new in 0.2.1? 36 | ------------------ 37 | Stopped returning only the 'results' array when listing things, because this would cause us to not have access to page numbers, total counts, etc. 38 | 39 | What's new in 0.2.0? 40 | ------------------ 41 | Added support for Topics, Articles, and Macros, which is currently all that is available in the Assistly API. 42 | 43 | What's new in 0.1.5? 44 | ------------------ 45 | Added the ability to create outbound communications over email 46 | 47 | 48 | What's new in 0.1? 49 | ------------------ 50 | This is the first release of the Assistly gem, based on the [Twitter gem](http://github.com/jnunemaker/twitter). Support for most of the [Assistly API](http://dev.desk.com/docs/api) are handled. 51 | There is not yet support for Content or Macros. 52 | 53 | Help! I'm getting: "Did not recognize your engine specification. Please specify either a symbol or a class. (RuntimeError)" 54 | --------------------------------------------------------------------------------------------------------------------------- 55 | 56 | You'll need to explicitly require a JSON library. We recommend [yajl-ruby](http://github.com/brianmario/yajl-ruby). 57 | 58 | Usage Examples 59 | -------------- 60 | require "rubygems" 61 | require "desk" 62 | 63 | # All methods require authentication. To get your Desk OAuth credentials, 64 | # register an app in the Desk.com admin for your account at http://your-domain.desk.com/admin 65 | Desk.configure do |config| 66 | config.support_email = "help@example.com" 67 | config.subdomain = YOUR_DESK_SUBDOMAIN 68 | config.consumer_key = YOUR_CONSUMER_KEY 69 | config.consumer_secret = YOUR_CONSUMER_SECRET 70 | config.oauth_token = YOUR_OAUTH_TOKEN 71 | config.oauth_token_secret = YOUR_OAUTH_TOKEN_SECRET 72 | end 73 | 74 | ###### 75 | # Cases 76 | ###### 77 | 78 | # List cases 79 | Desk.cases 80 | Desk.cases(:since_id => 12345) 81 | 82 | # Get a specific case 83 | Desk.case(12345) 84 | 85 | # Update a specific case 86 | Desk.update_case(12345, :subject => "Something Else") 87 | 88 | # Get a case url 89 | Desk.case_url(12345) 90 | 91 | ###### 92 | # Customers 93 | ###### 94 | 95 | # List customers 96 | Desk.customers 97 | Desk.customers(:since_id => 12345, :count => 5) 98 | 99 | # Get a specific customer 100 | Desk.customer(12345) 101 | 102 | # Create a customer 103 | Desk.create_customer(:name => "Chris Warren", :twitter => "cdwarren") 104 | 105 | # Update a customer 106 | Desk.update_customer(12345, :name => "Christopher Warren") 107 | 108 | # Add a customer email 109 | Desk.create_customer_email(12345, "foo@example.com") 110 | Desk.create_customer_email(12345, "foo@example.com", :customer_contact_type => "work") 111 | 112 | # Update a customer email 113 | Desk.update_customer_email(12345, 54321, :email => "foo@example.com") 114 | Desk.update_customer_email(12345, 54321, :customer_contact_type => "work") 115 | 116 | # Add a customer phone number 117 | Desk.create_customer_phone(12345, "555-368-7147") 118 | Desk.create_customer_phone(12345, "555-368-7147", :customer_contact_type => "work") 119 | 120 | # Update a customer phone number 121 | Desk.update_customer_phone(12345, 54321, :phone => "555-368-7147") 122 | Desk.update_customer_phone(12345, 54321, :customer_contact_type => "work") 123 | 124 | ###### 125 | # Interactions 126 | ###### 127 | 128 | # List interactions 129 | Desk.interactions 130 | Desk.interactions(:since_id => 12345) 131 | Desk.interactions(:since_id => 12345, :count => 5) 132 | 133 | # Create an inbound interaction 134 | Desk.create_interaction(:interaction_subject => "help me", :customer_email => "foo@example.com", :interaction_body => "You're my only hope.") 135 | Desk.create_inbound_interaction(:interaction_subject => "help me", :customer_email => "foo@example.com", :interaction_body => "You're my only hope.") 136 | 137 | # Create an outbound interaction 138 | # Desk.com's API doesn't support creating outbound communications, so we do this over email with a BCC back to Desk and customer headers. 139 | # Desk.support_email must be set to your Desk.com email address so that the email can be sent to the account and give the customer someone to respond to. 140 | # 141 | # Read more at http://support.desk.com/customer/portal/articles/4180 142 | # Additional headers can be passed as well http://support.desk.com/customer/portal/articles/6728 143 | # 144 | # Email is sent using Pony https://github.com/benprew/pony 145 | Desk.create_interaction(:interaction_subject => "Missed Your Call", :customer_email => "foo@example.com", :interaction_body => "Sorry we missed yoru call. What's up?", :direction => "outbound") 146 | Desk.create_outbound_interaction("foo@example.com", "Missed Your Call", "Sorry we missed yoru call. What's up?") 147 | 148 | ###### 149 | # Users 150 | ###### 151 | 152 | # List users 153 | Desk.users 154 | 155 | # Get a specific user 156 | Desk.user(12345) 157 | 158 | ###### 159 | # Topics 160 | ###### 161 | 162 | # List Topics 163 | Desk.topics 164 | 165 | # Get a specific topic 166 | Desk.topic(12345) 167 | 168 | # Create a new topic 169 | Desk.create_topic("name", :description => "description") 170 | 171 | # Update a topic 172 | Desk.update_topic(12345, :subject => "Updated") 173 | 174 | # Delete a topic 175 | Desk.delete_topic(12345) 176 | 177 | ###### 178 | # Articles 179 | ###### 180 | 181 | # List articles for a topic 182 | Desk.articles(1) 183 | 184 | # Get a specific article 185 | Desk.article(12345) 186 | 187 | # Create a new article within a topic 188 | Desk.create_article(1, :subject => "API Tips", :main_content => "Tips on using our API") 189 | 190 | # Update an article 191 | Desk.update_article(12345, :subject => "Updated API Tips") 192 | 193 | # Delete an article 194 | Desk.delete_article(12345) 195 | 196 | ###### 197 | # Macros 198 | ###### 199 | 200 | # List Macros 201 | Desk.macros 202 | 203 | # Get a specific macro 204 | Desk.macro(12345) 205 | 206 | # Create a new macro 207 | Desk.create_macro("name", :labels => "escalated") 208 | 209 | # Update a macro 210 | Desk.update_macro(12345, :name => "Updated Name") 211 | 212 | # Delete a macro 213 | Desk.delete_macro(12345) 214 | 215 | # Macro Actions 216 | Desk.macro_actions(12345) 217 | 218 | # Macro Action 219 | Desk.macro_action(12345, "set-case-description") 220 | 221 | # Update Macro Action 222 | Desk.update_macro_action(12345, "set-case-description", :value => "New Subject") 223 | 224 | Contributing 225 | ------------ 226 | In the spirit of [free software](http://www.fsf.org/licensing/essays/free-sw.html), **everyone** is encouraged to help improve this project. 227 | 228 | Here are some ways *you* can contribute: 229 | 230 | * by using alpha, beta, and prerelease versions 231 | * by reporting bugs 232 | * by suggesting new features 233 | * by writing or editing documentation 234 | * by writing specifications 235 | * by writing code (**no patch is too small**: fix typos, add comments, clean up inconsistent whitespace) 236 | * by refactoring code 237 | * by closing [issues](http://github.com/zencoder/desk/issues) 238 | * by reviewing patches 239 | 240 | All contributors will be added to the [HISTORY](https://github.com/zencoder/desk/blob/master/HISTORY.mkd) 241 | file and will receive the respect and gratitude of the community. 242 | 243 | Submitting an Issue 244 | ------------------- 245 | We use the [GitHub issue tracker](http://github.com/zencoder/desk/issues) to track bugs and 246 | features. Before submitting a bug report or feature request, check to make sure it hasn't already 247 | been submitted. You can indicate support for an existing issuse by voting it up. When submitting a 248 | bug report, please include a [Gist](http://gist.github.com/) that includes a stack trace and any 249 | details that may be necessary to reproduce the bug, including your gem version, Ruby version, and 250 | operating system. Ideally, a bug report should include a pull request with failing specs. 251 | 252 | Submitting a Pull Request 253 | ------------------------- 254 | 1. Fork the project. 255 | 2. Create a topic branch. 256 | 3. Implement your feature or bug fix. 257 | 4. Add documentation for your feature or bug fix. 258 | 5. Run bundle exec rake doc:yard. If your changes are not 100% documented, go back to step 4. 259 | 6. Add specs for your feature or bug fix. 260 | 7. Run bundle exec rake spec. If your changes are not 100% covered, go back to step 6. 261 | 8. Commit and push your changes. 262 | 9. Submit a pull request. Please do not include changes to the gemspec, version, or history file. (If you want to create your own version for some reason, please do so in a separate commit.) 263 | 264 | Copyright 265 | --------- 266 | Copyright (c) 2012 Chris Warren/[Zencoder](http://zencoder.com) 267 | See [LICENSE](https://github.com/zencoder/desk/blob/master/LICENSE.mkd) for details. 268 | --------------------------------------------------------------------------------