android-publisher 0.0.11
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.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/.pairs +28 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +29 -0
- data/Rakefile +1 -0
- data/android-publisher.gemspec +30 -0
- data/bin/apublisher +46 -0
- data/lib/android/publisher.rb +81 -0
- data/lib/android/publisher/apks.rb +20 -0
- data/lib/android/publisher/connection.rb +81 -0
- data/lib/android/publisher/edit.rb +99 -0
- data/lib/android/publisher/edit_connection.rb +18 -0
- data/lib/android/publisher/response.rb +20 -0
- data/lib/android/publisher/secrets.rb +95 -0
- data/lib/android/publisher/track.rb +44 -0
- data/lib/android/publisher/version.rb +5 -0
- data/spec/android/publisher/connection_spec.rb +81 -0
- data/spec/android/publisher/edit_spec.rb +31 -0
- data/spec/spec_helper.rb +10 -0
- metadata +166 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 59662998f617795133eda1d73372d15e1d099028
|
4
|
+
data.tar.gz: 221dc553b5724162fce41f21c49571dc350ffe9e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d9fcf754e50830f76aa89bc4e6dad917a1fdc72e0c2576d0b76a9e5a5744ef2e7f20e3fa9a8de564d67029b800e81cdced9914fad660be1ffa1da861042d99dc
|
7
|
+
data.tar.gz: 10deaa862d055869c6f6496c92dbc9a7942d426e7259bceed8f53a2c14d628979040b2dfb70f2da12357b8d9e312ca2d97abe23d174652ce0f03d528c23b35c8
|
data/.gitignore
ADDED
data/.pairs
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
pairs:
|
2
|
+
# <initials>: <Firstname> <Lastname>[; <email-id>]
|
3
|
+
jb: Jan Berkel; jan
|
4
|
+
ov: Olga Vasylchenko; olga.vasylchenko@soundcloud.com
|
5
|
+
sk: Slavko Krucaj; slavko
|
6
|
+
me: Michael England; me
|
7
|
+
rs: Rob Siwek; robert
|
8
|
+
ss: Slawomir Smiechura; slawomir
|
9
|
+
vg: Vincent Garrigues; vincent
|
10
|
+
ms: Mustafa Sezgin; mustafa
|
11
|
+
rh: Richard Howell; richard
|
12
|
+
ds: Dennis Schmidt; dennis
|
13
|
+
hf: Herval Freire; herval
|
14
|
+
mb: Matej Balantic; matej
|
15
|
+
kk: Kerem Karatal; kerem.karatal
|
16
|
+
ag: Aleksandra Gavrilovska; aleks
|
17
|
+
fg: Felix Gabel; felix.gabel
|
18
|
+
mmk: Meghan Kane; meghan.kane
|
19
|
+
dc: Dave Cameron; dave.bc
|
20
|
+
fl: Fabio Lessa; fabio
|
21
|
+
pm: Peter Minarik; peter
|
22
|
+
kp: Kunal Parikh; kunal.parikh
|
23
|
+
kr: Kristina Roddewig; kristina.roddewig
|
24
|
+
email:
|
25
|
+
prefix: pair
|
26
|
+
domain: soundcloud.com
|
27
|
+
no_solo_prefix: true
|
28
|
+
#global: true
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Slawomir Smiechura
|
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,29 @@
|
|
1
|
+
# Google::Publisher
|
2
|
+
|
3
|
+
TODO: Write a gem description
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'google-publisher'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install google-publisher
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
TODO: Write usage instructions here
|
22
|
+
|
23
|
+
## Contributing
|
24
|
+
|
25
|
+
1. Fork it ( http://github.com/<my-github-username>/google-publisher/fork )
|
26
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
27
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
28
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
29
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'android/publisher/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "android-publisher"
|
8
|
+
spec.version = Android::Publisher::VERSION
|
9
|
+
spec.authors = ["Slawomir Smiechura"]
|
10
|
+
spec.email = ["slawomir@soundcloud.com"]
|
11
|
+
spec.summary = 'The gem allows you to upload and control your Google Android applications'
|
12
|
+
spec.description = ''
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
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_development_dependency "bundler", "~> 1.5"
|
22
|
+
spec.add_development_dependency 'rake', '~>10.1.0'
|
23
|
+
spec.add_development_dependency 'rspec', '>=2.14.1'
|
24
|
+
spec.add_development_dependency "coveralls"
|
25
|
+
spec.add_development_dependency "simplecov"
|
26
|
+
|
27
|
+
|
28
|
+
spec.add_dependency 'oauth2'
|
29
|
+
spec.add_dependency 'trollop'
|
30
|
+
end
|
data/bin/apublisher
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'android/publisher'
|
4
|
+
|
5
|
+
require 'trollop'
|
6
|
+
opts = Trollop::options do
|
7
|
+
opt :name, 'Package name', :type => :string, :short => '-n'
|
8
|
+
opt :apk, 'Path to the apk file', :type => :string, :short => '-a'
|
9
|
+
opt :ver, 'VersionCode to be deployed', :default => 0, :short => '-v'
|
10
|
+
opt :track, 'Track to assign the apk to', :default => '', :short => '-t'
|
11
|
+
opt :fraction,'Portion of the users who should receive this version of the app', :type => :double, :short => '-f'
|
12
|
+
opt :dry_run, 'Verify command to be run without actually running it', :default => false, :short => '-d'
|
13
|
+
end
|
14
|
+
|
15
|
+
p params = {
|
16
|
+
:name => opts[:name],
|
17
|
+
:apk_path => opts[:apk],
|
18
|
+
:version => opts[:ver] == 0 ? nil : opts[:ver],
|
19
|
+
:track => opts[:track],
|
20
|
+
:fraction => opts[:fraction],
|
21
|
+
}
|
22
|
+
|
23
|
+
# Maybe worth reading the package name?
|
24
|
+
# aapt dump badging <path-to-apk>#
|
25
|
+
|
26
|
+
publisher = Android::Publisher.new(params[:name], params[:apk_path], params[:version])
|
27
|
+
|
28
|
+
if opts[:dry_run]
|
29
|
+
puts 'This was just a dry run! Nothing was actually executed!'
|
30
|
+
else
|
31
|
+
if params[:track] == 'alpha'
|
32
|
+
publisher.deploy_to_alpha
|
33
|
+
elsif params[:track] == 'beta' && params[:fraction] == 0
|
34
|
+
publisher.clear_beta
|
35
|
+
elsif params[:track] == 'beta'
|
36
|
+
publisher.deploy_to_beta
|
37
|
+
elsif params[:track] == 'production'
|
38
|
+
publisher.deploy_to_production
|
39
|
+
elsif params[:track] == 'rollout' && !params[:fraction]
|
40
|
+
publisher.rollout
|
41
|
+
elsif params[:track] == 'rollout' && params[:fraction] == 0
|
42
|
+
publisher.clear_rollout
|
43
|
+
elsif params[:track] == 'rollout' && params[:fraction]
|
44
|
+
publisher.update_rollout(params[:fraction])
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require "android/publisher/version"
|
2
|
+
require 'android/publisher/connection'
|
3
|
+
require 'android/publisher/edit'
|
4
|
+
require 'android/publisher/edit_connection'
|
5
|
+
require 'android/publisher/response'
|
6
|
+
require 'android/publisher/secrets'
|
7
|
+
require 'android/publisher/apks'
|
8
|
+
require 'android/publisher/track'
|
9
|
+
|
10
|
+
|
11
|
+
module Android
|
12
|
+
class Publisher
|
13
|
+
def initialize(package_name, apk_path = nil, version_code = nil)
|
14
|
+
@package_name = package_name
|
15
|
+
@apk_path = apk_path
|
16
|
+
@version_code = version_code
|
17
|
+
end
|
18
|
+
|
19
|
+
def rollout(user_fraction = 0.01)
|
20
|
+
edit.insert
|
21
|
+
edit.upload_apk(@apk_path) unless @apk_path.nil?
|
22
|
+
edit.assign_to_staged_rollout_track(user_fraction)
|
23
|
+
edit.commit
|
24
|
+
end
|
25
|
+
|
26
|
+
def update_rollout(user_fraction)
|
27
|
+
edit.insert
|
28
|
+
edit.update_rollout(user_fraction)
|
29
|
+
edit.commit
|
30
|
+
end
|
31
|
+
|
32
|
+
def deploy_to_alpha
|
33
|
+
edit.insert
|
34
|
+
edit.upload_apk(@apk_path) unless @apk_path.nil?
|
35
|
+
edit.assign_to_alpha_track
|
36
|
+
edit.commit
|
37
|
+
end
|
38
|
+
|
39
|
+
def deploy_to_beta
|
40
|
+
edit.insert
|
41
|
+
edit.upload_apk(@apk_path) unless @apk_path.nil?
|
42
|
+
edit.assign_to_beta_track
|
43
|
+
edit.commit
|
44
|
+
end
|
45
|
+
|
46
|
+
def deploy_to_production
|
47
|
+
edit.insert
|
48
|
+
edit.upload_apk(@apk_path) unless @apk_path.nil?
|
49
|
+
edit.assign_to_production_track
|
50
|
+
edit.commit
|
51
|
+
end
|
52
|
+
|
53
|
+
def clear_rollout
|
54
|
+
edit.insert
|
55
|
+
if edit.track_has_deployed_apks?(:rollout)
|
56
|
+
edit.clear_rollout
|
57
|
+
edit.commit
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def clear_beta
|
62
|
+
edit.insert
|
63
|
+
if edit.track_has_deployed_apks?(:beta)
|
64
|
+
edit.clear_beta
|
65
|
+
edit.commit
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def authorized_connection
|
70
|
+
@authorized_connection ||= Android::Publisher::Secrets.load.to_authorized_connection
|
71
|
+
end
|
72
|
+
|
73
|
+
def client
|
74
|
+
@client ||= Android::Publisher::Connection.new(authorized_connection, @package_name)
|
75
|
+
end
|
76
|
+
|
77
|
+
def edit
|
78
|
+
@edit ||= Android::Publisher::Edit.new(client, @version_code)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Android
|
2
|
+
class Publisher
|
3
|
+
class Apks
|
4
|
+
ENDPOINT = 'apks'
|
5
|
+
|
6
|
+
def initialize(connection)
|
7
|
+
@client = connection.add_endpoint(ENDPOINT)
|
8
|
+
end
|
9
|
+
|
10
|
+
def upload(path_to_apk)
|
11
|
+
Response.parse(@client.upload(File.new(path_to_apk)))
|
12
|
+
end
|
13
|
+
|
14
|
+
def list
|
15
|
+
Response.parse(@client.get)
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module Android
|
5
|
+
class Publisher
|
6
|
+
class Connection
|
7
|
+
API_URL = URI.parse('https://www.googleapis.com/androidpublisher/v2/applications/')
|
8
|
+
|
9
|
+
UPLOAD_URL = URI.parse('https://www.googleapis.com/upload/androidpublisher/v2/applications/')
|
10
|
+
|
11
|
+
def initialize(authorized_connection, package_name, endpoints = [])
|
12
|
+
@authorized_connection = authorized_connection
|
13
|
+
@package_name = package_name
|
14
|
+
@endpoints = endpoints
|
15
|
+
end
|
16
|
+
|
17
|
+
def add_endpoint(endpoint)
|
18
|
+
Connection.new(authorized_connection,package_name, [*@endpoints, endpoint])
|
19
|
+
end
|
20
|
+
|
21
|
+
def remove_endpoint
|
22
|
+
Connection.new(authorized_connection,package_name, @endpoints[0..-2])
|
23
|
+
end
|
24
|
+
|
25
|
+
def put(params = {})
|
26
|
+
authorized_connection.put(append(""), params)
|
27
|
+
end
|
28
|
+
|
29
|
+
def get(path = "")
|
30
|
+
authorized_connection.get(append(path))
|
31
|
+
end
|
32
|
+
|
33
|
+
def just_post(path = "", params = {})
|
34
|
+
authorized_connection.post(URI.join(API_URL, "#{package_name}/", path), params)
|
35
|
+
end
|
36
|
+
|
37
|
+
def post(path = "", params = {})
|
38
|
+
authorized_connection.post(append(path), params)
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
|
43
|
+
def delete(path = "")
|
44
|
+
authorized_connection.delete(append(path))
|
45
|
+
end
|
46
|
+
|
47
|
+
def patch(path = "", params = {})
|
48
|
+
authorized_connection.patch(append(path), params)
|
49
|
+
end
|
50
|
+
|
51
|
+
def upload(file)
|
52
|
+
params = {
|
53
|
+
:headers => { 'Content-Type' => 'application/octet-stream', 'Content-Length'=> file.size.to_s },
|
54
|
+
:body => Faraday::UploadIO.new(file.path, 'application/octet-stream')
|
55
|
+
}
|
56
|
+
|
57
|
+
authorized_connection.post(upload_uri, params)
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
attr_reader :authorized_connection, :package_name, :endpoints
|
62
|
+
|
63
|
+
def upload_uri
|
64
|
+
URI.join(UPLOAD_URL, "#{package_name}/", get_endpoints, "?uploadType=media").to_s
|
65
|
+
end
|
66
|
+
|
67
|
+
def response(response_body)
|
68
|
+
JSON.parse response_body
|
69
|
+
end
|
70
|
+
|
71
|
+
def append(path)
|
72
|
+
path.gsub!(/^\//, "")
|
73
|
+
URI.join(API_URL, "#{package_name}/", get_endpoints, path).to_s
|
74
|
+
end
|
75
|
+
|
76
|
+
def get_endpoints
|
77
|
+
endpoints.empty? ? "" : "#{endpoints.join("/")}/"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
module Android
|
2
|
+
class Publisher
|
3
|
+
class Edit
|
4
|
+
ENDPOINT = "edits"
|
5
|
+
|
6
|
+
def initialize(connection, version_code)
|
7
|
+
@client = connection.add_endpoint(ENDPOINT)
|
8
|
+
@version_code = version_code
|
9
|
+
end
|
10
|
+
|
11
|
+
def insert
|
12
|
+
response = Response.parse(@client.post)
|
13
|
+
@id = response['id']
|
14
|
+
@client = @client.add_endpoint(id)
|
15
|
+
response
|
16
|
+
end
|
17
|
+
|
18
|
+
def get
|
19
|
+
Response.parse(@client.get)
|
20
|
+
end
|
21
|
+
|
22
|
+
def commit
|
23
|
+
response = connection.commit
|
24
|
+
reset_status
|
25
|
+
response
|
26
|
+
end
|
27
|
+
|
28
|
+
def delete
|
29
|
+
if (id)
|
30
|
+
response = Response.parse(@client.delete)
|
31
|
+
reset_status
|
32
|
+
response
|
33
|
+
else
|
34
|
+
raise "Edit is not created, please insert one"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def track_has_deployed_apks?(track)
|
39
|
+
Track.new(@client, track).has_deployed_apks?
|
40
|
+
end
|
41
|
+
|
42
|
+
def upload_apk(path_to_apk)
|
43
|
+
apks.upload(path_to_apk)
|
44
|
+
end
|
45
|
+
|
46
|
+
def assign_to_alpha_track
|
47
|
+
Track.new(@client, :alpha).update(version_code)
|
48
|
+
end
|
49
|
+
|
50
|
+
def assign_to_beta_track
|
51
|
+
Track.new(@client, :beta).update(version_code)
|
52
|
+
end
|
53
|
+
|
54
|
+
def assign_to_production_track
|
55
|
+
Track.new(@client, :production).update(version_code)
|
56
|
+
end
|
57
|
+
|
58
|
+
def assign_to_staged_rollout_track(user_fraction)
|
59
|
+
Track.new(@client, :rollout).update(version_code, user_fraction)
|
60
|
+
end
|
61
|
+
|
62
|
+
def update_rollout(user_fraction)
|
63
|
+
Track.new(@client, :rollout).patch(version_code, user_fraction)
|
64
|
+
end
|
65
|
+
|
66
|
+
def clear_rollout
|
67
|
+
Track.new(@client, :rollout).update(nil)
|
68
|
+
end
|
69
|
+
|
70
|
+
def clear_beta
|
71
|
+
Track.new(@client, :beta).update(nil)
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
attr_reader :client, :id
|
76
|
+
|
77
|
+
def reset_status
|
78
|
+
@client = @client.remove_endpoint
|
79
|
+
@id = nil
|
80
|
+
end
|
81
|
+
|
82
|
+
def apks
|
83
|
+
Apks.new(@client)
|
84
|
+
end
|
85
|
+
|
86
|
+
def apk_latest_version_code
|
87
|
+
apks.list['apks'].last['versionCode']
|
88
|
+
end
|
89
|
+
|
90
|
+
def version_code
|
91
|
+
@version_code || apk_latest_version_code
|
92
|
+
end
|
93
|
+
|
94
|
+
def connection
|
95
|
+
@connection ||= Android::Publisher::EditConnection.new(@client, @id)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Android
|
2
|
+
class Publisher
|
3
|
+
class EditConnection
|
4
|
+
ENDPOINT = "edits"
|
5
|
+
def initialize(client, edit_id)
|
6
|
+
@client = client
|
7
|
+
@edit_id = edit_id
|
8
|
+
end
|
9
|
+
|
10
|
+
def commit
|
11
|
+
Response.parse(client.just_post("edits/#{edit_id}:commit"))
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
attr_reader :client, :edit_id
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Android
|
2
|
+
class Publisher
|
3
|
+
class Response
|
4
|
+
class << self
|
5
|
+
#TODO: create proper object instead of parsing response
|
6
|
+
def parse(response)
|
7
|
+
if response.status == 204
|
8
|
+
{ :status => "OK" }
|
9
|
+
else
|
10
|
+
JSON.parse response.body
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(body)
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'oauth2'
|
3
|
+
|
4
|
+
module Android
|
5
|
+
class Publisher
|
6
|
+
class Secrets
|
7
|
+
# @example
|
8
|
+
# {
|
9
|
+
# "web": {
|
10
|
+
# "client_id": "asdfjasdljfasdkjf",
|
11
|
+
# "client_secret": "1912308409123890",
|
12
|
+
# "redirect_uris": ["https://www.example.com/oauth2callback"],
|
13
|
+
# "auth_uri": "https://accounts.android.com/o/oauth2/auth",
|
14
|
+
# "token_uri": "https://accounts.android.com/o/oauth2/token"
|
15
|
+
# }
|
16
|
+
# }
|
17
|
+
#
|
18
|
+
# @example
|
19
|
+
# {
|
20
|
+
# "installed": {
|
21
|
+
# "client_id": "837647042410-75ifg...usercontent.com",
|
22
|
+
# "client_secret":"asdlkfjaskd",
|
23
|
+
# "redirect_uris": ["http://localhost", "urn:ietf:oauth:2.0:oob"],
|
24
|
+
# "auth_uri": "https://accounts.android.com/o/oauth2/auth",
|
25
|
+
# "token_uri": "https://accounts.android.com/o/oauth2/token"
|
26
|
+
# }
|
27
|
+
# }
|
28
|
+
def self.load(filename=nil)
|
29
|
+
while filename == nil
|
30
|
+
search_path ||= File.expand_path('.')
|
31
|
+
if File.exist?(File.join(search_path, 'client_secrets.json'))
|
32
|
+
filename = File.join(search_path, 'client_secrets.json')
|
33
|
+
else
|
34
|
+
raise ArgumentError,
|
35
|
+
'No client_secrets.json filename supplied ' +
|
36
|
+
'and/or could not be found in search path.'
|
37
|
+
end
|
38
|
+
end
|
39
|
+
data = File.open(filename, 'r') { |file| JSON.parse(file.read) }
|
40
|
+
return self.new(data)
|
41
|
+
end
|
42
|
+
|
43
|
+
##
|
44
|
+
# Intialize OAuth client settings.
|
45
|
+
#
|
46
|
+
# @param [Hash] options
|
47
|
+
# Parsed client secrets files
|
48
|
+
def initialize(options={})
|
49
|
+
@flow = options[:flow] || options.keys.first.to_s || 'web'
|
50
|
+
fdata = options[@flow]
|
51
|
+
|
52
|
+
# Client auth config
|
53
|
+
@client_id = fdata[:client_id] || fdata["client_id"]
|
54
|
+
@client_secret = fdata[:client_secret] || fdata["client_secret"]
|
55
|
+
|
56
|
+
# Redirects info
|
57
|
+
@redirect_uris = fdata[:redirect_uris] || fdata["redirect_uris"] || [fdata[:redirect_uri]]
|
58
|
+
@javascript_origins = fdata[:javascript_origins] || fdata["javascript_origins"]|| [fdata[:javascript_origin]]
|
59
|
+
|
60
|
+
# Endpoints info
|
61
|
+
@authorization_uri = URI.parse(fdata[:auth_uri] || fdata["auth_uri"] || fdata[:authorization_uri])
|
62
|
+
@token_credential_uri = URI.parse(fdata[:token_uri] || fdata["token_uri"] || fdata[:token_credential_uri])
|
63
|
+
|
64
|
+
# Associated token info
|
65
|
+
@access_token = fdata[:access_token] || fdata["access_token"]
|
66
|
+
@refresh_token = fdata[:refresh_token] || fdata["refresh_token"]
|
67
|
+
@id_token = fdata[:id_token] || fdata["id_token"]
|
68
|
+
@expires_in = fdata[:expires_in] || fdata["expires_in"]
|
69
|
+
@expires_at = fdata[:expires_at] || fdata["expires_at"]
|
70
|
+
@issued_at = fdata[:issued_at] || fdata["issued_at"]
|
71
|
+
end
|
72
|
+
|
73
|
+
attr_reader(
|
74
|
+
:flow, :client_id, :client_secret, :redirect_uris, :javascript_origins,
|
75
|
+
:authorization_uri, :token_credential_uri, :access_token,
|
76
|
+
:refresh_token, :id_token, :expires_in, :expires_at, :issued_at
|
77
|
+
)
|
78
|
+
|
79
|
+
def to_authorized_connection
|
80
|
+
# OAuth2::Client.new(
|
81
|
+
# "49761657086.apps.googleusercontent.com", "Cp43nWEtueuPVEKPDJhmr4Mb",
|
82
|
+
# {:site => "https://accounts.android.com/", :authorize_url=>"/o/oauth2/auth", :token_url=>"/o/oauth2/token"}
|
83
|
+
# )
|
84
|
+
params = {
|
85
|
+
:site => URI.join(authorization_uri, '/'),
|
86
|
+
:authorize_url => authorization_uri.path,
|
87
|
+
:token_url => token_credential_uri.path
|
88
|
+
}
|
89
|
+
client = OAuth2::Client.new(client_id, client_secret, params)
|
90
|
+
token = OAuth2::AccessToken.new(client, access_token, { :refresh_token => refresh_token })
|
91
|
+
token.refresh!
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Android
|
2
|
+
class Publisher
|
3
|
+
class Track
|
4
|
+
ENDPOINT = 'tracks'
|
5
|
+
def initialize(client, track)
|
6
|
+
@track = track.to_s
|
7
|
+
@client = client.add_endpoint("#{ENDPOINT}/#{@track}")
|
8
|
+
end
|
9
|
+
|
10
|
+
def has_deployed_apks?
|
11
|
+
list['versionCodes'].size > 0
|
12
|
+
end
|
13
|
+
|
14
|
+
def update(version_code, user_fraction=nil)
|
15
|
+
params = {
|
16
|
+
:headers => { "Content-Type" => 'application/json' },
|
17
|
+
:body => { :track => @track, :versionCodes => [version_code], :userFraction=>user_fraction }.to_json
|
18
|
+
}
|
19
|
+
|
20
|
+
Response.parse(@client.put(params))
|
21
|
+
end
|
22
|
+
|
23
|
+
def patch(version_code, user_fraction)
|
24
|
+
params = {
|
25
|
+
:headers => { "Content-Type" => 'application/json' },
|
26
|
+
:body => { :track => @track, :versionCodes => [version_code], :userFraction=>user_fraction }.to_json
|
27
|
+
}
|
28
|
+
|
29
|
+
Response.parse(@client.patch(params))
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
attr_reader :track
|
34
|
+
|
35
|
+
def list
|
36
|
+
begin
|
37
|
+
Response.parse(@client.get())
|
38
|
+
rescue OAuth2::Error
|
39
|
+
{'versionCodes' => []}
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Android::Publisher::Connection do
|
4
|
+
API_URL = 'https://www.googleapis.com/androidpublisher/v2/applications/com.package.name'
|
5
|
+
|
6
|
+
let(:auth_connection) { double('OAuth2')}
|
7
|
+
let(:publisher) { described_class.new(auth_connection, "com.package.name") }
|
8
|
+
|
9
|
+
describe '#get' do
|
10
|
+
it 'should send a :get request' do
|
11
|
+
auth_connection.
|
12
|
+
should_receive(:get).
|
13
|
+
with("#{API_URL}/endpoint")
|
14
|
+
|
15
|
+
publisher.get("endpoint")
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'should send correct request without parameters' do
|
19
|
+
pub = publisher.add_endpoint('apks')
|
20
|
+
auth_connection.
|
21
|
+
should_receive(:get).
|
22
|
+
with("#{API_URL}/apks/")
|
23
|
+
|
24
|
+
pub.get
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe '#post' do
|
29
|
+
it 'should send a :post request' do
|
30
|
+
auth_connection.
|
31
|
+
should_receive(:post).
|
32
|
+
with("#{API_URL}/endpoint", {})
|
33
|
+
|
34
|
+
publisher.post("endpoint", {})
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe '#delete' do
|
39
|
+
it 'should send a :delete request' do
|
40
|
+
auth_connection.
|
41
|
+
should_receive(:delete).
|
42
|
+
with("#{API_URL}/endpoint")
|
43
|
+
|
44
|
+
publisher.delete("endpoint")
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe '#patch' do
|
49
|
+
it 'should send a :patch request' do
|
50
|
+
auth_connection.
|
51
|
+
should_receive(:patch).
|
52
|
+
with("#{API_URL}/endpoint", {})
|
53
|
+
|
54
|
+
publisher.patch("endpoint", {})
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
describe '#append path' do
|
60
|
+
it 'shoud remove trailing "/" from the endpoint' do
|
61
|
+
result = "#{API_URL}/endpoint"
|
62
|
+
publisher.send(:append, "/endpoint").should == result
|
63
|
+
end
|
64
|
+
it 'should not modify other parts of path' do
|
65
|
+
result = "#{API_URL}/endpoint/whatever/whenever/"
|
66
|
+
|
67
|
+
publisher.send(:append, "/endpoint/whatever/whenever/").should == result
|
68
|
+
end
|
69
|
+
it 'should create valid uris' do
|
70
|
+
result = "#{API_URL}/endpoint/whatever/whenever/index.html?foo=bar"
|
71
|
+
|
72
|
+
publisher.send(:append, "/endpoint/whatever/whenever/index.html?foo=bar").should == result
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'should add endpoints' do
|
76
|
+
pub = publisher.add_endpoint('edits')
|
77
|
+
result = "#{API_URL}/edits/whatever/whenever/index.html?foo=bar"
|
78
|
+
pub.send(:append, "whatever/whenever/index.html?foo=bar").should == result
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'ostruct'
|
3
|
+
|
4
|
+
describe Android::Publisher::Edit do
|
5
|
+
let(:client) { double(Android::Publisher::Connection)}
|
6
|
+
|
7
|
+
#TODO: This is a clear indication that the design sucks
|
8
|
+
let(:client1) { double("Android::Publisher::Connection1")}
|
9
|
+
|
10
|
+
let(:edit) { described_class.new(client, nil) }
|
11
|
+
|
12
|
+
before do
|
13
|
+
client.stub(:add_endpoint).with(any_args).and_return(client1)
|
14
|
+
end
|
15
|
+
context 'insert' do
|
16
|
+
before do
|
17
|
+
client1.should_receive(:post).and_return(OpenStruct.new( :status => 200, :body=> {:id=>1}.to_json ))
|
18
|
+
client.should_receive(:add_endpoint).with('edits')
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'should add edit id to the client path ' do
|
22
|
+
client1.should_receive(:add_endpoint).with(1)
|
23
|
+
edit.insert
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should return the response' do
|
27
|
+
client1.should_receive(:add_endpoint).with(1)
|
28
|
+
edit.insert.should be_eql({'id' => 1})
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,166 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: android-publisher
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.11
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Slawomir Smiechura
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-11-26 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.5'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.5'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 10.1.0
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 10.1.0
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 2.14.1
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 2.14.1
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: coveralls
|
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: simplecov
|
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: oauth2
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :runtime
|
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: trollop
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
description: ''
|
112
|
+
email:
|
113
|
+
- slawomir@soundcloud.com
|
114
|
+
executables:
|
115
|
+
- apublisher
|
116
|
+
extensions: []
|
117
|
+
extra_rdoc_files: []
|
118
|
+
files:
|
119
|
+
- ".gitignore"
|
120
|
+
- ".pairs"
|
121
|
+
- Gemfile
|
122
|
+
- LICENSE.txt
|
123
|
+
- README.md
|
124
|
+
- Rakefile
|
125
|
+
- android-publisher.gemspec
|
126
|
+
- bin/apublisher
|
127
|
+
- lib/android/publisher.rb
|
128
|
+
- lib/android/publisher/apks.rb
|
129
|
+
- lib/android/publisher/connection.rb
|
130
|
+
- lib/android/publisher/edit.rb
|
131
|
+
- lib/android/publisher/edit_connection.rb
|
132
|
+
- lib/android/publisher/response.rb
|
133
|
+
- lib/android/publisher/secrets.rb
|
134
|
+
- lib/android/publisher/track.rb
|
135
|
+
- lib/android/publisher/version.rb
|
136
|
+
- spec/android/publisher/connection_spec.rb
|
137
|
+
- spec/android/publisher/edit_spec.rb
|
138
|
+
- spec/spec_helper.rb
|
139
|
+
homepage: ''
|
140
|
+
licenses:
|
141
|
+
- MIT
|
142
|
+
metadata: {}
|
143
|
+
post_install_message:
|
144
|
+
rdoc_options: []
|
145
|
+
require_paths:
|
146
|
+
- lib
|
147
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
148
|
+
requirements:
|
149
|
+
- - ">="
|
150
|
+
- !ruby/object:Gem::Version
|
151
|
+
version: '0'
|
152
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
153
|
+
requirements:
|
154
|
+
- - ">="
|
155
|
+
- !ruby/object:Gem::Version
|
156
|
+
version: '0'
|
157
|
+
requirements: []
|
158
|
+
rubyforge_project:
|
159
|
+
rubygems_version: 2.2.2
|
160
|
+
signing_key:
|
161
|
+
specification_version: 4
|
162
|
+
summary: The gem allows you to upload and control your Google Android applications
|
163
|
+
test_files:
|
164
|
+
- spec/android/publisher/connection_spec.rb
|
165
|
+
- spec/android/publisher/edit_spec.rb
|
166
|
+
- spec/spec_helper.rb
|