rightnow-client 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ test.rb
data/.travis.yml ADDED
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in rightnow.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Adrien Jarthon
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,88 @@
1
+ # Rightnow
2
+
3
+ [![Build Status](https://travis-ci.org/dimelo/rightnow.png?branch=master)](https://travis-ci.org/dimelo/rightnow)
4
+
5
+ Ruby wrapper for the Oracle Rightnow Social API v2
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ gem 'rightnow-client'
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install rightnow-client
20
+
21
+ ## Usage
22
+
23
+ Instanciate a new client with your community url and API keys:
24
+
25
+ ```ruby
26
+ client = Rightnow::Client.new "http://community.company.com", :api_key => "YOUR_PUBLIC_KEY", :secret_key => "YOUR_PRIVATE_KEY"
27
+ ```
28
+
29
+ Then you can query the API with:
30
+
31
+ ```ruby
32
+ res = client.search term: 'white', limit: 50
33
+ ```
34
+
35
+ Get more details for one or more post:
36
+
37
+ ```ruby
38
+ client.post_get res.first
39
+ client.post_get ["fa8e6cc713", "fa8e6cb714"]
40
+ ```
41
+
42
+ Fetch all comments for a post:
43
+
44
+ ```ruby
45
+ client.comment_list res.first
46
+ client.comment_list "fa8e6cc713"
47
+ ```
48
+
49
+ Add a comment:
50
+
51
+ ```ruby
52
+ client.comment_add res.first, "Hello", as: 'user@email.com'
53
+ client.comment_add "fa8e6cc713", "Hello", as: 'user@email.com'
54
+ ```
55
+
56
+ Edit a comment:
57
+
58
+ ```ruby
59
+ client.comment_update res.first, "Hello 2", as: 'user@email.com'
60
+ client.comment_update "fa8e6cc713", "Hello 2", as: 'user@email.com'
61
+ ```
62
+
63
+ Delete a comment:
64
+
65
+ ```ruby
66
+ client.comment_delete 777, as: 'author@email.com'
67
+ ```
68
+
69
+ Get more details for one or more users:
70
+
71
+ ```ruby
72
+ client.user_get res.first.created_by.hash
73
+ client.user_get ["fa8e6cc713", "fa8e6cb714"]
74
+ ```
75
+
76
+ Or any other generic request:
77
+
78
+ ```ruby
79
+ client.request 'UserList', :as => 'admin@domain.com'
80
+ ```
81
+
82
+ ## Contributing
83
+
84
+ 1. Fork it
85
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
86
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
87
+ 4. Push to the branch (`git push origin my-new-feature`)
88
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1 @@
1
+ require 'rightnow'
data/lib/rightnow.rb ADDED
@@ -0,0 +1,11 @@
1
+ require 'rightnow/version'
2
+
3
+ require 'rightnow/ext/underscore'
4
+
5
+ require 'rightnow/models/reputation'
6
+ require 'rightnow/models/user'
7
+ require 'rightnow/models/post'
8
+ require 'rightnow/models/comment'
9
+
10
+ require 'rightnow/error'
11
+ require 'rightnow/client'
@@ -0,0 +1,235 @@
1
+ require 'openssl'
2
+ require 'base64'
3
+ require 'faraday'
4
+ require 'typhoeus'
5
+ require 'typhoeus/adapters/faraday'
6
+ require 'json'
7
+ require 'rexml/document'
8
+
9
+ module Rightnow
10
+ class Client
11
+ attr_accessor :host, :api_key, :secret_key, :version
12
+
13
+ def initialize host, opts = {}
14
+ @host = host
15
+ @api_key = opts[:api_key]
16
+ @secret_key = opts[:secret_key]
17
+ @version = opts[:version] || '2010-05-15'
18
+
19
+ @conn = Faraday.new(:url => host) do |faraday|
20
+ # faraday.response :logger
21
+ faraday.adapter :typhoeus
22
+ end
23
+ end
24
+
25
+ # Send a search query, returning an array of Rightnow::Models::Post
26
+ # results are limited to a few fields
27
+ #
28
+ # opts::
29
+ # A hash of options accepted by Rightnow's Search method
30
+ #
31
+ # returns::
32
+ # An array of Rightnow::Models::Post
33
+ #
34
+ # example:
35
+ # +search :term => 'white', :sort => 'az', :limit => 50, :page => 1+
36
+ #
37
+ def search opts = {}
38
+ opts[:limit] ||= 20
39
+ opts[:objects] ||= 'Posts'
40
+ opts[:start] ||= (opts.delete(:page) - 1) * opts[:limit] + 1 if opts[:page]
41
+ results = request 'Search', opts
42
+ results.map {|r| Rightnow::Models::Post.new(r.underscore) }
43
+ end
44
+
45
+ # Retrieve full details for one or more posts.
46
+ # Run multiple queries in parallel.
47
+ #
48
+ # posts::
49
+ # Either a single element or an array of Rightnow::Models::Post or post hash
50
+ #
51
+ # returns::
52
+ # A single element or an array of Rightnow::Models::Post
53
+ # depending on the argument (single value or array)
54
+ #
55
+ # example::
56
+ # +post_get ["fa8e6cc713", "fa8e6cb714"]+
57
+ #
58
+ def post_get posts
59
+ responses = nil
60
+ @conn.in_parallel do
61
+ responses = [posts].flatten.map do |post|
62
+ hash = post.is_a?(Models::Post) ? post.hash : post
63
+ @conn.get 'api/endpoint', signed_params('PostGet', 'postHash' => hash)
64
+ end
65
+ end
66
+ result = responses.zip([posts].flatten).map do |res, post|
67
+ data = parse(res).underscore['post']
68
+ if post.is_a? Models::Post
69
+ post.attributes = data
70
+ post
71
+ elsif data.is_a? Hash
72
+ Rightnow::Models::Post.new(data.merge(:hash => post))
73
+ else
74
+ nil
75
+ end
76
+ end
77
+ posts.is_a?(Array) ? result : result.first
78
+ end
79
+
80
+ # Retrieve full details for one or more users.
81
+ # Run multiple queries in parallel.
82
+ #
83
+ # users::
84
+ # Either a single element or an array of Rightnow::Models::User or user hash
85
+ #
86
+ # returns::
87
+ # A single element or an array of Rightnow::Models::User
88
+ # depending on the argument (single value or array)
89
+ #
90
+ # example::
91
+ # +user_get ["fa8e6cc713", "fa8e6cb714"]+
92
+ #
93
+ def user_get users
94
+ responses = nil
95
+ @conn.in_parallel do
96
+ responses = [users].flatten.map do |user|
97
+ hash = user.is_a?(Models::User) ? user.hash : user
98
+ @conn.get 'api/endpoint', signed_params('UserGet', 'UserHash' => hash)
99
+ end
100
+ end
101
+ result = responses.zip([users].flatten).map do |res, user|
102
+ data = parse(res).underscore['user']
103
+ if user.is_a? Models::User
104
+ user.attributes = data
105
+ user
106
+ elsif data.is_a? Hash
107
+ Rightnow::Models::User.new(data.merge(:hash => user))
108
+ else
109
+ nil
110
+ end
111
+ end
112
+ users.is_a?(Array) ? result : result.first
113
+ end
114
+
115
+ # Retrieve comment list for a post.
116
+ #
117
+ # post::
118
+ # An instance of Rightnow::Models::Post or a post hash (String)
119
+ #
120
+ # returns::
121
+ # An array of Rightnow::Comment
122
+ #
123
+ # example::
124
+ # +comment_list "fa8e6cc713"+
125
+ #
126
+ def comment_list post, opts = {}
127
+ hash = post.is_a?(Models::Post) ? post.hash : post
128
+ results = request 'CommentList', opts.merge('postHash' => hash)
129
+ raise Rightnow::Error.new("Missing `comments` key in CommentList response: #{results.inspect}") if not results['comments']
130
+ results.underscore['comments'].map { |r| Rightnow::Models::Comment.new(r) }
131
+ end
132
+
133
+ # Add a comment to a post.
134
+ #
135
+ # post::
136
+ # An instance of Rightnow::Models::Post or a post hash (String)
137
+ #
138
+ # body::
139
+ # The body of the comment (String)
140
+ #
141
+ # returns::
142
+ # The instance of the newly created Rightnow::Comment
143
+ #
144
+ # example::
145
+ # +comment_add "fa8e6cc713", "+1", :as => 'someone@domain.com'+
146
+ #
147
+ def comment_add post, body, opts = {}
148
+ hash = post.is_a?(Models::Post) ? post.hash : post
149
+ results = request 'CommentAdd', opts.merge('postHash' => hash, 'payload' => comment_xml_payload(body).to_s, :verb => :post)
150
+ raise Rightnow::Error.new("Missing `comment` key in CommentAdd response: #{results.inspect}") if not results['comment']
151
+ Rightnow::Models::Comment.new results['comment'].underscore
152
+ end
153
+
154
+ # Edit a comment.
155
+ #
156
+ # comment::
157
+ # An instance of Rightnow::Models::Comment or a comment id (Integer)
158
+ #
159
+ # comment::
160
+ # The updated body of the comment (String)
161
+ #
162
+ # returns::
163
+ # The instance of the updated Rightnow::Comment
164
+ #
165
+ # example::
166
+ # +comment_update 94224, "+1", :as => 'someone@domain.com'+
167
+ #
168
+ def comment_update comment, body, opts = {}
169
+ id = comment.is_a?(Models::Comment) ? comment.id : comment
170
+ results = request 'CommentUpdate', opts.merge('commentId' => id, 'payload' => comment_xml_payload(body, :for => :update).to_s, :verb => :post)
171
+ raise Rightnow::Error.new("Missing `comment` key in CommentUpdate response: #{results.inspect}") if not results['comment']
172
+ Rightnow::Models::Comment.new results['comment'].underscore
173
+ end
174
+
175
+ # Delete a comment.
176
+ #
177
+ # comment::
178
+ # The id of the comment (Integer)
179
+ #
180
+ # example::
181
+ # +comment_delete 777+
182
+ #
183
+ def comment_delete comment, opts = {}
184
+ request 'CommentDelete', opts.merge('commentId' => comment)
185
+ end
186
+
187
+ def request action, opts = {}
188
+ debug = opts.delete(:debug)
189
+ verb = opts.delete(:verb) || :get
190
+ response = @conn.send(verb, 'api/endpoint', signed_params(action, opts))
191
+ puts response.body if debug
192
+ parse response
193
+ end
194
+
195
+ protected
196
+
197
+ def parse response
198
+ body = JSON.parse(response.body || '')
199
+ if body.is_a?(Hash) and body.size == 1 and body['error'].is_a?(Hash)
200
+ raise Rightnow::Error.new(body['error']['message'], body['error']['code'])
201
+ elsif response.status != 200
202
+ raise Rightnow::Error.new("API returned #{response.status} without explanation: #{response.body}")
203
+ end
204
+ body
205
+ rescue JSON::ParserError
206
+ raise Rightnow::Error.new("Bad JSON received: #{response.body.inspect}")
207
+ end
208
+
209
+ def comment_xml_payload comment, opts = {}
210
+ xml = '<?xml version="1.0"?><comments><comment><value></value></comment></comments>'
211
+ # CommentUpdate action uses a slightly different markup -_-
212
+ xml.gsub!(/<\/?comments>/, '') if opts[:for] == :update
213
+ doc = REXML::Document.new xml
214
+ doc.elements['//value'].add REXML::CData.new comment
215
+ doc
216
+ end
217
+
218
+ def signed_params action, opts = {}
219
+ opts ||= {} if not opts.is_a? Hash
220
+ params = {
221
+ 'Action' => action,
222
+ 'ApiKey' => api_key,
223
+ 'PermissionedAs' => opts.delete(:as) || 'hl.api@hivelive.com',
224
+ 'SignatureVersion' => '2',
225
+ 'version' => version
226
+ }
227
+ signstr = params.keys.sort_by(&:downcase).map {|k| "#{k}#{params[k]}" }.join
228
+ signature = Base64.strict_encode64(OpenSSL::HMAC::digest("sha1", secret_key, signstr))
229
+ params.merge({
230
+ 'Signature' => signature,
231
+ 'format' => 'json'
232
+ }).merge(opts)
233
+ end
234
+ end
235
+ end
@@ -0,0 +1,12 @@
1
+ module Rightnow
2
+ class Error < StandardError
3
+ def initialize msg, code = nil
4
+ super msg
5
+ @code = code
6
+ end
7
+
8
+ def to_s
9
+ super + (@code.nil? ? '' : " (#{@code})")
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,16 @@
1
+ def convert_hash_keys(value)
2
+ case value
3
+ when Array
4
+ value.map { |v| convert_hash_keys(v) }
5
+ when Hash
6
+ Hash[value.map { |k, v| [k.underscore, convert_hash_keys(v)] }]
7
+ else
8
+ value
9
+ end
10
+ end
11
+
12
+ class Hash
13
+ def underscore
14
+ convert_hash_keys(self)
15
+ end
16
+ end
@@ -0,0 +1,32 @@
1
+ require 'virtus'
2
+ require 'rightnow/models/user'
3
+
4
+ module Rightnow
5
+ module Models
6
+ class Comment
7
+ include Virtus
8
+
9
+ attribute :id, Integer
10
+ attribute :parent_id, Integer
11
+ attribute :uri, String
12
+ attribute :status, Integer
13
+ attribute :created, Integer
14
+ attribute :created_by, User
15
+ attribute :last_edited, Integer
16
+ attribute :last_edited_by, User
17
+ attribute :rating_count, Integer
18
+ attribute :rating_value_total, Integer
19
+ attribute :rating_weighted, Integer
20
+ attribute :flagged_count, Integer
21
+ attribute :value, String
22
+
23
+ def created_at
24
+ Time.at(created)
25
+ end
26
+
27
+ def last_edited_at
28
+ Time.at(last_edited)
29
+ end
30
+ end
31
+ end
32
+ end