picasa 0.5.1 → 0.5.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
[![Build Status](https://secure.travis-ci.org/morgoth/picasa.png)](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
|