picasa 0.5.1 → 0.5.2
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/README.md +25 -6
- data/extra/Thorfile +23 -10
- data/lib/picasa/api/album.rb +9 -8
- data/lib/picasa/api/base.rb +10 -4
- data/lib/picasa/api/photo.rb +6 -5
- data/lib/picasa/api/tag.rb +8 -6
- data/lib/picasa/client.rb +45 -3
- data/lib/picasa/connection.rb +29 -63
- data/lib/picasa/utils.rb +8 -1
- data/lib/picasa/version.rb +1 -1
- data/test/client_test.rb +26 -5
- data/test/connection_test.rb +10 -54
- data/test/helper.rb +1 -1
- data/test/utils_test.rb +20 -0
- metadata +2 -2
data/README.md
CHANGED
@@ -24,14 +24,21 @@ client.photo.create("album_id", file_path: "path/to/my-photo.png")
|
|
24
24
|
# => Picasa::Presenter::Photo
|
25
25
|
```
|
26
26
|
|
27
|
-
|
28
|
-
This affect results to contain private data, however it can be controlled by `access` parameter.
|
27
|
+
### Authentication
|
29
28
|
|
30
|
-
|
29
|
+
When request is authenticated, response will contain private data, however this can be controlled by `access` parameter.
|
31
30
|
|
32
|
-
|
33
|
-
|
34
|
-
|
31
|
+
You can authenticate by specifing password:
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
client = Picasa::Client.new(user_id: "some.user@gmail.com", password: "secret")
|
35
|
+
```
|
36
|
+
|
37
|
+
Or by setting custom authorization header, i.e. taken from OAuth authentication:
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
client = Picasa::Client.new(user_id: "some.user@gmail.com", authorization_header: "OAuth token")
|
41
|
+
```
|
35
42
|
|
36
43
|
## Extra
|
37
44
|
|
@@ -51,6 +58,8 @@ And then use it (it will create album taking title from folder name and upload a
|
|
51
58
|
|
52
59
|
```
|
53
60
|
GOOGLE_USER_ID=your.email@gmail.com GOOGLE_PASSWORD=secret thor picasa_uploader:upload_all path-to-folder-with-photos
|
61
|
+
# Without specifing password
|
62
|
+
GOOGLE_USER_ID=your.email@gmail.com GOOGLE_AUTHORIZATION_HEADER="GoogleLogin auth=token" thor picasa_uploader:upload_all path-to-folder-with-photos
|
54
63
|
```
|
55
64
|
|
56
65
|
If your upload was somehow interrupted, you can resume it by adding `--continue` option:
|
@@ -59,6 +68,16 @@ If your upload was somehow interrupted, you can resume it by adding `--continue`
|
|
59
68
|
GOOGLE_USER_ID=your.email@gmail.com GOOGLE_PASSWORD=secret thor picasa_uploader:upload_all --continue path-to-folder-with-photos
|
60
69
|
```
|
61
70
|
|
71
|
+
If you run out of quota and want to resize images to fit Picasa free storage limits, you might be interested in Thor task for [that job](https://github.com/morgoth/ripper#usage)
|
72
|
+
|
73
|
+
## Caveats
|
74
|
+
|
75
|
+
Currently picasa wont work with `ox` xml parser.
|
76
|
+
|
77
|
+
Using `rexml` parser wont return `etag` attribute properly.
|
78
|
+
|
79
|
+
I recommend to use `libxml` or `nokogiri`.
|
80
|
+
|
62
81
|
## Continuous Integration
|
63
82
|
[](http://travis-ci.org/morgoth/picasa)
|
64
83
|
|
data/extra/Thorfile
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require "picasa"
|
2
|
+
require "thread"
|
2
3
|
|
3
4
|
# Temporary requirement
|
4
5
|
MultiXml.parser = :libxml
|
@@ -7,7 +8,8 @@ class PicasaUploader < Thor
|
|
7
8
|
include Thor::Actions
|
8
9
|
|
9
10
|
desc "upload_all DIR", "Uploads all photos from given directory (pass --continue to resume uploading)"
|
10
|
-
|
11
|
+
method_option :continue, type: :boolean, default: false, aliases: "-c", desc: "continue aborted upload"
|
12
|
+
method_option :threads, type: :numeric, default: 8, aliases: "-t", desc: "specify threads number"
|
11
13
|
def upload_all(dir = File.basename(Dir.getwd))
|
12
14
|
require_credentials
|
13
15
|
|
@@ -34,23 +36,34 @@ class PicasaUploader < Thor
|
|
34
36
|
album = create_album(album_name)
|
35
37
|
end
|
36
38
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
39
|
+
mutex = Mutex.new
|
40
|
+
entries_size = entries.size
|
41
|
+
number = 0
|
42
|
+
options[:threads].times.map do
|
43
|
+
Thread.new(entries) do |files|
|
44
|
+
while file = mutex.synchronize { files.pop }
|
45
|
+
mutex.synchronize { number += 1 }
|
46
|
+
say("Uploading photo #{file} to album #{album.title} - #{number}/#{entries_size}")
|
47
|
+
create_photo(album, file)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end.each(&:join)
|
51
|
+
say "Finished uploading #{number} photos"
|
43
52
|
end
|
44
53
|
end
|
45
54
|
|
46
55
|
no_tasks do
|
47
56
|
def client
|
48
|
-
@client ||= Picasa::Client.new(user_id: ENV["GOOGLE_USER_ID"],
|
57
|
+
@client ||= Picasa::Client.new(user_id: ENV["GOOGLE_USER_ID"],
|
58
|
+
authorization_header: ENV["GOOGLE_AUTHORIZATION_HEADER"],
|
59
|
+
password: ENV["GOOGLE_PASSWORD"])
|
49
60
|
end
|
50
61
|
|
51
62
|
def require_credentials
|
52
|
-
say "You must specify GOOGLE_USER_ID env variable" and exit
|
53
|
-
|
63
|
+
say "You must specify GOOGLE_USER_ID env variable" and exit if ENV["GOOGLE_USER_ID"].nil?
|
64
|
+
if ENV["GOOGLE_AUTHORIZATION_HEADER"].nil? && ENV["GOOGLE_PASSWORD"].nil?
|
65
|
+
say "You must specify GOOGLE_PASSWORD or GOOGLE_AUTHORIZATION_HEADER env variable" and exit
|
66
|
+
end
|
54
67
|
end
|
55
68
|
|
56
69
|
def create_album(title)
|
data/lib/picasa/api/album.rb
CHANGED
@@ -14,9 +14,9 @@ module Picasa
|
|
14
14
|
# @return [Presenter::AlbumList]
|
15
15
|
def list(options = {})
|
16
16
|
uri = URI.parse("/data/feed/api/user/#{user_id}")
|
17
|
-
|
17
|
+
response = Connection.new.get(:path => uri.path, :query => options, :headers => auth_header)
|
18
18
|
|
19
|
-
Presenter::AlbumList.new(
|
19
|
+
Presenter::AlbumList.new(MultiXml.parse(response.body)["feed"])
|
20
20
|
end
|
21
21
|
|
22
22
|
# Returns photo list for given album
|
@@ -31,9 +31,9 @@ module Picasa
|
|
31
31
|
# @raise [NotFoundError] raised when album cannot be found
|
32
32
|
def show(album_id, options = {})
|
33
33
|
uri = URI.parse("/data/feed/api/user/#{user_id}/albumid/#{album_id}")
|
34
|
-
|
34
|
+
response = Connection.new.get(:path => uri.path, :query => options, :headers => auth_header)
|
35
35
|
|
36
|
-
Presenter::Album.new(
|
36
|
+
Presenter::Album.new(MultiXml.parse(response.body)["feed"])
|
37
37
|
end
|
38
38
|
|
39
39
|
# Creates album
|
@@ -53,8 +53,9 @@ module Picasa
|
|
53
53
|
|
54
54
|
template = Template.new(:new_album, params)
|
55
55
|
uri = URI.parse("/data/feed/api/user/#{user_id}")
|
56
|
-
|
57
|
-
|
56
|
+
response = Connection.new.post(:path => uri.path, :body => template.render, :headers => auth_header)
|
57
|
+
|
58
|
+
Presenter::Album.new(MultiXml.parse(response.body)["entry"])
|
58
59
|
end
|
59
60
|
|
60
61
|
# Destroys given album
|
@@ -67,9 +68,9 @@ module Picasa
|
|
67
68
|
# @raise [NotFoundError] raised when album cannot be found
|
68
69
|
# @raise [PreconditionFailedError] raised when ETag does not match
|
69
70
|
def destroy(album_id, options = {})
|
70
|
-
headers = {"If-Match" => options.fetch(:etag, "*")}
|
71
|
+
headers = auth_header.merge({"If-Match" => options.fetch(:etag, "*")})
|
71
72
|
uri = URI.parse("/data/entry/api/user/#{user_id}/albumid/#{album_id}")
|
72
|
-
Connection.new
|
73
|
+
Connection.new.delete(:path => uri.path, :headers => headers)
|
73
74
|
true
|
74
75
|
end
|
75
76
|
alias :delete :destroy
|
data/lib/picasa/api/base.rb
CHANGED
@@ -1,17 +1,23 @@
|
|
1
1
|
module Picasa
|
2
2
|
module API
|
3
3
|
class Base
|
4
|
-
attr_reader :user_id, :
|
4
|
+
attr_reader :user_id, :authorization_header
|
5
5
|
|
6
6
|
# @param [Hash] credentials
|
7
7
|
# @option credentials [String] :user_id google username/email
|
8
|
-
# @option credentials [String] :
|
9
|
-
def initialize(credentials)
|
8
|
+
# @option credentials [String] :authorization_header header for authenticating requests
|
9
|
+
def initialize(credentials = {})
|
10
10
|
if MultiXml.parser.to_s == "MultiXml::Parsers::Ox"
|
11
11
|
raise StandardError, "MultiXml parser is set to :ox - picasa gem will not work with it currently, use one of: :libxml, :nokogiri, :rexml"
|
12
12
|
end
|
13
13
|
@user_id = credentials.fetch(:user_id)
|
14
|
-
@
|
14
|
+
@authorization_header = credentials[:authorization_header]
|
15
|
+
end
|
16
|
+
|
17
|
+
def auth_header
|
18
|
+
{}.tap do |header|
|
19
|
+
header["Authorization"] = authorization_header if authorization_header
|
20
|
+
end
|
15
21
|
end
|
16
22
|
end
|
17
23
|
end
|
data/lib/picasa/api/photo.rb
CHANGED
@@ -20,11 +20,12 @@ module Picasa
|
|
20
20
|
params[:content_type] ||= (file && file.content_type) || raise(ArgumentError.new("content_type must be specified"))
|
21
21
|
|
22
22
|
template = Template.new(:new_photo, params)
|
23
|
-
headers = {"Content-Type" => "multipart/related; boundary=\"#{params[:boundary]}\""}
|
23
|
+
headers = auth_header.merge({"Content-Type" => "multipart/related; boundary=\"#{params[:boundary]}\""})
|
24
24
|
|
25
25
|
uri = URI.parse("/data/feed/api/user/#{user_id}/albumid/#{album_id}")
|
26
|
-
|
27
|
-
|
26
|
+
response = Connection.new.post(:path => uri.path, :body => template.render, :headers => headers)
|
27
|
+
|
28
|
+
Presenter::Photo.new(MultiXml.parse(response.body)["entry"])
|
28
29
|
end
|
29
30
|
|
30
31
|
# Destroys given photo
|
@@ -38,9 +39,9 @@ module Picasa
|
|
38
39
|
# @raise [NotFoundError] raised when album or photo cannot be found
|
39
40
|
# @raise [PreconditionFailedError] raised when ETag does not match
|
40
41
|
def destroy(album_id, photo_id, options = {})
|
41
|
-
headers = {"If-Match" => options.fetch(:etag, "*")}
|
42
|
+
headers = auth_header.merge({"If-Match" => options.fetch(:etag, "*")})
|
42
43
|
uri = URI.parse("/data/entry/api/user/#{user_id}/albumid/#{album_id}/photoid/#{photo_id}")
|
43
|
-
Connection.new
|
44
|
+
Connection.new.delete(:path => uri.path, :headers => headers)
|
44
45
|
true
|
45
46
|
end
|
46
47
|
alias :delete :destroy
|
data/lib/picasa/api/tag.rb
CHANGED
@@ -11,8 +11,8 @@ module Picasa
|
|
11
11
|
#
|
12
12
|
# @return [Presenter::TagList]
|
13
13
|
def list(options = {})
|
14
|
-
album_id = options
|
15
|
-
photo_id = options
|
14
|
+
album_id = options.delete(:album_id)
|
15
|
+
photo_id = options.delete(:photo_id)
|
16
16
|
raise(ArgumentError, "You must specify album_id when providing photo_id") if photo_id && !album_id
|
17
17
|
|
18
18
|
path = "/data/feed/api/user/#{user_id}"
|
@@ -20,8 +20,9 @@ module Picasa
|
|
20
20
|
path << "/photoid/#{photo_id}" if photo_id
|
21
21
|
|
22
22
|
uri = URI.parse(path)
|
23
|
-
|
24
|
-
|
23
|
+
response = Connection.new.get(:path => uri.path, :query => options.merge(:kind => "tag"), :headers => auth_header)
|
24
|
+
|
25
|
+
Presenter::TagList.new(MultiXml.parse(response.body)["feed"])
|
25
26
|
end
|
26
27
|
|
27
28
|
# Creates a tag for a photo.
|
@@ -42,8 +43,9 @@ module Picasa
|
|
42
43
|
template = Template.new("new_tag", params)
|
43
44
|
|
44
45
|
uri = URI.parse(path)
|
45
|
-
|
46
|
-
|
46
|
+
response = Connection.new.post(:path => uri.path, :body => template.render, :headers => auth_header)
|
47
|
+
|
48
|
+
Presenter::Tag.new(MultiXml.parse(response.body)["entry"])
|
47
49
|
end
|
48
50
|
end
|
49
51
|
end
|
data/lib/picasa/client.rb
CHANGED
@@ -1,13 +1,15 @@
|
|
1
1
|
module Picasa
|
2
2
|
class Client
|
3
|
-
attr_reader :
|
3
|
+
attr_reader :user_id, :password, :authorization_header
|
4
4
|
|
5
5
|
# @param [Hash] credentials
|
6
6
|
# @option credentials [String] :user_id google username/email
|
7
7
|
# @option credentials [String] :password password for given username/email
|
8
|
+
# @option credentials [String] :authorization_header custom authorization header (i.e. taken from OAuth2)
|
8
9
|
def initialize(credentials = {})
|
9
|
-
credentials[:user_id] || raise(ArgumentError, "You must specify user_id")
|
10
|
-
@
|
10
|
+
@user_id = credentials[:user_id] || raise(ArgumentError, "You must specify user_id")
|
11
|
+
@password = credentials[:password]
|
12
|
+
@authorization_header = credentials[:authorization_header]
|
11
13
|
end
|
12
14
|
|
13
15
|
# @return [API::Album]
|
@@ -18,6 +20,7 @@ module Picasa
|
|
18
20
|
# album_list.title
|
19
21
|
# # => "My album"
|
20
22
|
def album
|
23
|
+
authenticate if authenticates?
|
21
24
|
API::Album.new(credentials)
|
22
25
|
end
|
23
26
|
|
@@ -29,6 +32,7 @@ module Picasa
|
|
29
32
|
# photo.id
|
30
33
|
# # => "4322232322421"
|
31
34
|
def photo
|
35
|
+
authenticate if authenticates?
|
32
36
|
API::Photo.new(credentials)
|
33
37
|
end
|
34
38
|
|
@@ -40,7 +44,45 @@ module Picasa
|
|
40
44
|
# tag_list.title
|
41
45
|
# # => "holidays"
|
42
46
|
def tag
|
47
|
+
authenticate if authenticates?
|
43
48
|
API::Tag.new(credentials)
|
44
49
|
end
|
50
|
+
|
51
|
+
# @return [String]
|
52
|
+
def authenticate
|
53
|
+
response = Connection.new.post(
|
54
|
+
:host => API_AUTH_URL,
|
55
|
+
:headers => {"Content-Type" => "application/x-www-form-urlencoded"},
|
56
|
+
:path => "/accounts/ClientLogin",
|
57
|
+
:body => Utils.inline_query(
|
58
|
+
"accountType" => "HOSTED_OR_GOOGLE",
|
59
|
+
"Email" => user_id,
|
60
|
+
"Passwd" => password,
|
61
|
+
"service" => "lh2",
|
62
|
+
"source" => "ruby-gem-v#{VERSION}"
|
63
|
+
)
|
64
|
+
)
|
65
|
+
|
66
|
+
key = extract_auth_key(response.body)
|
67
|
+
@authorization_header = "GoogleLogin auth=#{key}"
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def authenticates?
|
73
|
+
password && authorization_header.nil?
|
74
|
+
end
|
75
|
+
|
76
|
+
def credentials
|
77
|
+
{:user_id => user_id}.tap do |credentials|
|
78
|
+
credentials[:authorization_header] = authorization_header if authorization_header
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def extract_auth_key(data)
|
83
|
+
response = data.split("\n").map { |v| v.split("=") }
|
84
|
+
params = Hash[response]
|
85
|
+
params["Auth"]
|
86
|
+
end
|
45
87
|
end
|
46
88
|
end
|
data/lib/picasa/connection.rb
CHANGED
@@ -4,13 +4,6 @@ require "uri"
|
|
4
4
|
|
5
5
|
module Picasa
|
6
6
|
class Connection
|
7
|
-
attr_reader :user_id, :password
|
8
|
-
|
9
|
-
def initialize(credentials = {})
|
10
|
-
@user_id = credentials.fetch(:user_id)
|
11
|
-
@password = credentials.fetch(:password, nil)
|
12
|
-
end
|
13
|
-
|
14
7
|
def http(url = API_URL)
|
15
8
|
uri = URI.parse(url)
|
16
9
|
http = Net::HTTP.new(uri.host, uri.port)
|
@@ -18,40 +11,41 @@ module Picasa
|
|
18
11
|
http
|
19
12
|
end
|
20
13
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
14
|
+
# @param [Hash] params request arguments
|
15
|
+
# @option params [String] :host host of request
|
16
|
+
# @option params [String] :path request path
|
17
|
+
# @option params [String] :body request body (for POST)
|
18
|
+
# @option params [String] :query request url query
|
19
|
+
# @option params [String] :headers request headers
|
20
|
+
def get(params = {})
|
21
|
+
params[:headers] ||= {}
|
22
|
+
params[:query] ||= {}
|
23
|
+
params[:host] ||= API_URL
|
24
|
+
|
25
|
+
path = path_with_query(params[:path], params[:query])
|
26
|
+
request = Net::HTTP::Get.new(path, headers.merge(params[:headers]))
|
27
|
+
handle_response(http(params[:host]).request(request))
|
28
28
|
end
|
29
29
|
|
30
|
-
def post(
|
31
|
-
|
30
|
+
def post(params = {})
|
31
|
+
params[:headers] ||= {}
|
32
|
+
params[:host] ||= API_URL
|
32
33
|
|
33
|
-
request = Net::HTTP::Post.new(path, headers.merge(
|
34
|
-
request.body = body
|
35
|
-
|
36
|
-
MultiXml.parse(response.body)
|
34
|
+
request = Net::HTTP::Post.new(params[:path], headers.merge(params[:headers]))
|
35
|
+
request.body = params[:body]
|
36
|
+
handle_response(http(params[:host]).request(request))
|
37
37
|
end
|
38
38
|
|
39
|
-
def delete(
|
40
|
-
|
39
|
+
def delete(params = {})
|
40
|
+
params[:headers] ||= {}
|
41
|
+
params[:host] ||= API_URL
|
41
42
|
|
42
|
-
request = Net::HTTP::Delete.new(path, headers.merge(
|
43
|
-
handle_response(http.request(request))
|
43
|
+
request = Net::HTTP::Delete.new(params[:path], headers.merge(params[:headers]))
|
44
|
+
handle_response(http(params[:host]).request(request))
|
44
45
|
end
|
45
46
|
|
46
|
-
def
|
47
|
-
|
48
|
-
dasherized = key.to_s.gsub("_", "-")
|
49
|
-
"#{CGI.escape(dasherized)}=#{CGI.escape(value.to_s)}"
|
50
|
-
end.join("&")
|
51
|
-
end
|
52
|
-
|
53
|
-
def path_with_params(path, params = {})
|
54
|
-
path = path + "?" + inline_params(params) unless params.empty?
|
47
|
+
def path_with_query(path, query = {})
|
48
|
+
path = path + "?" + Utils.inline_query(query) unless query.empty?
|
55
49
|
URI.parse(path).to_s
|
56
50
|
end
|
57
51
|
|
@@ -74,38 +68,10 @@ module Picasa
|
|
74
68
|
|
75
69
|
def headers
|
76
70
|
{
|
77
|
-
"User-Agent" =>
|
71
|
+
"User-Agent" => "ruby-gem-v#{VERSION}",
|
78
72
|
"GData-Version" => API_VERSION,
|
79
73
|
"Content-Type" => "application/atom+xml"
|
80
|
-
}
|
81
|
-
headers["Authorization"] = "GoogleLogin auth=#{@auth_key}" if @auth_key
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
def auth?
|
86
|
-
!password.nil?
|
87
|
-
end
|
88
|
-
|
89
|
-
def authenticate
|
90
|
-
data = inline_params({"accountType" => "HOSTED_OR_GOOGLE",
|
91
|
-
"Email" => user_id,
|
92
|
-
"Passwd" => password,
|
93
|
-
"service" => "lh2",
|
94
|
-
"source" => client_name})
|
95
|
-
|
96
|
-
response = handle_response(http(API_AUTH_URL).post("/accounts/ClientLogin", data))
|
97
|
-
|
98
|
-
@auth_key = extract_auth_key(response.body)
|
99
|
-
end
|
100
|
-
|
101
|
-
def extract_auth_key(data)
|
102
|
-
response = data.split("\n").map { |v| v.split("=") }
|
103
|
-
params = Hash[response]
|
104
|
-
params["Auth"]
|
105
|
-
end
|
106
|
-
|
107
|
-
def client_name
|
108
|
-
"ruby-gem-v#{Picasa::VERSION}"
|
74
|
+
}
|
109
75
|
end
|
110
76
|
end
|
111
77
|
end
|
data/lib/picasa/utils.rb
CHANGED
@@ -39,6 +39,13 @@ module Picasa
|
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
42
|
-
|
42
|
+
def inline_query(query)
|
43
|
+
query.map do |key, value|
|
44
|
+
dasherized = key.to_s.gsub("_", "-")
|
45
|
+
"#{CGI.escape(dasherized)}=#{CGI.escape(value.to_s)}"
|
46
|
+
end.join("&")
|
47
|
+
end
|
48
|
+
|
49
|
+
module_function :safe_retrieve, :array_wrap, :map_to_integer, :map_to_boolean, :map_to_date, :inline_query
|
43
50
|
end
|
44
51
|
end
|
data/lib/picasa/version.rb
CHANGED
data/test/client_test.rb
CHANGED
@@ -1,14 +1,35 @@
|
|
1
1
|
require "helper"
|
2
2
|
|
3
3
|
describe Picasa::Client do
|
4
|
-
it "has credentials" do
|
5
|
-
client = Picasa::Client.new(:user_id => "john.doe")
|
6
|
-
assert_equal({:user_id => "john.doe"}, client.credentials)
|
7
|
-
end
|
8
|
-
|
9
4
|
it "raises ArgumentError when user_id is missing" do
|
10
5
|
assert_raises(Picasa::ArgumentError, /user_id/) do
|
11
6
|
Picasa::Client.new
|
12
7
|
end
|
13
8
|
end
|
9
|
+
|
10
|
+
it "allows to assign custom authorization header" do
|
11
|
+
client = Picasa::Client.new(:user_id => "john.doe", :authorization_header => "OAuth token")
|
12
|
+
assert_equal "OAuth token", client.authorization_header
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "#authenticate" do
|
16
|
+
it "successfully authenticates" do
|
17
|
+
client = Picasa::Client.new(:user_id => "john.doe@domain.com", :password => "secret")
|
18
|
+
|
19
|
+
stub_request(:post, "https://www.google.com/accounts/ClientLogin").to_return(fixture("auth/success.txt"))
|
20
|
+
|
21
|
+
client.authenticate
|
22
|
+
refute_nil client.authorization_header
|
23
|
+
end
|
24
|
+
|
25
|
+
it "raises an ForbiddenError when authentication failed" do
|
26
|
+
client = Picasa::Client.new(:user_id => "john.doe@domain.com", :password => "invalid")
|
27
|
+
|
28
|
+
stub_request(:post, "https://www.google.com/accounts/ClientLogin").to_return(fixture("exceptions/forbidden.txt"))
|
29
|
+
|
30
|
+
assert_raises(Picasa::ForbiddenError) do
|
31
|
+
client.authenticate
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
14
35
|
end
|
data/test/connection_test.rb
CHANGED
@@ -1,86 +1,42 @@
|
|
1
|
-
# -*- encoding: utf-8 -*-
|
2
1
|
require "helper"
|
3
2
|
|
4
3
|
describe Picasa::Connection do
|
5
4
|
before do
|
6
|
-
@connection = Picasa::Connection.new
|
5
|
+
@connection = Picasa::Connection.new
|
7
6
|
end
|
8
7
|
|
9
|
-
describe "#
|
10
|
-
it "converts params to inline style" do
|
11
|
-
params = @connection.inline_params({:alt => "json", :kind => "photo"})
|
12
|
-
# make ruby 1.8 tests pass
|
13
|
-
assert_equal "alt=json", params.split("&").sort[0]
|
14
|
-
assert_equal "kind=photo", params.split("&").sort[1]
|
15
|
-
end
|
16
|
-
|
17
|
-
it "changes param keys underscore to dash" do
|
18
|
-
params = @connection.inline_params({:max_results => 10})
|
19
|
-
assert_equal "max-results=10", params
|
20
|
-
end
|
21
|
-
|
22
|
-
it "escapes values" do
|
23
|
-
params = @connection.inline_params({:kind => "żółć"})
|
24
|
-
assert_equal "kind=%C5%BC%C3%B3%C5%82%C4%87", params
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
describe "#path_with_params" do
|
8
|
+
describe "#path_with_query" do
|
29
9
|
it "returns path when no params provided" do
|
30
|
-
path = @connection.
|
10
|
+
path = @connection.path_with_query("/data/feed/api")
|
31
11
|
assert_equal "/data/feed/api", path
|
32
12
|
end
|
33
13
|
|
34
14
|
it "adds params to path" do
|
35
|
-
path = @connection.
|
15
|
+
path = @connection.path_with_query("/data/feed/api", {:q => "bomb"})
|
36
16
|
assert_equal "/data/feed/api?q=bomb", path
|
37
17
|
end
|
38
18
|
end
|
39
19
|
|
40
20
|
it "raises NotFound exception when 404 returned" do
|
41
|
-
connection = Picasa::Connection.new
|
42
|
-
uri = URI.parse("/data/feed/api/user
|
21
|
+
connection = Picasa::Connection.new
|
22
|
+
uri = URI.parse("/data/feed/api/user/some.user/albumid/non-existing")
|
43
23
|
|
44
24
|
stub_request(:get, "https://picasaweb.google.com" + uri.path).to_return(fixture("exceptions/not_found.txt"))
|
45
25
|
|
46
26
|
assert_raises Picasa::NotFoundError, "Invalid entity id: non-existing" do
|
47
|
-
connection.get(uri.path)
|
27
|
+
connection.get(:path => uri.path)
|
48
28
|
end
|
49
29
|
end
|
50
30
|
|
51
31
|
it "raises PreconditionFailed exception when 412 returned" do
|
52
|
-
connection = Picasa::Connection.new
|
53
|
-
uri = URI.parse("/data/feed/api/user
|
32
|
+
connection = Picasa::Connection.new
|
33
|
+
uri = URI.parse("/data/feed/api/user/some.user/albumid/123")
|
54
34
|
|
55
35
|
stub_request(:post, "https://www.google.com/accounts/ClientLogin").to_return(fixture("auth/success.txt"))
|
56
36
|
stub_request(:delete, "https://picasaweb.google.com" + uri.path).to_return(fixture("exceptions/precondition_failed.txt"))
|
57
37
|
|
58
38
|
assert_raises Picasa::PreconditionFailedError, "Mismatch: etags = [oldetag], version = [7]" do
|
59
|
-
connection.delete(uri.path, {"If-Match" => "oldetag"})
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
describe "authentication" do
|
64
|
-
it "successfully authenticates" do
|
65
|
-
connection = Picasa::Connection.new(:user_id => "john.doe@domain.com", :password => "secret")
|
66
|
-
uri = URI.parse("/data/feed/api/user/#{connection.user_id}")
|
67
|
-
|
68
|
-
stub_request(:post, "https://www.google.com/accounts/ClientLogin").to_return(fixture("auth/success.txt"))
|
69
|
-
stub_request(:get, "https://picasaweb.google.com/data/feed/api/user/john.doe@domain.com").to_return(fixture("album/album-list.txt"))
|
70
|
-
|
71
|
-
connection.expects(:authenticate).returns(:result)
|
72
|
-
refute_nil connection.get(uri.path)
|
73
|
-
end
|
74
|
-
|
75
|
-
it "raises an ResponseError when authentication failed" do
|
76
|
-
connection = Picasa::Connection.new(:user_id => "john.doe@domain.com", :password => "secret")
|
77
|
-
uri = URI.parse("/data/feed/api/user/#{connection.user_id}")
|
78
|
-
|
79
|
-
stub_request(:post, "https://www.google.com/accounts/ClientLogin").to_return(fixture("exceptions/forbidden.txt"))
|
80
|
-
|
81
|
-
assert_raises(Picasa::ForbiddenError) do
|
82
|
-
connection.get(uri.path)
|
83
|
-
end
|
39
|
+
connection.delete(:path => uri.path, :headers => {"If-Match" => "oldetag"})
|
84
40
|
end
|
85
41
|
end
|
86
42
|
end
|
data/test/helper.rb
CHANGED
data/test/utils_test.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
1
2
|
require "helper"
|
2
3
|
|
3
4
|
describe Picasa::Utils do
|
@@ -81,4 +82,23 @@ describe Picasa::Utils do
|
|
81
82
|
assert_nil Picasa::Utils.map_to_boolean("truthy")
|
82
83
|
end
|
83
84
|
end
|
85
|
+
|
86
|
+
describe "#inline_query" do
|
87
|
+
it "converts params to inline style" do
|
88
|
+
params = Picasa::Utils.inline_query({:alt => "json", :kind => "photo"})
|
89
|
+
# make ruby 1.8 tests pass
|
90
|
+
assert_equal "alt=json", params.split("&").sort[0]
|
91
|
+
assert_equal "kind=photo", params.split("&").sort[1]
|
92
|
+
end
|
93
|
+
|
94
|
+
it "changes param keys underscore to dash" do
|
95
|
+
params = Picasa::Utils.inline_query({:max_results => 10})
|
96
|
+
assert_equal "max-results=10", params
|
97
|
+
end
|
98
|
+
|
99
|
+
it "escapes values" do
|
100
|
+
params = Picasa::Utils.inline_query({:kind => "żółć"})
|
101
|
+
assert_equal "kind=%C5%BC%C3%B3%C5%82%C4%87", params
|
102
|
+
end
|
103
|
+
end
|
84
104
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: picasa
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.2
|
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-09-
|
12
|
+
date: 2012-09-16 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: multi_xml
|