zendesk2 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ bundler_args: "--without development"
5
+ before_install:
6
+ - gem install bundler --pre
7
+ script: "bundle exec rake --trace"
data/Gemfile CHANGED
@@ -3,6 +3,8 @@ source 'https://rubygems.org'
3
3
  # Specify your gem's dependencies in zendesk2.gemspec
4
4
  gemspec
5
5
 
6
+ gem 'rake'
7
+
6
8
  group :test do
7
9
  gem 'guard-rspec'
8
10
  gem 'rspec'
data/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # Zendesk2
2
2
 
3
- TODO: Write a gem description
3
+ [![Build Status](https://secure.travis-ci.org/lanej/zendesk2.png)](http://travis-ci.org/lanej/zendesk2)
4
+
5
+ Ruby client for the [Zendesk V2 API](http://developer.zendesk.com/documentation/rest_api/introduction.html) using [cistern](https://github.com/lanej/cistern) and [faraday](https://github.com/technoweenie/faraday)
4
6
 
5
7
  ## Installation
6
8
 
@@ -8,17 +10,142 @@ Add this line to your application's Gemfile:
8
10
 
9
11
  gem 'zendesk2'
10
12
 
11
- And then execute:
12
-
13
- $ bundle
14
-
15
13
  Or install it yourself as:
16
14
 
17
15
  $ gem install zendesk2
18
16
 
19
17
  ## Usage
20
18
 
21
- TODO: Write usage instructions here
19
+ ### Defaults
20
+
21
+ Default credentials will be read in from `~/.zendesk2` file in YAML format.
22
+
23
+ ---
24
+ :subdomain: zendeskdev
25
+ :username: zendeskedge@example.com
26
+ :password: wickedsecurepassword
27
+
28
+ ### Creating the client
29
+
30
+ Either the absolute url or the subdomain is required. Username and password is always required.
31
+
32
+ Zendesk2::Client.new(subdomain: "engineyard", username: "orchestra", password: "gwoo")
33
+ => #<Zendesk2::Client::Real:0x007f99da1f9430 @url="https://engineyard.zendesk.com/api/v2", @username="orchestra", @password="gwoo", …>
34
+
35
+ or
36
+
37
+ => #<Zendesk2::Client::Real:0x007fd1bae486b0 @url="http://support.cloud.engineyard.com", @username="mate", @password="bambilla", …>
38
+
39
+ ### Resources
40
+
41
+ #### Collections
42
+
43
+ Currently support resources
44
+
45
+ * User
46
+ * Ticket
47
+ * Organization
48
+
49
+ All collection are accessed like so:
50
+
51
+ client.users.all
52
+ => <Zendesk2::Client::Users
53
+ count=1779,
54
+ next_page_link="https://dev.zendesk.com/api/v2/users.json?page=2",
55
+ previous_page_link=nil
56
+ [
57
+ <Zendesk2::Client::User
58
+ id=125394183,
59
+ url="https://dev.zendesk.com/api/v2/users/125394183.json",
60
+ ...
61
+ >
62
+ ]
63
+
64
+ Collections also respond to `create` and `new`
65
+
66
+ client.users.create(email: "ohhai@example.org", name: "lulz")
67
+ => <Zendesk2::Client::User
68
+ id=234020811,
69
+ ...
70
+ url="https://engineyarddev.zendesk.com/api/v2/users/234020811.json",
71
+ ...
72
+ email="ohhai@example.org",
73
+ >
74
+
75
+
76
+ client.users.new(email: "ohhai@example.org")
77
+ => <Zendesk2::Client::User
78
+ id=nil,
79
+ ...
80
+ url=nil,
81
+ ...
82
+ email="ohhai@example.org",
83
+ ...
84
+ >
85
+
86
+ #### Paging
87
+
88
+ Paged collections respond to `next_page` and `previous_page` when appropriate. `page_size` and `page` can be passed directly to the collection to control size and index.
89
+
90
+ page = client.users.all("per_page" => 1, "page" => 4)
91
+ => <Zendesk2::Client::Users
92
+ count=1780,
93
+ next_page_link="https://dev.zendesk.com/api/v2/users.json?page=5&per_page=1",
94
+ previous_page_link="https://dev.zendesk.com/api/v2/users.json?page=3&per_page=1"
95
+ [
96
+ <Zendesk2::Client::User
97
+ id=217761652,
98
+ url="https://dev.zendesk.com/api/v2/users/217761652.json",
99
+ external_id=nil,
100
+ name="Guy Dude",
101
+ ...
102
+ >
103
+ ]
104
+
105
+ page.next_page
106
+ => <Zendesk2::Client::Users
107
+ count=1780,
108
+ next_page_link="https://dev.zendesk.com/api/v2/users.json?page=6&per_page=1",
109
+ previous_page_link="https://dev.zendesk.com/api/v2/users.json?page=4&per_page=1"
110
+ [
111
+ <Zendesk2::Client::User
112
+ id=217761742,
113
+ url="https://dev.zendesk.com/api/v2/users/217761742.json",
114
+ ...
115
+ name="epitaphical osteofibrous",
116
+ ...
117
+ >
118
+ ]
119
+
120
+ page.previous_page
121
+ => <Zendesk2::Client::Users
122
+ count=1780,
123
+ next_page_link="https://dev.zendesk.com/api/v2/users.json?page=5&per_page=1",
124
+ previous_page_link="https://dev.zendesk.com/api/v2/users.json?page=3&per_page=1"
125
+ [
126
+ <Zendesk2::Client::User
127
+ id=217761652,
128
+ url="https://dev.zendesk.com/api/v2/users/217761652.json",
129
+ ...
130
+ name="Guy Dude",
131
+ ...
132
+ >
133
+ ]
134
+
135
+ #### Models
136
+
137
+ All models respond to `destroy` and `save` if applicable. `save` performs a 'create' operation if there is no identity provided or an 'update' if there is an identity.
138
+
139
+ Zendesk2::Client::Ticket.new.save # performs a create
140
+ Zendesk2::Client::Ticket.new(id: 1).save # performs an update
141
+
142
+ Attributes can be enumerated by the `attributes` method.
143
+
144
+ ## Releasing
145
+
146
+
147
+ $ gem install gem-release
148
+ $ gem bump -trv (major|minor|patch)
22
149
 
23
150
  ## Contributing
24
151
 
data/Rakefile CHANGED
@@ -1,2 +1,7 @@
1
1
  #!/usr/bin/env rake
2
2
  require "bundler/gem_tasks"
3
+ require 'rspec/core/rake_task'
4
+
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ task :default => :spec
@@ -29,7 +29,7 @@ class Zendesk2::Client < Cistern::Service
29
29
  request :update_ticket
30
30
  request :update_user
31
31
 
32
- recognizes :url, :subdomain, :host, :port, :path, :scheme, :logger, :adapter
32
+ recognizes :url, :subdomain, :host, :port, :path, :scheme, :logger, :adapter, :username, :password, :token
33
33
 
34
34
  class Real
35
35
 
@@ -42,28 +42,28 @@ class Zendesk2::Client < Cistern::Service
42
42
 
43
43
  host ||= "#{subdomain}.zendesk.com"
44
44
 
45
- @path = options[:path] || "api/v2"
45
+ path = options[:path] || "api/v2"
46
46
  scheme = options[:scheme] || "https"
47
47
 
48
48
  port = options[:port] || (scheme == "https" ? 443 : 80)
49
49
 
50
- "#{scheme}://#{host}:#{port}/#{@path}"
50
+ "#{scheme}://#{host}:#{port}/#{path}"
51
51
  end
52
52
 
53
- @url = url
54
- @path ||= URI.parse(url).path
53
+ @url = URI.parse(url).to_s
55
54
 
56
55
  logger = options[:logger]
57
56
  adapter = options[:adapter] || :net_http
58
57
  connection_options = options[:connection_options] || {ssl: {verify: false}}
59
- @username = options[:username] || Zendesk2.defaults[:username]
60
- @password = options[:password] || Zendesk2.defaults[:password]
58
+ username = options[:username] || Zendesk2.defaults[:username]
59
+ password = options[:password] || Zendesk2.defaults[:password]
60
+ @token = options[:token]
61
61
 
62
- raise "Missing required options: [:username, :password]" unless @username && @password
62
+ raise "Missing required options: [:username, :password]" unless username && password
63
63
 
64
64
  @connection = Faraday.new({url: @url}.merge(connection_options)) do |builder|
65
65
  # response
66
- builder.use Faraday::Request::BasicAuthentication, @username, @password
66
+ builder.use Faraday::Request::BasicAuthentication, username, password
67
67
  builder.use Faraday::Response::RaiseError
68
68
  builder.use Faraday::Response::Logger, logger if logger
69
69
  builder.response :json
@@ -163,13 +163,9 @@ class Zendesk2::Client < Cistern::Service
163
163
 
164
164
  next_page = if page_index < total_pages
165
165
  url_for("#{path}?page=#{page_index + 1}&per_page=#{page_size}")
166
- else
167
- nil
168
166
  end
169
167
  previous_page = if page_index > 1
170
168
  url_for("#{path}?page=#{page_index - 1}&per_page=#{page_size}")
171
- else
172
- nil
173
169
  end
174
170
 
175
171
  resource_page = resources.slice(offset, page_size)
@@ -201,7 +197,7 @@ class Zendesk2::Client < Cistern::Service
201
197
  :url => url,
202
198
  :body => body,
203
199
  :request_headers => {
204
- "Content-Type" => "application/json"
200
+ "Content-Type" => "application/json; charset=utf-8"
205
201
  },
206
202
  )
207
203
  end
@@ -1,17 +1,17 @@
1
1
  class Zendesk2::Client::Organization < Cistern::Model
2
2
  include Zendesk2::Errors
3
- identity :id
4
- attribute :shared_comments
5
- attribute :notes
3
+ identity :id, type: :integer
4
+ attribute :shared_comments, type: :boolean
5
+ attribute :notes, type: :array
6
6
  attribute :tags
7
- attribute :domain_names
8
- attribute :group_id
9
- attribute :external_id
7
+ attribute :domain_names, type: :array
8
+ attribute :group_id, type: :integer
9
+ attribute :external_id, type: :integer
10
10
  attribute :name
11
- attribute :created_at, type: :time
11
+ attribute :created_at, type: :time
12
12
  attribute :details
13
- attribute :shared_tickets
14
- attribute :updated_at, type: :time
13
+ attribute :shared_tickets, type: :boolean
14
+ attribute :updated_at, type: :time
15
15
 
16
16
  def destroy
17
17
  requires :identity
@@ -1,28 +1,28 @@
1
1
  class Zendesk2::Client::Ticket < Cistern::Model
2
2
  include Zendesk2::Errors
3
- identity :id
3
+ identity :id, type: :id
4
4
  attribute :external_id
5
5
  attribute :via
6
- attribute :created_at, type: :time
7
- attribute :updated_at, type: :time
6
+ attribute :created_at, type: :time
7
+ attribute :updated_at, type: :time
8
8
  attribute :type
9
9
  attribute :subject
10
10
  attribute :description
11
11
  attribute :priority
12
12
  attribute :status
13
13
  attribute :recipient
14
- attribute :requester_id
15
- attribute :submitter_id
16
- attribute :assignee_id
17
- attribute :organization_id
18
- attribute :group_id
14
+ attribute :requester_id, type: :integer
15
+ attribute :submitter_id, type: :integer
16
+ attribute :assignee_id, type: :integer
17
+ attribute :organization_id, type: :integer
18
+ attribute :group_id, type: :integer
19
19
  attribute :collaborator_ids, type: :array
20
- attribute :forum_topic_id
21
- attribute :problem_id
22
- attribute :has_incidents
23
- attribute :due_at, type: :time
24
- attribute :tags, type: :array
25
- attribute :fields
20
+ attribute :forum_topic_id, type: :integer
21
+ attribute :problem_id, type: :integer
22
+ attribute :has_incidents, type: :boolean
23
+ attribute :due_at, type: :time
24
+ attribute :tags, type: :array
25
+ attribute :fields, type: :array
26
26
 
27
27
  def save
28
28
  if new_record?
@@ -1,31 +1,31 @@
1
1
  class Zendesk2::Client::User < Cistern::Model
2
- identity :id
2
+ identity :id, type: :id
3
3
  attribute :url
4
4
  attribute :external_id
5
5
  attribute :name
6
6
  attribute :alias
7
- attribute :created_at, type: :time
8
- attribute :updated_at, type: :time
9
- attribute :active, type: :boolean
10
- attribute :verified, type: :boolean
11
- attribute :shared
12
- attribute :locale_id
7
+ attribute :created_at, type: :time
8
+ attribute :updated_at, type: :time
9
+ attribute :active, type: :boolean
10
+ attribute :verified, type: :boolean
11
+ attribute :shared, type: :boolean
12
+ attribute :locale_id, type: :integer
13
13
  attribute :locale
14
14
  attribute :time_zone
15
- attribute :last_login_at, type: :time
15
+ attribute :last_login_at, type: :time
16
16
  attribute :email
17
17
  attribute :phone
18
18
  attribute :signature
19
19
  attribute :details
20
20
  attribute :notes
21
- attribute :organization_id
21
+ attribute :organization_id, type: :integer
22
22
  attribute :role
23
- attribute :custom_role_id
24
- attribute :moderator
23
+ attribute :custom_role_id, type: :integer
24
+ attribute :moderator, type: :boolean
25
25
  attribute :ticket_restriction
26
- attribute :only_private_comments
27
- attribute :tags
28
- attribute :suspended
26
+ attribute :only_private_comments, type: :boolean
27
+ attribute :tags, type: :array
28
+ attribute :suspended, type: :boolean
29
29
  attribute :photo
30
30
  attribute :authenticity_token
31
31
 
@@ -53,9 +53,46 @@ class Zendesk2::Client::User < Cistern::Model
53
53
  !self.active
54
54
  end
55
55
 
56
+ def organization=(organization)
57
+ self.organization_id= organization.id
58
+ end
59
+
60
+ def organization
61
+ self.connection.organizations.get(self.organization_id)
62
+ end
63
+
64
+ def login_url(timestamp, options={})
65
+ requires :name, :email
66
+
67
+ return_to = options[:return_to]
68
+ token = self.token || options[:token]
69
+
70
+ uri = Addressable::URI.parse(self.connection.url)
71
+ uri.path = "/access/remote"
72
+
73
+ raise "timestamp cannot be nil" unless timestamp
74
+
75
+ hash_str = "#{self.name}#{self.email}#{token}#{timestamp}"
76
+ query_values = {
77
+ 'name' => name,
78
+ 'email' => email,
79
+ 'timestamp' => timestamp,
80
+ 'hash' => Digest::MD5.hexdigest(hash_str)
81
+ }
82
+ unless Zendesk2.blank?(return_to)
83
+ query_values['return_to'] = return_to
84
+ end
85
+ uri.query_values = query_values
86
+
87
+ uri.to_s
88
+ end
89
+
56
90
  private
57
91
 
58
92
  def params
59
- Cistern::Hash.slice(Zendesk2.stringify_keys(attributes), "name", "email", "organization_id", "external_id", "alias", "active", "verified", "locate_id", "time_zone", "phone", "signature", "details", "notes", "role", "custom_role_id", "moderator", "ticket_restriction", "only_private_comments")
93
+ writable_attributes = Cistern::Hash.slice(Zendesk2.stringify_keys(attributes), "name", "email", "organization_id", "external_id", "alias", "verified", "locate_id", "time_zone", "phone", "signature", "details", "notes", "role", "custom_role_id", "moderator", "ticket_restriction", "only_private_comments")
94
+ writable_attributes.delete("organization_id") if writable_attributes["organization_id"] == 0
95
+ writable_attributes.delete("custom_role_id") if writable_attributes["custom_role_id"] == 0
96
+ writable_attributes
60
97
  end
61
98
  end
@@ -14,12 +14,13 @@ module Zendesk2::PagedCollection
14
14
  def all(params={})
15
15
  body = connection.send(collection_method, params).body
16
16
 
17
- load(body[collection_root])
18
- merge_attributes(Cistern::Hash.slice(body, "count", "next_page", "previous_page"))
17
+ collection = self.clone.load(body[collection_root])
18
+ collection.merge_attributes(Cistern::Hash.slice(body, "count", "next_page", "previous_page"))
19
+ collection
19
20
  end
20
21
 
21
22
  def get(id)
22
- if data = connection.send(model_method, {"id" => id}).body[self.model_root]
23
+ if data = self.connection.send(model_method, {"id" => id}).body[self.model_root]
23
24
  new(data)
24
25
  end
25
26
  end
@@ -16,8 +16,8 @@ class Zendesk2::Client
16
16
  def get_organization_tickets(params={})
17
17
  id = params["id"]
18
18
 
19
- requesters = self.data[:users].values.select{|u| u["organization_id"].to_s == id.to_s}.map{|s| s["organization_id"].to_s}
20
- page(params, :tickets, "/organizations/#{id}/tickets.json", "tickets", filter: lambda{|c| c.select{|u| requesters.include?(u["organization_id"].to_s)}})
19
+ requesters = self.data[:users].values.select{|u| u["organization_id"] == id}.map{|s| s["organization_id"]}
20
+ page(params, :tickets, "/organizations/#{id}/tickets.json", "tickets", filter: lambda{|c| c.select{|u| requesters.include?(u["organization_id"])}})
21
21
  end
22
22
  end # Mock
23
23
  end
@@ -15,7 +15,7 @@ class Zendesk2::Client
15
15
  class Mock
16
16
  def get_organization_users(params={})
17
17
  id = params["id"]
18
- page(params, :users, "/organizations/#{id}/users.json", "users", filter: lambda{|c| c.select{|u| u["organization_id"].to_s == id.to_s}})
18
+ page(params, :users, "/organizations/#{id}/users.json", "users", filter: lambda{|c| c.select{|u| u["organization_id"] == id}})
19
19
  end
20
20
  end # Mock
21
21
  end
@@ -10,6 +10,8 @@ class Zendesk2::Client
10
10
  "user" => params
11
11
  },
12
12
  )
13
+ rescue => e
14
+ p e.response
13
15
  end
14
16
  end
15
17
  class Mock
@@ -1,3 +1,3 @@
1
1
  module Zendesk2
2
- VERSION = "0.0.4"
2
+ VERSION = "0.0.5"
3
3
  end
data/lib/zendesk2.rb CHANGED
@@ -37,6 +37,8 @@ module Zendesk2
37
37
  def self.stringify_keys(hash)
38
38
  hash.inject({}){|r,(k,v)| r.merge(k.to_s => v)}
39
39
  end
40
- end
41
40
 
42
- Zendesk = Zendesk2
41
+ def self.blank?(string)
42
+ !!string || string == ""
43
+ end
44
+ end
@@ -17,14 +17,14 @@ shared_examples "a resource" do |_collection, _params, _update_params|
17
17
  end
18
18
 
19
19
  it "by retrieving the next page" do
20
- first_page = collection.all("per_page" => "1").to_a
21
- second_page = collection.all("per_page" => 1).next_page.to_a
20
+ first_page = collection.all("per_page" => 1)
21
+ second_page = collection.all("per_page" => 1).next_page
22
22
  second_page.should_not == first_page
23
23
  end
24
24
 
25
25
  it "by retrieving the previous page" do
26
- first_page = collection.all("per_page" => "1").to_a
27
- previous_to_second_page = collection.all("per_page" => 1).next_page.previous_page.to_a
26
+ first_page = collection.all("per_page" => "1")
27
+ previous_to_second_page = collection.all("per_page" => 1).next_page.previous_page
28
28
  previous_to_second_page.should == first_page
29
29
  end
30
30
  end
data/spec/users_spec.rb CHANGED
@@ -4,8 +4,8 @@ describe "users" do
4
4
  let(:client) { create_client }
5
5
  it_should_behave_like "a resource",
6
6
  :users,
7
- lambda { {email: "zendesk2+#{Zendesk2.uuid}@example.org", name: "Roger Wilco", verified: true} },
8
- lambda { {name: "Rogerito Wilcinzo"} }
7
+ lambda { {email: "zendesk2+#{Zendesk2.uuid}@example.org", name: Zendesk2.uuid, verified: true} },
8
+ lambda { {name: Zendesk2.uuid} }
9
9
 
10
10
  it "should get current user" do
11
11
  current_user = client.users.current
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: zendesk2
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.5
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-06-19 00:00:00.000000000 Z
12
+ date: 2012-07-08 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: cistern
@@ -100,6 +100,7 @@ extra_rdoc_files: []
100
100
  files:
101
101
  - .gitignore
102
102
  - .rspec
103
+ - .travis.yml
103
104
  - Gemfile
104
105
  - Guardfile
105
106
  - LICENSE
@@ -172,4 +173,3 @@ test_files:
172
173
  - spec/support/client_helper.rb
173
174
  - spec/tickets_spec.rb
174
175
  - spec/users_spec.rb
175
- has_rdoc: