spaceship 0.31.10 → 0.32.0

Sign up to get free protection for your applications and to get access to all the features.
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