├── .gemtest ├── .rspec ├── lib ├── desk │ ├── version.rb │ ├── client │ │ ├── brand.rb │ │ ├── job.rb │ │ ├── custom_field.rb │ │ ├── filter.rb │ │ ├── facebook_user.rb │ │ ├── site_setting.rb │ │ ├── group.rb │ │ ├── label.rb │ │ ├── twitter_user.rb │ │ ├── company.rb │ │ ├── integration_url.rb │ │ ├── user.rb │ │ ├── twitter_account.rb │ │ ├── macro.rb │ │ ├── rule.rb │ │ ├── topic.rb │ │ ├── article.rb │ │ ├── system_message.rb │ │ ├── insight.rb │ │ ├── mailbox.rb │ │ ├── customer.rb │ │ └── case.rb │ ├── api.rb │ ├── authentication.rb │ ├── connection.rb │ ├── request.rb │ ├── error.rb │ ├── deash.rb │ └── client.rb ├── faraday │ ├── response │ │ ├── deashify.rb │ │ ├── raise_http_5xx.rb │ │ └── raise_http_4xx.rb │ └── request │ │ ├── multipart_with_file.rb │ │ └── oauth.rb └── desk.rb ├── Gemfile ├── .gitignore ├── spec ├── fixtures │ ├── group │ ├── system_message │ ├── site_setting │ ├── brand │ ├── custom_field │ ├── topic_translation │ ├── topic_translation_create │ ├── user_preference_update │ ├── label │ ├── label_create │ ├── topic_translation_update │ ├── label_update │ ├── rule │ ├── filter │ ├── twitter_account │ ├── job │ ├── job_create │ ├── macro │ ├── user_preference │ ├── macro_update │ ├── macro_action │ ├── macro_action_update │ ├── macro_create │ ├── facebook_user │ ├── integration_url │ ├── twitter_user │ ├── integration_url_create │ ├── integration_url_update │ ├── twitter_user_create │ ├── case_note │ ├── case_attachment │ ├── twitter_account_tweet │ ├── case_attachment_create │ ├── twitter_account_tweet_create │ ├── user │ ├── topic_update │ ├── topic_create │ ├── company │ ├── company_create │ ├── topic │ ├── company_update │ ├── case_reply_create │ ├── case_message │ ├── case_note_create │ ├── case_reply │ ├── case_reply_update │ ├── inbound_mailbox │ ├── article_translation │ ├── groups │ ├── article_translation_create │ ├── article_translation_update │ ├── site_settings │ ├── brands │ ├── case │ ├── customer_create │ ├── case_update │ ├── topic_translations │ ├── rules │ ├── case_create │ ├── labels │ ├── filters │ ├── group_filters │ ├── custom_fields │ ├── customer │ ├── customer_update │ ├── twitter_accounts │ ├── article │ ├── jobs │ ├── article_create │ ├── article_update │ ├── macro_actions │ ├── macros │ ├── facebook_users │ ├── integration_urls │ ├── twitter_users │ ├── case_notes │ ├── twitter_account_tweets │ ├── case_attachments │ ├── users │ ├── group_users │ ├── topics │ ├── companies │ ├── company_search │ ├── companies_search │ ├── case_replies │ ├── inbound_mailboxes │ ├── article_translations │ ├── cases │ ├── filter_cases │ ├── cases_search │ ├── articles │ ├── customers │ ├── customers_search │ ├── articles_search │ └── user_preferences ├── desk │ ├── client_spec.rb │ ├── client │ │ ├── brand_spec.rb │ │ ├── rule_spec.rb │ │ ├── site_setting_spec.rb │ │ ├── custom_fields_spec.rb │ │ ├── facebook_users_spec.rb │ │ ├── filter_spec.rb │ │ ├── twitter_user_spec.rb │ │ ├── companies_spec.rb │ │ ├── integration_url_spec.rb │ │ ├── job_spec.rb │ │ ├── label_spec.rb │ │ ├── twitter_account_spec.rb │ │ ├── customer_spec.rb │ │ ├── group_spec.rb │ │ ├── user_spec.rb │ │ ├── macro_spec.rb │ │ ├── system_message_spec.rb │ │ ├── topic_spec.rb │ │ ├── inbound_mailboxes_spec.rb │ │ ├── article_spec.rb │ │ └── case_spec.rb │ └── api_spec.rb ├── shared_context.rb ├── faraday │ └── response_spec.rb ├── helper.rb └── desk_spec.rb ├── .yardopts ├── Rakefile ├── LICENSE.mkd ├── desk.gemspec ├── HISTORY.mkd └── TRANSITION.mkd /.gemtest: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --format=nested 3 | --backtrace 4 | -------------------------------------------------------------------------------- /lib/desk/version.rb: -------------------------------------------------------------------------------- 1 | module Desk 2 | # The version of the gem 3 | VERSION = '1.0.10'.freeze unless defined?(::Desk::VERSION) 4 | end 5 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "http://rubygems.org" 2 | 3 | gemspec 4 | 5 | 6 | group :development do 7 | end 8 | 9 | group :test do 10 | end 11 | -------------------------------------------------------------------------------- /.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/group: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Support Ninjas", 3 | "_links": { 4 | "self": { 5 | "href": "/api/v2/groups/1", 6 | "class": "group" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /spec/fixtures/system_message: -------------------------------------------------------------------------------- 1 | { 2 | "message": "We're not doing maintenance today, but if we were then we would tell you about it here.", 3 | "updated_at": "2013-11-22T22:49:20Z" 4 | } 5 | -------------------------------------------------------------------------------- /lib/desk/client/brand.rb: -------------------------------------------------------------------------------- 1 | module Desk 2 | class Client 3 | module Brand 4 | 5 | def brand_endpoints 6 | [ :list, :show ] 7 | end 8 | 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /lib/desk/client/job.rb: -------------------------------------------------------------------------------- 1 | module Desk 2 | class Client 3 | module Job 4 | 5 | def job_endpoints 6 | [ :list, :show, :create ] 7 | end 8 | 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /lib/desk/client/custom_field.rb: -------------------------------------------------------------------------------- 1 | module Desk 2 | class Client 3 | module CustomField 4 | 5 | def custom_field_endpoints 6 | [ :list, :show ] 7 | end 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /lib/desk/client/filter.rb: -------------------------------------------------------------------------------- 1 | module Desk 2 | class Client 3 | module Filter 4 | 5 | def filter_endpoints 6 | [ :list, :show, :list_cases ] 7 | end 8 | 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /lib/desk/client/facebook_user.rb: -------------------------------------------------------------------------------- 1 | module Desk 2 | class Client 3 | module FacebookUser 4 | 5 | def facebook_user_endpoints 6 | [ :list, :show ] 7 | end 8 | 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /lib/desk/client/site_setting.rb: -------------------------------------------------------------------------------- 1 | module Desk 2 | class Client 3 | module SiteSetting 4 | 5 | def site_setting_endpoints 6 | [ :list, :show ] 7 | end 8 | 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /lib/desk/client/group.rb: -------------------------------------------------------------------------------- 1 | module Desk 2 | class Client 3 | module Group 4 | 5 | def group_endpoints 6 | [ :list, :show, :list_filters, :list_users ] 7 | end 8 | 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /lib/desk/client/label.rb: -------------------------------------------------------------------------------- 1 | module Desk 2 | class Client 3 | module Label 4 | 5 | def label_endpoints 6 | [ :list, :show, :create, :update, :delete ] 7 | end 8 | 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /lib/desk/client/twitter_user.rb: -------------------------------------------------------------------------------- 1 | module Desk 2 | class Client 3 | module TwitterUser 4 | 5 | def twitter_user_endpoints 6 | [ :list, :show, :create ] 7 | end 8 | 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /lib/desk/client/company.rb: -------------------------------------------------------------------------------- 1 | module Desk 2 | class Client 3 | module Company 4 | 5 | def company_endpoints 6 | [ :list, :show, :create, :update, :search, :list_cases ] 7 | end 8 | 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /spec/fixtures/site_setting: -------------------------------------------------------------------------------- 1 | { 2 | "name": "company_name", 3 | "value": "Cool Surfboard Co.", 4 | "_links": { 5 | "self": { 6 | "href": "/api/v2/site_settings/1", 7 | "class": "site_setting" 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /lib/desk/client/integration_url.rb: -------------------------------------------------------------------------------- 1 | module Desk 2 | class Client 3 | module IntegrationUrl 4 | 5 | def integration_url_endpoints 6 | [ :list, :show, :create, :update, :delete ] 7 | end 8 | 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /lib/desk/client/user.rb: -------------------------------------------------------------------------------- 1 | module Desk 2 | class Client 3 | module User 4 | 5 | def user_endpoints 6 | [ :list, :show, :list_preferences, :show_preference, :update_preference ] 7 | end 8 | 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /lib/desk/client/twitter_account.rb: -------------------------------------------------------------------------------- 1 | module Desk 2 | class Client 3 | module TwitterAccount 4 | 5 | def twitter_account_endpoints 6 | [ :list, :show, :list_tweets, :show_tweet, :create_tweet ] 7 | end 8 | 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /spec/fixtures/brand: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Desk.com", 3 | "created_at": "2013-11-22T22:49:20Z", 4 | "updated_at": "2013-11-22T22:49:20Z", 5 | "_links": { 6 | "self": { 7 | "href": "/api/v2/brands/1", 8 | "class": "brand" 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /lib/desk/client/macro.rb: -------------------------------------------------------------------------------- 1 | module Desk 2 | class Client 3 | module Macro 4 | 5 | def macro_endpoints 6 | [ :list, :show, :create, :update, :delete, 7 | :list_actions, :show_action, :update_action ] 8 | end 9 | 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /lib/desk/client/rule.rb: -------------------------------------------------------------------------------- 1 | module Desk 2 | class Client 3 | module Rule 4 | 5 | def rule_endpoints 6 | [ :list, :show ] 7 | # PLANNED 8 | # [ :list_actions, :show_actions, 9 | # :list_conditions, :show_conditions ] 10 | end 11 | 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /spec/fixtures/custom_field: -------------------------------------------------------------------------------- 1 | { 2 | "name": "frequent_buyer", 3 | "label": "Frequent Buyer", 4 | "type": "customer", 5 | "active": true, 6 | "data": { 7 | "type": "boolean" 8 | }, 9 | "_links": { 10 | "self": { 11 | "href": "/api/v2/custom_fields/1", 12 | "class": "custom_field" 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /spec/fixtures/topic_translation: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Customer Support", 3 | "locale": "en_us", 4 | "created_at": "2013-11-12T22:59:20Z", 5 | "updated_at": "2013-11-17T22:59:20Z", 6 | "_links": { 7 | "self": { 8 | "href": "/api/v2/topics/1/translations/en_us", 9 | "class": "topic_translation" 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /spec/fixtures/topic_translation_create: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Japanese", 3 | "locale": "ja", 4 | "created_at": "2013-11-22T22:59:24Z", 5 | "updated_at": "2013-11-22T22:59:24Z", 6 | "_links": { 7 | "self": { 8 | "href": "/api/v2/topics/1/translations/en_us", 9 | "class": "topic_translation" 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /spec/fixtures/user_preference_update: -------------------------------------------------------------------------------- 1 | { 2 | "name": "auto_accept_on_route", 3 | "value": true, 4 | "_links": { 5 | "self": { 6 | "href": "/api/v2/users/1/preferences/3", 7 | "class": "user_preference" 8 | }, 9 | "user": { 10 | "href": "/api/v2/users/1", 11 | "class": "user" 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /spec/fixtures/label: -------------------------------------------------------------------------------- 1 | { 2 | "name": "MyLabel", 3 | "description": "My Label Description", 4 | "types": [ 5 | "case", 6 | "macro" 7 | ], 8 | "active": true, 9 | "color": "green", 10 | "position": 1, 11 | "_links": { 12 | "self": { 13 | "href": "/api/v2/labels/1", 14 | "class": "label" 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /spec/fixtures/label_create: -------------------------------------------------------------------------------- 1 | { 2 | "name": "MyLabel", 3 | "description": "A Test Label", 4 | "types": [ 5 | "case", 6 | "macro" 7 | ], 8 | "active": true, 9 | "color": "blue", 10 | "position": 1, 11 | "_links": { 12 | "self": { 13 | "href": "/api/v2/labels/1", 14 | "class": "label" 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /lib/desk/client/topic.rb: -------------------------------------------------------------------------------- 1 | module Desk 2 | class Client 3 | module Topic 4 | 5 | def topic_endpoints 6 | [ :list, :show, :create, :update, :delete, 7 | :list_translations, :show_translation, :create_translation, 8 | :update_translation, :delete_translation ] 9 | end 10 | 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /spec/fixtures/topic_translation_update: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Updated Japanese Translation", 3 | "locale": "ja", 4 | "created_at": "2013-11-17T22:59:24Z", 5 | "updated_at": "2013-11-22T22:59:24Z", 6 | "_links": { 7 | "self": { 8 | "href": "/api/v2/topics/1/translations/ja", 9 | "class": "topic_translation" 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /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/label_update: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Label 5", 3 | "description": "My Label Description", 4 | "types": [ 5 | "case", 6 | "macro" 7 | ], 8 | "active": true, 9 | "color": "green", 10 | "position": 1, 11 | "_links": { 12 | "self": { 13 | "href": "/api/v2/labels/1", 14 | "class": "label" 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /spec/fixtures/rule: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Assign to Support", 3 | "description": "Assign inbound tweets to support group", 4 | "enabled": true, 5 | "created_at": "2013-11-22T22:49:20Z", 6 | "updated_at": "2013-11-22T22:49:20Z", 7 | "_links": { 8 | "self": { 9 | "href": "/api/v2/rules/1", 10 | "class": "rule" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /lib/desk/client/article.rb: -------------------------------------------------------------------------------- 1 | module Desk 2 | class Client 3 | module Article 4 | 5 | def article_endpoints 6 | [ :list, :show, :create, :update, :delete, :search, 7 | :list_translations, :show_translation, :create_translation, 8 | :update_translation, :delete_translation ] 9 | end 10 | 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /spec/fixtures/filter: -------------------------------------------------------------------------------- 1 | { 2 | "name": "My Active Cases", 3 | "sort": "priority", 4 | "sort_field": "priority", 5 | "sort_direction": "desc", 6 | "position": 1, 7 | "active": true, 8 | "_links": { 9 | "self": { 10 | "href": "/api/v2/filters/1", 11 | "class": "filter" 12 | }, 13 | "group": null, 14 | "user": null 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /lib/desk/client/system_message.rb: -------------------------------------------------------------------------------- 1 | module Desk 2 | class Client 3 | module SystemMessage 4 | 5 | def show_system_message 6 | get("system_message") 7 | end 8 | alias_method :system_message, :show_system_message 9 | 10 | def system_message_id(href, parent_id = false) 11 | nil 12 | end 13 | 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /spec/fixtures/twitter_account: -------------------------------------------------------------------------------- 1 | { 2 | "handle": "desk_dev", 3 | "name": "Desk.com Development", 4 | "profile_image": "http://www.example.com/image.png", 5 | "active": true, 6 | "created_at": "2013-05-22T21:59:20Z", 7 | "updated_at": "2013-06-22T21:59:20Z", 8 | "_links": { 9 | "self": { 10 | "href": "/api/v2/twitter_accounts/1", 11 | "class": "twitter_account" 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /spec/desk/client/brand_spec.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | 3 | describe Desk::Client do 4 | context "Brand" do 5 | 6 | let(:endpoint) { "brand" } 7 | let(:id) { 1 } 8 | let(:check_key) { "name" } 9 | let(:check_value) { "Desk.com" } 10 | 11 | include_context "basic configuration" 12 | 13 | it_behaves_like "a list endpoint" 14 | 15 | it_behaves_like "a show endpoint" 16 | 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /spec/fixtures/job: -------------------------------------------------------------------------------- 1 | { 2 | "type": "bulk_case_update", 3 | "status_message": "Completed", 4 | "progress": 100, 5 | "created_at": "2013-11-22T21:59:20Z", 6 | "completed_at": "2013-11-22T22:49:20Z", 7 | "_links": { 8 | "self": { 9 | "href": "/api/v2/jobs/1", 10 | "class": "job" 11 | }, 12 | "user": { 13 | "href": "/api/v2/users/1", 14 | "class": "user" 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /spec/desk/client/rule_spec.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | 3 | describe Desk::Client do 4 | context "Rule" do 5 | 6 | let(:endpoint) { "rule" } 7 | let(:id) { 1 } 8 | let(:check_key) { "name" } 9 | let(:check_value) { "Assign to Support" } 10 | 11 | include_context "basic configuration" 12 | 13 | it_behaves_like "a list endpoint" 14 | 15 | it_behaves_like "a show endpoint" 16 | 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /spec/fixtures/job_create: -------------------------------------------------------------------------------- 1 | { 2 | "type": "bulk_case_update", 3 | "status_message": "Completed", 4 | "progress": 100, 5 | "created_at": "2013-11-22T21:59:20Z", 6 | "completed_at": "2013-11-22T22:49:20Z", 7 | "_links": { 8 | "self": { 9 | "href": "/api/v2/jobs/1", 10 | "class": "job" 11 | }, 12 | "user": { 13 | "href": "/api/v2/users/1", 14 | "class": "user" 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /lib/desk/client/insight.rb: -------------------------------------------------------------------------------- 1 | module Desk 2 | class Client 3 | module Insight 4 | 5 | def show_insights_meta 6 | get("insights3/meta") 7 | end 8 | alias_method :insights_meta, :show_insights_meta 9 | 10 | def create_insights_report(*args) 11 | options = args.last.is_a?(Hash) ? args.pop : {} 12 | post("insights3/reports", options) 13 | end 14 | 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /spec/desk/client/site_setting_spec.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | 3 | describe Desk::Client do 4 | context "Site Setting" do 5 | 6 | let(:endpoint) { "site_setting" } 7 | let(:id) { 1 } 8 | let(:check_key) { "name" } 9 | let(:check_value) { "company_name" } 10 | 11 | include_context "basic configuration" 12 | 13 | it_behaves_like "a list endpoint" 14 | 15 | it_behaves_like "a show endpoint" 16 | 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /spec/fixtures/macro: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Macro Macro", 3 | "description": "On repeat", 4 | "enabled": true, 5 | "position": 1, 6 | "folders": [ 7 | "Sample Macros", 8 | "Favorites" 9 | ], 10 | "_links": { 11 | "self": { 12 | "href": "/api/v2/macros/1", 13 | "class": "macro" 14 | }, 15 | "actions": { 16 | "href": "/api/v2/macros/1/actions", 17 | "class": "macro_action" 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /spec/fixtures/user_preference: -------------------------------------------------------------------------------- 1 | { 2 | "name": "enable_routing_filter_on_login", 3 | "value": "12", 4 | "_links": { 5 | "self": { 6 | "href": "/api/v2/users/1/preferences/1", 7 | "class": "user_preference" 8 | }, 9 | "user": { 10 | "href": "/api/v2/users/1", 11 | "class": "user" 12 | }, 13 | "filter": { 14 | "href": "/api/v2/filters/12", 15 | "class": "filter" 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /spec/desk/client/custom_fields_spec.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | 3 | describe Desk::Client do 4 | context "Custom Fields" do 5 | 6 | let(:endpoint) { "custom_field" } 7 | let(:id) { 1 } 8 | let(:check_key) { "name" } 9 | let(:check_value) { "frequent_buyer" } 10 | 11 | include_context "basic configuration" 12 | 13 | it_behaves_like "a list endpoint" 14 | 15 | it_behaves_like "a show endpoint" 16 | 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /spec/fixtures/macro_update: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Macro Macro", 3 | "description": "On repeat", 4 | "enabled": true, 5 | "position": 1, 6 | "folders": [ 7 | "Sample Macros", 8 | "Favorites" 9 | ], 10 | "_links": { 11 | "self": { 12 | "href": "/api/v2/macros/1", 13 | "class": "macro" 14 | }, 15 | "actions": { 16 | "href": "/api/v2/macros/1/actions", 17 | "class": "macro_action" 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /spec/fixtures/macro_action: -------------------------------------------------------------------------------- 1 | { 2 | "type": "set-case-description", 3 | "value": "From a VIP Customer", 4 | "enabled": true, 5 | "created_at": "2013-11-22T22:49:20Z", 6 | "updated_at": "2013-11-22T22:49:20Z", 7 | "_links": { 8 | "self": { 9 | "href": "/api/v2/macros/1/actions/1", 10 | "class": "macro_action" 11 | }, 12 | "macro": { 13 | "href": "/api/v2/macros/1", 14 | "class": "macro" 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /spec/desk/client/facebook_users_spec.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | 3 | describe Desk::Client do 4 | context "Facebook User" do 5 | 6 | let(:endpoint) { "facebook_user" } 7 | let(:id) { 1 } 8 | let(:check_key) { "profile_url" } 9 | let(:check_value) { "https://www.facebook.com/zuck" } 10 | 11 | include_context "basic configuration" 12 | 13 | it_behaves_like "a list endpoint" 14 | 15 | it_behaves_like "a show endpoint" 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /spec/fixtures/macro_action_update: -------------------------------------------------------------------------------- 1 | { 2 | "type": "set-case-description", 3 | "value": "From a VIP Customer", 4 | "enabled": false, 5 | "created_at": "2013-11-22T22:49:20Z", 6 | "updated_at": "2013-11-22T22:49:20Z", 7 | "_links": { 8 | "self": { 9 | "href": "/api/v2/macros/1/actions/1", 10 | "class": "macro_action" 11 | }, 12 | "macro": { 13 | "href": "/api/v2/macros/1", 14 | "class": "macro" 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /spec/fixtures/macro_create: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Macro Macro", 3 | "description": "It's raining fire!", 4 | "enabled": true, 5 | "position": 1, 6 | "folders": [ 7 | "Sample Macros", 8 | "Favorites" 9 | ], 10 | "_links": { 11 | "self": { 12 | "href": "/api/v2/macros/1", 13 | "class": "macro" 14 | }, 15 | "actions": { 16 | "href": "/api/v2/macros/1/actions", 17 | "class": "macro_action" 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /spec/fixtures/facebook_user: -------------------------------------------------------------------------------- 1 | { 2 | "image_url": "https://graph.facebook.com/zuck/picture?type=square", 3 | "profile_url": "https://www.facebook.com/zuck", 4 | "created_at": "2013-11-12T22:59:20Z", 5 | "updated_at": "2013-11-17T22:59:20Z", 6 | "_links": { 7 | "self": { 8 | "href": "/api/v2/facebook_users/1", 9 | "class": "facebook_user" 10 | }, 11 | "customer": { 12 | "href": "/api/v2/customers/1", 13 | "class": "customer" 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /spec/shared_context.rb: -------------------------------------------------------------------------------- 1 | shared_context "basic configuration" do 2 | subject(:client) { 3 | Desk::Client.new( 4 | :subdomain => "example", 5 | :consumer_key => 'CK', 6 | :consumer_secret => 'CS', 7 | :oauth_token => 'OT', 8 | :oauth_token_secret => 'OS' 9 | ) 10 | } 11 | end 12 | 13 | shared_context "plural endpoint" do 14 | let(:endpoints) { Desk.plural(endpoint) } 15 | let(:sub_endpoints) { Desk.plural(sub_endpoint) } 16 | end 17 | -------------------------------------------------------------------------------- /spec/fixtures/integration_url: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Sample URL", 3 | "description": "A sample Integration URL", 4 | "enabled": true, 5 | "markup": "http://www.example.com/name={{customer.name | url_encode}}", 6 | "rendered": "http://www.example.com/name=Andrew", 7 | "created_at": "2013-11-22T22:49:20Z", 8 | "updated_at": "2013-11-22T22:49:20Z", 9 | "_links": { 10 | "self": { 11 | "href": "/api/v2/integration_urls/1", 12 | "class": "integration_url" 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /spec/fixtures/twitter_user: -------------------------------------------------------------------------------- 1 | { 2 | "handle": "desk_dev", 3 | "image_url": "http://example.com/image.png", 4 | "followers_count": "123", 5 | "verified": false, 6 | "created_at": "2013-11-22T22:49:20Z", 7 | "updated_at": "2013-11-22T22:49:20Z", 8 | "_links": { 9 | "self": { 10 | "href": "/api/v2/twitter_users/1", 11 | "class": "twitter_user" 12 | }, 13 | "customer": { 14 | "href": "/api/v2/customers/1", 15 | "class": "customer" 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /spec/fixtures/integration_url_create: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Sample URL", 3 | "description": "A sample Integration URL", 4 | "enabled": false, 5 | "markup": "http://www.example.com/name={{customer.name | url_encode}}", 6 | "rendered": "http://www.example.com/name=", 7 | "created_at": "2013-11-22T22:49:20Z", 8 | "updated_at": "2013-11-22T22:49:20Z", 9 | "_links": { 10 | "self": { 11 | "href": "/api/v2/integration_urls/1", 12 | "class": "integration_url" 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /spec/fixtures/integration_url_update: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Sample URL", 3 | "description": "A sample Integration URL", 4 | "enabled": false, 5 | "markup": "http://www.example.com/name={{customer.name | url_encode}}", 6 | "rendered": "http://www.example.com/name=", 7 | "created_at": "2013-11-22T22:49:20Z", 8 | "updated_at": "2013-11-22T22:49:20Z", 9 | "_links": { 10 | "self": { 11 | "href": "/api/v2/integration_urls/1", 12 | "class": "integration_url" 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /spec/fixtures/twitter_user_create: -------------------------------------------------------------------------------- 1 | { 2 | "handle": "desk_dev", 3 | "image_url": "http://example.com/image.png", 4 | "followers_count": "123", 5 | "verified": false, 6 | "created_at": "2013-11-22T22:49:20Z", 7 | "updated_at": "2013-11-22T22:49:20Z", 8 | "_links": { 9 | "self": { 10 | "href": "/api/v2/twitter_users/1", 11 | "class": "twitter_user" 12 | }, 13 | "customer": { 14 | "href": "/api/v2/customers/1", 15 | "class": "customer" 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /spec/fixtures/case_note: -------------------------------------------------------------------------------- 1 | { 2 | "body": "Please assist me with this case", 3 | "created_at": "2013-11-22T22:49:20Z", 4 | "updated_at": "2013-11-22T22:49:20Z", 5 | "erased_at": null, 6 | "_links": { 7 | "self": { 8 | "href": "/api/v2/cases/1/notes/1", 9 | "class": "note" 10 | }, 11 | "case": { 12 | "href": "/api/v2/cases/1", 13 | "class": "case" 14 | }, 15 | "user": { 16 | "href": "/api/v2/users/1", 17 | "class": "user" 18 | }, 19 | "erased_by": null 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /spec/fixtures/case_attachment: -------------------------------------------------------------------------------- 1 | { 2 | "file_name": "awesome_pic.png", 3 | "content_type": "image/png", 4 | "size": "500", 5 | "url": "http://example.com/short_lived_link_to_the_file_content", 6 | "erased_at": null, 7 | "created_at": "2013-11-22T22:49:20Z", 8 | "updated_at": "2013-11-22T22:49:20Z", 9 | "_links": { 10 | "self": { 11 | "href": "/api/v2/cases/1/attachments/1", 12 | "class": "attachment" 13 | }, 14 | "case": { 15 | "href": "/api/v2/cases/1", 16 | "class": "case" 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /spec/fixtures/twitter_account_tweet: -------------------------------------------------------------------------------- 1 | { 2 | "body": "Example tweet", 3 | "direction": "out", 4 | "type": "mention", 5 | "status": "sent", 6 | "to": null, 7 | "from": "desk_dev", 8 | "created_at": "2013-11-22T17:59:20Z", 9 | "updated_at": "2013-11-22T17:59:20Z", 10 | "_links": { 11 | "self": { 12 | "href": "/api/v2/twitter_accounts/1/tweets/1", 13 | "class": "tweet" 14 | }, 15 | "twitter_account": { 16 | "href": "/api/v2/twitter_accounts/1", 17 | "class": "twitter_account" 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /spec/fixtures/case_attachment_create: -------------------------------------------------------------------------------- 1 | { 2 | "file_name": "awesome_pic.png", 3 | "content_type": "image/png", 4 | "size": "500", 5 | "url": "http://example.com/short_lived_link_to_the_file_content", 6 | "erased_at": null, 7 | "created_at": "2013-11-22T22:49:20Z", 8 | "updated_at": "2013-11-22T22:49:20Z", 9 | "_links": { 10 | "self": { 11 | "href": "/api/v2/cases/1/attachments/1", 12 | "class": "attachment" 13 | }, 14 | "case": { 15 | "href": "/api/v2/cases/1", 16 | "class": "case" 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /spec/fixtures/twitter_account_tweet_create: -------------------------------------------------------------------------------- 1 | { 2 | "body": "Example tweet", 3 | "direction": "out", 4 | "type": "mention", 5 | "status": "sent", 6 | "to": null, 7 | "from": "desk_dev", 8 | "created_at": "2013-11-22T17:59:20Z", 9 | "updated_at": "2013-11-22T17:59:20Z", 10 | "_links": { 11 | "self": { 12 | "href": "/api/v2/twitter_accounts/1/tweets/1", 13 | "class": "tweet" 14 | }, 15 | "twitter_account": { 16 | "href": "/api/v2/twitter_accounts/1", 17 | "class": "twitter_account" 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /spec/fixtures/user: -------------------------------------------------------------------------------- 1 | { 2 | "name": "John Doe", 3 | "public_name": "John Doe", 4 | "email": "john@acme.com", 5 | "level": "agent", 6 | "created_at": "2012-11-22T22:59:20Z", 7 | "updated_at": "2013-11-15T22:59:20Z", 8 | "current_login_at": "2013-11-21T22:59:20Z", 9 | "last_login_at": "2013-11-15T22:59:20Z", 10 | "_links": { 11 | "self": { 12 | "href": "/api/v2/users/1", 13 | "class": "user" 14 | }, 15 | "preferences": { 16 | "href": "/api/v2/users/1/preferences", 17 | "class": "user_preference" 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /spec/fixtures/topic_update: -------------------------------------------------------------------------------- 1 | { 2 | "name": "New Name", 3 | "description": null, 4 | "position": 1, 5 | "allow_questions": false, 6 | "in_support_center": true, 7 | "created_at": "2013-11-17T22:59:24Z", 8 | "updated_at": "2013-11-22T22:59:24Z", 9 | "_links": { 10 | "self": { 11 | "href": "/api/v2/topics/1", 12 | "class": "topic" 13 | }, 14 | "articles": { 15 | "href": "/api/v2/topics/1/articles", 16 | "class": "article" 17 | }, 18 | "translations": { 19 | "href": "/api/v2/topics/1/translations", 20 | "class": "topic_translation" 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /spec/fixtures/topic_create: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Customer Support", 3 | "description": null, 4 | "position": 1, 5 | "allow_questions": true, 6 | "in_support_center": true, 7 | "created_at": "2013-11-22T22:59:24Z", 8 | "updated_at": "2013-11-22T22:59:24Z", 9 | "_links": { 10 | "self": { 11 | "href": "/api/v2/topics/1", 12 | "class": "topic" 13 | }, 14 | "articles": { 15 | "href": "/api/v2/topics/1/articles", 16 | "class": "article" 17 | }, 18 | "translations": { 19 | "href": "/api/v2/topics/1/translations", 20 | "class": "topic_translation" 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /spec/fixtures/company: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Acme Inc", 3 | "domains": [ 4 | "acmeinc.com", 5 | "acmeinc.net" 6 | ], 7 | "created_at": "2013-11-22T22:49:20Z", 8 | "updated_at": "2013-11-22T22:49:20Z", 9 | "custom_fields": { 10 | "employer_id": "123456789" 11 | }, 12 | "_links": { 13 | "self": { 14 | "href": "/api/v2/companies/1", 15 | "class": "company" 16 | }, 17 | "customers": { 18 | "href": "/api/v2/companies/1/customers", 19 | "class": "customer" 20 | }, 21 | "cases": { 22 | "href": "/api/v2/companies/1/cases", 23 | "class": "case" 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/faraday/response/deashify.rb: -------------------------------------------------------------------------------- 1 | require 'faraday_middleware/response/mashify' 2 | 3 | module FaradayMiddleware 4 | # Public: Converts parsed response bodies to a Hashie::Rash if they were of 5 | # Hash or Array type. 6 | class Deashify < Mashify 7 | dependency do 8 | require 'desk/deash' 9 | self.mash_class = ::Hashie::Deash 10 | end 11 | 12 | def parse(body) 13 | case body 14 | when Hash 15 | raw = mash_class.new({:raw => body}) 16 | when Array 17 | raw = body.map { |item| parse(item) } 18 | else 19 | raw = body 20 | end 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /spec/fixtures/company_create: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Acme Inc", 3 | "domains": [ 4 | "acmeinc.com", 5 | "acmeinc.net" 6 | ], 7 | "created_at": "2013-11-22T22:49:20Z", 8 | "updated_at": "2013-11-22T22:49:20Z", 9 | "custom_fields": { 10 | "employer_id": "123456789" 11 | }, 12 | "_links": { 13 | "self": { 14 | "href": "/api/v2/companies/1", 15 | "class": "company" 16 | }, 17 | "customers": { 18 | "href": "/api/v2/companies/1/customers", 19 | "class": "customer" 20 | }, 21 | "cases": { 22 | "href": "/api/v2/companies/1/cases", 23 | "class": "case" 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /spec/fixtures/topic: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Customer Support", 3 | "description": "This is key to going from good to great", 4 | "position": 1, 5 | "allow_questions": true, 6 | "in_support_center": true, 7 | "created_at": "2013-11-12T22:59:20Z", 8 | "updated_at": "2013-11-17T22:59:20Z", 9 | "_links": { 10 | "self": { 11 | "href": "/api/v2/topics/1", 12 | "class": "topic" 13 | }, 14 | "articles": { 15 | "href": "/api/v2/topics/1/articles", 16 | "class": "article" 17 | }, 18 | "translations": { 19 | "href": "/api/v2/topics/1/translations", 20 | "class": "topic_translation" 21 | } 22 | } 23 | } 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/company_update: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Acme Enterprises", 3 | "domains": [ 4 | "acme-ent.com", 5 | "acmeinc.com", 6 | "acmeinc.net" 7 | ], 8 | "created_at": "2013-11-22T22:49:20Z", 9 | "updated_at": "2013-11-22T22:49:20Z", 10 | "custom_fields": { 11 | "employer_id": "987654321" 12 | }, 13 | "_links": { 14 | "self": { 15 | "href": "/api/v2/companies/1", 16 | "class": "company" 17 | }, 18 | "customers": { 19 | "href": "/api/v2/companies/1/customers", 20 | "class": "customer" 21 | }, 22 | "cases": { 23 | "href": "/api/v2/companies/1/cases", 24 | "class": "case" 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /spec/fixtures/case_reply_create: -------------------------------------------------------------------------------- 1 | { 2 | "subject": "Re: Please help", 3 | "body": "My Reply", 4 | "direction": "out", 5 | "status": "pending", 6 | "to": "doe.john@example.com", 7 | "from": "john.doe@example.com", 8 | "cc": null, 9 | "bcc": null, 10 | "created_at": "2013-11-22T22:49:20Z", 11 | "updated_at": "2013-11-22T22:49:20Z", 12 | "_links": { 13 | "self": { 14 | "href": "/api/v2/cases/1/replies/1", 15 | "class": "email" 16 | }, 17 | "case": { 18 | "href": "/api/v2/cases/1", 19 | "class": "case" 20 | }, 21 | "customer": { 22 | "href": "/api/v2/customer/1", 23 | "class": "customer" 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /spec/fixtures/case_message: -------------------------------------------------------------------------------- 1 | { 2 | "subject": "Please help", 3 | "body": "Help me with my issue!", 4 | "direction": "in", 5 | "status": "pending", 6 | "to": "john.doe@example.com", 7 | "from": "doe.john@example.com", 8 | "cc": null, 9 | "bcc": null, 10 | "created_at": "2013-11-22T22:49:20Z", 11 | "updated_at": "2013-11-22T22:49:20Z", 12 | "_links": { 13 | "self": { 14 | "href": "/api/v2/cases/1/replies/1", 15 | "class": "email" 16 | }, 17 | "case": { 18 | "href": "/api/v2/cases/1", 19 | "class": "case" 20 | }, 21 | "customer": { 22 | "href": "/api/v2/customer/1", 23 | "class": "customer" 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /spec/desk/client/filter_spec.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | 3 | describe Desk::Client do 4 | context "Filter" do 5 | 6 | let(:endpoint) { "filter" } 7 | let(:id) { 1 } 8 | let(:check_key) { "name" } 9 | let(:check_value) { "My Active Cases" } 10 | 11 | include_context "basic configuration" 12 | 13 | it_behaves_like "a list endpoint" 14 | 15 | it_behaves_like "a show endpoint" 16 | 17 | context "Case" do 18 | 19 | let(:sub_endpoint) { "case" } 20 | let(:sub_id) { 1 } 21 | let(:check_key) { "subject" } 22 | let(:check_value) { "Welcome" } 23 | 24 | it_behaves_like "a sub list endpoint", false 25 | 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /spec/desk/client/twitter_user_spec.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | 3 | describe Desk::Client do 4 | context "Twitter User" do 5 | 6 | let(:endpoint) { "twitter_user" } 7 | let(:id) { 1 } 8 | let(:check_key) { "handle" } 9 | let(:check_value) { "desk_dev" } 10 | 11 | include_context "basic configuration" 12 | 13 | it_behaves_like "a list endpoint" 14 | 15 | it_behaves_like "a show endpoint" 16 | 17 | it_behaves_like "a create endpoint", { 18 | :handle => "desk_dev", 19 | :_links => { 20 | :customer => { 21 | :href => "/api/v2/customers/1", 22 | :class => "customer" 23 | } 24 | } 25 | } 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /spec/fixtures/case_note_create: -------------------------------------------------------------------------------- 1 | { 2 | "subject": "Please help", 3 | "body": "Help me with my issue!", 4 | "direction": "in", 5 | "status": "pending", 6 | "to": "john.doe@example.com", 7 | "from": "doe.john@example.com", 8 | "cc": null, 9 | "bcc": null, 10 | "created_at": "2013-11-22T22:49:20Z", 11 | "updated_at": "2013-11-22T22:49:20Z", 12 | "_links": { 13 | "self": { 14 | "href": "/api/v2/cases/1/replies/1", 15 | "class": "email" 16 | }, 17 | "case": { 18 | "href": "/api/v2/cases/1", 19 | "class": "case" 20 | }, 21 | "customer": { 22 | "href": "/api/v2/customer/1", 23 | "class": "customer" 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /spec/fixtures/case_reply: -------------------------------------------------------------------------------- 1 | { 2 | "subject": "Re: Please help", 3 | "body": "Thanks for your question. The answer is 42.", 4 | "direction": "out", 5 | "status": "pending", 6 | "to": "doe.john@example.com", 7 | "from": "john.doe@example.com", 8 | "cc": null, 9 | "bcc": null, 10 | "created_at": "2013-11-22T22:49:20Z", 11 | "updated_at": "2013-11-22T22:49:20Z", 12 | "_links": { 13 | "self": { 14 | "href": "/api/v2/cases/1/replies/2", 15 | "class": "email" 16 | }, 17 | "case": { 18 | "href": "/api/v2/cases/1", 19 | "class": "case" 20 | }, 21 | "customer": { 22 | "href": "/api/v2/customer/1", 23 | "class": "customer" 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /spec/fixtures/case_reply_update: -------------------------------------------------------------------------------- 1 | { 2 | "subject": "Re: Please help", 3 | "body": "Updated reply body", 4 | "direction": "out", 5 | "status": "draft", 6 | "to": "doe.john@example.com", 7 | "from": "john.doe@example.com", 8 | "cc": "new.email@example.com", 9 | "bcc": null, 10 | "created_at": "2013-11-22T22:49:20Z", 11 | "updated_at": "2013-11-22T22:49:20Z", 12 | "_links": { 13 | "self": { 14 | "href": "/api/v2/cases/1/replies/1", 15 | "class": "email" 16 | }, 17 | "case": { 18 | "href": "/api/v2/cases/1", 19 | "class": "case" 20 | }, 21 | "customer": { 22 | "href": "/api/v2/customer/1", 23 | "class": "customer" 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /spec/desk/client/companies_spec.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | 3 | describe Desk::Client do 4 | context "Company" do 5 | 6 | let(:endpoint) { "company" } 7 | let(:id) { 1 } 8 | let(:check_key) { "name" } 9 | let(:check_value) { "Acme Inc" } 10 | 11 | include_context "basic configuration" 12 | 13 | it_behaves_like "a list endpoint" 14 | 15 | it_behaves_like "a show endpoint" 16 | 17 | it_behaves_like "a create endpoint", { :name => "Acme Inc" } 18 | 19 | it_behaves_like "an update endpoint", { :name => "Acme Enterprises" } do 20 | let(:check_value) { "Acme Enterprises" } 21 | end 22 | 23 | it_behaves_like "a search endpoint", { :q => "acme" } 24 | 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /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 | "https://#{self.subdomain}.#{self.domain}"+api_path 22 | end 23 | 24 | def api_path 25 | "/api/#{self.version}/" 26 | end 27 | 28 | include Connection 29 | include Request 30 | include Authentication 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /spec/desk/client/integration_url_spec.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | 3 | describe Desk::Client do 4 | context "Integration URL" do 5 | 6 | let(:endpoint) { "integration_url" } 7 | let(:id) { 1 } 8 | let(:check_key) { "name" } 9 | let(:check_value) { "Sample URL" } 10 | 11 | include_context "basic configuration" 12 | 13 | it_behaves_like "a list endpoint" 14 | 15 | it_behaves_like "a show endpoint" 16 | 17 | it_behaves_like "a create endpoint", { 18 | :name => "Sample URL", 19 | :description => "A sample Integration URL", 20 | :markup => "http://www.example.com" 21 | } 22 | 23 | it_behaves_like "an update endpoint", { :enabled => false } 24 | 25 | it_behaves_like "a delete endpoint" 26 | 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /spec/desk/client/job_spec.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | 3 | describe Desk::Client do 4 | context "Job" do 5 | 6 | let(:endpoint) { "job" } 7 | let(:id) { 1 } 8 | let(:check_key) { "type" } 9 | let(:check_value) { "bulk_case_update" } 10 | 11 | include_context "basic configuration" 12 | 13 | it_behaves_like "a list endpoint" 14 | 15 | it_behaves_like "a show endpoint" 16 | 17 | it_behaves_like "a create endpoint", { 18 | :type => "bulk_case_update", 19 | :case => { 20 | :priority => 5, 21 | :_links => { 22 | :assigned_user => { 23 | :href => "/api/v2/users/1", 24 | :class => "user" 25 | } 26 | } 27 | }, 28 | :case_ids => [ 1, 2, 3 ] 29 | } 30 | 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 | -------------------------------------------------------------------------------- /spec/desk/client/label_spec.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | 3 | describe Desk::Client do 4 | context "Label" do 5 | 6 | let(:endpoint) { "label" } 7 | let(:id) { 1 } 8 | let(:check_key) { "name" } 9 | let(:check_value) { "MyLabel" } 10 | 11 | include_context "basic configuration" 12 | 13 | it_behaves_like "a list endpoint" 14 | 15 | it_behaves_like "a show endpoint" 16 | 17 | it_behaves_like "a create endpoint", { 18 | :name => "MyLabel", 19 | :description => "A Test Label", 20 | :types => [ "case", "macro" ], 21 | :color => "blue" 22 | } 23 | 24 | it_behaves_like "an update endpoint", { :name => "Label 5" } do 25 | let(:check_value) { "Label 5" } 26 | end 27 | 28 | it_behaves_like "a delete endpoint" 29 | 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /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, include_private=false) 29 | client.respond_to?(method, include_private) || super 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /spec/desk/client/twitter_account_spec.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | 3 | describe Desk::Client do 4 | context "Twitter Account" do 5 | 6 | let(:endpoint) { "twitter_account" } 7 | let(:id) { 1 } 8 | let(:check_key) { "handle" } 9 | let(:check_value) { "desk_dev" } 10 | 11 | include_context "basic configuration" 12 | 13 | it_behaves_like "a list endpoint" 14 | 15 | it_behaves_like "a show endpoint" 16 | 17 | context "Tweet" do 18 | 19 | let(:sub_endpoint) { "tweet" } 20 | let(:sub_id) { 1 } 21 | let(:check_key) { "body" } 22 | let(:check_value) { "Example tweet" } 23 | 24 | it_behaves_like "a sub list endpoint" 25 | 26 | it_behaves_like "a sub show endpoint" 27 | 28 | it_behaves_like "a sub create endpoint", { :body => "Example Tweet" } 29 | 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /spec/desk/client/customer_spec.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | 3 | describe Desk::Client do 4 | context "Customer" do 5 | 6 | let(:endpoint) { "customer" } 7 | let(:id) { 1 } 8 | let(:check_key) { "first_name" } 9 | let(:check_value) { "John" } 10 | 11 | include_context "basic configuration" 12 | 13 | it_behaves_like "a list endpoint" 14 | 15 | it_behaves_like "a show endpoint" 16 | 17 | it_behaves_like "a create endpoint", { 18 | :first_name => "John", 19 | :last_name => "Doe", 20 | :emails => [{:type => "work", :value => "joe.user@example.org"}] 21 | } 22 | 23 | it_behaves_like "an update endpoint", { :first_name => "Johnny" } do 24 | let(:check_value) { "Johnny" } 25 | end 26 | 27 | it_behaves_like "a search endpoint", { :since_created_at => 1385074763 } 28 | 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /lib/desk/client/mailbox.rb: -------------------------------------------------------------------------------- 1 | module Desk 2 | class Client 3 | module Mailbox 4 | 5 | def mailbox_list_inbound(*args) 6 | options = args.last.is_a?(Hash) ? args.pop : {} 7 | get("mailboxes/inbound", options) 8 | end 9 | alias_method :inbound_mailboxes, :mailbox_list_inbound 10 | alias_method :list_inbound_mailboxes, :mailbox_list_inbound 11 | 12 | def mailbox_show_inbound(mailbox_id) 13 | get("mailboxes/inbound/#{mailbox_id}") 14 | end 15 | alias_method :inbound_mailbox, :mailbox_show_inbound 16 | alias_method :show_inbound_mailbox, :mailbox_show_inbound 17 | 18 | def mailbox_id_inbound(href, parent_id = false) 19 | return nil if parent_id 20 | href.split("/")[5].to_i 21 | end 22 | alias_method :inbound_mailbox_id, :mailbox_id_inbound 23 | 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /spec/desk/client/group_spec.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | 3 | describe Desk::Client do 4 | context "Group" do 5 | 6 | let(:endpoint) { "group" } 7 | let(:id) { 1 } 8 | let(:check_key) { "name" } 9 | let(:check_value) { "Support Ninjas" } 10 | 11 | include_context "basic configuration" 12 | 13 | it_behaves_like "a list endpoint" 14 | 15 | it_behaves_like "a show endpoint" 16 | 17 | context "Filter" do 18 | 19 | let(:sub_endpoint) { "filter" } 20 | let(:sub_id) { 1 } 21 | let(:check_value) { "My Active Cases" } 22 | 23 | it_behaves_like "a sub list endpoint", false 24 | 25 | end 26 | 27 | context "User" do 28 | 29 | let(:sub_endpoint) { "user" } 30 | let(:sub_id) { 1 } 31 | let(:check_value) { "John Doe" } 32 | 33 | it_behaves_like "a sub list endpoint", false 34 | 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /spec/fixtures/inbound_mailbox: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Support Mailbox", 3 | "enabled": true, 4 | "type": "imaps", 5 | "hostname": "mail.example.com", 6 | "port": 993, 7 | "email": "support@example.com", 8 | "last_checked_at": "2013-11-22T22:49:20Z", 9 | "created_at": "2013-11-22T22:49:20Z", 10 | "updated_at": "2013-11-22T22:49:20Z", 11 | "last_error": null, 12 | "inbound_address_filter": null, 13 | "outbound_address_filter": null, 14 | "_links": { 15 | "self": { 16 | "href": "/api/v2/mailboxes/inbound/1", 17 | "class": "inbound_mailbox" 18 | }, 19 | "default_group": { 20 | "href": "/api/v2/groups/1", 21 | "class": "group" 22 | }, 23 | "created_by": { 24 | "href": "/api/v2/users/1", 25 | "class": "user" 26 | }, 27 | "updated_by": { 28 | "href": "/api/v2/users/1", 29 | "class": "user" 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /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 | break 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 | # Runscope Support 14 | realURL = env[:url].sub(/-desk-com-(\w)+.runscope.net/, ".desk.com") 15 | header = SimpleOAuth::Header.new(env[:method], realURL, signature_params, @options) 16 | env[:request_headers]['theURL'] = realURL 17 | env[:request_headers]['Authorization'] = header.to_s 18 | 19 | @app.call(env) 20 | end 21 | 22 | def initialize(app, options) 23 | @app, @options = app, options 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /spec/desk/client/user_spec.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | 3 | describe Desk::Client do 4 | context "User" do 5 | 6 | let(:endpoint) { "user" } 7 | let(:id) { 1 } 8 | let(:check_key) { "name" } 9 | let(:check_value) { "John Doe" } 10 | 11 | include_context "basic configuration" 12 | 13 | it_behaves_like "a list endpoint" 14 | 15 | it_behaves_like "a show endpoint" 16 | 17 | context "Preference" do 18 | 19 | let(:sub_endpoint) { "preference" } 20 | let(:sub_id) { 1 } 21 | let(:check_key) { "name" } 22 | let(:check_value) { "enable_routing_filter_on_login" } 23 | 24 | it_behaves_like "a sub list endpoint" 25 | 26 | it_behaves_like "a sub show endpoint" 27 | 28 | it_behaves_like "a sub update endpoint", { :value => true } do 29 | let(:sub_id) { 3 } 30 | let(:check_value) { "auto_accept_on_route" } 31 | end 32 | 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /spec/fixtures/article_translation: -------------------------------------------------------------------------------- 1 | { 2 | "locale": "en", 3 | "subject": "Awesome Subject", 4 | "body": "
Awesome apples
", 5 | "body_email": "Email for Awesome apples", 6 | "body_email_auto": true, 7 | "body_chat": "Awesome apples", 8 | "body_chat_auto": true, 9 | "body_web_callback": "Awesome Apples
", 10 | "body_web_callback_auto": true, 11 | "body_twitter": "Awesome apples", 12 | "body_twitter_auto": true, 13 | "body_qna": "Awesome apples", 14 | "body_qna_auto": true, 15 | "body_phone": "Awesome apples", 16 | "body_phone_auto": true, 17 | "body_facebook": "Awesome apples", 18 | "body_facebook_auto": true, 19 | "outdated": false, 20 | "publish_at": "2013-11-22T22:54:20Z", 21 | "created_at": "2013-11-22T22:49:20Z", 22 | "updated_at": "2013-11-22T22:54:20Z", 23 | "_links": { 24 | "self": { 25 | "href": "/api/v2/articles/1/translations/en", 26 | "class": "article" 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /spec/fixtures/groups: -------------------------------------------------------------------------------- 1 | { 2 | "total_entries": 2, 3 | "_links": { 4 | "self": { 5 | "href": "/api/v2/groups?page=1&per_page=30", 6 | "class": "page" 7 | }, 8 | "first": { 9 | "href": "/api/v2/groups?page=1&per_page=30", 10 | "class": "page" 11 | }, 12 | "last": { 13 | "href": "/api/v2/groups?page=1&per_page=30", 14 | "class": "page" 15 | }, 16 | "next": null, 17 | "previous": null 18 | }, 19 | "_embedded": { 20 | "entries": [ 21 | { 22 | "name": "Support Ninjas", 23 | "_links": { 24 | "self": { 25 | "href": "/api/v2/groups/1", 26 | "class": "group" 27 | } 28 | } 29 | }, 30 | { 31 | "name": "Administrators", 32 | "_links": { 33 | "self": { 34 | "href": "/api/v2/groups/2", 35 | "class": "group" 36 | } 37 | }, 38 | "position": 2 39 | } 40 | ] 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /spec/fixtures/article_translation_create: -------------------------------------------------------------------------------- 1 | { 2 | "locale": "es", 3 | "subject": "Spanish Translation", 4 | "body": "Traducción español aquí", 5 | "body_email": "Traducción español aquí", 6 | "body_email_auto": true, 7 | "body_chat": "Traducción español aquí", 8 | "body_chat_auto": true, 9 | "body_web_callback": "Traducción español aquí", 10 | "body_web_callback_auto": true, 11 | "body_twitter": "Traducción español aquí", 12 | "body_twitter_auto": true, 13 | "body_qna": "Traducción español aquí", 14 | "body_qna_auto": true, 15 | "body_phone": "Traducción español aquí", 16 | "body_phone_auto": true, 17 | "body_facebook": "Traducción español aquí", 18 | "body_facebook_auto": true, 19 | "outdated": false, 20 | "publish_at": "2013-11-22T22:54:20Z", 21 | "created_at": "2013-11-22T22:49:20Z", 22 | "updated_at": "2013-11-22T22:54:20Z", 23 | "_links": { 24 | "self": { 25 | "href": "/api/v2/articles/1/translations/es", 26 | "class": "article" 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /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 | 409 => Desk::Conflict, 15 | 422 => Desk::Unprocessable, 16 | 429 => Desk::EnhanceYourCalm, 17 | 500 => Desk::InternalServerError, 18 | 501 => Desk::NotImplemented, 19 | 502 => Desk::BadGateway, 20 | 503 => Desk::ServiceUnavailable, 21 | }.each do |status, exception| 22 | context "when HTTP status is #{status}" do 23 | 24 | before do 25 | stub_get('users/1'). 26 | with(:headers => {'Accept'=>'application/json', 'User-Agent'=>Desk::Configuration::DEFAULT_USER_AGENT}). 27 | to_return(:status => status) 28 | end 29 | 30 | it { expect{ @client.user(1) }.to raise_error(exception) } 31 | 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /spec/fixtures/article_translation_update: -------------------------------------------------------------------------------- 1 | { 2 | "locale": "es", 3 | "subject": "Updated Spanish Translation", 4 | "body": "Traducción español aquí", 5 | "body_email": "Traducción español aquí", 6 | "body_email_auto": true, 7 | "body_chat": "Traducción español aquí", 8 | "body_chat_auto": true, 9 | "body_web_callback": "Traducción español aquí", 10 | "body_web_callback_auto": true, 11 | "body_twitter": "Traducción español aquí", 12 | "body_twitter_auto": true, 13 | "body_qna": "Traducción español aquí", 14 | "body_qna_auto": true, 15 | "body_phone": "Traducción español aquí", 16 | "body_phone_auto": true, 17 | "body_facebook": "Traducción español aquí", 18 | "body_facebook_auto": true, 19 | "outdated": false, 20 | "publish_at": "2013-11-22T22:54:20Z", 21 | "created_at": "2013-11-22T22:49:20Z", 22 | "updated_at": "2013-11-22T22:54:20Z", 23 | "_links": { 24 | "self": { 25 | "href": "/api/v2/articles/1/translations/es", 26 | "class": "article" 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /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 501 12 | raise Desk::NotImplemented.new(error_message(env, "Not implemented."), env[:response_headers]) 13 | when 502 14 | raise Desk::BadGateway.new(error_message(env, "Desk.com is down or being upgraded."), env[:response_headers]) 15 | when 503 16 | raise Desk::ServiceUnavailable.new(error_message(env, "(__-){ Desk.com is over capacity."), env[:response_headers]) 17 | end 18 | end 19 | 20 | private 21 | 22 | def error_message(env, body=nil) 23 | "#{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." 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /spec/desk/client/macro_spec.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | 3 | describe Desk::Client do 4 | context "Macro" do 5 | 6 | let(:endpoint) { "macro" } 7 | let(:id) { 1 } 8 | let(:check_key) { "name" } 9 | let(:check_value) { "Macro Macro" } 10 | 11 | include_context "basic configuration" 12 | 13 | it_behaves_like "a list endpoint" 14 | 15 | it_behaves_like "a show endpoint" 16 | 17 | it_behaves_like "a create endpoint", { 18 | :name => "Macro Macro", 19 | :description => "It's raining fire!" 20 | } 21 | 22 | it_behaves_like "an update endpoint", { :description => "On repeat" } 23 | 24 | it_behaves_like "a delete endpoint" 25 | 26 | context "Action" do 27 | 28 | let(:sub_endpoint) { "action" } 29 | let(:sub_id) { 1 } 30 | let(:check_key) { "value" } 31 | let(:check_value) { "From a VIP Customer" } 32 | 33 | it_behaves_like "a sub list endpoint" 34 | 35 | it_behaves_like "a sub show endpoint" 36 | 37 | it_behaves_like "a sub update endpoint", { :enabled => false } 38 | 39 | end 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /spec/fixtures/site_settings: -------------------------------------------------------------------------------- 1 | { 2 | "total_entries": 2, 3 | "_links": { 4 | "self": { 5 | "href": "/api/v2/site_settings?page=1&per_page=30", 6 | "class": "page" 7 | }, 8 | "first": { 9 | "href": "/api/v2/site_settings?page=1&per_page=30", 10 | "class": "page" 11 | }, 12 | "last": { 13 | "href": "/api/v2/site_settings?page=1&per_page=30", 14 | "class": "page" 15 | }, 16 | "next": null, 17 | "previous": null 18 | }, 19 | "_embedded": { 20 | "entries": [ 21 | { 22 | "name": "company_name", 23 | "value": "Cool Surfboard Co.", 24 | "_links": { 25 | "self": { 26 | "href": "/api/v2/site_settings/1", 27 | "class": "site_setting" 28 | } 29 | } 30 | }, 31 | { 32 | "name": "timezone", 33 | "value": "Pacific Time (US & Canada)", 34 | "_links": { 35 | "self": { 36 | "href": "/api/v2/site_settings/2", 37 | "class": "site_setting" 38 | } 39 | } 40 | } 41 | ] 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /spec/desk/client/system_message_spec.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | 3 | describe Desk::Client do 4 | context "System Message" do 5 | 6 | let(:endpoint) { "system_message" } 7 | let(:id) { nil } 8 | let(:check_key) { "updated_at" } 9 | let(:check_value) { "2013-11-22T22:49:20Z" } 10 | 11 | include_context "basic configuration" 12 | 13 | context "the current message" do 14 | 15 | include_context "plural endpoint" 16 | 17 | subject { client.send("show_#{endpoint}") } 18 | 19 | before do 20 | stub_get("#{endpoint}").to_return(:body => fixture(endpoint)) 21 | end 22 | 23 | it "gets the correct resource" do 24 | subject 25 | expect(a_get("#{endpoint}")).to have_been_made 26 | end 27 | 28 | it { expect(subject).to be_a Hashie::Deash } 29 | 30 | it "has a valid entry" do 31 | expect(subject.id).to eq(id) 32 | expect(subject.send(check_key)).to eq(check_value) 33 | end 34 | 35 | it "allows raw access" do 36 | expect(subject.raw).to be_a Hashie::Deash 37 | end 38 | 39 | end 40 | 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /spec/fixtures/brands: -------------------------------------------------------------------------------- 1 | { 2 | "total_entries": 2, 3 | "_links": { 4 | "self": { 5 | "href": "/api/v2/brands?page=1&per_page=30", 6 | "class": "page" 7 | }, 8 | "first": { 9 | "href": "/api/v2/brands?page=1&per_page=30", 10 | "class": "page" 11 | }, 12 | "last": { 13 | "href": "/api/v2/brands?page=1&per_page=30", 14 | "class": "page" 15 | }, 16 | "next": null, 17 | "previous": null 18 | }, 19 | "_embedded": { 20 | "entries": [ 21 | { 22 | "name": "Desk.com", 23 | "created_at": "2013-11-22T22:49:20Z", 24 | "updated_at": "2013-11-22T22:49:20Z", 25 | "_links": { 26 | "self": { 27 | "href": "/api/v2/brands/1", 28 | "class": "brand" 29 | } 30 | } 31 | }, 32 | { 33 | "name": "Alpha Inc", 34 | "created_at": "2013-11-22T22:49:20Z", 35 | "updated_at": "2013-11-22T22:49:20Z", 36 | "_links": { 37 | "self": { 38 | "href": "/api/v2/brands/2", 39 | "class": "brand" 40 | } 41 | } 42 | } 43 | ] 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /spec/fixtures/case: -------------------------------------------------------------------------------- 1 | { 2 | "external_id": null, 3 | "subject": "Welcome", 4 | "priority": 5, 5 | "locked_until": null, 6 | "description": null, 7 | "status": "new", 8 | "type": "email", 9 | "language": "en_us", 10 | "created_at": "2013-11-22T22:49:20Z", 11 | "updated_at": "2013-11-22T22:54:20Z", 12 | "active_at": "2013-11-22T22:54:20Z", 13 | "received_at": "2013-11-22T22:49:20Z", 14 | "first_opened_at": "2013-11-22T22:50:20Z", 15 | "opened_at": "2013-11-22T22:51:20Z", 16 | "first_resolved_at": "2013-11-22T22:54:20Z", 17 | "resolved_at": "2013-11-22T22:54:20Z", 18 | "custom_fields": { 19 | "level": "vip" 20 | }, 21 | "_links": { 22 | "self": { 23 | "href": "/api/v2/cases/1", 24 | "class": "case" 25 | }, 26 | "message": { 27 | "href": "/api/v2/cases/1/message", 28 | "class": "message" 29 | }, 30 | "customer": { 31 | "href": "/api/v2/customers/1", 32 | "class": "customer" 33 | }, 34 | "assigned_user": { 35 | "href": "/api/v2/users/2", 36 | "class": "user" 37 | }, 38 | "assigned_group": { 39 | "href": "/api/v2/groups/1", 40 | "class": "group" 41 | }, 42 | "locked_by": null 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /spec/fixtures/customer_create: -------------------------------------------------------------------------------- 1 | { 2 | "first_name": "John", 3 | "last_name": "Doe", 4 | "company": null, 5 | "title": null, 6 | "external_id": null, 7 | "background": null, 8 | "language": "en_us", 9 | "locked_until": null, 10 | "created_at": "2013-11-22T22:49:20Z", 11 | "updated_at": "2013-11-22T22:49:20Z", 12 | "custom_fields": { 13 | "level": "vip" 14 | }, 15 | "emails": [ 16 | { 17 | "type": "work", 18 | "value": "john@acme.com" 19 | }, 20 | { 21 | "type": "home", 22 | "value": "john@home.com" 23 | } 24 | ], 25 | "phone_numbers": [ 26 | 27 | ], 28 | "addresses": [ 29 | 30 | ], 31 | "_links": { 32 | "self": { 33 | "href": "/api/v2/customers/1", 34 | "class": "customer" 35 | }, 36 | "cases": { 37 | "href": "/api/v2/customers/1/cases", 38 | "class": "case" 39 | }, 40 | "company": { 41 | "href": "/api/v2/companies/1", 42 | "class": "company" 43 | }, 44 | "facebook_user": { 45 | "href": "/api/v2/facebook_users/1", 46 | "class": "facebook_user" 47 | }, 48 | "twitter_user": { 49 | "href": "/api/v2/twitter_users/1", 50 | "class": "twitter_user" 51 | }, 52 | "locked_by": null 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /lib/desk/authentication.rb: -------------------------------------------------------------------------------- 1 | module Desk 2 | # @private 3 | module Authentication 4 | module Methods 5 | OAUTH = "oauth" 6 | BASIC = "basic" 7 | ALL = [ 8 | OAUTH, 9 | BASIC, 10 | ] 11 | end 12 | 13 | private 14 | 15 | # Authentication hash 16 | # 17 | # @return [Hash] 18 | def authentication 19 | if auth_method == Methods::BASIC 20 | basic_authentication 21 | else 22 | oauth_authentication 23 | end 24 | end 25 | 26 | # Authentication hash for OAUTH connections 27 | # 28 | # @return [Hash] 29 | def oauth_authentication 30 | { 31 | :consumer_key => consumer_key, 32 | :consumer_secret => consumer_secret, 33 | :token => oauth_token, 34 | :token_secret => oauth_token_secret 35 | } 36 | end 37 | 38 | # Authentication hash for Basic auth connections 39 | # 40 | # @return [Hash] 41 | def basic_authentication 42 | { 43 | :username => basic_auth_username, 44 | :password => basic_auth_password 45 | } 46 | end 47 | 48 | # Check whether user is authenticated 49 | # 50 | # @return [Boolean] 51 | def authenticated? 52 | authentication.values.all? 53 | end 54 | end 55 | end 56 | -------------------------------------------------------------------------------- /spec/fixtures/case_update: -------------------------------------------------------------------------------- 1 | { 2 | "external_id": null, 3 | "subject": "Updated", 4 | "priority": 5, 5 | "locked_until": null, 6 | "description": null, 7 | "status": "pending", 8 | "type": "email", 9 | "language": "en_us", 10 | "created_at": "2013-11-22T22:49:20Z", 11 | "updated_at": "2013-11-22T22:54:20Z", 12 | "active_at": "2013-11-22T22:54:20Z", 13 | "received_at": "2013-11-22T22:49:20Z", 14 | "first_opened_at": "2013-11-22T22:50:20Z", 15 | "opened_at": "2013-11-22T22:51:20Z", 16 | "first_resolved_at": "2013-11-22T22:54:20Z", 17 | "resolved_at": "2013-11-22T22:54:20Z", 18 | "custom_fields": { 19 | "level": "super" 20 | }, 21 | "_links": { 22 | "self": { 23 | "href": "/api/v2/cases/1", 24 | "class": "case" 25 | }, 26 | "message": { 27 | "href": "/api/v2/cases/1/message", 28 | "class": "message" 29 | }, 30 | "customer": { 31 | "href": "/api/v2/customers/1", 32 | "class": "customer" 33 | }, 34 | "assigned_user": { 35 | "href": "/api/v2/users/2", 36 | "class": "user" 37 | }, 38 | "assigned_group": { 39 | "href": "/api/v2/groups/1", 40 | "class": "group", 41 | "rel": "group" 42 | }, 43 | "locked_by": null 44 | }, 45 | "labels": [ 46 | "Spam", 47 | "Test" 48 | ] 49 | } 50 | -------------------------------------------------------------------------------- /spec/fixtures/topic_translations: -------------------------------------------------------------------------------- 1 | { 2 | "total_entries": 2, 3 | "_links": { 4 | "self": { 5 | "href": "/api/v2/topics/1/translations?page=1&per_page=50", 6 | "class": "page" 7 | }, 8 | "first": { 9 | "href": "/api/v2/topics/1/translations?page=1&per_page=50", 10 | "class": "page" 11 | }, 12 | "last": { 13 | "href": "/api/v2/topics/1/translations?page=1&per_page=50", 14 | "class": "page" 15 | }, 16 | "next": null, 17 | "previous": null 18 | }, 19 | "_embedded": { 20 | "entries": [ 21 | { 22 | "name": "Customer Support", 23 | "locale": "en_us", 24 | "created_at": "2013-11-12T22:59:20Z", 25 | "updated_at": "2013-11-17T22:59:20Z", 26 | "_links": { 27 | "self": { 28 | "href": "/api/v2/topics/1/translations/en_us", 29 | "class": "topic_translation" 30 | } 31 | } 32 | }, 33 | { 34 | "name": "Japanese Translation", 35 | "locale": "ja", 36 | "created_at": "2013-11-22T22:58:24Z", 37 | "updated_at": "2013-11-22T22:58:24Z", 38 | "_links": { 39 | "self": { 40 | "href": "/api/v2/topics/1/translations/ja", 41 | "class": "topic_translation" 42 | } 43 | } 44 | } 45 | ] 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /spec/fixtures/rules: -------------------------------------------------------------------------------- 1 | { 2 | "total_entries": 2, 3 | "_links": { 4 | "self": { 5 | "href": "/api/v2/rules?page=1&per_page=30", 6 | "class": "page" 7 | }, 8 | "first": { 9 | "href": "/api/v2/rules?page=1&per_page=30", 10 | "class": "page" 11 | }, 12 | "last": { 13 | "href": "/api/v2/rules?page=1&per_page=30", 14 | "class": "page" 15 | }, 16 | "next": null, 17 | "previous": null 18 | }, 19 | "_embedded": { 20 | "entries": [ 21 | { 22 | "name": "Assign to Support", 23 | "description": "Assign inbound tweets to support group", 24 | "enabled": true, 25 | "created_at": "2013-11-22T22:49:20Z", 26 | "updated_at": "2013-11-22T22:49:20Z", 27 | "_links": { 28 | "self": { 29 | "href": "/api/v2/rules/1", 30 | "class": "rule" 31 | } 32 | } 33 | }, 34 | { 35 | "name": "Increase Priority for Influential Twitter Users", 36 | "description": null, 37 | "enabled": true, 38 | "created_at": "2013-11-22T22:49:20Z", 39 | "updated_at": "2013-11-22T22:49:20Z", 40 | "_links": { 41 | "self": { 42 | "href": "/api/v2/rules/2", 43 | "class": "rule" 44 | } 45 | } 46 | } 47 | ] 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /spec/fixtures/case_create: -------------------------------------------------------------------------------- 1 | { 2 | "external_id": null, 3 | "subject": "Creating a case via the API", 4 | "priority": 4, 5 | "locked_until": null, 6 | "description": null, 7 | "status": "open", 8 | "type": "email", 9 | "language": "fr", 10 | "created_at": "2013-11-22T22:49:20Z", 11 | "updated_at": "2013-11-22T22:54:20Z", 12 | "active_at": "2013-11-22T22:54:20Z", 13 | "received_at": "2013-11-22T22:49:20Z", 14 | "first_opened_at": "2013-11-22T22:50:20Z", 15 | "opened_at": "2013-11-22T22:51:20Z", 16 | "first_resolved_at": "2013-11-22T22:54:20Z", 17 | "resolved_at": "2013-11-22T22:54:20Z", 18 | "custom_fields": { 19 | "level": "vip" 20 | }, 21 | "_links": { 22 | "self": { 23 | "href": "/api/v2/cases/1", 24 | "class": "case" 25 | }, 26 | "message": { 27 | "href": "/api/v2/cases/1/message", 28 | "class": "message" 29 | }, 30 | "customer": { 31 | "href": "/api/v2/customers/1", 32 | "class": "customer" 33 | }, 34 | "assigned_user": { 35 | "href": "/api/v2/users/1", 36 | "class": "user" 37 | }, 38 | "assigned_group": { 39 | "href": "/api/v2/groups/1", 40 | "class": "group" 41 | }, 42 | "locked_by": { 43 | "href": "/api/v2/users/1", 44 | "class": "user" 45 | } 46 | }, 47 | "labels": [ 48 | "Spam", 49 | "Ignore" 50 | ] 51 | } 52 | -------------------------------------------------------------------------------- /spec/fixtures/labels: -------------------------------------------------------------------------------- 1 | { 2 | "total_entries": 2, 3 | "_links": { 4 | "self": { 5 | "href": "/api/v2/labels?page=1&per_page=30", 6 | "class": "page" 7 | }, 8 | "first": { 9 | "href": "/api/v2/labels?page=1&per_page=30", 10 | "class": "page" 11 | }, 12 | "last": { 13 | "href": "/api/v2/labels?page=1&per_page=30", 14 | "class": "page" 15 | }, 16 | "next": null, 17 | "previous": null 18 | }, 19 | "_embedded": { 20 | "entries": [ 21 | { 22 | "name": "MyLabel", 23 | "description": "My Label Description", 24 | "types": [ 25 | "case", 26 | "macro" 27 | ], 28 | "active": true, 29 | "color": "green", 30 | "position": 1, 31 | "_links": { 32 | "self": { 33 | "href": "/api/v2/labels/1", 34 | "class": "label" 35 | } 36 | } 37 | }, 38 | { 39 | "name": "Another Label", 40 | "description": "Label Description", 41 | "types": [ 42 | "case", 43 | "macro" 44 | ], 45 | "active": true, 46 | "color": "green", 47 | "position": 2, 48 | "_links": { 49 | "self": { 50 | "href": "/api/v2/labels/2", 51 | "class": "label" 52 | } 53 | } 54 | } 55 | ] 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /spec/fixtures/filters: -------------------------------------------------------------------------------- 1 | { 2 | "total_entries": 2, 3 | "_links": { 4 | "self": { 5 | "href": "/api/v2/filters?page=1&per_page=30", 6 | "class": "page" 7 | }, 8 | "first": { 9 | "href": "/api/v2/filters?page=1&per_page=30", 10 | "class": "page" 11 | }, 12 | "last": { 13 | "href": "/api/v2/filters?page=1&per_page=30", 14 | "class": "page" 15 | }, 16 | "next": null, 17 | "previous": null 18 | }, 19 | "_embedded": { 20 | "entries": [ 21 | { 22 | "name": "My Active Cases", 23 | "sort": "priority", 24 | "sort_field": "priority", 25 | "sort_direction": "desc", 26 | "position": 1, 27 | "active": true, 28 | "_links": { 29 | "self": { 30 | "href": "/api/v2/filters/1", 31 | "class": "filter" 32 | }, 33 | "group": null, 34 | "user": null 35 | } 36 | }, 37 | { 38 | "name": "Spam Cases", 39 | "sort": "priority", 40 | "sort_field": "priority", 41 | "sort_direction": "desc", 42 | "position": 2, 43 | "active": true, 44 | "_links": { 45 | "self": { 46 | "href": "/api/v2/filters/2", 47 | "class": "filter" 48 | }, 49 | "group": null, 50 | "user": null 51 | } 52 | } 53 | ] 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /spec/fixtures/group_filters: -------------------------------------------------------------------------------- 1 | { 2 | "total_entries": 2, 3 | "_links": { 4 | "self": { 5 | "href": "/api/v2/groups/1/filters?page=1&per_page=50", 6 | "class": "page" 7 | }, 8 | "first": { 9 | "href": "/api/v2/groups/1/filters?page=1&per_page=50", 10 | "class": "page" 11 | }, 12 | "last": { 13 | "href": "/api/v2/groups/1/filters?page=1&per_page=50", 14 | "class": "page" 15 | }, 16 | "next": null, 17 | "previous": null 18 | }, 19 | "_embedded": { 20 | "entries": [ 21 | { 22 | "name": "My Active Cases", 23 | "sort": "priority", 24 | "sort_field": "priority", 25 | "sort_direction": "desc", 26 | "position": 1, 27 | "active": true, 28 | "_links": { 29 | "self": { 30 | "href": "/api/v2/filters/1", 31 | "class": "filter" 32 | }, 33 | "group": null, 34 | "user": null 35 | } 36 | }, 37 | { 38 | "name": "New Cases", 39 | "sort": "priority", 40 | "sort_field": "priority", 41 | "sort_direction": "desc", 42 | "position": 1, 43 | "active": true, 44 | "_links": { 45 | "self": { 46 | "href": "/api/v2/filters/2", 47 | "class": "filter" 48 | }, 49 | "group": null, 50 | "user": null 51 | } 52 | } 53 | ] 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /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 | 13 | require 'shared_context' 14 | require 'shared_examples' 15 | 16 | RSpec.configure do |config| 17 | config.include WebMock::API 18 | end 19 | 20 | def a_delete(path) 21 | a_request(:delete, Desk.endpoint + path) 22 | end 23 | 24 | def a_get(path) 25 | a_request(:get, Desk.endpoint + path) 26 | end 27 | 28 | def a_patch(path) 29 | a_request(:patch, Desk.endpoint + path) 30 | end 31 | 32 | def a_post(path) 33 | a_request(:post, Desk.endpoint + path) 34 | end 35 | 36 | def a_put(path) 37 | a_request(:put, Desk.endpoint + path) 38 | end 39 | 40 | def stub_delete(path) 41 | stub_request(:delete, Desk.endpoint + path) 42 | end 43 | 44 | def stub_get(path) 45 | stub_request(:get, Desk.endpoint + path) 46 | end 47 | 48 | def stub_patch(path) 49 | stub_request(:patch, Desk.endpoint + path) 50 | end 51 | 52 | def stub_post(path) 53 | stub_request(:post, Desk.endpoint + path) 54 | end 55 | 56 | def stub_put(path) 57 | stub_request(:put, Desk.endpoint + path) 58 | end 59 | 60 | def fixture_path 61 | File.expand_path("../fixtures", __FILE__) 62 | end 63 | 64 | def fixture(file) 65 | File.new(fixture_path + '/' + file) 66 | end 67 | -------------------------------------------------------------------------------- /spec/fixtures/custom_fields: -------------------------------------------------------------------------------- 1 | { 2 | "total_entries": 2, 3 | "_links": { 4 | "self": { 5 | "href": "/api/v2/custom_fields?page=1&per_page=30", 6 | "class": "page" 7 | }, 8 | "first": { 9 | "href": "/api/v2/custom_fields?page=1&per_page=30", 10 | "class": "page" 11 | }, 12 | "last": { 13 | "href": "/api/v2/custom_fields?page=1&per_page=30", 14 | "class": "page" 15 | }, 16 | "next": null, 17 | "previous": null 18 | }, 19 | "_embedded": { 20 | "entries": [ 21 | { 22 | "name": "frequent_buyer", 23 | "label": "Frequent Buyer", 24 | "type": "customer", 25 | "active": true, 26 | "data": { 27 | "type": "boolean" 28 | }, 29 | "_links": { 30 | "self": { 31 | "href": "/api/v2/custom_fields/1", 32 | "class": "custom_field" 33 | } 34 | } 35 | }, 36 | { 37 | "name": "last_agent", 38 | "label": "Last Agent to Update", 39 | "type": "ticket", 40 | "active": false, 41 | "data": { 42 | "type": "list", 43 | "choices": [ 44 | "Marianne", 45 | "Elinor", 46 | "Margaret" 47 | ] 48 | }, 49 | "_links": { 50 | "self": { 51 | "href": "/api/v2/custom_fields/2", 52 | "class": "custom_field" 53 | } 54 | } 55 | } 56 | ] 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /spec/fixtures/customer: -------------------------------------------------------------------------------- 1 | { 2 | "first_name": "John", 3 | "last_name": "Doe", 4 | "company": "ACME, Inc", 5 | "title": "Senior Ninja", 6 | "external_id": null, 7 | "background": "This guy can be a challenge to work with", 8 | "language": "en_us", 9 | "locked_until": null, 10 | "created_at": "2013-11-22T22:49:20Z", 11 | "updated_at": "2013-11-22T22:49:20Z", 12 | "custom_fields": { 13 | "level": "vip" 14 | }, 15 | "emails": [ 16 | { 17 | "type": "work", 18 | "value": "john@acme.com" 19 | }, 20 | { 21 | "type": "home", 22 | "value": "john@home.com" 23 | } 24 | ], 25 | "phone_numbers": [ 26 | { 27 | "type": "work", 28 | "value": "123-456-7890" 29 | } 30 | ], 31 | "addresses": [ 32 | { 33 | "type": "work", 34 | "value": "123 Main St, San Francisco, CA 94105" 35 | } 36 | ], 37 | "_links": { 38 | "self": { 39 | "href": "/api/v2/customers/1", 40 | "class": "customer" 41 | }, 42 | "cases": { 43 | "href": "/api/v2/customers/1/cases", 44 | "class": "case" 45 | }, 46 | "company": { 47 | "href": "/api/v2/companies/1", 48 | "class": "company" 49 | }, 50 | "facebook_user": { 51 | "href": "/api/v2/facebook_users/1", 52 | "class": "facebook_user" 53 | }, 54 | "twitter_user": { 55 | "href": "/api/v2/twitter_users/1", 56 | "class": "twitter_user" 57 | }, 58 | "locked_by": null 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /spec/fixtures/customer_update: -------------------------------------------------------------------------------- 1 | { 2 | "first_name": "Johnny", 3 | "last_name": "Doe", 4 | "company": "ACME, Inc", 5 | "title": "Senior Ninja", 6 | "external_id": null, 7 | "background": "This guy can be a challenge to work with", 8 | "language": "en_us", 9 | "locked_until": null, 10 | "created_at": "2013-11-22T22:49:20Z", 11 | "updated_at": "2013-11-22T22:49:20Z", 12 | "custom_fields": { 13 | "level": "super" 14 | }, 15 | "emails": [ 16 | { 17 | "type": "work", 18 | "value": "johnny@acme.com" 19 | }, 20 | { 21 | "type": "other", 22 | "value": "johnny@other.com" 23 | } 24 | ], 25 | "phone_numbers": [ 26 | { 27 | "type": "work", 28 | "value": "123-456-7890" 29 | } 30 | ], 31 | "addresses": [ 32 | { 33 | "type": "work", 34 | "value": "123 Main St, San Francisco, CA 94105" 35 | } 36 | ], 37 | "_links": { 38 | "self": { 39 | "href": "/api/v2/customers/1", 40 | "class": "customer" 41 | }, 42 | "cases": { 43 | "href": "/api/v2/customers/1/cases", 44 | "class": "case" 45 | }, 46 | "company": { 47 | "href": "/api/v2/companies/1", 48 | "class": "company" 49 | }, 50 | "facebook_user": { 51 | "href": "/api/v2/facebook_users/1", 52 | "class": "facebook_user" 53 | }, 54 | "twitter_user": { 55 | "href": "/api/v2/twitter_users/1", 56 | "class": "twitter_user" 57 | }, 58 | "locked_by": null 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /spec/desk/client/topic_spec.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | 3 | describe Desk::Client do 4 | context "Topic" do 5 | 6 | let(:endpoint) { "topic" } 7 | let(:id) { 1 } 8 | let(:check_key) { "name" } 9 | let(:check_value) { "Customer Support" } 10 | 11 | include_context "basic configuration" 12 | 13 | it_behaves_like "a list endpoint" 14 | 15 | it_behaves_like "a show endpoint" 16 | 17 | it_behaves_like "a create endpoint", { :name => "Social Media" } 18 | 19 | it_behaves_like "an update endpoint", { :name => "New Name" } do 20 | let(:check_value) { "New Name" } 21 | end 22 | 23 | it_behaves_like "a delete endpoint" 24 | 25 | context "Translation" do 26 | 27 | let(:sub_endpoint) { "translation" } 28 | let(:sub_id) { "en_us" } 29 | let(:check_key) { "name" } 30 | let(:check_value) { "Customer Support" } 31 | 32 | it_behaves_like "a sub list endpoint" 33 | 34 | it_behaves_like "a sub show endpoint" 35 | 36 | it_behaves_like "a sub create endpoint", { 37 | :name => "Japanese", 38 | :locale => "ja" 39 | } do 40 | let(:check_value) { "Japanese" } 41 | end 42 | 43 | it_behaves_like "a sub update endpoint", { 44 | :name => "Updated Japanese Translation" 45 | } do 46 | let(:sub_id) { "ja" } 47 | let(:check_value) { "Updated Japanese Translation" } 48 | end 49 | 50 | it_behaves_like "a sub delete endpoint" 51 | 52 | end 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /spec/fixtures/twitter_accounts: -------------------------------------------------------------------------------- 1 | { 2 | "total_entries": 2, 3 | "_links": { 4 | "self": { 5 | "href": "/api/v2/twitter_accounts?page=1&per_page=30", 6 | "class": "page" 7 | }, 8 | "first": { 9 | "href": "/api/v2/twitter_accounts?page=1&per_page=30", 10 | "class": "page" 11 | }, 12 | "last": { 13 | "href": "/api/v2/twitter_accounts?page=1&per_page=30", 14 | "class": "page" 15 | }, 16 | "next": null, 17 | "previous": null 18 | }, 19 | "_embedded": { 20 | "entries": [ 21 | { 22 | "handle": "desk_dev", 23 | "name": "Desk.com Development", 24 | "profile_image": "http://www.example.com/image.png", 25 | "active": true, 26 | "created_at": "2013-05-22T21:59:20Z", 27 | "updated_at": "2013-06-22T21:59:20Z", 28 | "_links": { 29 | "self": { 30 | "href": "/api/v2/twitter_accounts/1", 31 | "class": "twitter_account" 32 | } 33 | } 34 | }, 35 | { 36 | "handle": "desk_ops", 37 | "name": "Desk.com Operations", 38 | "profile_image": "http://www.example.com/image.png", 39 | "active": true, 40 | "created_at": "2013-05-22T21:59:20Z", 41 | "updated_at": "2013-06-22T21:59:20Z", 42 | "_links": { 43 | "self": { 44 | "href": "/api/v2/twitter_accounts/2", 45 | "class": "twitter_account" 46 | } 47 | } 48 | } 49 | ] 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /spec/fixtures/article: -------------------------------------------------------------------------------- 1 | { 2 | "subject": "Awesome Subject", 3 | "body": "Awesome apples
", 4 | "body_email": "Email for Awesome apples", 5 | "body_email_auto": false, 6 | "body_chat": "Awesome apples", 7 | "body_chat_auto": true, 8 | "body_web_callback": "Awesome Apples
", 9 | "body_web_callback_auto": false, 10 | "body_twitter": "Awesome apples", 11 | "body_twitter_auto": true, 12 | "body_qna": "Awesome apples", 13 | "body_qna_auto": true, 14 | "body_phone": "Awesome apples", 15 | "body_phone_auto": true, 16 | "body_facebook": "Awesome apples", 17 | "body_facebook_auto": true, 18 | "rating": 75, 19 | "rating_count": 4, 20 | "rating_score": 3, 21 | "position": 1, 22 | "quickcode": "AWESOME", 23 | "in_support_center": true, 24 | "internal_notes": "Notes to the agent here", 25 | "publish_at": "2013-11-12T18:07:34Z", 26 | "created_at": "2013-11-12T18:02:34Z", 27 | "updated_at": "2013-11-12T18:07:34Z", 28 | "_links": { 29 | "self": { 30 | "href": "/api/v2/articles/1", 31 | "class": "article" 32 | }, 33 | "topic": { 34 | "href": "/api/v2/topics/1", 35 | "class": "topic" 36 | }, 37 | "translations": { 38 | "href": "/api/v2/articles/1/translations", 39 | "class": "article_translation" 40 | }, 41 | "created_by": { 42 | "href": "/api/v2/users/1", 43 | "class": "user" 44 | }, 45 | "updated_by": { 46 | "href": "/api/v2/users/1", 47 | "class": "user" 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /spec/fixtures/jobs: -------------------------------------------------------------------------------- 1 | { 2 | "total_entries": 2, 3 | "_links": { 4 | "self": { 5 | "href": "/api/v2/jobs?page=1&per_page=30", 6 | "class": "page" 7 | }, 8 | "first": { 9 | "href": "/api/v2/jobs?page=1&per_page=30", 10 | "class": "page" 11 | }, 12 | "last": { 13 | "href": "/api/v2/jobs?page=1&per_page=30", 14 | "class": "page" 15 | }, 16 | "next": null, 17 | "previous": null 18 | }, 19 | "_embedded": { 20 | "entries": [ 21 | { 22 | "type": "bulk_case_update", 23 | "status_message": "Completed", 24 | "progress": 100, 25 | "created_at": "2013-11-22T21:59:20Z", 26 | "completed_at": "2013-11-22T22:49:20Z", 27 | "_links": { 28 | "self": { 29 | "href": "/api/v2/jobs/1", 30 | "class": "job" 31 | }, 32 | "user": { 33 | "href": "/api/v2/users/1", 34 | "class": "user" 35 | } 36 | } 37 | }, 38 | { 39 | "type": "bulk_case_update", 40 | "status_message": "Completed", 41 | "progress": 100, 42 | "created_at": "2013-11-22T21:59:20Z", 43 | "completed_at": "2013-11-22T22:49:20Z", 44 | "_links": { 45 | "self": { 46 | "href": "/api/v2/jobs/2", 47 | "class": "job" 48 | }, 49 | "user": { 50 | "href": "/api/v2/users/1", 51 | "class": "user" 52 | } 53 | } 54 | } 55 | ] 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /spec/fixtures/article_create: -------------------------------------------------------------------------------- 1 | { 2 | "subject": "Awesome Subject", 3 | "body": "Use Desk.com", 4 | "body_email": "Email just doesn't cut it", 5 | "body_email_auto": false, 6 | "body_chat": "Use Desk.com", 7 | "body_chat_auto": true, 8 | "body_web_callback": "Use Desk.com", 9 | "body_web_callback_auto": false, 10 | "body_twitter": "Use Desk.com in 140 chars or less", 11 | "body_twitter_auto": false, 12 | "body_qna": "Use Desk.com", 13 | "body_qna_auto": true, 14 | "body_phone": "Use Desk.com", 15 | "body_phone_auto": true, 16 | "body_facebook": "Use Desk.com", 17 | "body_facebook_auto": true, 18 | "rating": 75, 19 | "rating_count": 4, 20 | "rating_score": 3, 21 | "position": 1, 22 | "quickcode": "AWESOME", 23 | "in_support_center": true, 24 | "internal_notes": "Notes to the agent here", 25 | "publish_at": "2013-11-12T18:07:34Z", 26 | "created_at": "2013-11-12T18:02:34Z", 27 | "updated_at": "2013-11-12T18:07:34Z", 28 | "_links": { 29 | "self": { 30 | "href": "/api/v2/articles/1", 31 | "class": "article" 32 | }, 33 | "topic": { 34 | "href": "/api/v2/topics/1", 35 | "class": "topic" 36 | }, 37 | "translations": { 38 | "href": "/api/v2/articles/1/translations", 39 | "class": "article_translation" 40 | }, 41 | "created_by": { 42 | "href": "/api/v2/users/1", 43 | "class": "user" 44 | }, 45 | "updated_by": { 46 | "href": "/api/v2/users/1", 47 | "class": "user" 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /spec/fixtures/article_update: -------------------------------------------------------------------------------- 1 | { 2 | "subject": "How to make your customers happy", 3 | "body": "Use Desk.com", 4 | "body_email": "Email just doesn't cut it", 5 | "body_email_auto": false, 6 | "body_chat": "Use Desk.com", 7 | "body_chat_auto": true, 8 | "body_web_callback": "Use Desk.com", 9 | "body_web_callback_auto": false, 10 | "body_twitter": "Use Desk.com in 140 chars or less", 11 | "body_twitter_auto": false, 12 | "body_qna": "Use Desk.com", 13 | "body_qna_auto": true, 14 | "body_phone": "Use Desk.com", 15 | "body_phone_auto": true, 16 | "body_facebook": "Use Desk.com", 17 | "body_facebook_auto": true, 18 | "rating": 75, 19 | "rating_count": 4, 20 | "rating_score": 3, 21 | "position": 1, 22 | "quickcode": "AWESOME", 23 | "in_support_center": true, 24 | "internal_notes": "Notes to the agent here", 25 | "publish_at": "2013-11-12T18:07:34Z", 26 | "created_at": "2013-11-12T18:02:34Z", 27 | "updated_at": "2013-11-12T18:07:34Z", 28 | "_links": { 29 | "self": { 30 | "href": "/api/v2/articles/1", 31 | "class": "article" 32 | }, 33 | "topic": { 34 | "href": "/api/v2/topics/2", 35 | "class": "topic" 36 | }, 37 | "translations": { 38 | "href": "/api/v2/articles/1/translations", 39 | "class": "article_translation" 40 | }, 41 | "created_by": { 42 | "href": "/api/v2/users/1", 43 | "class": "user" 44 | }, 45 | "updated_by": { 46 | "href": "/api/v2/users/1", 47 | "class": "user" 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /spec/fixtures/macro_actions: -------------------------------------------------------------------------------- 1 | { 2 | "total_entries": 2, 3 | "_links": { 4 | "self": { 5 | "href": "/api/v2/macros/1/actions?page=1&per_page=50", 6 | "class": "page" 7 | }, 8 | "first": { 9 | "href": "/api/v2/macros/1/actions?page=1&per_page=50", 10 | "class": "page" 11 | }, 12 | "last": { 13 | "href": "/api/v2/macros/1/actions?page=1&per_page=50", 14 | "class": "page" 15 | }, 16 | "next": null, 17 | "previous": null 18 | }, 19 | "_embedded": { 20 | "entries": [ 21 | { 22 | "type": "set-case-description", 23 | "value": "From a VIP Customer", 24 | "enabled": true, 25 | "created_at": "2013-11-22T22:49:20Z", 26 | "updated_at": "2013-11-22T22:49:20Z", 27 | "_links": { 28 | "self": { 29 | "href": "/api/v2/macros/1/actions/1", 30 | "class": "macro_action" 31 | }, 32 | "macro": { 33 | "href": "/api/v2/macros/1", 34 | "class": "macro" 35 | } 36 | } 37 | }, 38 | { 39 | "type": "set-case-priority", 40 | "value": "10", 41 | "enabled": true, 42 | "created_at": "2013-11-22T22:49:20Z", 43 | "updated_at": "2013-11-22T22:49:20Z", 44 | "_links": { 45 | "self": { 46 | "href": "/api/v2/macros/1/actions/2", 47 | "class": "macro_action" 48 | }, 49 | "macro": { 50 | "href": "/api/v2/macros/1", 51 | "class": "macro" 52 | } 53 | } 54 | } 55 | ] 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /spec/fixtures/macros: -------------------------------------------------------------------------------- 1 | { 2 | "total_entries": 2, 3 | "_links": { 4 | "self": { 5 | "href": "/api/v2/macros?page=1&per_page=30", 6 | "class": "page" 7 | }, 8 | "first": { 9 | "href": "/api/v2/macros?page=1&per_page=30", 10 | "class": "page" 11 | }, 12 | "last": { 13 | "href": "/api/v2/macros?page=1&per_page=30", 14 | "class": "page" 15 | }, 16 | "next": null, 17 | "previous": null 18 | }, 19 | "_embedded": { 20 | "entries": [ 21 | { 22 | "name": "Macro Macro", 23 | "description": "On repeat", 24 | "enabled": true, 25 | "position": 1, 26 | "folders": [ 27 | "Sample Macros", 28 | "Favorites" 29 | ], 30 | "_links": { 31 | "self": { 32 | "href": "/api/v2/macros/1", 33 | "class": "macro" 34 | }, 35 | "actions": { 36 | "href": "/api/v2/macros/1/actions", 37 | "class": "macro_action" 38 | } 39 | } 40 | }, 41 | { 42 | "name": "Another Macro", 43 | "description": null, 44 | "enabled": true, 45 | "position": 2, 46 | "folders": [ 47 | "Sample Macros", 48 | "Favorites" 49 | ], 50 | "_links": { 51 | "self": { 52 | "href": "/api/v2/macros/2", 53 | "class": "macro" 54 | }, 55 | "actions": { 56 | "href": "/api/v2/macros/1/actions", 57 | "class": "macro_action" 58 | } 59 | } 60 | } 61 | ] 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /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 409 20 | raise Desk::Conflict.new(error_message(env), env[:response_headers]) 21 | when 422 22 | raise Desk::Unprocessable.new(error_message(env), env[:response_headers]) 23 | when 429 24 | raise Desk::EnhanceYourCalm.new(error_message(env), env[:response_headers]) 25 | end 26 | end 27 | 28 | private 29 | 30 | def error_message(env) 31 | "#{env[:method].to_s.upcase} #{env[:url].to_s}: #{env[:status]}#{error_body(env[:body])}" 32 | end 33 | 34 | def error_body(body) 35 | if body.nil? 36 | nil 37 | elsif body['error'] 38 | ": #{body['error']}" 39 | elsif body['errors'] 40 | first = body['errors'].to_a.first 41 | if first.kind_of? Hash 42 | ": #{first['message'].chomp}" 43 | else 44 | ": #{first.chomp}" 45 | end 46 | end 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /spec/fixtures/facebook_users: -------------------------------------------------------------------------------- 1 | { 2 | "total_entries": 2, 3 | "_links": { 4 | "self": { 5 | "href": "/api/v2/facebook_users?page=1&per_page=30", 6 | "class": "page" 7 | }, 8 | "first": { 9 | "href": "/api/v2/facebook_users?page=1&per_page=30", 10 | "class": "page" 11 | }, 12 | "last": { 13 | "href": "/api/v2/facebook_users?page=1&per_page=30", 14 | "class": "page" 15 | }, 16 | "next": null, 17 | "previous": null 18 | }, 19 | "_embedded": { 20 | "entries": [ 21 | { 22 | "image_url": "https://graph.facebook.com/zuck/picture?type=square", 23 | "profile_url": "https://www.facebook.com/zuck", 24 | "created_at": "2013-11-12T22:59:20Z", 25 | "updated_at": "2013-11-17T22:59:20Z", 26 | "_links": { 27 | "self": { 28 | "href": "/api/v2/facebook_users/1", 29 | "class": "facebook_user" 30 | }, 31 | "customer": { 32 | "href": "/api/v2/customers/1", 33 | "class": "customer" 34 | } 35 | } 36 | }, 37 | { 38 | "image_url": "https://example.com/facebook_user_image", 39 | "profile_url": "https://example.com/facebook_profile", 40 | "created_at": "2013-11-12T22:59:20Z", 41 | "updated_at": "2013-11-17T22:59:20Z", 42 | "_links": { 43 | "self": { 44 | "href": "/api/v2/facebook_users/2", 45 | "class": "facebook_user" 46 | }, 47 | "customer": { 48 | "href": "/api/v2/customers/2", 49 | "class": "customer" 50 | } 51 | } 52 | } 53 | ] 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /spec/fixtures/integration_urls: -------------------------------------------------------------------------------- 1 | { 2 | "total_entries": 2, 3 | "_links": { 4 | "self": { 5 | "href": "/api/v2/integration_urls?page=1&per_page=30", 6 | "class": "page" 7 | }, 8 | "first": { 9 | "href": "/api/v2/integration_urls?page=1&per_page=30", 10 | "class": "page" 11 | }, 12 | "last": { 13 | "href": "/api/v2/integration_urls?page=1&per_page=30", 14 | "class": "page" 15 | }, 16 | "next": null, 17 | "previous": null 18 | }, 19 | "_embedded": { 20 | "entries": [ 21 | { 22 | "name": "Sample URL", 23 | "description": "A sample Integration URL", 24 | "enabled": true, 25 | "markup": "http://www.example.com/name={{customer.name | url_encode}}", 26 | "rendered": "http://www.example.com/name=", 27 | "created_at": "2013-11-22T22:49:20Z", 28 | "updated_at": "2013-11-22T22:49:20Z", 29 | "_links": { 30 | "self": { 31 | "href": "/api/v2/integration_urls/1", 32 | "class": "integration_url" 33 | } 34 | } 35 | }, 36 | { 37 | "name": "Another URL", 38 | "description": "A sample Integration URL", 39 | "enabled": true, 40 | "markup": "http://www.example.com/caseid={{case.id}}", 41 | "rendered": "http://www.example.com/caseid=", 42 | "created_at": "2013-11-22T22:49:20Z", 43 | "updated_at": "2013-11-22T22:49:20Z", 44 | "_links": { 45 | "self": { 46 | "href": "/api/v2/integration_urls/2", 47 | "class": "integration_url" 48 | } 49 | }, 50 | "position": 2 51 | } 52 | ] 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /lib/desk/client/customer.rb: -------------------------------------------------------------------------------- 1 | module Desk 2 | class Client 3 | module Customer 4 | 5 | def customer_endpoints 6 | [ :list, :show, :create, :update, :search ] 7 | end 8 | 9 | def customer_add_key(key, customer, value, type) 10 | customer.send(key) << {:value => value, :type => type} 11 | customer = Desk.update_customer(customer.id, {key.to_sym => customer.send(key)}) 12 | end 13 | 14 | def customer_delete_key(key, customer, *args) 15 | a = args.last.is_a?(Array) ? args.pop : args 16 | customer.send(key).delete_if do |item| 17 | a.include?(item.type) || a.include?(item.value) 18 | end 19 | customer = Desk.update_customer(customer.id, {key.to_sym => customer.send(key)}) 20 | end 21 | 22 | def customer_add_address(customer, address, type = "home") 23 | customer_add_key("addresses", customer, address, type) 24 | end 25 | 26 | def customer_delete_address(customer, *args) 27 | customer_delete_key("addresses", customer, args) 28 | end 29 | 30 | def customer_add_email(customer, email, type = "home") 31 | customer_add_key("emails", customer, email, type) 32 | end 33 | 34 | def customer_delete_email(customer, *args) 35 | customer_delete_key("emails", customer, args) 36 | end 37 | 38 | def customer_add_phone_number(customer, phone_number, type = "home") 39 | customer_add_key("phone_numbers", customer, phone_number, type) 40 | end 41 | 42 | def customer_delete_phone_number(customer, *args) 43 | customer_delete_key("phone_numbers", customer, args) 44 | end 45 | 46 | end 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /spec/fixtures/twitter_users: -------------------------------------------------------------------------------- 1 | { 2 | "total_entries": 2, 3 | "_links": { 4 | "self": { 5 | "href": "/api/v2/twitter_users?page=1&per_page=30", 6 | "class": "page" 7 | }, 8 | "first": { 9 | "href": "/api/v2/twitter_users?page=1&per_page=30", 10 | "class": "page" 11 | }, 12 | "last": { 13 | "href": "/api/v2/twitter_users?page=1&per_page=30", 14 | "class": "page" 15 | }, 16 | "next": null, 17 | "previous": null 18 | }, 19 | "_embedded": { 20 | "entries": [ 21 | { 22 | "handle": "desk_dev", 23 | "image_url": "http://example.com/image.png", 24 | "followers_count": "123", 25 | "verified": false, 26 | "created_at": "2013-11-22T22:49:20Z", 27 | "updated_at": "2013-11-22T22:49:20Z", 28 | "_links": { 29 | "self": { 30 | "href": "/api/v2/twitter_users/1", 31 | "class": "twitter_user" 32 | }, 33 | "customer": { 34 | "href": "/api/v2/customers/1", 35 | "class": "customer" 36 | } 37 | } 38 | }, 39 | { 40 | "handle": "desk", 41 | "image_url": "http://example.com/image.png", 42 | "followers_count": "123", 43 | "verified": false, 44 | "created_at": "2013-11-22T22:49:20Z", 45 | "updated_at": "2013-11-22T22:49:20Z", 46 | "_links": { 47 | "self": { 48 | "href": "/api/v2/twitter_users/2", 49 | "class": "twitter_user" 50 | }, 51 | "customer": { 52 | "href": "/api/v2/customers/2", 53 | "class": "customer" 54 | } 55 | } 56 | } 57 | ] 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /lib/desk/connection.rb: -------------------------------------------------------------------------------- 1 | require 'faraday_middleware' 2 | require 'faraday/request/multipart_with_file' 3 | require 'faraday/response/deashify' 4 | require 'faraday/response/raise_http_4xx' 5 | require 'faraday/response/raise_http_5xx' 6 | 7 | module Desk 8 | # @private 9 | module Connection 10 | private 11 | 12 | def connection(raw=false) 13 | options = { 14 | :headers => {'Accept' => "application/#{format}", 'User-Agent' => user_agent}, 15 | :proxy => proxy, 16 | :ssl => {:verify => false, :version => 'SSLv23'}, 17 | :url => api_endpoint, 18 | :request => {}, 19 | } 20 | 21 | options[:request][:timeout] = timeout if timeout 22 | 23 | Faraday.new(options) do |builder| 24 | builder.use Faraday::Request::MultipartWithFile 25 | if authenticated? 26 | if auth_method == Desk::Authentication::Methods::BASIC 27 | builder.use Faraday::Request::BasicAuthentication,basic_auth_username, basic_auth_password 28 | else 29 | builder.use Faraday::Request::OAuth, authentication 30 | end 31 | end 32 | builder.use Faraday::Request::Multipart 33 | builder.use Faraday::Request::UrlEncoded 34 | builder.use Faraday::Response::RaiseHttp4xx 35 | builder.use FaradayMiddleware::Deashify unless raw 36 | unless raw 37 | case format.to_s.downcase 38 | when 'json' 39 | builder.use Faraday::Response::ParseJson 40 | when 'xml' 41 | builder.use Faraday::Response::ParseXml 42 | end 43 | end 44 | builder.use Faraday::Response::RaiseHttp5xx 45 | builder.adapter(adapter) 46 | builder.response :logger, logger, :bodies => true unless logger.nil? 47 | end 48 | end 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /spec/fixtures/case_notes: -------------------------------------------------------------------------------- 1 | { 2 | "total_entries": 2, 3 | "_links": { 4 | "self": { 5 | "href": "/api/v2/cases/1/notes?page=1&per_page=50", 6 | "class": "page" 7 | }, 8 | "first": { 9 | "href": "/api/v2/cases/1/notes?page=1&per_page=50", 10 | "class": "page" 11 | }, 12 | "last": { 13 | "href": "/api/v2/cases/1/notes?page=1&per_page=50", 14 | "class": "page" 15 | }, 16 | "next": null, 17 | "previous": null 18 | }, 19 | "_embedded": { 20 | "entries": [ 21 | { 22 | "body": "Please assist me with this case", 23 | "created_at": "2013-11-22T22:49:20Z", 24 | "updated_at": "2013-11-22T22:49:20Z", 25 | "erased_at": null, 26 | "_links": { 27 | "self": { 28 | "href": "/api/v2/cases/1/notes/1", 29 | "class": "note" 30 | }, 31 | "case": { 32 | "href": "/api/v2/cases/1", 33 | "class": "case" 34 | }, 35 | "user": { 36 | "href": "/api/v2/users/1", 37 | "class": "user" 38 | }, 39 | "erased_by": null 40 | } 41 | }, 42 | { 43 | "body": "No problem, I'm investigating", 44 | "created_at": "2013-11-22T22:58:22Z", 45 | "updated_at": "2013-11-22T22:58:22Z", 46 | "erased_at": null, 47 | "_links": { 48 | "self": { 49 | "href": "/api/v2/cases/1/notes/2", 50 | "class": "note" 51 | }, 52 | "case": { 53 | "href": "/api/v2/cases/1", 54 | "class": "case" 55 | }, 56 | "user": { 57 | "href": "/api/v2/users/2", 58 | "class": "user" 59 | }, 60 | "erased_by": null 61 | } 62 | } 63 | ] 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /spec/fixtures/twitter_account_tweets: -------------------------------------------------------------------------------- 1 | { 2 | "total_entries": 2, 3 | "_links": { 4 | "self": { 5 | "href": "/api/v2/tweets?page=1&per_page=30", 6 | "class": "page" 7 | }, 8 | "first": { 9 | "href": "/api/v2/tweets?page=1&per_page=30", 10 | "class": "page" 11 | }, 12 | "last": { 13 | "href": "/api/v2/tweets?page=1&per_page=30", 14 | "class": "page" 15 | }, 16 | "next": null, 17 | "previous": null 18 | }, 19 | "_embedded": { 20 | "entries": [ 21 | { 22 | "body": "Example tweet", 23 | "direction": "out", 24 | "type": "mention", 25 | "status": "sent", 26 | "to": null, 27 | "from": "desk_dev", 28 | "created_at": "2013-11-22T17:59:20Z", 29 | "updated_at": "2013-11-22T17:59:20Z", 30 | "_links": { 31 | "self": { 32 | "href": "/api/v2/twitter_accounts/1/tweets/1", 33 | "class": "tweet" 34 | }, 35 | "twitter_account": { 36 | "href": "/api/v2/twitter_accounts/1", 37 | "class": "twitter_account" 38 | } 39 | } 40 | }, 41 | { 42 | "body": "we're checking out your API examples @desk_dev", 43 | "direction": "out", 44 | "type": "mention", 45 | "status": "sent", 46 | "to": null, 47 | "from": "desk_dev", 48 | "created_at": "2013-11-22T17:59:20Z", 49 | "updated_at": "2013-11-22T17:59:20Z", 50 | "_links": { 51 | "self": { 52 | "href": "/api/v2/twitter_accounts/1/tweets/2", 53 | "class": "tweet" 54 | }, 55 | "twitter_account": { 56 | "href": "/api/v2/twitter_accounts/1", 57 | "class": "twitter_account" 58 | } 59 | } 60 | } 61 | ] 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /spec/fixtures/case_attachments: -------------------------------------------------------------------------------- 1 | { 2 | "total_entries": 2, 3 | "_links": { 4 | "self": { 5 | "href": "/api/v2/cases/1/attachments?page=1&per_page=50", 6 | "class": "page" 7 | }, 8 | "first": { 9 | "href": "/api/v2/cases/1/attachments?page=1&per_page=50", 10 | "class": "page" 11 | }, 12 | "last": { 13 | "href": "/api/v2/cases/1/attachments?page=1&per_page=50", 14 | "class": "page" 15 | }, 16 | "next": null, 17 | "previous": null 18 | }, 19 | "_embedded": { 20 | "entries": [ 21 | { 22 | "file_name": "awesome_pic.png", 23 | "content_type": "image/png", 24 | "size": "500", 25 | "url": "http://example.com/short_lived_link_to_the_file_content", 26 | "erased_at": null, 27 | "created_at": "2013-11-22T22:49:20Z", 28 | "updated_at": "2013-11-22T22:49:20Z", 29 | "_links": { 30 | "self": { 31 | "href": "/api/v2/cases/1/attachments/1", 32 | "class": "attachment" 33 | }, 34 | "case": { 35 | "href": "/api/v2/cases/1", 36 | "class": "case" 37 | } 38 | } 39 | }, 40 | { 41 | "file_name": "another_awesome_pic.png", 42 | "content_type": "image/png", 43 | "size": "500", 44 | "url": "http://example.com/short_lived_link_to_the_file_content", 45 | "erased_at": null, 46 | "created_at": "2013-11-22T22:49:20Z", 47 | "updated_at": "2013-11-22T22:49:20Z", 48 | "_links": { 49 | "self": { 50 | "href": "/api/v2/cases/1/attachments/2", 51 | "class": "attachment" 52 | }, 53 | "case": { 54 | "href": "/api/v2/cases/2", 55 | "class": "case" 56 | } 57 | } 58 | } 59 | ] 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /spec/fixtures/users: -------------------------------------------------------------------------------- 1 | { 2 | "total_entries": 2, 3 | "_links": { 4 | "self": { 5 | "href": "/api/v2/users?page=1&per_page=30", 6 | "class": "page" 7 | }, 8 | "first": { 9 | "href": "/api/v2/users?page=1&per_page=30", 10 | "class": "page" 11 | }, 12 | "last": { 13 | "href": "/api/v2/users?page=1&per_page=30", 14 | "class": "page" 15 | }, 16 | "next": null, 17 | "previous": null 18 | }, 19 | "_embedded": { 20 | "entries": [ 21 | { 22 | "name": "John Doe", 23 | "public_name": "John Doe", 24 | "email": "john@acme.com", 25 | "level": "agent", 26 | "created_at": "2012-11-22T22:59:20Z", 27 | "updated_at": "2013-11-15T22:59:20Z", 28 | "current_login_at": "2013-11-21T22:59:20Z", 29 | "last_login_at": "2013-11-15T22:59:20Z", 30 | "_links": { 31 | "self": { 32 | "href": "/api/v2/users/1", 33 | "class": "user" 34 | }, 35 | "preferences": { 36 | "href": "/api/v2/users/1/preferences", 37 | "class": "user_preference" 38 | } 39 | } 40 | }, 41 | { 42 | "name": "Jane Doe", 43 | "public_name": "Jane Doe", 44 | "email": "jane@acme.com", 45 | "level": "agent", 46 | "created_at": "2012-11-22T22:59:20Z", 47 | "updated_at": "2013-11-15T22:59:20Z", 48 | "current_login_at": "2013-11-21T22:59:20Z", 49 | "last_login_at": "2013-11-15T22:59:20Z", 50 | "_links": { 51 | "self": { 52 | "href": "/api/v2/users/2", 53 | "class": "user" 54 | }, 55 | "preferences": { 56 | "href": "/api/v2/users/1/preferences", 57 | "class": "user_preference" 58 | } 59 | } 60 | } 61 | ] 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /spec/fixtures/group_users: -------------------------------------------------------------------------------- 1 | { 2 | "total_entries": 2, 3 | "_links": { 4 | "self": { 5 | "href": "/api/v2/groups/1/users?page=1&per_page=50", 6 | "class": "page" 7 | }, 8 | "first": { 9 | "href": "/api/v2/groups/1/users?page=1&per_page=50", 10 | "class": "page" 11 | }, 12 | "last": { 13 | "href": "/api/v2/groups/1/users?page=1&per_page=50", 14 | "class": "page" 15 | }, 16 | "next": null, 17 | "previous": null 18 | }, 19 | "_embedded": { 20 | "entries": [ 21 | { 22 | "name": "John Doe", 23 | "public_name": "John Doe", 24 | "email": "john@acme.com", 25 | "level": "agent", 26 | "created_at": "2012-11-22T22:59:20Z", 27 | "updated_at": "2013-11-15T22:59:20Z", 28 | "current_login_at": "2013-11-21T22:59:20Z", 29 | "last_login_at": "2013-11-15T22:59:20Z", 30 | "_links": { 31 | "self": { 32 | "href": "/api/v2/users/1", 33 | "class": "user" 34 | }, 35 | "preferences": { 36 | "href": "/api/v2/users/1/preferences", 37 | "class": "user_preference" 38 | } 39 | } 40 | }, 41 | { 42 | "name": "Jane Smith", 43 | "public_name": "Jane Smith", 44 | "email": "jane@acme.com", 45 | "level": "agent", 46 | "created_at": "2012-11-22T22:59:20Z", 47 | "updated_at": "2013-11-15T22:59:20Z", 48 | "current_login_at": "2013-11-21T22:59:20Z", 49 | "last_login_at": "2013-11-15T22:59:20Z", 50 | "_links": { 51 | "self": { 52 | "href": "/api/v2/users/2", 53 | "class": "user" 54 | }, 55 | "preferences": { 56 | "href": "/api/v2/users/1/preferences", 57 | "class": "user_preference" 58 | } 59 | } 60 | } 61 | ] 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /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.5') 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.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', '~> 3.4', '>= 3.4.2' 16 | s.add_runtime_dependency('faraday', '~> 0.9.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.6') 20 | s.add_runtime_dependency('multi_xml', '~> 0.5') 21 | s.add_runtime_dependency('simple_oauth', '~> 0.2.0') 22 | s.add_runtime_dependency('pony', '~> 1.1') 23 | s.authors = ["Chris Warren"] 24 | s.description = %q{A Ruby wrapper for the Desk.com REST API} 25 | s.email = ['chris@zencoder.com'] 26 | s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } 27 | s.files = `git ls-files`.split("\n") 28 | s.homepage = 'https://github.com/zencoder/desk' 29 | s.name = 'desk' 30 | s.license = 'MIT' 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/inbound_mailboxes_spec.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | 3 | describe Desk::Client do 4 | context "Inbound Mailbox" do 5 | 6 | let(:endpoint) { "inbound_mailbox" } 7 | let(:id) { 1 } 8 | let(:check_key) { "name" } 9 | let(:check_value) { "Support Mailbox" } 10 | 11 | include_context "basic configuration" 12 | 13 | context "list all inbound mailboxes" do 14 | 15 | include_context "plural endpoint" 16 | 17 | subject { client.send("list_#{endpoints}") } 18 | 19 | before do 20 | stub_get("mailboxes/inbound").to_return(:body => fixture(endpoints)) 21 | end 22 | 23 | it "gets the correct resource" do 24 | subject 25 | expect(a_get("mailboxes/inbound")).to have_been_made 26 | end 27 | 28 | it { expect(subject).to be_a Hashie::Deash } 29 | 30 | it "has valid entries" do 31 | expect(subject.first.id).to eq(id) 32 | expect(subject.first.send(check_key)).to eq(check_value) 33 | end 34 | 35 | it "allows raw access" do 36 | expect(subject.raw.first).to be_a Array 37 | end 38 | 39 | end 40 | 41 | context "retrieve a single inbound mailbox" do 42 | 43 | include_context "plural endpoint" 44 | 45 | subject { client.send("show_#{endpoint}", id) } 46 | 47 | before do 48 | stub_get("mailboxes/inbound/#{id}").to_return(:body => fixture(endpoint)) 49 | end 50 | 51 | it "gets the correct resource" do 52 | subject 53 | expect(a_get("mailboxes/inbound/#{id}")).to have_been_made 54 | end 55 | 56 | it { expect(subject).to be_a Hashie::Deash } 57 | 58 | it "has a valid entry" do 59 | expect(subject.id).to eq(id) 60 | expect(subject.send(check_key)).to eq(check_value) 61 | end 62 | 63 | it "allows raw access" do 64 | expect(subject.raw).to be_a Hashie::Deash 65 | end 66 | 67 | end 68 | 69 | end 70 | end 71 | -------------------------------------------------------------------------------- /lib/desk/request.rb: -------------------------------------------------------------------------------- 1 | module Desk 2 | # Defines HTTP request methods 3 | module Request 4 | require 'json' unless defined?(::JSON) 5 | REQUEST_METHODS = [ 6 | 'get', 7 | 'patch', 8 | 'post', 9 | 'put', 10 | 'delete' 11 | ].freeze 12 | 13 | def method_missing(method_name, *args, &block) 14 | if (REQUEST_METHODS.include? method_name.to_s) && (args.length > 0) 15 | path = args[0] 16 | options = args[1] ? args[1] : {} 17 | raw = args[2] ? args[2] : false 18 | request(method_name.to_sym, path, options, raw) 19 | else 20 | super 21 | end 22 | end 23 | 24 | def respond_to?(method_name, include_private = false) 25 | if (REQUEST_METHODS.include? method_name.to_s) 26 | true 27 | else 28 | super 29 | end 30 | end 31 | 32 | private 33 | 34 | def before_request 35 | if Desk.minute != Time.now.min 36 | Desk.minute = Time.now.min 37 | Desk.counter = 0 38 | end 39 | 40 | Desk.counter += 1 41 | if Desk.use_max_requests 42 | if Desk.counter > Desk.max_requests 43 | raise Desk::TooManyRequests 44 | end 45 | end 46 | end 47 | 48 | # Perform an HTTP request 49 | def request(method, path, options, raw=false) 50 | before_request 51 | response = connection(raw).send(method) do |request| 52 | case method 53 | when :get, :delete 54 | request.url(formatted_path(path), options) 55 | when :patch, :post, :put 56 | request.path = formatted_path(path) 57 | request.headers['Content-Type'] = 'application/json' 58 | request.body = options.to_json unless options.empty? 59 | end 60 | end 61 | raw ? response : response.body 62 | end 63 | 64 | def formatted_path(path) 65 | if(self.version == "v1") 66 | [path, format].compact.join('.') 67 | else 68 | path 69 | end 70 | end 71 | end 72 | end 73 | -------------------------------------------------------------------------------- /spec/fixtures/topics: -------------------------------------------------------------------------------- 1 | { 2 | "total_entries": 2, 3 | "_links": { 4 | "self": { 5 | "href": "/api/v2/topics?page=1&per_page=30", 6 | "class": "page" 7 | }, 8 | "first": { 9 | "href": "/api/v2/topics?page=1&per_page=30", 10 | "class": "page" 11 | }, 12 | "last": { 13 | "href": "/api/v2/topics?page=1&per_page=30", 14 | "class": "page" 15 | }, 16 | "next": null, 17 | "previous": null 18 | }, 19 | "_embedded": { 20 | "entries": [ 21 | { 22 | "name": "Customer Support", 23 | "description": "This is key to going from good to great", 24 | "position": 1, 25 | "allow_questions": true, 26 | "in_support_center": true, 27 | "created_at": "2013-11-12T22:59:20Z", 28 | "updated_at": "2013-11-17T22:59:20Z", 29 | "_links": { 30 | "self": { 31 | "href": "/api/v2/topics/1", 32 | "class": "topic" 33 | }, 34 | "articles": { 35 | "href": "/api/v2/topics/1/articles", 36 | "class": "article" 37 | }, 38 | "translations": { 39 | "href": "/api/v2/topics/1/translations", 40 | "class": "topic_translation" 41 | } 42 | } 43 | }, 44 | { 45 | "name": "Another Topic", 46 | "description": "Not the first one, but another one!", 47 | "position": 2, 48 | "allow_questions": true, 49 | "in_support_center": true, 50 | "created_at": "2013-11-12T22:59:20Z", 51 | "updated_at": "2013-11-17T22:59:20Z", 52 | "_links": { 53 | "self": { 54 | "href": "/api/v2/topics/2", 55 | "class": "topic" 56 | }, 57 | "articles": { 58 | "href": "/api/v2/topics/1/articles", 59 | "class": "article" 60 | }, 61 | "translations": { 62 | "href": "/api/v2/topics/1/translations", 63 | "class": "topic_translation" 64 | } 65 | } 66 | } 67 | ] 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /spec/fixtures/companies: -------------------------------------------------------------------------------- 1 | { 2 | "total_entries": 2, 3 | "_links": { 4 | "self": { 5 | "href": "/api/v2/companies?page=1&per_page=30", 6 | "class": "page" 7 | }, 8 | "first": { 9 | "href": "/api/v2/companies?page=1&per_page=30", 10 | "class": "page" 11 | }, 12 | "last": { 13 | "href": "/api/v2/companies?page=1&per_page=30", 14 | "class": "page" 15 | }, 16 | "next": null, 17 | "previous": null 18 | }, 19 | "_embedded": { 20 | "entries": [ 21 | { 22 | "name": "Acme Inc", 23 | "domains": [ 24 | "acmeinc.com", 25 | "acmeinc.net" 26 | ], 27 | "created_at": "2013-11-22T22:49:20Z", 28 | "updated_at": "2013-11-22T22:49:20Z", 29 | "custom_fields": { 30 | "employer_id": "123456789" 31 | }, 32 | "_links": { 33 | "self": { 34 | "href": "/api/v2/companies/1", 35 | "class": "company" 36 | }, 37 | "customers": { 38 | "href": "/api/v2/companies/1/customers", 39 | "class": "customer" 40 | }, 41 | "cases": { 42 | "href": "/api/v2/companies/1/cases", 43 | "class": "case" 44 | } 45 | } 46 | }, 47 | { 48 | "name": "Desk.com", 49 | "domains": [ 50 | "desk.com", 51 | "salesforce.com" 52 | ], 53 | "created_at": "2013-11-22T22:49:20Z", 54 | "updated_at": "2013-11-22T22:49:20Z", 55 | "custom_fields": { 56 | "employer_id": "123456789" 57 | }, 58 | "_links": { 59 | "self": { 60 | "href": "/api/v2/companies/2", 61 | "class": "company" 62 | }, 63 | "customers": { 64 | "href": "/api/v2/companies/2/customers", 65 | "class": "customer" 66 | }, 67 | "cases": { 68 | "href": "/api/v2/companies/2/cases", 69 | "class": "case" 70 | } 71 | } 72 | } 73 | ] 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /spec/fixtures/company_search: -------------------------------------------------------------------------------- 1 | { 2 | "total_entries": 2, 3 | "_links": { 4 | "self": { 5 | "href": "/api/v2/companies?page=1&per_page=30", 6 | "class": "page" 7 | }, 8 | "first": { 9 | "href": "/api/v2/companies?page=1&per_page=30", 10 | "class": "page" 11 | }, 12 | "last": { 13 | "href": "/api/v2/companies?page=1&per_page=30", 14 | "class": "page" 15 | }, 16 | "next": null, 17 | "previous": null 18 | }, 19 | "_embedded": { 20 | "entries": [ 21 | { 22 | "name": "Acme Inc", 23 | "domains": [ 24 | "acmeinc.com", 25 | "acmeinc.net" 26 | ], 27 | "created_at": "2013-11-22T22:49:20Z", 28 | "updated_at": "2013-11-22T22:49:20Z", 29 | "custom_fields": { 30 | "employer_id": "123456789" 31 | }, 32 | "_links": { 33 | "self": { 34 | "href": "/api/v2/companies/1", 35 | "class": "company" 36 | }, 37 | "customers": { 38 | "href": "/api/v2/companies/1/customers", 39 | "class": "customer" 40 | }, 41 | "cases": { 42 | "href": "/api/v2/companies/1/cases", 43 | "class": "case" 44 | } 45 | } 46 | }, 47 | { 48 | "name": "Acme Enterprises", 49 | "domains": [ 50 | "acme-ent.com", 51 | "acmeinc.com", 52 | "acmeinc.net" 53 | ], 54 | "created_at": "2013-11-22T22:49:20Z", 55 | "updated_at": "2013-11-22T22:49:20Z", 56 | "custom_fields": { 57 | "employer_id": "987654321" 58 | }, 59 | "_links": { 60 | "self": { 61 | "href": "/api/v2/companies/2", 62 | "class": "company" 63 | }, 64 | "customers": { 65 | "href": "/api/v2/companies/2/customers", 66 | "class": "customer" 67 | }, 68 | "cases": { 69 | "href": "/api/v2/companies/1/cases", 70 | "class": "case" 71 | } 72 | } 73 | } 74 | ] 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /spec/fixtures/companies_search: -------------------------------------------------------------------------------- 1 | { 2 | "total_entries": 2, 3 | "_links": { 4 | "self": { 5 | "href": "/api/v2/companies?page=1&per_page=30", 6 | "class": "page" 7 | }, 8 | "first": { 9 | "href": "/api/v2/companies?page=1&per_page=30", 10 | "class": "page" 11 | }, 12 | "last": { 13 | "href": "/api/v2/companies?page=1&per_page=30", 14 | "class": "page" 15 | }, 16 | "next": null, 17 | "previous": null 18 | }, 19 | "_embedded": { 20 | "entries": [ 21 | { 22 | "name": "Acme Inc", 23 | "domains": [ 24 | "acmeinc.com", 25 | "acmeinc.net" 26 | ], 27 | "created_at": "2013-11-22T22:49:20Z", 28 | "updated_at": "2013-11-22T22:49:20Z", 29 | "custom_fields": { 30 | "employer_id": "123456789" 31 | }, 32 | "_links": { 33 | "self": { 34 | "href": "/api/v2/companies/1", 35 | "class": "company" 36 | }, 37 | "customers": { 38 | "href": "/api/v2/companies/1/customers", 39 | "class": "customer" 40 | }, 41 | "cases": { 42 | "href": "/api/v2/companies/1/cases", 43 | "class": "case" 44 | } 45 | } 46 | }, 47 | { 48 | "name": "Acme Enterprises", 49 | "domains": [ 50 | "acme-ent.com", 51 | "acmeinc.com", 52 | "acmeinc.net" 53 | ], 54 | "created_at": "2013-11-22T22:49:20Z", 55 | "updated_at": "2013-11-22T22:49:20Z", 56 | "custom_fields": { 57 | "employer_id": "987654321" 58 | }, 59 | "_links": { 60 | "self": { 61 | "href": "/api/v2/companies/2", 62 | "class": "company" 63 | }, 64 | "customers": { 65 | "href": "/api/v2/companies/2/customers", 66 | "class": "customer" 67 | }, 68 | "cases": { 69 | "href": "/api/v2/companies/1/cases", 70 | "class": "case" 71 | } 72 | } 73 | } 74 | ] 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /HISTORY.mkd: -------------------------------------------------------------------------------- 1 | 1.0.2 2 | ------------------ 3 | Completed handling of respond_to? (Thanks to @davidlibrera) 4 | 5 | 1.0.1 6 | ------------------ 7 | Handle TooManyRequests Response (Thanks to @alexanderdean) 8 | Bumped Faraday (Thanks to Marcin Lewandowski) 9 | 10 | 1.0.0 January 31, 2014 11 | ------------------ 12 | Total overhaul of code structure (Thanks to @colinc) 13 | Works with the new Desk.com API V2 14 | 15 | 0.3.2 February 25, 2013 16 | ------------------ 17 | Support for groups (Thanks to @tstachl) 18 | Support for max_requests (Thanks to @tstachl) 19 | New multi_json and multi_xml version 20 | 21 | 0.3.0 July 15, 2012 22 | ------------------ 23 | Renamed from Assistly to Desk 24 | Updated Faraday and Hashie versions 25 | 26 | 0.2.6 October 3, 2011 27 | ------------------ 28 | Newer Faraday and multixml versions 29 | 30 | 0.2.5 August 17, 2011 31 | ------------------ 32 | Newer multijson version for Rails 3.1 compatibility 33 | 34 | 0.2.3 April 18, 2011 35 | ------------------ 36 | 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. 37 | 38 | 0.2.2 - April 18, 2011 39 | ------------------ 40 | Added deep_merge! support so that we keep any custom email headers when creating outbound interactions. 41 | Required pony in the gem so you don't have to include it in your app. 42 | 43 | 0.2.1 - April 18, 2011 44 | ------------------ 45 | Stopped returning only the 'results' array when listing things, because this would cause us to not have access to page numbers, total counts, etc. 46 | 47 | 0.2.0 - April 16, 2011 48 | ------------------------- 49 | * Support for Topics, Articles, and Macros added 50 | * Update Readme 51 | 52 | 0.1.5 - April 15, 2011 53 | ------------------------- 54 | * Support for creating outbound interactions via email 55 | 56 | 0.1.1 - 0.1.4 - April 14, 2011 57 | ------------------------- 58 | * Miscellaneous bug fixes found when actually using this in production. 59 | * Moved to Zencoder Github account 60 | 61 | 0.1 - April 12, 2011 62 | ------------------------- 63 | * Initial release 64 | * Support for Case, Customer, Interaction, and User APIs. 65 | -------------------------------------------------------------------------------- /spec/fixtures/case_replies: -------------------------------------------------------------------------------- 1 | { 2 | "total_entries": 2, 3 | "_links": { 4 | "self": { 5 | "href": "/api/v2/cases/1/replies?page=1&per_page=50", 6 | "class": "page" 7 | }, 8 | "first": { 9 | "href": "/api/v2/cases/1/replies?page=1&per_page=50", 10 | "class": "page" 11 | }, 12 | "last": { 13 | "href": "/api/v2/cases/1/replies?page=1&per_page=50", 14 | "class": "page" 15 | }, 16 | "next": null, 17 | "previous": null 18 | }, 19 | "_embedded": { 20 | "entries": [ 21 | { 22 | "subject": "Please help", 23 | "body": "Help me with my issue!", 24 | "direction": "in", 25 | "status": "pending", 26 | "to": "john.doe@example.com", 27 | "from": "doe.john@example.com", 28 | "cc": null, 29 | "bcc": null, 30 | "created_at": "2013-11-22T22:49:20Z", 31 | "updated_at": "2013-11-22T22:49:20Z", 32 | "_links": { 33 | "self": { 34 | "href": "/api/v2/cases/1/replies/1", 35 | "class": "email" 36 | }, 37 | "case": { 38 | "href": "/api/v2/cases/1", 39 | "class": "case" 40 | }, 41 | "customer": { 42 | "href": "/api/v2/customer/1", 43 | "class": "customer" 44 | } 45 | } 46 | }, 47 | { 48 | "subject": "Re: Please help", 49 | "body": "Thanks for your question. The answer is 42.", 50 | "direction": "out", 51 | "status": "pending", 52 | "to": "doe.john@example.com", 53 | "from": "john.doe@example.com", 54 | "cc": null, 55 | "bcc": null, 56 | "created_at": "2013-11-22T22:49:20Z", 57 | "updated_at": "2013-11-22T22:49:20Z", 58 | "_links": { 59 | "self": { 60 | "href": "/api/v2/cases/1/replies/2", 61 | "class": "email" 62 | }, 63 | "case": { 64 | "href": "/api/v2/cases/1", 65 | "class": "case" 66 | }, 67 | "customer": { 68 | "href": "/api/v2/customer/1", 69 | "class": "customer" 70 | } 71 | } 72 | } 73 | ] 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /spec/desk/client/article_spec.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | 3 | describe Desk::Client do 4 | context "Article" do 5 | 6 | let(:endpoint) { "article" } 7 | let(:id) { 1 } 8 | let(:check_key) { "subject" } 9 | let(:check_value) { "Awesome Subject" } 10 | 11 | include_context "basic configuration" 12 | 13 | it_behaves_like "a list endpoint" 14 | 15 | it_behaves_like "a show endpoint" 16 | 17 | it_behaves_like "a create endpoint", { 18 | :subject => "Awesome Subject", 19 | :body => "Simply post here", 20 | :_links => { 21 | :topic => { 22 | :href => "/api/v2/topics/1", 23 | :class => "topic" 24 | } 25 | } 26 | } 27 | 28 | it_behaves_like "an update endpoint", { 29 | :subject => "How to make your customers happy", 30 | :body => "Use Desk.com", 31 | :body_email => "Custom email body for article", 32 | :body_email_auto => false, 33 | :_links => { 34 | :topic => { 35 | :href => "/api/v2/topics/1", 36 | :class => "topic" 37 | } 38 | } 39 | } do 40 | let(:check_value) { "How to make your customers happy" } 41 | end 42 | 43 | it_behaves_like "a delete endpoint" 44 | 45 | it_behaves_like "a search endpoint", { 46 | :text => "happy", 47 | :topic_ids => "1,2,4" 48 | } 49 | 50 | context "Translation" do 51 | 52 | let(:sub_endpoint) { "translation" } 53 | let(:sub_id) { "en" } 54 | let(:check_key) { "subject" } 55 | let(:check_value) { "Awesome Subject" } 56 | 57 | it_behaves_like "a sub list endpoint" 58 | 59 | it_behaves_like "a sub show endpoint" 60 | 61 | it_behaves_like "a sub create endpoint", { 62 | :locale => "es", 63 | :subject => "Spanish Translation", 64 | :body => "Traducción español aquí" 65 | } do 66 | let(:sub_id) { "es" } 67 | let(:check_value) { "Spanish Translation" } 68 | end 69 | 70 | it_behaves_like "a sub update endpoint", { 71 | :subject => "Updated Spanish Translation" 72 | } do 73 | let(:sub_id) { "es" } 74 | let(:check_value) { "Updated Spanish Translation" } 75 | end 76 | 77 | end 78 | end 79 | end 80 | -------------------------------------------------------------------------------- /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 | @http_headers.values_at('x-rate-limit-reset', 'X-Rate-Limit-Reset').detect {|value| value }.to_i 14 | end 15 | 16 | def ratelimit_limit 17 | @http_headers.values_at('x-rate-limit-limit', 'X-Rate-Limit-Limit').detect {|value| value }.to_i 18 | end 19 | 20 | def ratelimit_remaining 21 | @http_headers.values_at('x-rate-limit-remaining', 'X-Rate-Limit-Remaining').detect {|value| value }.to_i 22 | end 23 | 24 | def retry_after 25 | ratelimit_reset 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 409 45 | class Conflict < Error; end 46 | 47 | # Raised when Desk returns the HTTP status code 422 48 | class Unprocessable < Error; end 49 | 50 | # Raised when Desk returns the HTTP status code 429 51 | # Called EnhanceYourCalm because TooManyRequests is taken (see below) 52 | class EnhanceYourCalm < Error; 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 501 61 | class NotImplemented < Error; end 62 | 63 | # Raised when Desk returns the HTTP status code 502 64 | class BadGateway < Error; end 65 | 66 | # Raised when Desk returns the HTTP status code 503 67 | class ServiceUnavailable < Error; end 68 | 69 | # Gem Specific Errors 70 | class DeskError < StandardError; end 71 | 72 | class SupportEmailNotSet < DeskError; end 73 | end 74 | -------------------------------------------------------------------------------- /spec/fixtures/inbound_mailboxes: -------------------------------------------------------------------------------- 1 | { 2 | "total_entries": 2, 3 | "_links": { 4 | "self": { 5 | "href": "/api/v2/inbound_mailboxes?page=1&per_page=30", 6 | "class": "page" 7 | }, 8 | "first": { 9 | "href": "/api/v2/inbound_mailboxes?page=1&per_page=30", 10 | "class": "page" 11 | }, 12 | "last": { 13 | "href": "/api/v2/inbound_mailboxes?page=1&per_page=30", 14 | "class": "page" 15 | }, 16 | "next": null, 17 | "previous": null 18 | }, 19 | "_embedded": { 20 | "entries": [ 21 | { 22 | "name": "Support Mailbox", 23 | "enabled": true, 24 | "type": "imaps", 25 | "hostname": "mail.example.com", 26 | "port": 993, 27 | "email": "support@example.com", 28 | "last_checked_at": "2013-11-22T22:49:20Z", 29 | "created_at": "2013-11-22T22:49:20Z", 30 | "updated_at": "2013-11-22T22:49:20Z", 31 | "last_error": null, 32 | "inbound_address_filter": null, 33 | "outbound_address_filter": null, 34 | "_links": { 35 | "self": { 36 | "href": "/api/v2/mailboxes/inbound/1", 37 | "class": "inbound_mailbox" 38 | }, 39 | "default_group": { 40 | "href": "/api/v2/groups/1", 41 | "class": "group" 42 | }, 43 | "created_by": { 44 | "href": "/api/v2/users/1", 45 | "class": "user" 46 | }, 47 | "updated_by": { 48 | "href": "/api/v2/users/1", 49 | "class": "user" 50 | } 51 | } 52 | }, 53 | { 54 | "name": "Another inbound mailbox", 55 | "enabled": true, 56 | "type": "imaps", 57 | "hostname": "mail.example.com", 58 | "port": 993, 59 | "email": "another_support@example.com", 60 | "last_checked_at": "2013-11-22T22:49:20Z", 61 | "created_at": "2013-11-22T22:49:20Z", 62 | "updated_at": "2013-11-22T22:49:20Z", 63 | "last_error": null, 64 | "inbound_address_filter": null, 65 | "outbound_address_filter": null, 66 | "_links": { 67 | "self": { 68 | "href": "/api/v2/mailboxes/inbound/2", 69 | "class": "inbound_mailbox" 70 | }, 71 | "default_group": { 72 | "href": "/api/v2/groups/1", 73 | "class": "group" 74 | }, 75 | "created_by": { 76 | "href": "/api/v2/users/1", 77 | "class": "user" 78 | }, 79 | "updated_by": { 80 | "href": "/api/v2/users/1", 81 | "class": "user" 82 | } 83 | } 84 | } 85 | ] 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /spec/fixtures/article_translations: -------------------------------------------------------------------------------- 1 | { 2 | "total_entries": 2, 3 | "_links": { 4 | "self": { 5 | "href": "/api/v2/articles/1/translations?page=1&per_page=50", 6 | "class": "page" 7 | }, 8 | "first": { 9 | "href": "/api/v2/articles/1/translations?page=1&per_page=50", 10 | "class": "page" 11 | }, 12 | "last": { 13 | "href": "/api/v2/articles/1/translations?page=1&per_page=50", 14 | "class": "page" 15 | }, 16 | "next": null, 17 | "previous": null 18 | }, 19 | "_embedded": { 20 | "entries": [ 21 | { 22 | "locale": "en", 23 | "subject": "Awesome Subject", 24 | "body": "Awesome apples
", 25 | "body_email": "Email for Awesome apples", 26 | "body_email_auto": true, 27 | "body_chat": "Awesome apples", 28 | "body_chat_auto": true, 29 | "body_web_callback": "Awesome Apples
", 30 | "body_web_callback_auto": true, 31 | "body_twitter": "Awesome apples", 32 | "body_twitter_auto": true, 33 | "body_qna": "Awesome apples", 34 | "body_qna_auto": true, 35 | "body_phone": "Awesome apples", 36 | "body_phone_auto": true, 37 | "body_facebook": "Awesome apples", 38 | "body_facebook_auto": true, 39 | "outdated": false, 40 | "publish_at": "2013-11-22T22:54:20Z", 41 | "created_at": "2013-11-22T22:49:20Z", 42 | "updated_at": "2013-11-22T22:54:20Z", 43 | "_links": { 44 | "self": { 45 | "href": "/api/v2/articles/1/translations/en", 46 | "class": "article" 47 | } 48 | } 49 | }, 50 | { 51 | "locale": "es", 52 | "subject": "Spanish Translation", 53 | "body": "Traducción español aquí", 54 | "body_email": "Traducción español aquí", 55 | "body_email_auto": true, 56 | "body_chat": "Traducción español aquí", 57 | "body_chat_auto": true, 58 | "body_web_callback": "Traducción español aquí", 59 | "body_web_callback_auto": true, 60 | "body_twitter": "Traducción español aquí", 61 | "body_twitter_auto": true, 62 | "body_qna": "Traducción español aquí", 63 | "body_qna_auto": true, 64 | "body_phone": "Traducción español aquí", 65 | "body_phone_auto": true, 66 | "body_facebook": "Traducción español aquí", 67 | "body_facebook_auto": true, 68 | "outdated": false, 69 | "publish_at": "2013-11-22T22:54:20Z", 70 | "created_at": "2013-11-22T22:49:20Z", 71 | "updated_at": "2013-11-22T22:54:20Z", 72 | "_links": { 73 | "self": { 74 | "href": "/api/v2/articles/1/translations/es", 75 | "class": "article" 76 | } 77 | } 78 | } 79 | ] 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /lib/desk/client/case.rb: -------------------------------------------------------------------------------- 1 | module Desk 2 | class Client 3 | # Defines methods related to cases 4 | module Case 5 | 6 | def case_endpoints 7 | [ :list, :search, :create, :update, :delete, 8 | :list_replies, :show_reply, :create_reply, :update_reply, 9 | :list_notes, :show_note, :create_note, 10 | :list_attachments, :show_attachment, :create_attachment, :delete_attachment, 11 | :list_history 12 | ] 13 | end 14 | 15 | def show_case(case_id, *args) 16 | options = args.last.is_a?(Hash) ? args.pop : {} 17 | case_id = "e-#{case_id}" if options[:by] == "external_id" 18 | get("cases/#{case_id}") 19 | end 20 | alias_method :case, :show_case 21 | 22 | def show_case_message(case_id) 23 | get("cases/#{case_id}/message") 24 | end 25 | alias_method :case_message, :show_case_message 26 | 27 | def list_case_message_attachments(case_id, *args) 28 | options = args.last.is_a?(Hash) ? args.pop : {} 29 | get("cases/#{case_id}/message/attachments", options) 30 | end 31 | alias_method :case_message_attachments, :list_case_message_attachments 32 | 33 | def list_case_reply_attachments(case_id, reply_id, *args) 34 | options = args.last.is_a?(Hash) ? args.pop : {} 35 | get("cases/#{case_id}/replies/#{reply_id}/attachments", options) 36 | end 37 | alias_method :case_reply_attachments, :list_case_reply_attachments 38 | 39 | def show_case_message_attachment(case_id, attachment_id) 40 | get("cases/#{case_id}/message/attachments/#{attachment_id}") 41 | end 42 | alias_method :case_message_attachment, :show_case_message_attachment 43 | 44 | def show_case_reply_attachment(case_id, reply_id, attachment_id) 45 | get("cases/#{case_id}/replies/#{reply_id}/attachments/#{attachment_id}") 46 | end 47 | alias_method :case_reply_attachment, :show_case_reply_attachment 48 | 49 | def create_case_message_attachment(case_id, *args) 50 | options = args.last.is_a?(Hash) ? args.pop : {} 51 | post("cases/#{case_id}/message/attachments", options) 52 | end 53 | 54 | def create_case_reply_attachment(case_id, reply_id, *args) 55 | options = args.last.is_a?(Hash) ? args.pop : {} 56 | post("cases/#{case_id}/replies/#{reply_id}/attachments", options) 57 | end 58 | 59 | def delete_case_message_attachment(case_id, attachment_id) 60 | delete("cases/#{case_id}/message/attachments/#{attachment_id}") 61 | end 62 | 63 | def delete_case_reply_attachment(case_id, reply_id, attachment_id) 64 | delete("cases/#{case_id}/replies/#{reply_id}/attachments/#{attachment_id}") 65 | end 66 | 67 | def case_url(case_id) 68 | "https://#{subdomain}.#{domain}/agent/case/#{case_id}" 69 | end 70 | end 71 | end 72 | end 73 | -------------------------------------------------------------------------------- /lib/desk/deash.rb: -------------------------------------------------------------------------------- 1 | require 'hashie/mash' 2 | 3 | module Hashie 4 | class Links 5 | 6 | def initialize(links) 7 | @links = links 8 | end 9 | 10 | def method_missing(method, *args, &block) 11 | if @links.key?(method) 12 | return nil if !@links[method] 13 | return Desk.get(@links[method].href.sub("/api/#{Desk.version}/", "")) 14 | end 15 | return super 16 | end 17 | end 18 | 19 | class Deash < Mash 20 | disable_warnings if respond_to?(:disable_warnings) 21 | 22 | def count 23 | if includes_key_chain?("raw._embedded.entries") 24 | self.raw._embedded['entries'].count 25 | else 26 | super 27 | end 28 | end 29 | 30 | def each 31 | if includes_key_chain?("raw._embedded.entries") 32 | self.raw._embedded['entries'].each do |entry| 33 | yield entry 34 | end 35 | else 36 | super 37 | end 38 | end 39 | 40 | def method_missing(method, *args, &block) 41 | return self.[](method) if key?(method) 42 | # TODO: Make this DRY 43 | if includes_key_chain?("_links."+method.to_s) 44 | return nil if !self._links[method] 45 | return Desk.get(self._links[method].href.sub(Desk.api_path, "")) 46 | elsif includes_key_chain?("raw._links."+method.to_s) 47 | return nil if !self.raw._links[method] 48 | return Desk.get(self.raw._links[method].href.sub(Desk.api_path, "")) 49 | elsif includes_key_chain?("raw."+method.to_s) 50 | return nil if !self.raw[method] 51 | return self.raw[method] 52 | end 53 | return super 54 | end 55 | 56 | def dynamic_cached_method(meth, value) 57 | (class << self; self; end).class_eval do 58 | define_method meth do 59 | instance_variable_set("@#{meth}", value) 60 | end 61 | end 62 | end 63 | 64 | def id(parent_id = false) 65 | id = nil 66 | if includes_key_chain?("raw._links.self.href") || 67 | includes_key_chain?("_links.self.href") 68 | c = self._links.self['class'] 69 | if Desk.respond_to? "#{c}_id" 70 | id = Desk.send("#{c}_id", self._links.self.href, parent_id) 71 | else 72 | p = self._links.self.href.split("/") 73 | if p.size > 5 && !parent_id 74 | id = p[6] 75 | elsif (p.size < 6 && !parent_id) || (p.size > 5 && parent_id) 76 | id = p[4] 77 | end 78 | id = id.to_i if id.to_i != 0 79 | end 80 | end 81 | id 82 | end 83 | 84 | def parent_id 85 | id(true) 86 | end 87 | 88 | def includes_key_chain?(chain) 89 | current_chain = self 90 | chain.split(".").each do |k| 91 | return false if !current_chain.key?(k) 92 | current_chain = current_chain[k] 93 | end 94 | true 95 | end 96 | 97 | def links 98 | Links.new(self._links) if key?("_links") 99 | Links.new(self.raw._links) if includes_key_chain?("raw._links") 100 | end 101 | 102 | def results 103 | self._embedded['entries'] if self.raw.key?('_embedded') && self._embedded.key?('entries') 104 | end 105 | 106 | end 107 | end 108 | -------------------------------------------------------------------------------- /spec/fixtures/cases: -------------------------------------------------------------------------------- 1 | { 2 | "total_entries": 2, 3 | "_links": { 4 | "self": { 5 | "href": "/api/v2/cases?page=1&per_page=30", 6 | "class": "page" 7 | }, 8 | "first": { 9 | "href": "/api/v2/cases?page=1&per_page=30", 10 | "class": "page" 11 | }, 12 | "last": { 13 | "href": "/api/v2/cases?page=1&per_page=30", 14 | "class": "page" 15 | }, 16 | "next": null, 17 | "previous": null 18 | }, 19 | "_embedded": { 20 | "entries": [ 21 | { 22 | "external_id": null, 23 | "subject": "Welcome", 24 | "priority": 5, 25 | "locked_until": null, 26 | "description": null, 27 | "status": "new", 28 | "type": "email", 29 | "language": "en_us", 30 | "created_at": "2013-11-22T22:49:20Z", 31 | "updated_at": "2013-11-22T22:54:20Z", 32 | "active_at": "2013-11-22T22:54:20Z", 33 | "received_at": "2013-11-22T22:49:20Z", 34 | "first_opened_at": "2013-11-22T22:50:20Z", 35 | "opened_at": "2013-11-22T22:51:20Z", 36 | "first_resolved_at": "2013-11-22T22:54:20Z", 37 | "resolved_at": "2013-11-22T22:54:20Z", 38 | "custom_fields": { 39 | "level": "vip" 40 | }, 41 | "_links": { 42 | "self": { 43 | "href": "/api/v2/cases/1", 44 | "class": "case" 45 | }, 46 | "message": { 47 | "href": "/api/v2/cases/1/message", 48 | "class": "message" 49 | }, 50 | "customer": { 51 | "href": "/api/v2/customers/1", 52 | "class": "customer" 53 | }, 54 | "assigned_user": { 55 | "href": "/api/v2/users/2", 56 | "class": "user" 57 | }, 58 | "assigned_group": { 59 | "href": "/api/v2/groups/1", 60 | "class": "group" 61 | }, 62 | "locked_by": null 63 | } 64 | }, 65 | { 66 | "external_id": null, 67 | "subject": "Help Please!", 68 | "priority": 5, 69 | "locked_until": null, 70 | "description": null, 71 | "status": "new", 72 | "type": "email", 73 | "language": "en_us", 74 | "created_at": "2013-11-22T22:49:20Z", 75 | "updated_at": "2013-11-22T22:54:20Z", 76 | "active_at": "2013-11-22T22:54:20Z", 77 | "received_at": "2013-11-22T22:49:20Z", 78 | "first_opened_at": "2013-11-22T22:50:20Z", 79 | "opened_at": "2013-11-22T22:51:20Z", 80 | "first_resolved_at": "2013-11-22T22:54:20Z", 81 | "resolved_at": "2013-11-22T22:54:20Z", 82 | "custom_fields": { 83 | "level": "vip" 84 | }, 85 | "_links": { 86 | "self": { 87 | "href": "/api/v2/cases/2", 88 | "class": "case" 89 | }, 90 | "message": { 91 | "href": "/api/v2/cases/1/message", 92 | "class": "message" 93 | }, 94 | "customer": { 95 | "href": "/api/v2/customers/1", 96 | "class": "customer" 97 | }, 98 | "assigned_user": { 99 | "href": "/api/v2/users/2", 100 | "class": "user" 101 | }, 102 | "assigned_group": { 103 | "href": "/api/v2/groups/1", 104 | "class": "group" 105 | }, 106 | "locked_by": null 107 | } 108 | } 109 | ] 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /spec/fixtures/filter_cases: -------------------------------------------------------------------------------- 1 | { 2 | "total_entries": 2, 3 | "_links": { 4 | "self": { 5 | "href": "/api/v2/cases?page=1&per_page=30", 6 | "class": "page" 7 | }, 8 | "first": { 9 | "href": "/api/v2/cases?page=1&per_page=30", 10 | "class": "page" 11 | }, 12 | "last": { 13 | "href": "/api/v2/cases?page=1&per_page=30", 14 | "class": "page" 15 | }, 16 | "next": null, 17 | "previous": null 18 | }, 19 | "_embedded": { 20 | "entries": [ 21 | { 22 | "external_id": null, 23 | "subject": "Welcome", 24 | "priority": 5, 25 | "locked_until": null, 26 | "description": null, 27 | "status": "new", 28 | "type": "email", 29 | "language": "en_us", 30 | "created_at": "2013-11-22T22:49:20Z", 31 | "updated_at": "2013-11-22T22:54:20Z", 32 | "active_at": "2013-11-22T22:54:20Z", 33 | "received_at": "2013-11-22T22:49:20Z", 34 | "first_opened_at": "2013-11-22T22:50:20Z", 35 | "opened_at": "2013-11-22T22:51:20Z", 36 | "first_resolved_at": "2013-11-22T22:54:20Z", 37 | "resolved_at": "2013-11-22T22:54:20Z", 38 | "custom_fields": { 39 | "level": "vip" 40 | }, 41 | "_links": { 42 | "self": { 43 | "href": "/api/v2/cases/1", 44 | "class": "case" 45 | }, 46 | "message": { 47 | "href": "/api/v2/cases/1/message", 48 | "class": "message" 49 | }, 50 | "customer": { 51 | "href": "/api/v2/customers/1", 52 | "class": "customer" 53 | }, 54 | "assigned_user": { 55 | "href": "/api/v2/users/2", 56 | "class": "user" 57 | }, 58 | "assigned_group": { 59 | "href": "/api/v2/groups/1", 60 | "class": "group" 61 | }, 62 | "locked_by": null 63 | } 64 | }, 65 | { 66 | "external_id": null, 67 | "subject": "Help Please!", 68 | "priority": 5, 69 | "locked_until": null, 70 | "description": null, 71 | "status": "new", 72 | "type": "email", 73 | "language": "en_us", 74 | "created_at": "2013-11-22T22:49:20Z", 75 | "updated_at": "2013-11-22T22:54:20Z", 76 | "active_at": "2013-11-22T22:54:20Z", 77 | "received_at": "2013-11-22T22:49:20Z", 78 | "first_opened_at": "2013-11-22T22:50:20Z", 79 | "opened_at": "2013-11-22T22:51:20Z", 80 | "first_resolved_at": "2013-11-22T22:54:20Z", 81 | "resolved_at": "2013-11-22T22:54:20Z", 82 | "custom_fields": { 83 | "level": "vip" 84 | }, 85 | "_links": { 86 | "self": { 87 | "href": "/api/v2/cases/2", 88 | "class": "case" 89 | }, 90 | "message": { 91 | "href": "/api/v2/cases/1/message", 92 | "class": "message" 93 | }, 94 | "customer": { 95 | "href": "/api/v2/customers/1", 96 | "class": "customer" 97 | }, 98 | "assigned_user": { 99 | "href": "/api/v2/users/2", 100 | "class": "user" 101 | }, 102 | "assigned_group": { 103 | "href": "/api/v2/groups/1", 104 | "class": "group" 105 | }, 106 | "locked_by": null 107 | } 108 | } 109 | ] 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /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 | :auth_method => Desk::Authentication::Methods::BASIC, 34 | :basic_auth_username => 'UN', 35 | :basic_auth_password => 'PW', 36 | :consumer_key => 'CK', 37 | :consumer_secret => 'CS', 38 | :domain => 'example.com', 39 | :oauth_token => 'OT', 40 | :oauth_token_secret => 'OS', 41 | :adapter => :typhoeus, 42 | :format => :xml, 43 | :max_requests => 50, 44 | :proxy => 'http://erik:sekret@proxy.example.com:8080', 45 | :subdomain => 'zencoder', 46 | :support_email => 'help@zencoder.com', 47 | :use_max_requests => true, 48 | :user_agent => 'Custom User Agent', 49 | :version => "amazing", 50 | :logger => double('logger') 51 | } 52 | 53 | @alternative_configuration = { 54 | :consumer_key => 'Louie', 55 | :consumer_secret => 'CounterStrike', 56 | :oauth_token => 'plOT', 57 | :oauth_token_secret => 'OperatingSystem', 58 | :adapter => :sueohpyt, 59 | :format => :json, 60 | :max_requests => 5, 61 | :proxy => 'http://tsuk:public@proxy.example.com:8080', 62 | :subdomain => 'stresscoder', 63 | :support_email => 'problem@stresscoder.com', 64 | :use_max_requests => false, 65 | :user_agent => 'Generic User Agent', 66 | :version => "boring", 67 | :logger => double('logger') 68 | } 69 | end 70 | 71 | context "during initialization" 72 | 73 | it "should override module configuration" do 74 | api = Desk::API.new(@configuration) 75 | @keys.each do |key| 76 | api.send(key).should == @configuration[key] 77 | end 78 | end 79 | 80 | context "after initilization" do 81 | 82 | it "should override module configuration after initialization" do 83 | api = Desk::API.new 84 | @configuration.each do |key, value| 85 | api.send("#{key}=", value) 86 | end 87 | @keys.each do |key| 88 | api.send(key).should == @configuration[key] 89 | end 90 | end 91 | 92 | it 'should keep different configurations for each thread' do 93 | Thread.new do 94 | api = Desk::API.new 95 | @configuration.each do |key, value| 96 | api.send("#{key}=", value) 97 | end 98 | 99 | Thread.new do 100 | alt_api = Desk::API.new 101 | @alternative_configuration.each do |key, value| 102 | alt_api.send("#{key}=", value) 103 | end 104 | end.join 105 | 106 | @configuration.each do |key, value| 107 | api.send(key).should eq(value) 108 | end 109 | end.join 110 | end 111 | end 112 | end 113 | end 114 | end 115 | -------------------------------------------------------------------------------- /spec/fixtures/cases_search: -------------------------------------------------------------------------------- 1 | { 2 | "total_entries": 4, 3 | "_links": { 4 | "self": { 5 | "href": "/api/v2/cases/search?subject=please+help&name=jimmy&page=1&per_page=2", 6 | "class": "page" 7 | }, 8 | "first": { 9 | "href": "/api/v2/cases/search?subject=please+help&name=jimmy&page=1&per_page=2", 10 | "class": "page" 11 | }, 12 | "last": { 13 | "href": "/api/v2/cases/search?subject=please+help&name=jimmy&page=2&per_page=2", 14 | "class": "page" 15 | }, 16 | "next": { 17 | "href": "/api/v2/cases/search?subject=please+help&name=jimmy&page=2&per_page=2" 18 | }, 19 | "previous": null 20 | }, 21 | "_embedded": { 22 | "entries": [ 23 | { 24 | "external_id": null, 25 | "subject": "Welcome", 26 | "priority": 5, 27 | "locked_until": null, 28 | "description": null, 29 | "status": "new", 30 | "type": "email", 31 | "language": "en_us", 32 | "created_at": "2013-11-22T22:49:20Z", 33 | "updated_at": "2013-11-22T22:54:20Z", 34 | "active_at": "2013-11-22T22:54:20Z", 35 | "received_at": "2013-11-22T22:49:20Z", 36 | "first_opened_at": "2013-11-22T22:50:20Z", 37 | "opened_at": "2013-11-22T22:51:20Z", 38 | "first_resolved_at": "2013-11-22T22:54:20Z", 39 | "resolved_at": "2013-11-22T22:54:20Z", 40 | "custom_fields": { 41 | "level": "vip" 42 | }, 43 | "_links": { 44 | "self": { 45 | "href": "/api/v2/cases/1", 46 | "class": "case" 47 | }, 48 | "message": { 49 | "href": "/api/v2/cases/1/message", 50 | "class": "message" 51 | }, 52 | "customer": { 53 | "href": "/api/v2/customers/1", 54 | "class": "customer" 55 | }, 56 | "assigned_user": { 57 | "href": "/api/v2/users/2", 58 | "class": "user" 59 | }, 60 | "assigned_group": { 61 | "href": "/api/v2/groups/1", 62 | "class": "group" 63 | }, 64 | "locked_by": null 65 | } 66 | }, 67 | { 68 | "external_id": null, 69 | "subject": "Help Please!", 70 | "priority": 5, 71 | "locked_until": null, 72 | "description": null, 73 | "status": "new", 74 | "type": "email", 75 | "language": "en_us", 76 | "created_at": "2013-11-22T22:49:20Z", 77 | "updated_at": "2013-11-22T22:54:20Z", 78 | "active_at": "2013-11-22T22:54:20Z", 79 | "received_at": "2013-11-22T22:49:20Z", 80 | "first_opened_at": "2013-11-22T22:50:20Z", 81 | "opened_at": "2013-11-22T22:51:20Z", 82 | "first_resolved_at": "2013-11-22T22:54:20Z", 83 | "resolved_at": "2013-11-22T22:54:20Z", 84 | "custom_fields": { 85 | "level": "vip" 86 | }, 87 | "_links": { 88 | "self": { 89 | "href": "/api/v2/cases/2", 90 | "class": "case" 91 | }, 92 | "message": { 93 | "href": "/api/v2/cases/1/message", 94 | "class": "message" 95 | }, 96 | "customer": { 97 | "href": "/api/v2/customers/1", 98 | "class": "customer" 99 | }, 100 | "assigned_user": { 101 | "href": "/api/v2/users/2", 102 | "class": "user" 103 | }, 104 | "assigned_group": { 105 | "href": "/api/v2/groups/1", 106 | "class": "group" 107 | }, 108 | "locked_by": null 109 | } 110 | } 111 | ] 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /spec/fixtures/articles: -------------------------------------------------------------------------------- 1 | { 2 | "total_entries": 2, 3 | "_links": { 4 | "self": { 5 | "href": "/api/v2/articles?page=1&per_page=30", 6 | "class": "page" 7 | }, 8 | "first": { 9 | "href": "/api/v2/articles?page=1&per_page=30", 10 | "class": "page" 11 | }, 12 | "last": { 13 | "href": "/api/v2/articles?page=1&per_page=30", 14 | "class": "page" 15 | }, 16 | "next": null, 17 | "previous": null 18 | }, 19 | "_embedded": { 20 | "entries": [ 21 | { 22 | "subject": "Awesome Subject", 23 | "body": "Awesome apples
", 24 | "body_email": "Email for Awesome apples", 25 | "body_email_auto": false, 26 | "body_chat": "Awesome apples", 27 | "body_chat_auto": true, 28 | "body_web_callback": "Awesome Apples
", 29 | "body_web_callback_auto": false, 30 | "body_twitter": "Awesome apples", 31 | "body_twitter_auto": true, 32 | "body_qna": "Awesome apples", 33 | "body_qna_auto": true, 34 | "body_phone": "Awesome apples", 35 | "body_phone_auto": true, 36 | "body_facebook": "Awesome apples", 37 | "body_facebook_auto": true, 38 | "rating": 75, 39 | "rating_count": 4, 40 | "rating_score": 3, 41 | "position": 1, 42 | "quickcode": "AWESOME", 43 | "in_support_center": true, 44 | "internal_notes": "Notes to the agent here", 45 | "publish_at": "2013-11-12T18:07:34Z", 46 | "created_at": "2013-11-12T18:02:34Z", 47 | "updated_at": "2013-11-12T18:07:34Z", 48 | "_links": { 49 | "self": { 50 | "href": "/api/v2/articles/1", 51 | "class": "article" 52 | }, 53 | "topic": { 54 | "href": "/api/v2/topics/1", 55 | "class": "topic" 56 | }, 57 | "translations": { 58 | "href": "/api/v2/articles/1/translations", 59 | "class": "article_translation" 60 | }, 61 | "created_by": { 62 | "href": "/api/v2/users/1", 63 | "class": "user" 64 | }, 65 | "updated_by": { 66 | "href": "/api/v2/users/1", 67 | "class": "user" 68 | } 69 | } 70 | }, 71 | { 72 | "subject": "How to make your customers happy", 73 | "body": "Use Desk.com", 74 | "body_email": "Email just doesn't cut it", 75 | "body_email_auto": false, 76 | "body_chat": "Use Desk.com", 77 | "body_chat_auto": true, 78 | "body_web_callback": "Use Desk.com", 79 | "body_web_callback_auto": false, 80 | "body_twitter": "Use Desk.com in 140 chars or less", 81 | "body_twitter_auto": false, 82 | "body_qna": "Use Desk.com", 83 | "body_qna_auto": true, 84 | "body_phone": "Use Desk.com", 85 | "body_phone_auto": true, 86 | "body_facebook": "Use Desk.com", 87 | "body_facebook_auto": true, 88 | "rating": 75, 89 | "rating_count": 4, 90 | "rating_score": 3, 91 | "position": 1, 92 | "quickcode": "AWESOME", 93 | "in_support_center": true, 94 | "internal_notes": "Notes to the agent here", 95 | "publish_at": "2013-11-12T18:07:34Z", 96 | "created_at": "2013-11-12T18:02:34Z", 97 | "updated_at": "2013-11-12T18:07:34Z", 98 | "_links": { 99 | "self": { 100 | "href": "/api/v2/articles/2", 101 | "class": "article" 102 | }, 103 | "topic": { 104 | "href": "/api/v2/topics/1", 105 | "class": "topic" 106 | }, 107 | "translations": { 108 | "href": "/api/v2/articles/2/translations", 109 | "class": "article_translation" 110 | }, 111 | "created_by": { 112 | "href": "/api/v2/users/1", 113 | "class": "user" 114 | }, 115 | "updated_by": { 116 | "href": "/api/v2/users/1", 117 | "class": "user" 118 | } 119 | } 120 | } 121 | ] 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /spec/fixtures/customers: -------------------------------------------------------------------------------- 1 | { 2 | "total_entries": 2, 3 | "_links": { 4 | "self": { 5 | "href": "/api/v2/customers?page=1&per_page=30", 6 | "class": "page" 7 | }, 8 | "first": { 9 | "href": "/api/v2/customers?page=1&per_page=30", 10 | "class": "page" 11 | }, 12 | "last": { 13 | "href": "/api/v2/customers?page=1&per_page=30", 14 | "class": "page" 15 | }, 16 | "next": null, 17 | "previous": null 18 | }, 19 | "_embedded": { 20 | "entries": [ 21 | { 22 | "first_name": "John", 23 | "last_name": "Doe", 24 | "company": "ACME, Inc", 25 | "title": "Senior Ninja", 26 | "external_id": null, 27 | "background": "This guy can be a challenge to work with", 28 | "language": "en_us", 29 | "locked_until": null, 30 | "created_at": "2013-11-22T22:49:20Z", 31 | "updated_at": "2013-11-22T22:49:20Z", 32 | "custom_fields": { 33 | "level": "vip" 34 | }, 35 | "emails": [ 36 | { 37 | "type": "work", 38 | "value": "john@acme.com" 39 | }, 40 | { 41 | "type": "home", 42 | "value": "john@home.com" 43 | } 44 | ], 45 | "phone_numbers": [ 46 | { 47 | "type": "work", 48 | "value": "123-456-7890" 49 | } 50 | ], 51 | "addresses": [ 52 | { 53 | "type": "work", 54 | "value": "123 Main St, San Francisco, CA 94105" 55 | } 56 | ], 57 | "_links": { 58 | "self": { 59 | "href": "/api/v2/customers/1", 60 | "class": "customer" 61 | }, 62 | "cases": { 63 | "href": "/api/v2/customers/1/cases", 64 | "class": "case" 65 | }, 66 | "company": { 67 | "href": "/api/v2/companies/1", 68 | "class": "company" 69 | }, 70 | "facebook_user": { 71 | "href": "/api/v2/facebook_users/1", 72 | "class": "facebook_user" 73 | }, 74 | "twitter_user": { 75 | "href": "/api/v2/twitter_users/1", 76 | "class": "twitter_user" 77 | }, 78 | "locked_by": null 79 | } 80 | }, 81 | { 82 | "first_name": "Bob", 83 | "last_name": "Doe", 84 | "company": "ACME, Inc", 85 | "title": "Senior Ninja", 86 | "external_id": null, 87 | "background": "Easy to work with", 88 | "language": "en_us", 89 | "locked_until": null, 90 | "created_at": "2013-11-22T22:49:20Z", 91 | "updated_at": "2013-11-22T22:49:20Z", 92 | "custom_fields": { 93 | "level": "vip" 94 | }, 95 | "emails": [ 96 | { 97 | "type": "work", 98 | "value": "bob@acme.com" 99 | }, 100 | { 101 | "type": "home", 102 | "value": "bob@home.com" 103 | } 104 | ], 105 | "phone_numbers": [ 106 | { 107 | "type": "work", 108 | "value": "123-456-7890" 109 | } 110 | ], 111 | "addresses": [ 112 | { 113 | "type": "work", 114 | "value": "123 Main St, San Francisco, CA 94105" 115 | } 116 | ], 117 | "_links": { 118 | "self": { 119 | "href": "/api/v2/customers/2", 120 | "class": "customer" 121 | }, 122 | "cases": { 123 | "href": "/api/v2/customers/1/cases", 124 | "class": "case" 125 | }, 126 | "company": { 127 | "href": "/api/v2/companies/1", 128 | "class": "company" 129 | }, 130 | "facebook_user": { 131 | "href": "/api/v2/facebook_users/1", 132 | "class": "facebook_user" 133 | }, 134 | "twitter_user": { 135 | "href": "/api/v2/twitter_users/1", 136 | "class": "twitter_user" 137 | }, 138 | "locked_by": null 139 | } 140 | } 141 | ] 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /spec/fixtures/customers_search: -------------------------------------------------------------------------------- 1 | { 2 | "total_entries": 2, 3 | "_links": { 4 | "self": { 5 | "href": "/api/v2/customers?page=1&per_page=30", 6 | "class": "page" 7 | }, 8 | "first": { 9 | "href": "/api/v2/customers?page=1&per_page=30", 10 | "class": "page" 11 | }, 12 | "last": { 13 | "href": "/api/v2/customers?page=1&per_page=30", 14 | "class": "page" 15 | }, 16 | "next": null, 17 | "previous": null 18 | }, 19 | "_embedded": { 20 | "entries": [ 21 | { 22 | "first_name": "John", 23 | "last_name": "Doe", 24 | "company": "ACME, Inc", 25 | "title": "Senior Ninja", 26 | "external_id": null, 27 | "background": "This guy can be a challenge to work with", 28 | "language": "en_us", 29 | "locked_until": null, 30 | "created_at": "2013-11-22T22:49:20Z", 31 | "updated_at": "2013-11-22T22:49:20Z", 32 | "custom_fields": { 33 | "level": "vip" 34 | }, 35 | "emails": [ 36 | { 37 | "type": "work", 38 | "value": "john@acme.com" 39 | }, 40 | { 41 | "type": "home", 42 | "value": "john@home.com" 43 | } 44 | ], 45 | "phone_numbers": [ 46 | { 47 | "type": "work", 48 | "value": "123-456-7890" 49 | } 50 | ], 51 | "addresses": [ 52 | { 53 | "type": "work", 54 | "value": "123 Main St, San Francisco, CA 94105" 55 | } 56 | ], 57 | "_links": { 58 | "self": { 59 | "href": "/api/v2/customers/1", 60 | "class": "customer" 61 | }, 62 | "cases": { 63 | "href": "/api/v2/customers/1/cases", 64 | "class": "case" 65 | }, 66 | "company": { 67 | "href": "/api/v2/companies/1", 68 | "class": "company" 69 | }, 70 | "facebook_user": { 71 | "href": "/api/v2/facebook_users/1", 72 | "class": "facebook_user" 73 | }, 74 | "twitter_user": { 75 | "href": "/api/v2/twitter_users/1", 76 | "class": "twitter_user" 77 | }, 78 | "locked_by": null 79 | } 80 | }, 81 | { 82 | "first_name": "Bob", 83 | "last_name": "Doe", 84 | "company": "ACME, Inc", 85 | "title": "Senior Ninja", 86 | "external_id": null, 87 | "background": "Easy to work with", 88 | "language": "en_us", 89 | "locked_until": null, 90 | "created_at": "2013-11-22T22:49:20Z", 91 | "updated_at": "2013-11-22T22:49:20Z", 92 | "custom_fields": { 93 | "level": "vip" 94 | }, 95 | "emails": [ 96 | { 97 | "type": "work", 98 | "value": "bob@acme.com" 99 | }, 100 | { 101 | "type": "home", 102 | "value": "bob@home.com" 103 | } 104 | ], 105 | "phone_numbers": [ 106 | { 107 | "type": "work", 108 | "value": "123-456-7890" 109 | } 110 | ], 111 | "addresses": [ 112 | { 113 | "type": "work", 114 | "value": "123 Main St, San Francisco, CA 94105" 115 | } 116 | ], 117 | "_links": { 118 | "self": { 119 | "href": "/api/v2/customers/2", 120 | "class": "customer" 121 | }, 122 | "cases": { 123 | "href": "/api/v2/customers/1/cases", 124 | "class": "case" 125 | }, 126 | "company": { 127 | "href": "/api/v2/companies/1", 128 | "class": "company" 129 | }, 130 | "facebook_user": { 131 | "href": "/api/v2/facebook_users/1", 132 | "class": "facebook_user" 133 | }, 134 | "twitter_user": { 135 | "href": "/api/v2/twitter_users/1", 136 | "class": "twitter_user" 137 | }, 138 | "locked_by": null 139 | } 140 | } 141 | ] 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /spec/fixtures/articles_search: -------------------------------------------------------------------------------- 1 | { 2 | "total_entries": 2, 3 | "_links": { 4 | "self": { 5 | "href": "/api/v2/articles?page=1&per_page=30", 6 | "class": "page" 7 | }, 8 | "first": { 9 | "href": "/api/v2/articles?page=1&per_page=30", 10 | "class": "page" 11 | }, 12 | "last": { 13 | "href": "/api/v2/articles?page=1&per_page=30", 14 | "class": "page" 15 | }, 16 | "next": null, 17 | "previous": null 18 | }, 19 | "_embedded": { 20 | "entries": [ 21 | { 22 | "subject": "Awesome Subject", 23 | "body": "Awesome apples
", 24 | "body_email": "Email for Awesome apples", 25 | "body_email_auto": false, 26 | "body_chat": "Awesome apples", 27 | "body_chat_auto": true, 28 | "body_web_callback": "Awesome Apples
", 29 | "body_web_callback_auto": false, 30 | "body_twitter": "Awesome apples", 31 | "body_twitter_auto": true, 32 | "body_qna": "Awesome apples", 33 | "body_qna_auto": true, 34 | "body_phone": "Awesome apples", 35 | "body_phone_auto": true, 36 | "body_facebook": "Awesome apples", 37 | "body_facebook_auto": true, 38 | "rating": 75, 39 | "rating_count": 4, 40 | "rating_score": 3, 41 | "position": 1, 42 | "quickcode": "AWESOME", 43 | "in_support_center": true, 44 | "internal_notes": "Notes to the agent here", 45 | "publish_at": "2013-11-22T22:54:20Z", 46 | "created_at": "2013-11-22T22:49:20Z", 47 | "updated_at": "2013-11-22T22:54:20Z", 48 | "_links": { 49 | "self": { 50 | "href": "/api/v2/articles/1", 51 | "class": "article" 52 | }, 53 | "topic": { 54 | "href": "/api/v2/topics/1", 55 | "class": "topic" 56 | }, 57 | "translations": { 58 | "href": "/api/v2/articles/1/translations", 59 | "class": "article_translation" 60 | }, 61 | "created_by": { 62 | "href": "/api/v2/users/1", 63 | "class": "user" 64 | }, 65 | "updated_by": { 66 | "href": "/api/v2/users/1", 67 | "class": "user" 68 | } 69 | } 70 | }, 71 | { 72 | "subject": "How to make your customers happy", 73 | "body": "Use Desk.com", 74 | "body_email": "Email just doesn't cut it", 75 | "body_email_auto": false, 76 | "body_chat": "Use Desk.com", 77 | "body_chat_auto": true, 78 | "body_web_callback": "Use Desk.com", 79 | "body_web_callback_auto": false, 80 | "body_twitter": "Use Desk.com in 140 chars or less", 81 | "body_twitter_auto": false, 82 | "body_qna": "Use Desk.com", 83 | "body_qna_auto": true, 84 | "body_phone": "Use Desk.com", 85 | "body_phone_auto": true, 86 | "body_facebook": "Use Desk.com", 87 | "body_facebook_auto": true, 88 | "rating": 75, 89 | "rating_count": 4, 90 | "rating_score": 3, 91 | "position": 1, 92 | "quickcode": "AWESOME", 93 | "in_support_center": true, 94 | "internal_notes": "Notes to the agent here", 95 | "publish_at": "2013-11-22T22:54:20Z", 96 | "created_at": "2013-11-22T22:49:20Z", 97 | "updated_at": "2013-11-22T22:54:20Z", 98 | "_links": { 99 | "self": { 100 | "href": "/api/v2/articles/1", 101 | "class": "article" 102 | }, 103 | "topic": { 104 | "href": "/api/v2/topics/1", 105 | "class": "topic" 106 | }, 107 | "translations": { 108 | "href": "/api/v2/articles/1/translations", 109 | "class": "article_translation" 110 | }, 111 | "created_by": { 112 | "href": "/api/v2/users/1", 113 | "class": "user" 114 | }, 115 | "updated_by": { 116 | "href": "/api/v2/users/1", 117 | "class": "user" 118 | } 119 | }, 120 | "body_text": "Use Desk.com" 121 | } 122 | ] 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /TRANSITION.mkd: -------------------------------------------------------------------------------- 1 | Desk.com API v1 to v2 transition guide 2 | ====================================== 3 | 4 | The API v2 endpoints and responses are both highly standardized and 5 | significantly different compared to v1 of the API. As a result the Desk.com Ruby 6 | gem was almost entirely rewritten to take advantage of the changes. An 7 | unfortunate side effect is that likely all projects upgrading from v1 to v2 will 8 | need to be rewritten to some extent. 9 | 10 | Below are some ideas and tips for utilizing the new features as well as 11 | transition guides for the original endpoints. 12 | 13 | General Pagination 14 | ------------------ 15 | 16 | Previously, to paginate through results (like all cases) you would need to do 17 | something similar to the following: 18 | 19 | page = 1 20 | while page > 0 do 21 | results = Desk.cases(:count => 10, :page => page) 22 | # Do something with results 23 | if page*10 >= results.total 24 | page = 0 25 | else 26 | page += 1 27 | end 28 | end 29 | 30 | Now, utilizing the new "\_links" callbacks (see the main README for more 31 | information), pagination can be done like: 32 | 33 | results = Desk.list_cases 34 | while results 35 | # Do something with results 36 | results = results.next 37 | end 38 | 39 | Result objects 40 | -------------- 41 | 42 | Previously, the raw results from the API were returned as a Hashie object. This 43 | required your code to dig down like: 44 | 45 | r = Desk.cases(:count => 5) 46 | r.results.each do |c| 47 | theActualCaseObject = c.case 48 | # Do something with theActualCaseObject 49 | end 50 | 51 | r = Desk.case(12345) 52 | theActualCaseObject = r.case 53 | # Do soemthing with theActualCaseObject 54 | 55 | Now, the object returned (an extended Hashie object of type Deash) gives root 56 | level access to the endpoint fields. This allows for: 57 | 58 | Desk.cases(:per_page => 5).each do |theCase| 59 | # Do something with theCase 60 | end 61 | 62 | theCase = Desk.case(12345) 63 | # Do soemthing with theCase 64 | 65 | For more information see the specific endpoint examples below. 66 | 67 | Cases & Interaction endpoint examples 68 | ------------------------------------- 69 | 70 | Previously cases and interactions were seperate endpoints. Additionally all 71 | interactions were returned with no direct access to a specific interaction, the 72 | original message or notes. Just to get, for example, the original interaction 73 | for all cases assigned to a user your code likely looked something like this: 74 | 75 | r = Desk.cases(:assigned_user => "joe") 76 | r.results.each do |c| 77 | in = Desk.interactions(:case_id => c.case.case_id) 78 | in.results.each do |i| 79 | if( i.interaction.basis == "original" ) 80 | i.interaction.interactionable.each do |interaction_type, interaction| 81 | case interaction_type 82 | when "email" 83 | puts "Message: #{interaction.body} 84 | when "tweet" 85 | puts "Message: #{interaction.subject}" 86 | when "chat" 87 | puts "Message: #{interaction.messages.first.message.text}" 88 | end 89 | end 90 | end 91 | end 92 | end 93 | 94 | Now, because of standardized fields and responses along with the _links 95 | callbacks, the above can be simplified to: 96 | 97 | Desk.cases(:assigned_user => "joe").each do |c| 98 | puts "Message: #{c.message.body}" 99 | end 100 | 101 | For more information on accessing case messages, replies, notes, attachments and 102 | history site the README and http://dev.desk.com/API/cases/ 103 | 104 | Customer endpoint examples 105 | -------------------------- 106 | 107 | The redundancy for customers is gone as well. Email addresses, phone numbers and 108 | addresses are no longer burried and are all accessible in a uniform way. So, with 109 | API v1, what looked like: 110 | 111 | customers = Desk.customers(:since_created_at => 1279139906) 112 | customers.results.each do |customer| 113 | puts "#{customer.customer.first_name} #{customer.customer.last_name}" 114 | customer.customer.addresses.each do |address| 115 | puts address.address.location 116 | end 117 | customer.customer.phones.each do |phone| 118 | # seriously, this is correct, but it feels a bit excessive 119 | puts phone.phone.phone 120 | end 121 | customer.customer.twitters.each do |twitter| 122 | puts twitter.twitter.login 123 | end 124 | end 125 | 126 | using the API v2 looks like: 127 | 128 | Desk.customers(:since_created_at => 1279139906).each do |customer| 129 | puts "#{customer.first_name} #{customer.last_name}" 130 | customer.addresses.each { |address| puts address.value } 131 | customer.phone_numbers.each { |phone| puts phone.value } 132 | puts customer.twitter_user.handle 133 | end 134 | -------------------------------------------------------------------------------- /spec/fixtures/user_preferences: -------------------------------------------------------------------------------- 1 | { 2 | "total_entries": 14, 3 | "_links": { 4 | "self": { 5 | "href": "/api/v2/users/1/preferences?page=1&per_page=50", 6 | "class": "page" 7 | }, 8 | "first": { 9 | "href": "/api/v2/users/1/preferences?page=1&per_page=50", 10 | "class": "page" 11 | }, 12 | "last": { 13 | "href": "/api/v2/users/1/preferences?page=1&per_page=50", 14 | "class": "page" 15 | }, 16 | "previous": null, 17 | "next": null 18 | }, 19 | "_embedded": { 20 | "entries": [ 21 | { 22 | "name": "enable_routing_filter_on_login", 23 | "value": "12", 24 | "_links": { 25 | "self": { 26 | "href": "/api/v2/users/1/preferences/1", 27 | "class": "user_preference" 28 | }, 29 | "user": { 30 | "href": "/api/v2/users/1", 31 | "class": "user" 32 | }, 33 | "filter": { 34 | "href": "/api/v2/filters/12", 35 | "class": "filter" 36 | } 37 | } 38 | }, 39 | { 40 | "name": "scroll_to_bottom_on_open", 41 | "value": "1", 42 | "_links": { 43 | "self": { 44 | "href": "/api/v2/users/1/preferences/2", 45 | "class": "user_preference" 46 | }, 47 | "user": { 48 | "href": "/api/v2/users/1", 49 | "class": "user" 50 | } 51 | } 52 | }, 53 | { 54 | "name": "auto_accept_on_route", 55 | "value": false, 56 | "_links": { 57 | "self": { 58 | "href": "/api/v2/users/1/preferences/3", 59 | "class": "user_preference" 60 | }, 61 | "user": { 62 | "href": "/api/v2/users/1", 63 | "class": "user" 64 | } 65 | } 66 | }, 67 | { 68 | "name": "case_filter_view", 69 | "value": "200", 70 | "_links": { 71 | "self": { 72 | "href": "/api/v2/users/1/preferences/4", 73 | "class": "user_preference" 74 | }, 75 | "user": { 76 | "href": "/api/v2/users/1", 77 | "class": "user" 78 | } 79 | } 80 | }, 81 | { 82 | "name": "warn_onbeforeunload", 83 | "value": false, 84 | "_links": { 85 | "self": { 86 | "href": "/api/v2/users/1/preferences/5", 87 | "class": "user_preference" 88 | }, 89 | "user": { 90 | "href": "/api/v2/users/1", 91 | "class": "user" 92 | } 93 | } 94 | }, 95 | { 96 | "name": "screenpop_sound", 97 | "value": "80", 98 | "_links": { 99 | "self": { 100 | "href": "/api/v2/users/1/preferences/6", 101 | "class": "user_preference" 102 | }, 103 | "user": { 104 | "href": "/api/v2/users/1", 105 | "class": "user" 106 | } 107 | } 108 | }, 109 | { 110 | "name": "screenpop_sound_repeat_enabled", 111 | "value": false, 112 | "_links": { 113 | "self": { 114 | "href": "/api/v2/users/1/preferences/7", 115 | "class": "user_preference" 116 | }, 117 | "user": { 118 | "href": "/api/v2/users/1", 119 | "class": "user" 120 | } 121 | } 122 | }, 123 | { 124 | "name": "inactivity_sound", 125 | "value": "10", 126 | "_links": { 127 | "self": { 128 | "href": "/api/v2/users/1/preferences/8", 129 | "class": "user_preference" 130 | }, 131 | "user": { 132 | "href": "/api/v2/users/1", 133 | "class": "user" 134 | } 135 | } 136 | }, 137 | { 138 | "name": "inactivity_sound_repeat_enabled", 139 | "value": false, 140 | "_links": { 141 | "self": { 142 | "href": "/api/v2/users/1/preferences/9", 143 | "class": "user_preference" 144 | }, 145 | "user": { 146 | "href": "/api/v2/users/1", 147 | "class": "user" 148 | } 149 | } 150 | }, 151 | { 152 | "name": "new_chat_message_sound", 153 | "value": "70", 154 | "_links": { 155 | "self": { 156 | "href": "/api/v2/users/1/preferences/10", 157 | "class": "user_preference" 158 | }, 159 | "user": { 160 | "href": "/api/v2/users/1", 161 | "class": "user" 162 | } 163 | } 164 | }, 165 | { 166 | "name": "confirm_on_short_replies", 167 | "value": true, 168 | "_links": { 169 | "self": { 170 | "href": "/api/v2/users/1/preferences/11", 171 | "class": "user_preference" 172 | }, 173 | "user": { 174 | "href": "/api/v2/users/1", 175 | "class": "user" 176 | } 177 | } 178 | }, 179 | { 180 | "name": "case_default_view", 181 | "value": "-1", 182 | "_links": { 183 | "self": { 184 | "href": "/api/v2/users/1/preferences/12", 185 | "class": "user_preference" 186 | }, 187 | "user": { 188 | "href": "/api/v2/users/1", 189 | "class": "user" 190 | } 191 | } 192 | }, 193 | { 194 | "name": "show_labels_in_case_filter_view", 195 | "value": true, 196 | "_links": { 197 | "self": { 198 | "href": "/api/v2/users/1/preferences/13", 199 | "class": "user_preference" 200 | }, 201 | "user": { 202 | "href": "/api/v2/users/1", 203 | "class": "user" 204 | } 205 | } 206 | }, 207 | { 208 | "name": "bulk_case_filter_manage_confirm", 209 | "value": true, 210 | "_links": { 211 | "self": { 212 | "href": "/api/v2/users/1/preferences/14", 213 | "class": "user_preference" 214 | }, 215 | "user": { 216 | "href": "/api/v2/users/1", 217 | "class": "user" 218 | } 219 | } 220 | } 221 | ] 222 | } 223 | } 224 | -------------------------------------------------------------------------------- /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 ".auth_method" do 49 | it "should return the default auth method" do 50 | Desk.auth_method = Desk::Configuration::DEFAULT_AUTH_METHOD 51 | end 52 | end 53 | 54 | describe ".auth_method=" do 55 | it "should set the auth_method for all auth types" do 56 | valid_methods = Desk::Authentication::Methods::ALL*5 57 | valid_methods.each do |method| 58 | Desk.auth_method = method 59 | Desk.auth_method.should == method 60 | end 61 | end 62 | end 63 | 64 | describe ".domain" do 65 | it "should return the default domain" do 66 | Desk.domain.should == Desk::Configuration::DEFAULT_DOMAIN 67 | end 68 | end 69 | 70 | describe ".domain=" do 71 | before do 72 | Desk.domain = "example.org" 73 | end 74 | 75 | it "should set the domain" do 76 | Desk.domain.should == "example.org" 77 | end 78 | 79 | it "should change the endpoint" do 80 | Desk.endpoint.should == "https://#{Desk::Configuration::DEFAULT_SUBDOMAIN}.example.org/api/#{Desk::Configuration::DEFAULT_VERSION}/" 81 | end 82 | end 83 | 84 | describe ".subdomain=" do 85 | before do 86 | Desk.subdomain = "zencoder" 87 | end 88 | 89 | it "should set the subdomain" do 90 | Desk.subdomain.should == "zencoder" 91 | end 92 | 93 | it "should change the endpoint" do 94 | Desk.endpoint.should == "https://zencoder.#{Desk::Configuration::DEFAULT_DOMAIN}/api/#{Desk::Configuration::DEFAULT_VERSION}/" 95 | end 96 | end 97 | 98 | describe ".support_email" do 99 | it "should return the default support_email" do 100 | Desk.support_email.should == Desk::Configuration::DEFAULT_SUPPORT_EMAIL 101 | end 102 | end 103 | 104 | describe ".support_email=" do 105 | it "should set the support_email" do 106 | Desk.support_email = "help@example.com" 107 | Desk.support_email.should == "help@example.com" 108 | end 109 | end 110 | 111 | describe ".timeout" do 112 | it "should return the default timeout" do 113 | Desk.timeout.should == Desk::Configuration::DEFAULT_TIMEOUT 114 | end 115 | end 116 | 117 | describe ".timeout=" do 118 | it "should set the timeout" do 119 | Desk.timeout = 30 120 | Desk.timeout.should == 30 121 | end 122 | end 123 | 124 | describe ".version=" do 125 | before do 126 | Desk.version = "v4" 127 | end 128 | 129 | it "should set the version" do 130 | Desk.version.should == "v4" 131 | end 132 | 133 | it "should change the endpoint" do 134 | Desk.endpoint.should == "https://#{Desk::Configuration::DEFAULT_SUBDOMAIN}.desk.com/api/v4/" 135 | end 136 | end 137 | 138 | describe ".format" do 139 | it "should return the default format" do 140 | Desk.format.should == Desk::Configuration::DEFAULT_FORMAT 141 | end 142 | end 143 | 144 | describe ".format=" do 145 | it "should set the format" do 146 | Desk.format = 'xml' 147 | Desk.format.should == 'xml' 148 | end 149 | end 150 | 151 | describe ".max_requests" do 152 | it "should return the default max requests" do 153 | Desk.max_requests.should == Desk::Configuration::DEFAULT_MAX_REQUESTS 154 | end 155 | end 156 | 157 | describe ".max_requests=" do 158 | it "should set the max_requests" do 159 | Desk.max_requests = 50 160 | Desk.max_requests.should == 50 161 | end 162 | end 163 | 164 | describe ".use_max_requests" do 165 | it "should return the default max requests flag" do 166 | Desk.use_max_requests.should == Desk::Configuration::DEFAULT_USE_MAX_REQUESTS 167 | end 168 | end 169 | 170 | describe ".use_max_requests=" do 171 | it "should set the use_max_requests flag" do 172 | Desk.max_requests = true 173 | Desk.max_requests.should == true 174 | end 175 | end 176 | 177 | describe ".user_agent" do 178 | it "should return the default user agent" do 179 | Desk.user_agent.should == Desk::Configuration::DEFAULT_USER_AGENT 180 | end 181 | end 182 | 183 | describe ".user_agent=" do 184 | it "should set the user_agent" do 185 | Desk.user_agent = 'Custom User Agent' 186 | Desk.user_agent.should == 'Custom User Agent' 187 | end 188 | end 189 | 190 | describe ".configure" do 191 | 192 | Desk::Configuration::VALID_OPTIONS_KEYS.each do |key| 193 | 194 | it "should set the #{key}" do 195 | Desk.configure do |config| 196 | config.send("#{key}=", key) 197 | Desk.send(key).should == key 198 | end 199 | end 200 | end 201 | end 202 | 203 | describe ".counter" do 204 | before do 205 | Desk.counter = 0 206 | stub_get("cases"). 207 | to_return(:body => fixture("cases"), :headers => {:content_type => "application/json; charset=utf-8"}) 208 | end 209 | 210 | it "should be 0 in the beginning" do 211 | Desk.counter.should == 0 212 | end 213 | 214 | it "should count the requests" do 215 | 5.times { 216 | Desk.cases 217 | } 218 | Desk.counter.should == 5 219 | end 220 | 221 | context "max requests enabled" do 222 | before do 223 | Desk.use_max_requests = true 224 | end 225 | 226 | it "should only allow 60 requests" do 227 | expect { 228 | 70.times { 229 | Desk.cases 230 | } 231 | }.to raise_error 232 | end 233 | 234 | it "should only allow defined requests" do 235 | Desk.max_requests = 50 236 | expect { 237 | 55.times { 238 | Desk.cases 239 | } 240 | }.to raise_error 241 | end 242 | 243 | def make_request 244 | Desk.cases 245 | rescue Desk::TooManyRequests 246 | sleep(5) 247 | make_request 248 | end 249 | 250 | xit "should allow more requests after minute has passed" do 251 | 70.times { 252 | make_request 253 | } 254 | Desk.counter.should == 10 255 | end 256 | end 257 | end 258 | 259 | describe ".minute" do 260 | before do 261 | Desk.minute = Time.now.min 262 | end 263 | 264 | it "should be the current minute" do 265 | Desk.minute.should == Time.now.min 266 | end 267 | end 268 | end 269 | -------------------------------------------------------------------------------- /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/article' 11 | require 'desk/client/brand' 12 | require 'desk/client/case' 13 | require 'desk/client/company' 14 | require 'desk/client/custom_field' 15 | require 'desk/client/customer' 16 | require 'desk/client/facebook_user' 17 | require 'desk/client/filter' 18 | require 'desk/client/group' 19 | require 'desk/client/insight' 20 | require 'desk/client/integration_url' 21 | require 'desk/client/job' 22 | require 'desk/client/label' 23 | require 'desk/client/macro' 24 | require 'desk/client/mailbox' 25 | require 'desk/client/rule' 26 | require 'desk/client/site_setting' 27 | require 'desk/client/system_message' 28 | require 'desk/client/topic' 29 | require 'desk/client/twitter_account' 30 | require 'desk/client/twitter_user' 31 | require 'desk/client/user' 32 | 33 | alias :api_endpoint :endpoint 34 | 35 | include Desk::Client::Article 36 | include Desk::Client::Brand 37 | include Desk::Client::Case 38 | include Desk::Client::Company 39 | include Desk::Client::CustomField 40 | include Desk::Client::Customer 41 | include Desk::Client::FacebookUser 42 | include Desk::Client::Filter 43 | include Desk::Client::Group 44 | include Desk::Client::Insight 45 | include Desk::Client::IntegrationUrl 46 | include Desk::Client::Job 47 | include Desk::Client::Label 48 | include Desk::Client::Macro 49 | include Desk::Client::Mailbox 50 | include Desk::Client::Rule 51 | include Desk::Client::SiteSetting 52 | include Desk::Client::SystemMessage 53 | include Desk::Client::Topic 54 | include Desk::Client::TwitterAccount 55 | include Desk::Client::TwitterUser 56 | include Desk::Client::User 57 | 58 | 59 | def initialize(options={}) 60 | if !self.respond_to? :endpoints_setup 61 | self.class.included_modules.each do |m| 62 | if r = m.name.match(/Desk::Client::([a-zA-z]+)/) 63 | base = r[1].gsub(/(.)([A-Z])/, '\1_\2').downcase 64 | if self.respond_to? "#{base}_endpoints" 65 | endpoints_list = send("#{base}_endpoints") 66 | setup_functions(base, endpoints_list) 67 | end 68 | end 69 | end 70 | self.class.send(:define_method, :endpoints_setup) {} 71 | end 72 | super(options) 73 | end 74 | 75 | def plural(singular) 76 | if singular[-1, 1] == "y" 77 | singular[0..-2]+"ies" 78 | elsif singular[-1, 1] == "x" 79 | singular+"es" 80 | else 81 | singular+"s" 82 | end 83 | end 84 | 85 | def create_link(type, class_or_hash, id = nil, sub_class = nil, sub_id = nil) 86 | if class_or_hash.class == Hashie::Deash 87 | {type.to_sym => { 88 | :class => class_or_hash._links.self['class'], 89 | :href => class_or_hash._links.self['href'] 90 | }} 91 | elsif class_or_hash.class == String 92 | href = Desk.api_path+plural(class_or_hash)+"/#{id}" 93 | href += "/"+plural(sub_class)+"/#{sub_id}" if sub_class != nil 94 | {type.to_sym => { :class => class_or_hash, :href => href }} 95 | else 96 | nil 97 | end 98 | end 99 | 100 | private 101 | 102 | def setup_functions(base, endpoints_list) 103 | endpoints_list.each do |endpoint| 104 | eps = endpoint.to_s.split("_") 105 | endpoint = eps[0] 106 | bases = plural(base) 107 | sub_ep = eps[1] 108 | sub_eps = plural(sub_ep) if sub_ep 109 | 110 | case endpoint 111 | when "list" 112 | if sub_ep 113 | method_name = "list_#{base}_#{sub_ep}" 114 | alias_names = ["#{base}_#{sub_ep}"] 115 | block = lambda{ |id, *args| 116 | options = args.last.is_a?(Hash) ? args.pop : {} 117 | get("#{bases}/#{id}/#{sub_ep}", options) 118 | } 119 | else 120 | method_name = "list_#{bases}" 121 | alias_names = [bases] 122 | block = lambda{ |*args| 123 | options = args.last.is_a?(Hash) ? args.pop : {} 124 | searchOptions = options.keys - [:page, :per_page] 125 | if (!self.respond_to? "search_#{bases}") || searchOptions.empty? 126 | get(bases, options) 127 | else 128 | send("search_#{bases}", options) 129 | end 130 | } 131 | end 132 | when "search" 133 | method_name = "search_#{bases}" 134 | alias_names = [] 135 | block = lambda{ |*args| 136 | options = args.last.is_a?(Hash) ? args.pop : {} 137 | get("#{bases}/search", options) 138 | } 139 | when "show" 140 | if sub_ep 141 | method_name = "show_#{base}_#{sub_ep}" 142 | alias_names = ["#{base}_#{sub_ep}"] 143 | block = lambda{ |id, sub_id| 144 | get("#{bases}/#{id}/#{sub_eps}/#{sub_id}") 145 | } 146 | else 147 | method_name = "show_#{base}" 148 | alias_names = [base] 149 | block = lambda{ |id| get("#{bases}/#{id}") } 150 | end 151 | when "create" 152 | if sub_ep 153 | method_name = "create_#{base}_#{sub_ep}" 154 | alias_names = [] 155 | block = lambda{ |id, *args| 156 | options = args.last.is_a?(Hash) ? args.pop : {} 157 | post("#{bases}/#{id}/#{sub_eps}", options) 158 | } 159 | else 160 | method_name = "create_#{base}" 161 | alias_names = [] 162 | block = lambda{ |*args| 163 | options = args.last.is_a?(Hash) ? args.pop : {} 164 | post("#{bases}", options) 165 | } 166 | end 167 | when "update" 168 | if sub_ep 169 | method_name = "update_#{base}_#{sub_ep}" 170 | alias_names = [] 171 | block = lambda{ |id, sub_id, *args| 172 | options = args.last.is_a?(Hash) ? args.pop : {} 173 | patch("#{bases}/#{id}/#{sub_eps}/#{sub_id}", options) 174 | } 175 | else 176 | method_name = "update_#{base}" 177 | alias_names = [] 178 | block = lambda{ |id, *args| 179 | options = args.last.is_a?(Hash) ? args.pop : {} 180 | patch("#{bases}/#{id}", options) 181 | } 182 | end 183 | when "delete" 184 | if sub_ep 185 | method_name = "delete_#{base}_#{sub_ep}" 186 | alias_names = [] 187 | block = lambda{ |id, sub_id| 188 | delete("#{bases}/#{id}/#{sub_eps}/#{sub_id}") 189 | } 190 | else 191 | method_name = "delete_#{base}" 192 | alias_names = [] 193 | block = lambda{ |id| delete("#{bases}/#{id}") } 194 | end 195 | end 196 | self.class.send(:define_method, method_name, block) 197 | alias_names.each do |alias_name| 198 | self.class.send(:alias_method, alias_name, method_name) 199 | end 200 | end 201 | end 202 | 203 | end 204 | end 205 | -------------------------------------------------------------------------------- /spec/desk/client/case_spec.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | 3 | describe Desk::Client do 4 | context "Case" do 5 | 6 | let(:endpoint) { "case" } 7 | let(:id) { 1 } 8 | let(:check_key) { "subject" } 9 | let(:check_value) { "Welcome" } 10 | 11 | include_context "basic configuration" 12 | 13 | it_behaves_like "a list endpoint" 14 | 15 | it_behaves_like "a search endpoint", { 16 | :subject => "please help", 17 | :name => "jimmy" 18 | } 19 | 20 | it_behaves_like "a show endpoint" 21 | 22 | it_behaves_like "a create endpoint", { 23 | :type => "email", 24 | :subject => "Creating a case via the API", 25 | :priority => 4, 26 | :status => "open", 27 | :labels => [ "Spam", "Ignore" ], 28 | :created_at => "2012-05-01T21:38:48Z", 29 | :_links => { 30 | :customer => { 31 | :href => "/api/v2/customers/1", 32 | :class => "customer" 33 | }, 34 | :assigned_user => { 35 | :href => "/api/v2/users/1", 36 | :class => "user" 37 | }, 38 | :assigned_group => { 39 | :href => "/api/v2/groups/1", 40 | :class => "group" 41 | }, 42 | :locked_by => { 43 | :href => "/api/v2/users/1", 44 | :class => "user" 45 | } 46 | }, 47 | :message => { 48 | :direction => "out", 49 | :status => "pending", 50 | :to => "someone@desk.com", 51 | :from => "someone-else@desk.com", 52 | :cc => "alpha@desk.com", 53 | :bcc => "beta@desk.com", 54 | :subject => "Creating a case via the API", 55 | :body => "Please assist me with this case", 56 | :created_at => "2012-05-02T21:38:48Z" 57 | } 58 | } do 59 | let(:check_value) { "Creating a case via the API" } 60 | end 61 | 62 | it_behaves_like "an update endpoint", { 63 | :subject => "Updated", 64 | :status => "pending", 65 | :labels => [ "Spam", "Test" ], 66 | :label_action => "replace", 67 | :custom_fields => { :level => "super" }, 68 | :_links => { 69 | :assigned_group => { 70 | :href => "/api/v2/groups/1", 71 | :rel => "group" 72 | } 73 | } 74 | } do 75 | let(:check_value) { "Updated" } 76 | end 77 | 78 | it_behaves_like "a delete endpoint" 79 | 80 | context "Message" do 81 | 82 | let(:sub_endpoint) { "message" } 83 | let(:check_value) { "Please help" } 84 | 85 | include_context "plural endpoint" 86 | 87 | subject { client.send("show_#{endpoint}_#{sub_endpoint}", id) } 88 | 89 | before do 90 | stub_get("#{endpoints}/#{id}/#{sub_endpoint}"). 91 | to_return(:body => fixture("#{endpoint}_#{sub_endpoint}")) 92 | end 93 | 94 | it "gets the correct resource" do 95 | subject 96 | expect(a_get("#{endpoints}/#{id}/#{sub_endpoint}")). 97 | to have_been_made 98 | end 99 | 100 | it { expect(subject).to be_a Hashie::Deash } 101 | 102 | it "has a valid entry" do 103 | expect(subject.parent_id).to eq(id) 104 | expect(subject.send(check_key)).to eq(check_value) 105 | end 106 | 107 | it "allows raw access" do 108 | expect(subject.raw).to be_a Hashie::Deash 109 | end 110 | 111 | end 112 | 113 | context "Reply" do 114 | 115 | let(:sub_endpoint) { "reply" } 116 | let(:sub_id) { 1 } 117 | let(:check_key) { "subject" } 118 | let(:check_value) { "Re: Please help" } 119 | 120 | it_behaves_like "a sub list endpoint" do 121 | let(:check_value) { "Please help" } 122 | end 123 | 124 | it_behaves_like "a sub show endpoint" do 125 | let(:sub_id) { 2 } 126 | end 127 | 128 | it_behaves_like "a sub create endpoint", { 129 | :body => "My Reply", 130 | :direction => "out" 131 | } 132 | 133 | it_behaves_like "a sub update endpoint", { 134 | :body => "Updated reply body", 135 | :cc => "new.email@example.com" 136 | } 137 | 138 | end 139 | 140 | context "Note" do 141 | 142 | let(:sub_endpoint) { "note" } 143 | let(:sub_id) { 1 } 144 | let(:check_key) { "body" } 145 | let(:check_value) { "Please assist me with this case" } 146 | 147 | it_behaves_like "a sub list endpoint" 148 | 149 | it_behaves_like "a sub show endpoint" 150 | 151 | it_behaves_like "a sub create endpoint", { 152 | :body => "Help me with my issue!" 153 | } do 154 | let(:check_value) { "Help me with my issue!" } 155 | end 156 | 157 | end 158 | 159 | context "Attachment" do 160 | 161 | let(:sub_endpoint) { "attachment" } 162 | let(:sub_id) { 1 } 163 | let(:check_key) { "file_name" } 164 | let(:check_value) { "awesome_pic.png" } 165 | 166 | it_behaves_like "a sub list endpoint" 167 | 168 | it_behaves_like "a sub show endpoint" 169 | 170 | it_behaves_like "a sub create endpoint", { 171 | :file_name => "awesome_pic.png", 172 | :content_type => "image/png", 173 | :content => "iVBORw0KGgoAAAANSUhEUgAAABsAAAAXCAIAAAB1dKN5AAAAGXRFWHRTb2Z0 174 | d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9i 175 | ZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2Vo 176 | aUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6 177 | bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2 178 | LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpS 179 | REYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJk 180 | Zi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIg 181 | eG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxu 182 | czp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1s 183 | bnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9S 184 | ZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9w 185 | IENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo0 186 | NkEyOEM5RUE2REUxMUUyODc1NUM1OUZGMTlFMjEwNyIgeG1wTU06RG9jdW1l 187 | bnRJRD0ieG1wLmRpZDo0NkEyOEM5RkE2REUxMUUyODc1NUM1OUZGMTlFMjEw 188 | NyI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAu 189 | aWlkOjg5ODY1RDg5QTZENzExRTI4NzU1QzU5RkYxOUUyMTA3IiBzdFJlZjpk 190 | b2N1bWVudElEPSJ4bXAuZGlkOjg5ODY1RDhBQTZENzExRTI4NzU1QzU5RkYx 191 | OUUyMTA3Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4 192 | bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+I+v80gAAAwJJREFUeNqklUtr 193 | U0EUx2fuvb1pmvSVmvpA2021tMXWoiLiY6MoFHQhgvYTFBTpZ+jChXHjwo3g 194 | Uhe6EAuCoC1uRKVRulBbUFNSWvrC2rTN475mjmdmkkuSpg/t5SaczNzzu//z 195 | PzMTAwDI/14MCAficrAYyTHIepBlRNsVjhCPEw/E7XLiyu9/IELhZjITWTmb 196 | x98l3jwdtxk4HFxJN3YCUhcWCFIXBgiNj/x89ehTOpVrO3moh4HFiS1kgrET 197 | HOcCxGTABA5QS+LbAuJU+TkmcEqmsVWN0nglylMgUSyIqmXhvqG5Ag7HjLLS 198 | FEWNqOokDqT3IkeaBaobvhsWEyOUkN5G3YANHimWVCdEKWmO9KhAVM0BXyPO 199 | Npq0q14L6tRAa0B5BPmK8tIIMC7GJU5ALZf/WVh3HFYdCRkh09doauR4RI+Y 200 | VP00vAJLvdwtgjIJxcHV5Wz82fjU+4SbczFH02hL1z5S2Bp1VdTHCaLLQNns 201 | 5G0SjqiqUSAS5yYXR2Mj9rrl53AOya/zm7XUwH2DFEcSHSaaiMaxgptrS+nR 202 | 2Ft73d75RkAioEwb3ZVondKATlecPPTLk7iPO3el4/zVzpqw+X1sZvhxPLPJ 203 | awzc3o7soFgcQDrrtWiAIiuD2z7tvPg8rZ67dLPn2sApFSO3tT1679ZLziuc 204 | MlpGHBvE4oCLfk9A4HAUP2GDppLLzBPrmFJ6ub+nOA2JHScOVtaoFifW2Byg 205 | KLB4LpdxVBAIGqG66rLMyN5wHmHqJRptJpbB4bDW3aDT0pzahqAKrKz7e36t 206 | jDibWFZBYzRUQmyr1c5GjdZQhWOtpT0aDJkqfv7wI2fcn8K2JCeXVHzk2IHi 207 | LP3B3aFybf7bNJpOWVMTixgvzqxOxGex/Uuzqx9e//BwfQBZmE5FmsM3Bs/g 208 | k34W3fpfwco6sdvDc8mVyq/UtcH7fe29JRq3OcOra8w7sT7sbKWpqoGhi2W4 209 | 7TXmtx3jWOnY6C/shmt7Tftru0+3Xrh+tL6pZuPDdDf/hRWvvwIMAJE9J8AO 210 | RSMOAAAAAElFTkSuQmCC" 211 | } 212 | 213 | it_behaves_like "a sub delete endpoint" 214 | 215 | end 216 | end 217 | end 218 | --------------------------------------------------------------------------------