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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4b410b389cf8004d24c610840d5e830680c1725d
4
- data.tar.gz: 18b0892ee506b0b7cd73df036d44d830c068d5f3
3
+ metadata.gz: 446e6df28c4691d7dea2ecb1d0e9ef8543c418a7
4
+ data.tar.gz: 7ea38ddea78ce1183853b9e619b724ada951a8da
5
5
  SHA512:
6
- metadata.gz: 03390791a5a2bff1e17f109c649055ff8aff11e16bdf3cd6b1addb79e30128e7153ffe7d7e3e23e95386e94fd893102bb890b72b8bc266776695a47e4709e8dd
7
- data.tar.gz: 453285e84082f6909f743eb4d39b5efdd130d809b6d5581cefe22d1f4248c08ffc6a162b042a0885013b7d1911ea18adcb275791f4865e3c1c8dba42c3fbd675
6
+ metadata.gz: 27fc2dd359daf036dc9e0d22274ccf99ff9336239a765d9ba1c0ff18a23f7d2062498e345f4d74cc08d29a9c52ed24ad82ebe7a09c72df7fb7635f90d4031f1c
7
+ data.tar.gz: e28fd65e2a39cac2d175a3f615bde469ba0f30b00c0af1700345fd9046ec93bba459c157dbbfe39ea323e2f16d9c6dec58ff2818274d1dc35e94e5cc3800d0e7
@@ -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 ||= js.body.match(/itcServiceKey = '(.*)'/)[1]
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
- # Calling provisioning_profiles uses a different api end point than creating or deleting them so we
357
- # use a different entity to gather the csrf token
358
- ensure_csrf(Spaceship::App)
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
- # Calling provisioning_profiles uses a different api end point than creating or deleting them so we
376
- # use a different entity to gather the csrf token
377
- ensure_csrf(Spaceship::App)
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
- # Calling provisioning_profiles uses a different api end point than creating or deleting them so we
393
- # use a different entity to gather the csrf token
394
- ensure_csrf(Spaceship::App)
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
- # Calling provisioning_profiles uses a different api end point than creating or deleting them so we
405
- # use a different entity to gather the csrf token
406
- ensure_csrf(Spaceship::App)
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
  #####################################################
@@ -1,4 +1,4 @@
1
1
  module Spaceship
2
- VERSION = "0.31.10".freeze
2
+ VERSION = "0.32.0".freeze
3
3
  DESCRIPTION = "Ruby library to access the Apple Dev Center and iTunes Connect".freeze
4
4
  end
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.31.10
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-25 00:00:00.000000000 Z
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.5.1
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