lib/spaceship/tunes/tunes_client.rb in spaceship-0.20.0 vs lib/spaceship/tunes/tunes_client.rb in spaceship-0.21.0
- old
+ new
@@ -3,10 +3,14 @@
class TunesClient < Spaceship::Client
# ITunesConnectError is only thrown when iTunes Connect raises an exception
class ITunesConnectError < StandardError
end
+ # raised if the server failed to save temporarily
+ class ITunesConnectTemporaryError < ITunesConnectError
+ end
+
attr_reader :du_client
def initialize
super
@@ -118,30 +122,36 @@
js = request(:get, "https://itunesconnect.apple.com/itc/static-resources/controllers/login_cntrl.js")
@service_key ||= js.body.match(/itcServiceKey = '(.*)'/)[1]
end
def send_login_request(user, password)
+ clear_user_cached_data
+
data = {
accountName: user,
password: password,
rememberMe: true
}
- response = request(:post) do |req|
- req.url "https://idmsa.apple.com/appleauth/auth/signin?widgetKey=#{service_key}"
- req.body = data.to_json
- req.headers['Content-Type'] = 'application/json'
- req.headers['X-Requested-With'] = 'XMLHttpRequest'
- req.headers['Accept'] = 'application/json, text/javascript'
+ begin
+ response = request(:post) do |req|
+ req.url "https://idmsa.apple.com/appleauth/auth/signin?widgetKey=#{service_key}"
+ req.body = data.to_json
+ req.headers['Content-Type'] = 'application/json'
+ req.headers['X-Requested-With'] = 'XMLHttpRequest'
+ req.headers['Accept'] = 'application/json, text/javascript'
+ end
+ rescue UnauthorizedAccessError
+ raise InvalidUserCredentialsError.new, "Invalid username and password combination. Used '#{user}' as the username."
end
# get woinst, wois, and itctx cookie values
request(:get, "https://itunesconnect.apple.com/WebObjects/iTunesConnect.woa/wa/route?noext")
request(:get, "https://itunesconnect.apple.com/WebObjects/iTunesConnect.woa")
case response.status
- when 403, 401
+ when 403
raise InvalidUserCredentialsError.new, "Invalid username and password combination. Used '#{user}' as the username."
when 200
return response
else
if response["Location"] == "/auth" # redirect to 2 step auth page
@@ -201,10 +211,12 @@
errors << different_error if different_error
if errors.count > 0 # they are separated by `.` by default
if errors.count == 1 and errors.first == "You haven't made any changes."
# This is a special error which we really don't care about
+ elsif errors.count == 1 and errors.first.include?("try again later")
+ raise ITunesConnectTemporaryError.new, errors.first
else
raise ITunesConnectError.new, errors.join(' ')
end
end
@@ -336,17 +348,19 @@
def update_app_version!(app_id, version_id, data)
raise "app_id is required" unless app_id
raise "version_id is required" unless version_id.to_i > 0
- r = request(:post) do |req|
- req.url "ra/apps/#{app_id}/platforms/ios/versions/#{version_id}"
- req.body = data.to_json
- req.headers['Content-Type'] = 'application/json'
- end
+ with_tunes_retry do
+ r = request(:post) do |req|
+ req.url "ra/apps/#{app_id}/platforms/ios/versions/#{version_id}"
+ req.body = data.to_json
+ req.headers['Content-Type'] = 'application/json'
+ end
- handle_itc_response(r.body)
+ handle_itc_response(r.body)
+ end
end
#####################################################
# @!group Pricing
#####################################################
@@ -778,22 +792,41 @@
update_tester_from_app!(tester, app_id, false)
end
private
+ def with_tunes_retry(tries = 5, &_block)
+ return yield
+ rescue Spaceship::TunesClient::ITunesConnectTemporaryError => ex
+ unless (tries -= 1).zero?
+ msg = "ITC temporary save error received: '#{ex.message}'. Retrying after 60 seconds (remaining: #{tries})..."
+ puts msg
+ logger.warn msg
+ sleep 60 unless defined? SpecHelper # unless FastlaneCore::Helper.is_test?
+ retry
+ end
+ raise ex # re-raise the exception
+ end
+
+ def clear_user_cached_data
+ @content_provider_id = nil
+ @sso_token_for_image = nil
+ @sso_token_for_video = nil
+ end
+
# the contentProviderIr found in the UserDetail instance
def content_provider_id
- user_detail_data.content_provider_id
+ @content_provider_id ||= user_detail_data.content_provider_id
end
# the ssoTokenForImage found in the AppVersionRef instance
def sso_token_for_image
- ref_data.sso_token_for_image
+ @sso_token_for_image ||= ref_data.sso_token_for_image
end
# the ssoTokenForVideo found in the AppVersionRef instance
def sso_token_for_video
- ref_data.sso_token_for_video
+ @sso_token_for_video ||= ref_data.sso_token_for_video
end
def update_tester_from_app!(tester, app_id, testing)
url = tester.class.url(app_id)[:update_by_app]
data = {