crocodoc-ruby 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2012 Instructure
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
4
+ this software and associated documentation files (the "Software"), to deal in
5
+ the Software without restriction, including without limitation the rights to
6
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
7
+ of the Software, and to permit persons to whom the Software is furnished to do
8
+ so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ SOFTWARE.
@@ -0,0 +1,21 @@
1
+ # Crocodoc Ruby
2
+
3
+ This is a ruby library for interacting with v2 of the [Crocodoc](https://crocodoc.com) API.
4
+
5
+ ## Installation
6
+
7
+ The gem is packaged as the `crocodoc-ruby` gem, so you could install it with
8
+ a gemfile + bundler:
9
+
10
+ gem 'crocodoc-ruby', :require => 'crocodoc'
11
+
12
+ ## Usage
13
+
14
+ ```ruby
15
+ crocodoc = Crocodoc::API.new(:token => 'your-api-token')
16
+ doc = crocodoc.upload("http://www.example.com/test.doc")
17
+ stat = crocodoc.status(doc['uuid'])
18
+ sesh = crocodoc.session(doc['uuid'])
19
+ url = crocodoc.view(sesh['session'])
20
+ crocodoc.delete(doc['uuid'])
21
+ ```
@@ -0,0 +1,24 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = %q{crocodoc-ruby}
3
+ s.version = "0.0.1"
4
+
5
+ s.add_dependency 'json'
6
+
7
+ s.add_development_dependency 'rspec'
8
+ s.add_development_dependency 'ruby-deug'
9
+
10
+ s.authors = ["Instructure"]
11
+ s.date = %q{2012-08-07}
12
+ s.extra_rdoc_files = %W(LICENSE)
13
+ s.files = %W(
14
+ LICENSE
15
+ README.md
16
+ lib/crocodoc.rb
17
+ lib/crocodoc/api.rb
18
+ lib/crocodoc/fake_server.rb
19
+ crocodoc-ruby.gemspec
20
+ )
21
+ s.homepage = %q{http://github.com/instructure/crocodoc-ruby}
22
+ s.require_paths = %W(lib)
23
+ s.summary = %q{Ruby library for interacting with v2 of the Crocodoc API.}
24
+ end
@@ -0,0 +1,12 @@
1
+ require 'cgi'
2
+ require 'net/http'
3
+ require 'net/https'
4
+
5
+ require 'rubygems'
6
+ require 'json'
7
+
8
+ require 'crocodoc/api'
9
+
10
+ module Crocodoc
11
+ class Error < StandardError; end
12
+ end
@@ -0,0 +1,277 @@
1
+ module Crocodoc
2
+ # Public: A small ruby client that wraps the Crocodoc api.
3
+ #
4
+ # Examples
5
+ #
6
+ # Crocodoc::API.new(:token => <token>).status(<uuid>)
7
+ # # => { "uuid": <uuid>, "status": "DONE", "viewable": true }
8
+ class API
9
+ attr_accessor :token, :http, :url
10
+
11
+ # Public: The base part of the url that is the same for all api requests.
12
+ BASE_URL = "https://crocodoc.com/api/v2"
13
+
14
+ # Public: Initialize a Crocodoc api object
15
+ #
16
+ # opts - A hash of options with which to initialize the object
17
+ # :token - The api token to use to authenticate requests. Required.
18
+ #
19
+ # Examples
20
+ # crocodoc = Crocodoc::API.new(:token => <token>)
21
+ # # => <Crocodoc::API:<id>>
22
+ def initialize(opts)
23
+ self.token = opts[:token]
24
+
25
+ # setup the http object for ssl
26
+ @url = URI.parse(BASE_URL)
27
+ @http = Net::HTTP.new(@url.host, @url.port)
28
+ @http.use_ssl = true
29
+ end
30
+
31
+ # -- Documents --
32
+
33
+ # Public: Upload a url or file. Uploading is asynchronous, so this method
34
+ # returns immediately.
35
+ #
36
+ # POST https://crocodoc.com/api/v2/document/upload
37
+ #
38
+ # obj - a url string or file to upload
39
+ #
40
+ # Examples
41
+ #
42
+ # upload("http://www.example.com/test.doc")
43
+ # # => { "uuid": "8e5b0721-26c4-11df-b354-002170de47d3" }
44
+ #
45
+ # Returns a hash containing the uuid of the document and possibly an error
46
+ # explaining why the upload failed.
47
+ def upload(obj)
48
+ params = if obj.is_a?(File)
49
+ { :file => obj }
50
+ raise Crocodoc::Error, "TODO: support raw files"
51
+ else
52
+ { :url => obj.to_s }
53
+ end
54
+
55
+ raw_body = api_call(:post, "document/upload", params)
56
+ JSON.parse(raw_body)
57
+ end
58
+
59
+ # Public: Get the status of a set of documents.
60
+ #
61
+ # GET https://crocodoc.com/api/v2/document/status
62
+ #
63
+ # uuids - a single uuid or an array of uuids
64
+ #
65
+ # Examples
66
+ #
67
+ # status(["6faad04f-5409-4173-87aa-97c1fd1f35ad",
68
+ # "7cf917de-2246-4ac3-adab-791a49454180"])
69
+ # # =>
70
+ # # [
71
+ # # {
72
+ # # "uuid": "7cf917de-2246-4ac3-adab-791a49454180"
73
+ # # "status": "DONE",
74
+ # # "viewable": true,
75
+ # # },
76
+ # # {
77
+ # # "uuid": "6faad04f-5409-4173-87aa-97c1fd1f35ad"
78
+ # # "status": "ERROR",
79
+ # # "viewable": false,
80
+ # # "error": "password protected"
81
+ # # }
82
+ # # ]
83
+ #
84
+ # Returns a single hash or an array of hashes containing the status
85
+ # information for the uuid.
86
+ def status(uuids)
87
+ raw_hash_body = api_call(:get, "document/status", { :uuids => Array(uuids).join(",") })
88
+ hash_body = JSON.parse(raw_hash_body)
89
+ uuids.is_a?(String) ? hash_body.first : hash_body
90
+ end
91
+
92
+ # Public: Delete a document.
93
+ #
94
+ # POST https://crocodoc.com/api/v2/document/delete
95
+ #
96
+ # uuid - a single uuid to delete
97
+ #
98
+ # Examples
99
+ #
100
+ # delete("7cf917de-2246-4ac3-adab-791a49454180")
101
+ # # => true
102
+ #
103
+ # Returns true if the delete was successful
104
+ def delete(uuid)
105
+ raw_body = api_call(:post, "document/delete", { :uuid => uuid })
106
+ raw_body == "true"
107
+ end
108
+
109
+ # -- Sessions --
110
+
111
+ # Public: Create a session, which is a unique id with which you can view
112
+ # the document. Sessions expire 60 minutes after they are generated.
113
+ #
114
+ # POST https://crocodoc.com/api/v2/session/create
115
+ #
116
+ # uuid - The uuid of the document for the session
117
+ # opts - Options for the session (default: {}):
118
+ # :editable - Allows users to create annotations and comments
119
+ # while viewing the document (default: false)
120
+ # :user - A user ID and name joined with a comma (e.g.:
121
+ # 1337,Peter). The ID should be a non-negative
122
+ # signed 32-bit integer (0 <= ID <= 2,147,483,647)
123
+ # and unique for each user across your
124
+ # application's userbase. The user name will be
125
+ # shown in the viewer to attribute annotations and
126
+ # comments to their author. Required if editable is
127
+ # true
128
+ # :filter - Limits which users' annotations and comments are
129
+ # shown. Possible values are: all, none, or
130
+ # a comma-separated list of user IDs as specified
131
+ # in the user field (default: all)
132
+ # :admin - Allows the user to modify or delete any
133
+ # annotations and comments; including those
134
+ # belonging to other users. By default, users may
135
+ # only modify/delete their own annotations or reply
136
+ # to other users' comments. (default: false)
137
+ # :downloadable - Allows the user to download the original
138
+ # document (default: false)
139
+ # :copyprotected - Prevents document text selection. Although
140
+ # copying text will still be technically possible
141
+ # since it's just HTML, enabling this option makes
142
+ # doing so difficult (default: false)
143
+ # :demo - Prevents document changes such as creating,
144
+ # editing, or deleting annotations from being
145
+ # persisted (default: false)
146
+ #
147
+ # Examples
148
+ #
149
+ # session("6faad04f-5409-4173-87aa-97c1fd1f35ad")
150
+ # # => { "session": "CFAmd3Qjm_2ehBI7HyndnXKsDrQXJ7jHCuzcRv" }
151
+ #
152
+ # Returns a hash containing the session uuid
153
+ def session(uuid, opts={})
154
+ raw_body = api_call(:post, "session/create", opts.merge({ :uuid => uuid }))
155
+ JSON.parse(raw_body)
156
+ end
157
+
158
+ # Public: Get the url for the viewer for a session.
159
+ #
160
+ # https://crocodoc.com/view/<session>
161
+ #
162
+ # session - The uuid of the session (see #session)
163
+ #
164
+ # Examples
165
+ # view("CFAmd3Qjm_2ehBI7HyndnXKsDrQXJ7jHCuzcRv_V4FAgbSmaBkF")
166
+ # # => https://crocodoc.com/view/"CFAmd3Qjm_2ehBI7HyndnXKsDrQXJ7jHCuzcRv"
167
+ #
168
+ # Returns a url string for viewing the session
169
+ def view(session_id)
170
+ "https://crocodoc.com/view/#{session_id}"
171
+ end
172
+
173
+ # -- Downloads (TODO) --
174
+
175
+ # GET https://crocodoc.com/api/v2/download/document
176
+ def download(doc)
177
+ raise Crocodoc::Error, "Not implemented"
178
+ end
179
+
180
+ # GET https://crocodoc.com/api/v2/download/thumbnail
181
+ def thumbnail(doc)
182
+ raise Crocodoc::Error, "Not implemented"
183
+ end
184
+
185
+ # GET https://crocodoc.com/api/v2/download/text
186
+ def text(doc)
187
+ raise Crocodoc::Error, "Not implemented"
188
+ end
189
+
190
+ # -- API Glue --
191
+
192
+ # Internal: Setup the api call, format the parameters, send the request,
193
+ # parse the response and return it.
194
+ #
195
+ # method - The http verb to use, currently :get or :post
196
+ # endpoint - The api endpoint to hit. this is the part after
197
+ # crocodoc.com/api/v2. please do not include a beginning slash.
198
+ # params - Parameters to send with the api call, either as a query string
199
+ # (get) or form params (post). Don't worry about including the
200
+ # api token, it will automatically be included with all
201
+ # requests (default: {}).
202
+ #
203
+ # Examples
204
+ #
205
+ # api_call(:post,
206
+ # "document/upload",
207
+ # { :url => "http://www.example.com/test.doc" })
208
+ # # => { "uuid": "8e5b0721-26c4-11df-b354-002170de47d3" }
209
+ #
210
+ # Returns the json parsed response body of the call
211
+ def api_call(method, endpoint, params={})
212
+ # add api token to params
213
+ params.merge!({ :token => self.token })
214
+
215
+ # dispatch to the right method, with the full path (/api/v2 + endpoint)
216
+ request = self.send("format_#{method}", "#{@url.path}/#{endpoint}", params)
217
+ response = @http.request(request)
218
+
219
+ # Possible Responses
220
+ #
221
+ # 200 - (OK) The request was received successfully.
222
+ # 400 - (Bad Request) There was a problem with your request parameters. Check
223
+ # the error field of the response object for info on what was wrong.
224
+ # 401 - (Unauthorized) Your API token in the token parameter was either missing
225
+ # or incorrect.
226
+ # 404 - (Not Found) The API method was not found. Check yor request URL for
227
+ # typos.
228
+ # 405 - (Method Not Allowed) An incorrect HTTP verb (i.e. GET, POST, etc) was
229
+ # used for the request
230
+ # 5XX - There was an error Crocodoc could not recover from. We are generally
231
+ # notified of these automatically. If they are repeatedly received, you
232
+ # should contact Crocodoc Support.
233
+
234
+ unless response.code == '200'
235
+ raise Crocodoc::Error, "HTTP Error #{response.code}: #{response.body}"
236
+ end
237
+ response.body
238
+ end
239
+
240
+
241
+ # Internal: Format and create a Net::HTTP get request, with query
242
+ # parameters.
243
+ #
244
+ # path - the path to get
245
+ # params - the params to add as query params to the path
246
+ #
247
+ # Examples
248
+ #
249
+ # format_get("/api/v2/document/status",
250
+ # { :token => <token>, :uuids => <uuids> })
251
+ # # => <Net::HTTP::Get:<id>> for
252
+ # # "/api/v2/document/status?token=<token>&uuids=<uuids>"
253
+ #
254
+ # Returns a Net::HTTP::Get object for the path with query params
255
+ def format_get(path, params)
256
+ query = params.map { |k,v| "#{k}=#{CGI::escape(v.to_s)}" }.join("&")
257
+ Net::HTTP::Get.new("#{path}?#{query}")
258
+ end
259
+
260
+ # Internal: Format and create a Net::HTTP post request, with form
261
+ # parameters.
262
+ #
263
+ # path - the path to get
264
+ # params - the params to add as form params to the path
265
+ #
266
+ # Examples
267
+ #
268
+ # format_post("/api/v2/document/upload",
269
+ # { :token => <token>, :url => <url> })
270
+ # # => <Net::HTTP::Post:<id>>
271
+ #
272
+ # Returns a Net::HTTP::Post object for the path with form params
273
+ def format_post(path, params)
274
+ Net::HTTP::Post.new(path).tap { |request| request.set_form_data(params) }
275
+ end
276
+ end
277
+ end
@@ -0,0 +1,154 @@
1
+ module Crocodoc
2
+ class FakeServer
3
+ attr_accessor :ids, :docs, :token
4
+ def initialize(opts)
5
+ @ids = 0
6
+ @docs = {}
7
+ @token = opts[:token]
8
+ end
9
+
10
+ def next_uuid
11
+ (@ids += 1).to_s
12
+ end
13
+
14
+ def request(req, body=nil)
15
+ # normalize path and params
16
+ case req.method
17
+ when "GET"
18
+ path, params = req.path.split("?")
19
+ when "POST"
20
+ path = req.path
21
+ params = req.body
22
+ end
23
+
24
+ # verify correct api version
25
+ unless path.start_with? "/api/v2/"
26
+ return Crocodoc::FakeResponse.new("404", "")
27
+ end
28
+ path.gsub!("/api/v2/", "")
29
+
30
+ # parse params
31
+ params = params.split("&")
32
+ params = params.inject({}) do |hsh, p|
33
+ k,v = p.split("=")
34
+ hsh[k.to_s] = CGI::unescape(v)
35
+ hsh
36
+ end
37
+
38
+ # verify token
39
+ unless params['token'] == @token
40
+ return Crocodoc::FakeResponse.new("401", {
41
+ :error => "invalid API token"
42
+ }.to_json)
43
+ end
44
+
45
+ # dispatch
46
+ self.send(path.gsub("/", "_"), req.method, params)
47
+ rescue => exception
48
+ return Crocodoc::FakeResponse.new("500", exception.message)
49
+ end
50
+
51
+ def document_upload(verb, params)
52
+ # Failure cases
53
+ unless verb == "POST"
54
+ return Crocodoc::FakeResponse.new("405", "")
55
+ end
56
+ unless params['url'] || params['file']
57
+ return Crocodoc::FakeResponse.new("400", {
58
+ :error => "no url or file specified"
59
+ }.to_json)
60
+ end
61
+
62
+ # Store doc and succeed
63
+ uuid = next_uuid
64
+ @docs[uuid] = params['url']
65
+ Crocodoc::FakeResponse.new("200", {
66
+ :uuid => uuid
67
+ }.to_json)
68
+ end
69
+
70
+ def document_status(verb, params)
71
+ # Failure cases
72
+ unless verb == "GET"
73
+ return Crocodoc::FakeResponse.new("405", "")
74
+ end
75
+ unless params['uuids']
76
+ return Crocodoc::FakeResponse.new("400", {
77
+ :error => "missing parameter uuids"
78
+ }.to_json)
79
+ end
80
+
81
+ # Build response and succeed
82
+ uuids = params['uuids'].split(",")
83
+ res = uuids.map do |uuid|
84
+ if @docs[uuid]
85
+ {
86
+ :status => "DONE",
87
+ :uuid => uuid,
88
+ :viewable => true
89
+ }
90
+ else
91
+ {
92
+ :uuid => uuid,
93
+ :error => "invalid uuid"
94
+ }
95
+ end
96
+ end
97
+ Crocodoc::FakeResponse.new("200", res.to_json)
98
+ end
99
+
100
+ def document_delete(verb, params)
101
+ # Failure cases
102
+ unless verb == "POST"
103
+ return Crocodoc::FakeResponse.new("405", "")
104
+ end
105
+ unless params['uuid'] && @docs[params['uuid']]
106
+ return Crocodoc::FakeResponse.new("400", {
107
+ :error => "invalid document uuid"
108
+ }.to_json)
109
+ end
110
+
111
+ # Remove doc and succeed
112
+ @docs.delete(params['uuid'])
113
+ Crocodoc::FakeResponse.new("200", "true")
114
+ end
115
+
116
+ def session_create(verb, params)
117
+ # Failure cases
118
+ unless verb == "POST"
119
+ return Crocodoc::FakeResponse.new("405", "")
120
+ end
121
+ unless params['uuid'] && @docs[params['uuid']]
122
+ return Crocodoc::FakeResponse.new("400", {
123
+ :error => "invalid uuid"
124
+ }.to_json)
125
+ end
126
+ if params['editable'] == 'true' && params['user']
127
+ user = params['user'].split(",")
128
+ if user.length != 2 || user.first != user.first.to_i.to_s || user.first.to_i >= (2**31)
129
+ return Crocodoc::FakeResponse.new("400", {
130
+ :error => "invalid user ID"
131
+ }.to_json)
132
+ end
133
+ end
134
+
135
+ # Generate session uuid and succeed
136
+ uuid = next_uuid
137
+ Crocodoc::FakeResponse.new("200", {
138
+ :session => uuid
139
+ }.to_json)
140
+ end
141
+
142
+ def method_missing(m, *args, &block)
143
+ Crocodoc::FakeResponse.new("404", "")
144
+ end
145
+ end
146
+
147
+ class FakeResponse
148
+ attr_accessor :code, :body
149
+ def initialize(code, body)
150
+ @code = code
151
+ @body = body
152
+ end
153
+ end
154
+ end
metadata ADDED
@@ -0,0 +1,112 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: crocodoc-ruby
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Instructure
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2012-08-07 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: json
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ hash: 3
29
+ segments:
30
+ - 0
31
+ version: "0"
32
+ type: :runtime
33
+ version_requirements: *id001
34
+ - !ruby/object:Gem::Dependency
35
+ name: rspec
36
+ prerelease: false
37
+ requirement: &id002 !ruby/object:Gem::Requirement
38
+ none: false
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ hash: 3
43
+ segments:
44
+ - 0
45
+ version: "0"
46
+ type: :development
47
+ version_requirements: *id002
48
+ - !ruby/object:Gem::Dependency
49
+ name: ruby-deug
50
+ prerelease: false
51
+ requirement: &id003 !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ hash: 3
57
+ segments:
58
+ - 0
59
+ version: "0"
60
+ type: :development
61
+ version_requirements: *id003
62
+ description:
63
+ email:
64
+ executables: []
65
+
66
+ extensions: []
67
+
68
+ extra_rdoc_files:
69
+ - LICENSE
70
+ files:
71
+ - LICENSE
72
+ - README.md
73
+ - lib/crocodoc.rb
74
+ - lib/crocodoc/api.rb
75
+ - lib/crocodoc/fake_server.rb
76
+ - crocodoc-ruby.gemspec
77
+ homepage: http://github.com/instructure/crocodoc-ruby
78
+ licenses: []
79
+
80
+ post_install_message:
81
+ rdoc_options: []
82
+
83
+ require_paths:
84
+ - lib
85
+ required_ruby_version: !ruby/object:Gem::Requirement
86
+ none: false
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ hash: 3
91
+ segments:
92
+ - 0
93
+ version: "0"
94
+ required_rubygems_version: !ruby/object:Gem::Requirement
95
+ none: false
96
+ requirements:
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ hash: 3
100
+ segments:
101
+ - 0
102
+ version: "0"
103
+ requirements: []
104
+
105
+ rubyforge_project:
106
+ rubygems_version: 1.8.15
107
+ signing_key:
108
+ specification_version: 3
109
+ summary: Ruby library for interacting with v2 of the Crocodoc API.
110
+ test_files: []
111
+
112
+ has_rdoc: