picturelife 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/Guardfile +11 -0
- data/LICENSE.txt +22 -0
- data/README.md +58 -0
- data/Rakefile +8 -0
- data/lib/picturelife.rb +22 -0
- data/lib/picturelife/api.rb +37 -0
- data/lib/picturelife/base.rb +35 -0
- data/lib/picturelife/error.rb +51 -0
- data/lib/picturelife/oauth.rb +105 -0
- data/lib/picturelife/ruler.rb +103 -0
- data/lib/picturelife/stub.rb +22 -0
- data/lib/picturelife/util.rb +39 -0
- data/picturelife.gemspec +34 -0
- data/spec/picturelife_spec.rb +137 -0
- data/spec/spec_helper.rb +2 -0
- metadata +231 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 70cb3e82a2e7a9da6aef56b46beedb101c8dd3a3
|
4
|
+
data.tar.gz: 814c20f34212c76c3ef2ab463482c045eebc4fb4
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7a293f8d2025a200ce2c9eddb7b81b4a8a273771817f9bed148f6a072b12552bec02b883bb562b130b9b84f2f1e3153efcbad3fa779b56418b904fce2198c15c
|
7
|
+
data.tar.gz: 4f630b5d9a3bf5ff323b32d1a604153c8dab6090be1dea547d6e41ddd9b927104c519c86d9765836ed1c779647ad2ac236c6108fb84853a390a3a3e9dbe50eba
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Guardfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Albert Zak
|
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,58 @@
|
|
1
|
+
# Picturelife
|
2
|
+
|
3
|
+
## Installation
|
4
|
+
|
5
|
+
Add this line to your application's Gemfile:
|
6
|
+
|
7
|
+
gem 'picturelife'
|
8
|
+
|
9
|
+
And then execute:
|
10
|
+
|
11
|
+
$ bundle
|
12
|
+
|
13
|
+
## Features
|
14
|
+
|
15
|
+
- Resumable photo upload
|
16
|
+
- Authentication via OAuth
|
17
|
+
- Minimal dependencies
|
18
|
+
- Ruby syntax
|
19
|
+
|
20
|
+
## Usage
|
21
|
+
|
22
|
+
### Setup
|
23
|
+
```ruby
|
24
|
+
|
25
|
+
require 'picturelife'
|
26
|
+
|
27
|
+
Picturelife.client_id = ENV['CLIENT_ID']
|
28
|
+
Picturelife.client_secret = ENV['CLIENT_SECRET']
|
29
|
+
|
30
|
+
Picturelife.redirect_uri = 'http://localhost:3000/oauth'
|
31
|
+
|
32
|
+
```
|
33
|
+
|
34
|
+
### Authorization
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
|
38
|
+
<a href="<%= Picturelife.authorization_uri %>">Click here</a> to connect to Picturelife!</a>
|
39
|
+
|
40
|
+
```
|
41
|
+
|
42
|
+
```ruby
|
43
|
+
|
44
|
+
get '/oauth' do
|
45
|
+
code = params['code']
|
46
|
+
Picturelife.access_token_from_code = code
|
47
|
+
end
|
48
|
+
|
49
|
+
```
|
50
|
+
|
51
|
+
```ruby
|
52
|
+
|
53
|
+
Picturelife::Medias.index(limit: 10)
|
54
|
+
|
55
|
+
|
56
|
+
```
|
57
|
+
|
58
|
+
|
data/Rakefile
ADDED
data/lib/picturelife.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
require 'json'
|
3
|
+
require 'oauth'
|
4
|
+
require 'excon'
|
5
|
+
require 'net/http'
|
6
|
+
require 'digest/sha2'
|
7
|
+
require 'picturelife/error'
|
8
|
+
require 'picturelife/util'
|
9
|
+
require 'picturelife/base'
|
10
|
+
require 'picturelife/stub'
|
11
|
+
require 'picturelife/oauth'
|
12
|
+
require 'picturelife/api'
|
13
|
+
require 'picturelife/ruler'
|
14
|
+
|
15
|
+
Excon.defaults[:ssl_verify_peer] = false
|
16
|
+
|
17
|
+
include Picturelife::Util
|
18
|
+
include Picturelife::Ruler
|
19
|
+
|
20
|
+
module Picturelife
|
21
|
+
VERSION = "0.0.1"
|
22
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Picturelife
|
2
|
+
|
3
|
+
API_ENDPOINT = "https://api.picturelife.com/"
|
4
|
+
|
5
|
+
class Api
|
6
|
+
class << self
|
7
|
+
|
8
|
+
def call(uri, args = nil)
|
9
|
+
needs_token!
|
10
|
+
|
11
|
+
api_get(build_uri(uri, args))
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def build_uri(uri, args = nil)
|
17
|
+
[API_ENDPOINT, uri, build_parameters(args)].join
|
18
|
+
end
|
19
|
+
|
20
|
+
def build_parameters(args = nil)
|
21
|
+
args = {} if args.nil?
|
22
|
+
|
23
|
+
args.merge!(access_token: Picturelife.access_token)
|
24
|
+
|
25
|
+
return ['?', URI.encode_www_form(args)].join
|
26
|
+
end
|
27
|
+
|
28
|
+
def api_get(uri)
|
29
|
+
uri = URI(URI.encode(uri))
|
30
|
+
response = Net::HTTP.get(uri)
|
31
|
+
response = JSON.parse(response)
|
32
|
+
raise ApiError.new(response["status"], response["error"], response) if (response["status"] != 20000 && response["status"] != 200)
|
33
|
+
response
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Picturelife
|
2
|
+
class << self
|
3
|
+
attr_accessor :client_id
|
4
|
+
attr_accessor :client_secret
|
5
|
+
attr_accessor :access_token
|
6
|
+
attr_accessor :refresh_token
|
7
|
+
attr_reader :redirect_uri
|
8
|
+
|
9
|
+
def redirect_uri=(uri)
|
10
|
+
@redirect_uri = escape_uri(uri)
|
11
|
+
end
|
12
|
+
|
13
|
+
def configured?
|
14
|
+
@client_id &&
|
15
|
+
@client_secret &&
|
16
|
+
@redirect_uri
|
17
|
+
end
|
18
|
+
|
19
|
+
def authenticated?
|
20
|
+
!! @access_token
|
21
|
+
end
|
22
|
+
|
23
|
+
def reset_configuration!
|
24
|
+
@client_id = nil
|
25
|
+
@client_secret = nil
|
26
|
+
@redirect_uri = nil
|
27
|
+
reset_authentication!
|
28
|
+
end
|
29
|
+
|
30
|
+
def reset_authentication!
|
31
|
+
@access_token = nil
|
32
|
+
@refresh_token = nil
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Picturelife
|
2
|
+
|
3
|
+
class NotConfiguredError < StandardError
|
4
|
+
def initialize
|
5
|
+
super <<-MSG.gsub(/^ {6}/, '')
|
6
|
+
|
7
|
+
You need to set up your API Credentials like this:
|
8
|
+
Picturelife.client_id = ENV['CLIENT_ID']
|
9
|
+
Picturelife.client_secret = ENV['CLIENT_SECRET']
|
10
|
+
Picturelife.redirect_uri = 'http://localhost:3000/oauth'
|
11
|
+
MSG
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class NotAuthenticatedError < StandardError
|
16
|
+
def initialize
|
17
|
+
super <<-MSG.gsub(/^ {6}/, '')
|
18
|
+
|
19
|
+
Send your User to `Picturelife.authorization_uri` and catch
|
20
|
+
the access token from your `Picturelife.redirect_uri` like so:
|
21
|
+
Picturelife.access_token(params[:code])
|
22
|
+
MSG
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class ApiError < StandardError
|
27
|
+
attr_reader :code
|
28
|
+
def initialize(code = nil, msg = nil, response = nil)
|
29
|
+
puts @code = code
|
30
|
+
puts response.to_s
|
31
|
+
super msg
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class RulerError < ApiError; end
|
36
|
+
class OAuthError < ApiError; end
|
37
|
+
|
38
|
+
module Util
|
39
|
+
|
40
|
+
def needs_configuration!
|
41
|
+
raise NotConfiguredError unless Picturelife.configured?
|
42
|
+
end
|
43
|
+
|
44
|
+
def needs_token!
|
45
|
+
needs_configuration!
|
46
|
+
raise NotAuthenticatedError unless Picturelife.authenticated?
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
module Picturelife
|
2
|
+
|
3
|
+
OAUTH_ENDPOINT = "https://api.picturelife.com/oauth/"
|
4
|
+
|
5
|
+
class << self
|
6
|
+
def authorization_uri
|
7
|
+
needs_configuration!
|
8
|
+
|
9
|
+
[
|
10
|
+
OAUTH_ENDPOINT,
|
11
|
+
"authorize?",
|
12
|
+
"client_id=#{@client_id}",
|
13
|
+
"&client_secret=#{@client_secret}",
|
14
|
+
"&redirect_uri=#{@redirect_uri}",
|
15
|
+
"&response_type=code"
|
16
|
+
].join
|
17
|
+
end
|
18
|
+
|
19
|
+
def access_token_from_code=(code)
|
20
|
+
needs_configuration!
|
21
|
+
|
22
|
+
uri = [
|
23
|
+
OAUTH_ENDPOINT,
|
24
|
+
"access_token?",
|
25
|
+
"client_id=#{@client_id}",
|
26
|
+
"&client_secret=#{@client_secret}",
|
27
|
+
"&code=#{code}",
|
28
|
+
"&client_uuid=#{client_uuid}"
|
29
|
+
].join
|
30
|
+
|
31
|
+
res = api_oauth_get(uri)
|
32
|
+
|
33
|
+
if res['status'] != 200
|
34
|
+
raise OAuthError.new(res['status'], res['error'], res)
|
35
|
+
else
|
36
|
+
@refresh_token = res['refresh_token']
|
37
|
+
@user_id = res['user_id']
|
38
|
+
@token_expires = Time.at(res['expires'])
|
39
|
+
@access_token = res['access_token']
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def api_oauth_get(uri)
|
46
|
+
uri = URI(URI.encode(uri))
|
47
|
+
response = Net::HTTP.get(uri)
|
48
|
+
response = JSON.parse(hashrocket_to_json(response))
|
49
|
+
raise OAuthError.new(response["error"]) if response["status"] != 200
|
50
|
+
response
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
class OAuth
|
56
|
+
|
57
|
+
attr_accessor :verb, :uri, :body
|
58
|
+
|
59
|
+
def initialize(verb, uri, body)
|
60
|
+
@verb = verb
|
61
|
+
@uri = uri
|
62
|
+
@body = body
|
63
|
+
end
|
64
|
+
|
65
|
+
def authorization_header
|
66
|
+
header = "OAuth "
|
67
|
+
oauth_params.each do |k, v|
|
68
|
+
header += "#{k}=\"#{v}\", "
|
69
|
+
end
|
70
|
+
header.slice(0..-3) # chop off last ", "
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
def oauth_params
|
76
|
+
{
|
77
|
+
'oauth_timestamp' => Time.now.getutc.to_i.to_s,
|
78
|
+
'oauth_token' => escape_uri(Picturelife.access_token),
|
79
|
+
'oauth_consumer_key' => Picturelife.client_id,
|
80
|
+
'oauth_signature_method' => 'HMAC-SHA1',
|
81
|
+
'oauth_signature' => signature,
|
82
|
+
'oauth_nonce' => nonce,
|
83
|
+
'oauth_version' => '1.0'
|
84
|
+
}
|
85
|
+
end
|
86
|
+
|
87
|
+
def signature
|
88
|
+
body = @body.dup
|
89
|
+
body['access_token'] = Picturelife.access_token
|
90
|
+
body = body.map { |k, v| [k.to_s, v.to_s] }
|
91
|
+
body = body.sort.map { |k, v| escape_uri("#{k}=#{v}") }.join('%26')
|
92
|
+
|
93
|
+
base = [verb, escape_uri(uri), body].join('&')
|
94
|
+
key = [Picturelife.client_secret, Picturelife.access_token].join('&')
|
95
|
+
escape_uri(sign(key, base))
|
96
|
+
end
|
97
|
+
|
98
|
+
def sign(key, base)
|
99
|
+
digest = OpenSSL::Digest::Digest.new('sha1')
|
100
|
+
hmac = OpenSSL::HMAC.digest(digest, key, base)
|
101
|
+
Base64.encode64(hmac).chomp.gsub(/\n/, '')
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
module Picturelife
|
2
|
+
|
3
|
+
RULER_ENDPOINT = "https://services.picturelife.com/ruler/"
|
4
|
+
|
5
|
+
module Ruler
|
6
|
+
|
7
|
+
def upload(file_path, options = {}, processing_needs = [])
|
8
|
+
needs_token!
|
9
|
+
|
10
|
+
@file_path = file_path
|
11
|
+
@signature = get_signature(file_path)
|
12
|
+
@filename = get_filename(file_path)
|
13
|
+
@force = !! options[:force]
|
14
|
+
@options = options
|
15
|
+
@processing_needs = processing_needs
|
16
|
+
|
17
|
+
media_exists || perform_upload
|
18
|
+
|
19
|
+
rescue RulerError => e
|
20
|
+
(e.code == 519256) ? retry : raise(e)
|
21
|
+
rescue Timeout::Error, Errno::ECONNRESET
|
22
|
+
retry
|
23
|
+
end
|
24
|
+
|
25
|
+
def head(file_path)
|
26
|
+
@file_path = file_path
|
27
|
+
@signature = get_signature(file_path)
|
28
|
+
@filename = get_filename(file_path)
|
29
|
+
|
30
|
+
ruler('HEAD')
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def media_exists
|
36
|
+
medias = Picturelife::Medias.check_signatures(signatures: @signature)['signatures'] || {}
|
37
|
+
return false if medias.empty?
|
38
|
+
return Picturelife::Medias.show(media_id: medias[@signature]['media_id']) unless @force
|
39
|
+
return false
|
40
|
+
end
|
41
|
+
|
42
|
+
def perform_upload
|
43
|
+
header = { 'Content-Range' => "bytes #{filesize_uploaded}-#{filesize_total}/#{filesize_total}" }
|
44
|
+
location = ruler(:put, file_partial, header)['location']
|
45
|
+
create_media_from_ruler(location)
|
46
|
+
end
|
47
|
+
|
48
|
+
def file_partial
|
49
|
+
IO.binread(@file_path, nil, filesize_uploaded)
|
50
|
+
end
|
51
|
+
|
52
|
+
def filesize_total
|
53
|
+
File.size(@file_path)
|
54
|
+
end
|
55
|
+
|
56
|
+
def filesize_uploaded
|
57
|
+
@filesize_uploaded ||= lambda do
|
58
|
+
uploaded = ruler(:head)['X-Ruler-Size']
|
59
|
+
uploaded = uploaded.first if uploaded
|
60
|
+
uploaded.to_i
|
61
|
+
end.call
|
62
|
+
end
|
63
|
+
|
64
|
+
def create_media_from_ruler(location)
|
65
|
+
Picturelife::Media.create({
|
66
|
+
url: location,
|
67
|
+
signature: @signature,
|
68
|
+
force: (!!@force).to_s,
|
69
|
+
local_path: @file_path,
|
70
|
+
processing_needs: @processing_needs.map(&:to_s).join(',')
|
71
|
+
}.merge(@options))
|
72
|
+
end
|
73
|
+
|
74
|
+
def auth
|
75
|
+
{
|
76
|
+
'Authorization' => "Bearer #{Picturelife.access_token}",
|
77
|
+
'User-Agent' => "rubygem/#{VERSION}"
|
78
|
+
}
|
79
|
+
end
|
80
|
+
|
81
|
+
def ruler(verb, data = nil, headers = {})
|
82
|
+
http = Excon.new(RULER_ENDPOINT + @filename)
|
83
|
+
res = http.request(
|
84
|
+
method: verb,
|
85
|
+
query: { signature: @signature },
|
86
|
+
headers: auth.merge(headers),
|
87
|
+
body: data,
|
88
|
+
idempotent: true
|
89
|
+
)
|
90
|
+
|
91
|
+
body = res.body
|
92
|
+
body = (body && body.length >= 2) ? JSON.parse(body) : {}
|
93
|
+
body.merge!(res.headers)
|
94
|
+
|
95
|
+
unless body['X-Ruler-Error-Code'].to_s.empty?
|
96
|
+
raise RulerError.new(body['X-Ruler-Error-Code'], body['X-Ruler-Error'], body)
|
97
|
+
else
|
98
|
+
body
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Picturelife
|
2
|
+
|
3
|
+
def self.const_missing(name)
|
4
|
+
Stub.new(underscore(name.to_s))
|
5
|
+
end
|
6
|
+
|
7
|
+
class Stub
|
8
|
+
|
9
|
+
attr_reader :module_name
|
10
|
+
|
11
|
+
def initialize(module_name)
|
12
|
+
@module_name = module_name
|
13
|
+
end
|
14
|
+
|
15
|
+
def method_missing(name, args = nil)
|
16
|
+
method = [module_name, name].join('/')
|
17
|
+
Api.call(method, args)
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Picturelife
|
2
|
+
module Util
|
3
|
+
|
4
|
+
def escape_uri(text)
|
5
|
+
return '' if text.nil?
|
6
|
+
CGI::escape(text)
|
7
|
+
end
|
8
|
+
|
9
|
+
def underscore(camelcase)
|
10
|
+
camelcase.gsub(/(.)([A-Z])/,'\1_\2').downcase
|
11
|
+
end
|
12
|
+
|
13
|
+
def api_get(uri)
|
14
|
+
uri = URI(URI.encode(uri))
|
15
|
+
JSON.parse(Net::HTTP.get(uri))
|
16
|
+
end
|
17
|
+
|
18
|
+
def hashrocket_to_json(string)
|
19
|
+
string.gsub('=>', ':')
|
20
|
+
end
|
21
|
+
|
22
|
+
def client_uuid
|
23
|
+
@client_uuid ||= rand(10**10).to_s
|
24
|
+
end
|
25
|
+
|
26
|
+
def nonce
|
27
|
+
rand(10**10).to_s
|
28
|
+
end
|
29
|
+
|
30
|
+
def get_signature(file_path)
|
31
|
+
Digest::SHA256.hexdigest File.read(file_path)
|
32
|
+
end
|
33
|
+
|
34
|
+
def get_filename(file_path)
|
35
|
+
[get_signature(file_path), '_', client_uuid, File.extname(file_path)].join
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
data/picturelife.gemspec
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'picturelife'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'picturelife'
|
8
|
+
spec.version = Picturelife::VERSION
|
9
|
+
spec.authors = ['Albert Zak']
|
10
|
+
spec.email = ['me@albertzak.com']
|
11
|
+
spec.summary = %q{Picturelife API. OAuth. Resumable Uploads.}
|
12
|
+
spec.description = 'A gem to access the Picturelife API.'
|
13
|
+
spec.homepage = 'https://github.com/albertzak/picturelife'
|
14
|
+
spec.license = 'MIT'
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ['lib']
|
20
|
+
|
21
|
+
spec.add_dependency 'json'
|
22
|
+
spec.add_dependency 'excon'
|
23
|
+
spec.add_dependency 'oauth'
|
24
|
+
|
25
|
+
spec.add_development_dependency 'bundler'
|
26
|
+
spec.add_development_dependency 'rake'
|
27
|
+
spec.add_development_dependency 'rspec'
|
28
|
+
spec.add_development_dependency 'rspec-nc'
|
29
|
+
spec.add_development_dependency 'guard'
|
30
|
+
spec.add_development_dependency 'guard-rspec'
|
31
|
+
spec.add_development_dependency 'pry'
|
32
|
+
spec.add_development_dependency 'pry-remote'
|
33
|
+
spec.add_development_dependency 'pry-nav'
|
34
|
+
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Picturelife do
|
4
|
+
before(:each) do
|
5
|
+
Picturelife.client_id = 'aaa'
|
6
|
+
Picturelife.client_secret = 'bbb'
|
7
|
+
Picturelife.redirect_uri = 'ccc'
|
8
|
+
|
9
|
+
Picturelife.stub(:authenticated?).and_return(true)
|
10
|
+
|
11
|
+
Net::HTTP.stub(:get).and_return('{"status":20000}')
|
12
|
+
|
13
|
+
File.stub(:read).and_return('image')
|
14
|
+
File.stub(:extname).and_return('jpg')
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'accepts api keys' do
|
18
|
+
expect { Picturelife.client_id = 'test' }.not_to raise_error
|
19
|
+
expect { Picturelife.client_secret = 'test' }.not_to raise_error
|
20
|
+
expect { Picturelife.redirect_uri = 'test' }.not_to raise_error
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'raises error if not configured' do
|
24
|
+
Picturelife.reset_configuration!
|
25
|
+
expect { Picturelife.authorization_uri }. to raise_error(Picturelife::NotConfiguredError)
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'raises error if not authenticated' do
|
29
|
+
Picturelife.stub(:authenticated?).and_return(false)
|
30
|
+
expect { Picturelife::Api.call('/') }. to raise_error(Picturelife::NotAuthenticatedError)
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'escapes redirect uri' do
|
34
|
+
Picturelife.redirect_uri = 'http://localhost:4567/?code=a'
|
35
|
+
expect(Picturelife.redirect_uri).to eq 'http%3A%2F%2Flocalhost%3A4567%2F%3Fcode%3Da'
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'builds valid authorization url' do
|
39
|
+
expect(Picturelife.authorization_uri).to include 'aaa'
|
40
|
+
expect(Picturelife.authorization_uri).to include 'bbb'
|
41
|
+
expect(Picturelife.authorization_uri).to include 'ccc'
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'gets and sets access token' do
|
45
|
+
Net::HTTP.stub(:get).and_return('{"access_token":"token","status":200}')
|
46
|
+
|
47
|
+
Picturelife.access_token_from_code = 'code'
|
48
|
+
expect(Picturelife.access_token).to eq 'token'
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'generated client uuid' do
|
52
|
+
expect(Picturelife.send(:client_uuid)).to be_kind_of(String)
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'caches uuid' do
|
56
|
+
uuid = Proc.new { Picturelife.send(:client_uuid) }
|
57
|
+
expect(uuid).to eq uuid
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'queries API' do
|
61
|
+
expect(Net::HTTP).to receive(:get) do |uri|
|
62
|
+
expect(uri.scheme).to eq 'https'
|
63
|
+
expect(uri.query).to include 'client_id=aaa'
|
64
|
+
expect(uri.query).to include 'client_secret=bbb'
|
65
|
+
expect(uri.query).to include 'code=code'
|
66
|
+
expect(uri.to_s).to include Picturelife::OAUTH_ENDPOINT
|
67
|
+
end.and_return('{"access_token"=>"ddd","status"=>200}')
|
68
|
+
|
69
|
+
Picturelife.access_token_from_code = 'code'
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'API returns hashrocket that is converted to json' do
|
73
|
+
parsable = hashrocket_to_json('{"access_token"=>"ddd"}')
|
74
|
+
expect { JSON.parse(parsable) }.not_to raise_error
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'stubs missing constant names' do
|
78
|
+
expect { Picturelife::SmartFilters }.not_to raise_error
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'stubs missing methods on missing constants' do
|
82
|
+
expect { Picturelife::SmartFilters.index }.not_to raise_error
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'stubs call correct api method' do
|
86
|
+
expect(Picturelife::Api).to receive(:call).with('smart_filters/index', nil)
|
87
|
+
Picturelife::SmartFilters.index
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'stubs call correct api method with argument' do
|
91
|
+
expect(Picturelife::Api).to receive(:call).with('smart_filters/index', include_system: true)
|
92
|
+
Picturelife::SmartFilters.index(include_system: true)
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'stubs call correct api method with multiple args' do
|
96
|
+
expect(Picturelife::Api).to receive(:call).with('smart_filters/index', { include_system: true, limit: 10 })
|
97
|
+
Picturelife::SmartFilters.index({ include_system: true, limit: 10 })
|
98
|
+
end
|
99
|
+
|
100
|
+
it 'builds url parameters without args' do
|
101
|
+
url = Picturelife::Api.send(:build_uri, 'smart_filters/index')
|
102
|
+
expect(url).to include('https://api.picturelife.com/smart_filters/index')
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'builds url parameters from args' do
|
106
|
+
url = Picturelife::Api.send(:build_uri, 'smart_filters/index', {limit: 10})
|
107
|
+
expect(url).to include('https://api.picturelife.com/smart_filters/index?limit=10')
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'builds url parameters from multiple args' do
|
111
|
+
url = Picturelife::Api.send(:build_uri, 'smart_filters/index', {limit: 10, include_system: true})
|
112
|
+
expect(url).to include('https://api.picturelife.com/smart_filters/index?limit=10&include_system=true')
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'adds access token to url parameters' do
|
116
|
+
Picturelife.stub(:access_token).and_return('123')
|
117
|
+
url = Picturelife::Api.send(:build_uri, 'smart_filters/index', {limit: 10, include_system: true})
|
118
|
+
expect(url).to eq('https://api.picturelife.com/smart_filters/index?limit=10&include_system=true&access_token=123')
|
119
|
+
end
|
120
|
+
|
121
|
+
it 'needs authentication for calls to api' do
|
122
|
+
Picturelife.stub(:authenticated?).and_return(false)
|
123
|
+
expect { Picturelife::Api.call('index') }.to raise_error(Picturelife::NotAuthenticatedError)
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'needs authentication for calls to ruler' do
|
127
|
+
Picturelife.stub(:authenticated?).and_return(false)
|
128
|
+
expect { Picturelife::Ruler.upload(StringIO.new) }.to raise_error(Picturelife::NotAuthenticatedError)
|
129
|
+
end
|
130
|
+
|
131
|
+
it 'needs authentication for calls to ruler' do
|
132
|
+
Picturelife::Ruler.should_receive(:media_exists).and_return('media_id')
|
133
|
+
expect(Picturelife::Ruler.upload(StringIO.new)).to eq('media_id')
|
134
|
+
end
|
135
|
+
|
136
|
+
|
137
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,231 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: picturelife
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Albert Zak
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-06-06 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: json
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: excon
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: oauth
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: bundler
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rake
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - '>='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rspec
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - '>='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - '>='
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rspec-nc
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - '>='
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - '>='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: guard
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - '>='
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: guard-rspec
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - '>='
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - '>='
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: pry
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - '>='
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - '>='
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: pry-remote
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - '>='
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0'
|
160
|
+
type: :development
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - '>='
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
167
|
+
- !ruby/object:Gem::Dependency
|
168
|
+
name: pry-nav
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - '>='
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
type: :development
|
175
|
+
prerelease: false
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - '>='
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '0'
|
181
|
+
description: A gem to access the Picturelife API.
|
182
|
+
email:
|
183
|
+
- me@albertzak.com
|
184
|
+
executables: []
|
185
|
+
extensions: []
|
186
|
+
extra_rdoc_files: []
|
187
|
+
files:
|
188
|
+
- .gitignore
|
189
|
+
- Gemfile
|
190
|
+
- Guardfile
|
191
|
+
- LICENSE.txt
|
192
|
+
- README.md
|
193
|
+
- Rakefile
|
194
|
+
- lib/picturelife.rb
|
195
|
+
- lib/picturelife/api.rb
|
196
|
+
- lib/picturelife/base.rb
|
197
|
+
- lib/picturelife/error.rb
|
198
|
+
- lib/picturelife/oauth.rb
|
199
|
+
- lib/picturelife/ruler.rb
|
200
|
+
- lib/picturelife/stub.rb
|
201
|
+
- lib/picturelife/util.rb
|
202
|
+
- picturelife.gemspec
|
203
|
+
- spec/picturelife_spec.rb
|
204
|
+
- spec/spec_helper.rb
|
205
|
+
homepage: https://github.com/albertzak/picturelife
|
206
|
+
licenses:
|
207
|
+
- MIT
|
208
|
+
metadata: {}
|
209
|
+
post_install_message:
|
210
|
+
rdoc_options: []
|
211
|
+
require_paths:
|
212
|
+
- lib
|
213
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
214
|
+
requirements:
|
215
|
+
- - '>='
|
216
|
+
- !ruby/object:Gem::Version
|
217
|
+
version: '0'
|
218
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
219
|
+
requirements:
|
220
|
+
- - '>='
|
221
|
+
- !ruby/object:Gem::Version
|
222
|
+
version: '0'
|
223
|
+
requirements: []
|
224
|
+
rubyforge_project:
|
225
|
+
rubygems_version: 2.0.3
|
226
|
+
signing_key:
|
227
|
+
specification_version: 4
|
228
|
+
summary: Picturelife API. OAuth. Resumable Uploads.
|
229
|
+
test_files:
|
230
|
+
- spec/picturelife_spec.rb
|
231
|
+
- spec/spec_helper.rb
|