spaceship 0.31.10 → 0.32.0
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 +4 -4
- data/lib/spaceship/client.rb +11 -1
- data/lib/spaceship/portal/portal_client.rb +37 -37
- data/lib/spaceship/tunes/app_ratings.rb +107 -0
- data/lib/spaceship/tunes/application.rb +6 -0
- data/lib/spaceship/tunes/tunes.rb +1 -0
- data/lib/spaceship/tunes/tunes_client.rb +10 -0
- data/lib/spaceship/version.rb +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 446e6df28c4691d7dea2ecb1d0e9ef8543c418a7
|
4
|
+
data.tar.gz: 7ea38ddea78ce1183853b9e619b724ada951a8da
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 27fc2dd359daf036dc9e0d22274ccf99ff9336239a765d9ba1c0ff18a23f7d2062498e345f4d74cc08d29a9c52ed24ad82ebe7a09c72df7fb7635f90d4031f1c
|
7
|
+
data.tar.gz: e28fd65e2a39cac2d175a3f615bde469ba0f30b00c0af1700345fd9046ec93bba459c157dbbfe39ea323e2f16d9c6dec58ff2818274d1dc35e94e5cc3800d0e7
|
data/lib/spaceship/client.rb
CHANGED
@@ -310,6 +310,11 @@ module Spaceship
|
|
310
310
|
|
311
311
|
def itc_service_key
|
312
312
|
return @service_key if @service_key
|
313
|
+
|
314
|
+
# Check if we have a local cache of the key
|
315
|
+
itc_service_key_path = File.expand_path("~/Library/Caches/spaceship_itc_service_key.txt")
|
316
|
+
return File.read(itc_service_key_path) if File.exist?(itc_service_key_path)
|
317
|
+
|
313
318
|
# Some customers in Asia have had trouble with the CDNs there that cache and serve this content, leading
|
314
319
|
# to "buffer error (Zlib::BufError)" from deep in the Ruby HTTP stack. Setting this header requests that
|
315
320
|
# the content be served only as plain-text, which seems to work around their problem, while not affecting
|
@@ -319,7 +324,12 @@ module Spaceship
|
|
319
324
|
headers = { 'Accept-Encoding' => 'identity' }
|
320
325
|
# We need a service key from a JS file to properly auth
|
321
326
|
js = request(:get, "https://itunesconnect.apple.com/itc/static-resources/controllers/login_cntrl.js", nil, headers)
|
322
|
-
@service_key
|
327
|
+
@service_key = js.body.match(/itcServiceKey = '(.*)'/)[1]
|
328
|
+
|
329
|
+
# Cache the key locally
|
330
|
+
File.write(itc_service_key_path, @service_key)
|
331
|
+
|
332
|
+
return @service_key
|
323
333
|
end
|
324
334
|
|
325
335
|
#####################################################
|
@@ -9,29 +9,6 @@ module Spaceship
|
|
9
9
|
"https://developer.apple.com/services-account/#{PROTOCOL_VERSION}/"
|
10
10
|
end
|
11
11
|
|
12
|
-
# Fetches the latest API Key from the Apple Dev Portal
|
13
|
-
def api_key
|
14
|
-
cache_path = File.expand_path("~/Library/Caches/spaceship_api_key.txt")
|
15
|
-
begin
|
16
|
-
cached = File.read(cache_path)
|
17
|
-
rescue Errno::ENOENT
|
18
|
-
end
|
19
|
-
return cached if cached
|
20
|
-
|
21
|
-
landing_url = "https://developer.apple.com/account/"
|
22
|
-
logger.info("GET: " + landing_url)
|
23
|
-
headers = @client.get(landing_url).headers
|
24
|
-
results = headers['location'].match(/.*appIdKey=(\h+)/)
|
25
|
-
if (results || []).length > 1
|
26
|
-
api_key = results[1]
|
27
|
-
FileUtils.mkdir_p(File.dirname(cache_path))
|
28
|
-
File.write(cache_path, api_key) if api_key.length == 64
|
29
|
-
return api_key
|
30
|
-
else
|
31
|
-
raise "Could not find latest API Key from the Dev Portal - the server might be slow right now"
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
12
|
def send_login_request(user, password)
|
36
13
|
response = send_shared_login_request(user, password)
|
37
14
|
return response if self.cookie.include?("myacinfo")
|
@@ -353,9 +330,9 @@ module Spaceship
|
|
353
330
|
end
|
354
331
|
|
355
332
|
def create_provisioning_profile!(name, distribution_method, app_id, certificate_ids, device_ids, mac: false, sub_platform: nil)
|
356
|
-
|
357
|
-
|
358
|
-
|
333
|
+
ensure_csrf(Spaceship::ProvisioningProfile) do
|
334
|
+
fetch_csrf_token_for_provisioning
|
335
|
+
end
|
359
336
|
|
360
337
|
params = {
|
361
338
|
teamId: team_id,
|
@@ -372,9 +349,9 @@ module Spaceship
|
|
372
349
|
end
|
373
350
|
|
374
351
|
def download_provisioning_profile(profile_id, mac: false)
|
375
|
-
|
376
|
-
|
377
|
-
|
352
|
+
ensure_csrf(Spaceship::ProvisioningProfile) do
|
353
|
+
fetch_csrf_token_for_provisioning
|
354
|
+
end
|
378
355
|
|
379
356
|
r = request(:get, "account/#{platform_slug(mac)}/profile/downloadProfileContent", {
|
380
357
|
teamId: team_id,
|
@@ -389,9 +366,9 @@ module Spaceship
|
|
389
366
|
end
|
390
367
|
|
391
368
|
def delete_provisioning_profile!(profile_id, mac: false)
|
392
|
-
|
393
|
-
|
394
|
-
|
369
|
+
ensure_csrf(Spaceship::ProvisioningProfile) do
|
370
|
+
fetch_csrf_token_for_provisioning
|
371
|
+
end
|
395
372
|
|
396
373
|
r = request(:post, "account/#{platform_slug(mac)}/profile/deleteProvisioningProfile.action", {
|
397
374
|
teamId: team_id,
|
@@ -401,9 +378,9 @@ module Spaceship
|
|
401
378
|
end
|
402
379
|
|
403
380
|
def repair_provisioning_profile!(profile_id, name, distribution_method, app_id, certificate_ids, device_ids, mac: false)
|
404
|
-
|
405
|
-
|
406
|
-
|
381
|
+
ensure_csrf(Spaceship::ProvisioningProfile) do
|
382
|
+
fetch_csrf_token_for_provisioning
|
383
|
+
end
|
407
384
|
|
408
385
|
r = request(:post, "account/#{platform_slug(mac)}/profile/regenProvisioningProfile.action", {
|
409
386
|
teamId: team_id,
|
@@ -418,6 +395,27 @@ module Spaceship
|
|
418
395
|
parse_response(r, 'provisioningProfile')
|
419
396
|
end
|
420
397
|
|
398
|
+
# We need a custom way to fetch the csrf token for the provisioning profile requests, since
|
399
|
+
# we use a separate API endpoint (host of Xcode API) to fetch the provisioning profiles
|
400
|
+
# All we do is fetch one profile (if exists) to get a valid csrf token with its time stamp
|
401
|
+
# This method is being called from all requests that modify, create or downloading provisioning
|
402
|
+
# profiles.
|
403
|
+
# Source https://github.com/fastlane/fastlane/issues/5903
|
404
|
+
def fetch_csrf_token_for_provisioning(mac: false)
|
405
|
+
req = request(:post) do |r|
|
406
|
+
r.url "https://developer.apple.com/services-account/#{PROTOCOL_VERSION}/account/#{platform_slug(mac)}/profile/listProvisioningProfiles.action"
|
407
|
+
r.params = {
|
408
|
+
teamId: team_id,
|
409
|
+
pageSize: 1,
|
410
|
+
pageNumber: 1,
|
411
|
+
sort: "name=asc"
|
412
|
+
}
|
413
|
+
end
|
414
|
+
|
415
|
+
parse_response(req, 'provisioningProfiles')
|
416
|
+
return nil
|
417
|
+
end
|
418
|
+
|
421
419
|
private
|
422
420
|
|
423
421
|
# This is a cache of entity type (App, AppGroup, Certificate, Device) to csrf_tokens
|
@@ -428,6 +426,8 @@ module Spaceship
|
|
428
426
|
# Ensures that there are csrf tokens for the appropriate entity type
|
429
427
|
# Relies on store_csrf_tokens to set csrf_tokens to the appropriate value
|
430
428
|
# then stores that in the correct place in cache
|
429
|
+
# This method also takes a block, if you want to send a custom request, instead of
|
430
|
+
# calling `.all` on the given klass. This is used for provisioning profiles.
|
431
431
|
def ensure_csrf(klass)
|
432
432
|
if csrf_cache[klass]
|
433
433
|
self.csrf_tokens = csrf_cache[klass]
|
@@ -438,13 +438,13 @@ module Spaceship
|
|
438
438
|
|
439
439
|
# If we directly create a new resource (e.g. app) without querying anything before
|
440
440
|
# we don't have a valid csrf token, that's why we have to do at least one request
|
441
|
-
klass.all
|
441
|
+
block_given? ? yield : klass.all
|
442
442
|
|
443
443
|
# Update 18th August 2016
|
444
444
|
# For some reason, we have to query the resource twice to actually get a valid csrf_token
|
445
445
|
# I couldn't find out why, the first response does have a valid Set-Cookie header
|
446
446
|
# But it still needs this second request
|
447
|
-
klass.all
|
447
|
+
block_given? ? yield : klass.all
|
448
448
|
|
449
449
|
csrf_cache[klass] = self.csrf_tokens
|
450
450
|
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
module Spaceship
|
2
|
+
module Tunes
|
3
|
+
# Represents app ratings from iTunesConnect
|
4
|
+
class AppRatings < TunesBase
|
5
|
+
# @return (Spaceship::Tunes::Application) A reference to the application
|
6
|
+
# this version is for
|
7
|
+
attr_accessor :application
|
8
|
+
|
9
|
+
# @return (Spaceship::Tunes::AppRatingSummary) A summary of the overall ratings for the application
|
10
|
+
attr_accessor :rating_summary
|
11
|
+
|
12
|
+
# @return (Hash) mapping country codes to a (Spaceship::Tunes::AppRatingSummary) summary of ratings for that country
|
13
|
+
attr_reader :store_fronts
|
14
|
+
|
15
|
+
# @return (Hash) of iTunesConnect version id's to readable version numbers
|
16
|
+
attr_reader :versions
|
17
|
+
|
18
|
+
attr_mapping({
|
19
|
+
'versions' => :versions
|
20
|
+
})
|
21
|
+
|
22
|
+
class << self
|
23
|
+
# Create a new object based on a hash.
|
24
|
+
# This is used to create a new object based on the server response.
|
25
|
+
def factory(attrs)
|
26
|
+
obj = self.new(attrs)
|
27
|
+
|
28
|
+
obj.unfold_rating_summary(attrs['ratings'])
|
29
|
+
obj.unfold_store_fronts(attrs['storeFronts'])
|
30
|
+
|
31
|
+
return obj
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def unfold_rating_summary(attrs)
|
36
|
+
unfolded_rating_summary = AppRatingSummary.new(attrs)
|
37
|
+
instance_variable_set(:@rating_summary, unfolded_rating_summary)
|
38
|
+
end
|
39
|
+
|
40
|
+
def unfold_store_fronts(attrs)
|
41
|
+
unfolded_store_fronts = {}
|
42
|
+
|
43
|
+
attrs.each do |info|
|
44
|
+
unfolded_store_fronts[info['countryCode']] = AppRatingSummary.new(info['ratings'])
|
45
|
+
end
|
46
|
+
|
47
|
+
instance_variable_set(:@store_fronts, unfolded_store_fronts)
|
48
|
+
end
|
49
|
+
|
50
|
+
# @return (Array) of raw hashes representing user reviews for the given store front (and optional versionId)
|
51
|
+
def reviews(store_front, versionId = '')
|
52
|
+
client.get_reviews(application.apple_id, application.platform, store_front, versionId)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
class AppRatingSummary < TunesBase
|
57
|
+
# @return (Integer) total number of reviews recevied
|
58
|
+
attr_reader :review_count
|
59
|
+
|
60
|
+
# @return (Integer) total number of ratings recevied
|
61
|
+
attr_reader :rating_count
|
62
|
+
|
63
|
+
# @return (Integer) total number of one star ratings recevied
|
64
|
+
attr_reader :one_star_rating_count
|
65
|
+
|
66
|
+
# @return (Integer) total number of two star ratings recevied
|
67
|
+
attr_reader :two_star_rating_count
|
68
|
+
|
69
|
+
# @return (Integer) total number of three star ratings recevied
|
70
|
+
attr_reader :three_star_rating_count
|
71
|
+
|
72
|
+
# @return (Integer) total number of four star ratings recevied
|
73
|
+
attr_reader :four_star_rating_count
|
74
|
+
|
75
|
+
# @return (Integer) total number of five star ratings recevied
|
76
|
+
attr_reader :five_star_rating_count
|
77
|
+
|
78
|
+
attr_mapping({
|
79
|
+
'reviewCount' => :review_count,
|
80
|
+
'ratingCount' => :rating_count,
|
81
|
+
'ratingOneCount' => :one_star_rating_count,
|
82
|
+
'ratingTwoCount' => :two_star_rating_count,
|
83
|
+
'ratingThreeCount' => :three_star_rating_count,
|
84
|
+
'ratingFourCount' => :four_star_rating_count,
|
85
|
+
'ratingFiveCount' => :five_star_rating_count
|
86
|
+
})
|
87
|
+
|
88
|
+
class << self
|
89
|
+
# Create a new object based on a hash.
|
90
|
+
# This is used to create a new object based on the server response.
|
91
|
+
def factory(attrs)
|
92
|
+
obj = self.new(attrs)
|
93
|
+
return obj
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# @return (Float) the average rating for this summary (rounded to 2 decimal places)
|
98
|
+
def average_rating
|
99
|
+
((self.one_star_rating_count +
|
100
|
+
(self.two_star_rating_count * 2) +
|
101
|
+
(self.three_star_rating_count * 3) +
|
102
|
+
(self.four_star_rating_count * 4) +
|
103
|
+
(self.five_star_rating_count * 5)) / self.rating_count.to_f).round(2)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -120,6 +120,12 @@ module Spaceship
|
|
120
120
|
client.get_resolution_center(apple_id, platform)
|
121
121
|
end
|
122
122
|
|
123
|
+
def ratings
|
124
|
+
attrs = client.get_rating_summary(apple_id, platform)
|
125
|
+
attrs[:application] = self
|
126
|
+
Tunes::AppRatings.factory(attrs)
|
127
|
+
end
|
128
|
+
|
123
129
|
# kept for backward compatibility
|
124
130
|
# tries to guess the platform of the currently submitted apps
|
125
131
|
# note that as ITC now supports multiple app types, this might break
|
@@ -7,6 +7,7 @@ require 'spaceship/tunes/tunes_client'
|
|
7
7
|
require 'spaceship/tunes/language_item'
|
8
8
|
require 'spaceship/tunes/app_status'
|
9
9
|
require 'spaceship/tunes/app_image'
|
10
|
+
require 'spaceship/tunes/app_ratings'
|
10
11
|
require 'spaceship/tunes/app_version_ref'
|
11
12
|
require 'spaceship/tunes/app_version_history'
|
12
13
|
require 'spaceship/tunes/app_version_states_history'
|
@@ -271,6 +271,16 @@ module Spaceship
|
|
271
271
|
parse_response(r, 'data')
|
272
272
|
end
|
273
273
|
|
274
|
+
def get_rating_summary(app_id, platform, versionId = '')
|
275
|
+
r = request(:get, "ra/apps/#{app_id}/reviews/summary?platform=#{platform}&versionId=#{versionId}")
|
276
|
+
parse_response(r, 'data')
|
277
|
+
end
|
278
|
+
|
279
|
+
def get_reviews(app_id, platform, storefront, versionId = '')
|
280
|
+
r = request(:get, "ra/apps/#{app_id}/reviews?platform=#{platform}&storefront=#{storefront}&versionId=#{versionId}")
|
281
|
+
parse_response(r, 'data')['reviews']
|
282
|
+
end
|
283
|
+
|
274
284
|
#####################################################
|
275
285
|
# @!group AppVersions
|
276
286
|
#####################################################
|
data/lib/spaceship/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: spaceship
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.32.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Felix Krause
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2016-08-
|
12
|
+
date: 2016-08-29 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: credentials_manager
|
@@ -341,6 +341,7 @@ files:
|
|
341
341
|
- lib/spaceship/portal/ui/select_team.rb
|
342
342
|
- lib/spaceship/tunes/app_details.rb
|
343
343
|
- lib/spaceship/tunes/app_image.rb
|
344
|
+
- lib/spaceship/tunes/app_ratings.rb
|
344
345
|
- lib/spaceship/tunes/app_screenshot.rb
|
345
346
|
- lib/spaceship/tunes/app_status.rb
|
346
347
|
- lib/spaceship/tunes/app_submission.rb
|
@@ -391,7 +392,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
391
392
|
version: '0'
|
392
393
|
requirements: []
|
393
394
|
rubyforge_project:
|
394
|
-
rubygems_version: 2.
|
395
|
+
rubygems_version: 2.2.2
|
395
396
|
signing_key:
|
396
397
|
specification_version: 4
|
397
398
|
summary: Ruby library to access the Apple Dev Center and iTunes Connect
|