spaceship/lib/spaceship/tunes/tunes_client.rb in fastlane-2.11.0 vs spaceship/lib/spaceship/tunes/tunes_client.rb in fastlane-2.12.0

- old
+ new

@@ -44,13 +44,11 @@ "https://itunesconnect.apple.com/WebObjects/iTunesConnect.woa/" end # @return (Array) A list of all available teams def teams - return @teams if @teams - r = request(:get, "ra/user/detail") - @teams = parse_response(r, 'data')['associatedAccounts'].sort_by do |team| + user_details_data['associatedAccounts'].sort_by do |team| [ team['contentProvider']['name'], team['contentProvider']['contentProviderId'] ] end @@ -212,14 +210,17 @@ # e.g. {"warn"=>nil, "error"=>["operation_failed"], "info"=>nil} different_error = raw.fetch('messages', {}).fetch('error', nil) errors << different_error if different_error if errors.count > 0 # they are separated by `.` by default + # Sample `error` content: [["Forbidden"]] 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 + elsif errors.count == 1 and errors.first.include?("Forbidden") + raise_insuffient_permission_error! else raise ITunesConnectError.new, errors.join(' ') end end @@ -594,18 +595,65 @@ r = request(:get, '/WebObjects/iTunesConnect.woa/ra/apps/version/ref') data = parse_response(r, 'data') Spaceship::Tunes::AppVersionRef.factory(data) end + # Fetch the general information of the user, is used by various methods across spaceship + # Sample return value + # => {"associatedAccounts"=> + # [{"contentProvider"=>{"contentProviderId"=>11142800, "name"=>"Felix Krause", "contentProviderTypes"=>["Purple Software"]}, "roles"=>["Developer"], "lastLogin"=>1468784113000}], + # "sessionToken"=>{"dsId"=>"8501011116", "contentProviderId"=>18111111, "expirationDate"=>nil, "ipAddress"=>nil}, + # "permittedActivities"=> + # {"EDIT"=> + # ["UserManagementSelf", + # "GameCenterTestData", + # "AppAddonCreation"], + # "REPORT"=> + # ["UserManagementSelf", + # "AppAddonCreation"], + # "VIEW"=> + # ["TestFlightAppExternalTesterManagement", + # ... + # "HelpGeneral", + # "HelpApplicationLoader"]}, + # "preferredCurrencyCode"=>"EUR", + # "preferredCountryCode"=>nil, + # "countryOfOrigin"=>"AT", + # "isLocaleNameReversed"=>false, + # "feldsparToken"=>nil, + # "feldsparChannelName"=>nil, + # "hasPendingFeldsparBindingRequest"=>false, + # "isLegalUser"=>false, + # "userId"=>"1771111155", + # "firstname"=>"Detlef", + # "lastname"=>"Mueller", + # "isEmailInvalid"=>false, + # "hasContractInfo"=>false, + # "canEditITCUsersAndRoles"=>false, + # "canViewITCUsersAndRoles"=>true, + # "canEditIAPUsersAndRoles"=>false, + # "transporterEnabled"=>false, + # "contentProviderFeatures"=>["APP_SILOING", "PROMO_CODE_REDESIGN", ...], + # "contentProviderType"=>"Purple Software", + # "displayName"=>"Detlef", + # "contentProviderId"=>"18742800", + # "userFeatures"=>[], + # "visibility"=>true, + # "DYCVisibility"=>false, + # "contentProvider"=>"Felix Krause", + # "userName"=>"detlef@krausefx.com"} + def user_details_data + return @_cached_user_details if @_cached_user_details + r = request(:get, '/WebObjects/iTunesConnect.woa/ra/user/detail') + @_cached_user_details = parse_response(r, 'data') + end + # Fetches the User Detail information from ITC. This gets called often and almost never changes # so we cache it # @return [UserDetail] the response def user_detail_data - return @cached if @cached - r = request(:get, '/WebObjects/iTunesConnect.woa/ra/user/detail') - data = parse_response(r, 'data') - @cached = Spaceship::Tunes::UserDetail.factory(data) + @_cached_user_detail_data ||= Spaceship::Tunes::UserDetail.factory(user_details_data) end ##################################################### # @!group CandiateBuilds ##################################################### @@ -629,11 +677,17 @@ return parse_response(r, 'data') rescue Spaceship::Client::UnexpectedResponse => ex # Build trains fail randomly very often # we need to catch those errors and retry # https://github.com/fastlane/fastlane/issues/6419 - if ex.to_s.include?("ITC.response.error.OPERATION_FAILED") + retry_error_messages = [ + "ITC.response.error.OPERATION_FAILED", + "Internal Server Error", + "Service Unavailable" + ].freeze + + if retry_error_messages.any? { |message| ex.to_s.include?(message) } tries -= 1 if tries > 0 logger.warn("Received temporary server error from iTunes Connect. Retrying the request...") sleep 3 unless defined? SpecHelper retry @@ -868,10 +922,14 @@ url = tester.url(app_id)[:index_by_app] r = request(:get, url) parse_response(r, 'data')['users'] end + # Returns a list of available testing groups + # e.g. + # {"b6f65dbd-c845-4d91-bc39-0b661d608970" => "Boarding", + # "70402368-9deb-409f-9a26-bb3f215dfee3" => "Automatic"} def groups return @cached_groups if @cached_groups r = request(:get, '/WebObjects/iTunesConnect.woa/ra/users/pre/ext') @cached_groups = parse_response(r, 'data')['groups'] end @@ -879,24 +937,45 @@ def create_tester!(tester: nil, email: nil, first_name: nil, last_name: nil, groups: nil) url = tester.url[:create] raise "Action not provided for this tester type." unless url tester_data = { - emailAddress: { - value: email - }, - firstName: { - value: first_name || "" - }, - lastName: { - value: last_name || "" - }, - testing: { - value: true + emailAddress: { + value: email + }, + firstName: { + value: first_name || "" + }, + lastName: { + value: last_name || "" + }, + testing: { + value: true + } + } + + if groups + tester_data[:groups] = groups.map do |group_name_or_group_id| + if self.groups.value?(group_name_or_group_id) + # This is an existing group, let's use that, the user specified the group name + group_name = group_name_or_group_id + group_id = self.groups.key(group_name_or_group_id) + elsif self.groups.key?(group_name_or_group_id) + # This is an existing group, let's use that, the user specified the group ID + group_name = self.groups[group_name_or_group_id] + group_id = group_name_or_group_id + else + group_name = group_name_or_group_id + group_id = nil # this is expected by the iTC API + end + + { + "id" => group_id, + "name" => { + "value" => group_name } } - if groups - tester_data[:groups] = groups.map { |x| { "id" => x } } + end end data = { testers: [tester_data] } r = request(:post) do |req|