lib/spaceship/tunes/tunes_client.rb in spaceship-0.7.0 vs lib/spaceship/tunes/tunes_client.rb in spaceship-0.9.0
- old
+ new
@@ -1,19 +1,36 @@
-# rubocop:disable Metrics/ClassLength
module Spaceship
+ # rubocop:disable Metrics/ClassLength
class TunesClient < Spaceship::Client
-
# ITunesConnectError is only thrown when iTunes Connect raises an exception
class ITunesConnectError < StandardError
end
- # ITunesConnectNoChangesError is thrown when the only error is that there were no changes
- # usually those errors are irrelevant
- class ITunesConnectNoChangesError < ITunesConnectError
+ attr_reader :du_client
+ def initialize
+ super
+
+ @du_client = DUClient.new
end
+ class << self
+ # trailer preview screenshots are required to have a specific size
+ def video_preview_resolution_for(device, is_portrait)
+ resolutions = {
+ 'iphone4' => [1136, 640],
+ 'iphone6' => [1334, 750],
+ 'iphone6Plus' => [2208, 1242],
+ 'ipad' => [1024, 768]
+ }
+
+ r = resolutions[device]
+ r = [r[1], r[0]] if is_portrait
+ r
+ end
+ end
+
#####################################################
# @!group Init and Login
#####################################################
def self.hostname
@@ -88,12 +105,12 @@
return unless raw.kind_of? Hash
data = raw['data'] || raw # sometimes it's with data, sometimes it isn't
if data.fetch('sectionErrorKeys', []).count == 0 and
- data.fetch('sectionInfoKeys', []).count == 0 and
- data.fetch('sectionWarningKeys', []).count == 0
+ data.fetch('sectionInfoKeys', []).count == 0 and
+ data.fetch('sectionWarningKeys', []).count == 0
logger.debug("Request was successful")
end
handle_response_hash = lambda do |hash|
@@ -123,12 +140,11 @@
different_error = raw.fetch('messages', {}).fetch('error', nil)
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 for which we throw a separate exception
- raise ITunesConnectNoChangesError.new, errors.first
+ # This is a special error which we really don't care about
else
raise ITunesConnectError.new, errors.join(' ')
end
end
@@ -184,11 +200,11 @@
# some values are nil, that's why there is a hash
data['versionString'] = { value: version }
data['newApp']['name'] = { value: name }
data['newApp']['bundleId']['value'] = bundle_id
data['newApp']['primaryLanguage']['value'] = primary_language || 'English'
- data['newApp']['vendorId'] = {value: sku }
+ data['newApp']['vendorId'] = { value: sku }
data['newApp']['bundleIdSuffix']['value'] = bundle_id_suffix
data['companyName']['value'] = company_name if company_name
data['newApp']['appType'] = app_type
data['initialPlatform'] = app_type
@@ -254,10 +270,164 @@
handle_itc_response(r.body)
end
#####################################################
+ # @!group Pricing
+ #####################################################
+
+ def update_price_tier!(app_id, price_tier)
+ r = request(:get, "ra/apps/#{app_id}/pricing/intervals")
+ data = parse_response(r, 'data')
+
+ first_price = (data["pricingIntervalsFieldTO"]["value"] || []).count == 0 # first price
+ data["pricingIntervalsFieldTO"]["value"] ||= []
+ data["pricingIntervalsFieldTO"]["value"] << {} if data["pricingIntervalsFieldTO"]["value"].count == 0
+ data["pricingIntervalsFieldTO"]["value"].first["tierStem"] = price_tier.to_s
+
+ effective_date = (first_price ? nil : Time.now.to_i * 1000)
+ data["pricingIntervalsFieldTO"]["value"].first["priceTierEffectiveDate"] = effective_date
+ data["pricingIntervalsFieldTO"]["value"].first["priceTierEndDate"] = nil
+ data["countriesChanged"] = first_price
+ data["theWorld"] = true
+
+ if first_price # first price, need to set all countries
+ data["countries"] = supported_countries.collect do |c|
+ c.delete('region') # we don't care about le region
+ c
+ end
+ end
+
+ # send the changes back to Apple
+ r = request(:post) do |req|
+ req.url "ra/apps/#{app_id}/pricing/intervals"
+ req.body = data.to_json
+ req.headers['Content-Type'] = 'application/json'
+ end
+ handle_itc_response(r.body)
+ end
+
+ def price_tier(app_id)
+ r = request(:get, "ra/apps/#{app_id}/pricing/intervals")
+ data = parse_response(r, 'data')
+
+ begin
+ data["pricingIntervalsFieldTO"]["value"].first["tierStem"]
+ rescue
+ nil
+ end
+ end
+
+ # An array of supported countries
+ # [{
+ # "code": "AL",
+ # "name": "Albania",
+ # "region": "Europe"
+ # }, {
+ # ...
+ def supported_countries
+ r = request(:get, "ra/apps/pricing/supportedCountries")
+ parse_response(r, 'data')
+ end
+
+ #####################################################
+ # @!group App Icons
+ #####################################################
+ # Uploads a large icon
+ # @param app_version (AppVersion): The version of your app
+ # @param upload_image (UploadFile): The icon to upload
+ # @return [JSON] the response
+ def upload_large_icon(app_version, upload_image)
+ raise "app_version is required" unless app_version
+ raise "upload_image is required" unless upload_image
+
+ du_client.upload_large_icon(app_version, upload_image, content_provider_id, sso_token_for_image)
+ end
+
+ # Uploads a watch icon
+ # @param app_version (AppVersion): The version of your app
+ # @param upload_image (UploadFile): The icon to upload
+ # @return [JSON] the response
+ def upload_watch_icon(app_version, upload_image)
+ raise "app_version is required" unless app_version
+ raise "upload_image is required" unless upload_image
+
+ du_client.upload_watch_icon(app_version, upload_image, content_provider_id, sso_token_for_image)
+ end
+
+ # Uploads a screenshot
+ # @param app_version (AppVersion): The version of your app
+ # @param upload_image (UploadFile): The image to upload
+ # @param device (string): The target device
+ # @return [JSON] the response
+ def upload_screenshot(app_version, upload_image, device)
+ raise "app_version is required" unless app_version
+ raise "upload_image is required" unless upload_image
+ raise "device is required" unless device
+
+ du_client.upload_screenshot(app_version, upload_image, content_provider_id, sso_token_for_image, device)
+ end
+
+ # Uploads the transit app file
+ # @param app_version (AppVersion): The version of your app
+ # @param upload_file (UploadFile): The image to upload
+ # @return [JSON] the response
+ def upload_geojson(app_version, upload_file)
+ raise "app_version is required" unless app_version
+ raise "upload_file is required" unless upload_file
+
+ du_client.upload_geojson(app_version, upload_file, content_provider_id, sso_token_for_image)
+ end
+
+ # Uploads the transit app file
+ # @param app_version (AppVersion): The version of your app
+ # @param upload_trailer (UploadFile): The trailer to upload
+ # @return [JSON] the response
+ def upload_trailer(app_version, upload_trailer)
+ raise "app_version is required" unless app_version
+ raise "upload_trailer is required" unless upload_trailer
+
+ du_client.upload_trailer(app_version, upload_trailer, content_provider_id, sso_token_for_video)
+ end
+
+ # Uploads the trailer preview
+ # @param app_version (AppVersion): The version of your app
+ # @param upload_trailer_preview (UploadFile): The trailer preview to upload
+ # @return [JSON] the response
+ def upload_trailer_preview(app_version, upload_trailer_preview)
+ raise "app_version is required" unless app_version
+ raise "upload_trailer_preview is required" unless upload_trailer_preview
+
+ du_client.upload_trailer_preview(app_version, upload_trailer_preview, content_provider_id, sso_token_for_image)
+ end
+
+ # Fetches the App Version Reference information from ITC
+ # @return [AppVersionRef] the response
+ def ref_data
+ r = request(:get, '/WebObjects/iTunesConnect.woa/ra/apps/version/ref')
+ data = parse_response(r, 'data')
+ Spaceship::Tunes::AppVersionRef.factory(data)
+ end
+
+ # Fetches the User Detail information from ITC
+ # @return [UserDetail] the response
+ def user_detail_data
+ r = request(:get, '/WebObjects/iTunesConnect.woa/ra/user/detail')
+ data = parse_response(r, 'data')
+ Spaceship::Tunes::UserDetail.factory(data)
+ end
+
+ #####################################################
+ # @!group CandiateBuilds
+ #####################################################
+
+ def candidate_builds(app_id, version_id)
+ r = request(:get, "ra/apps/#{app_id}/versions/#{version_id}/candidateBuilds")
+ parse_response(r, 'data')['builds']
+ end
+
+ #####################################################
# @!group Build Trains
#####################################################
# @param (testing_type) internal or external
def build_trains(app_id, testing_type)
@@ -285,10 +455,11 @@
req.headers['Content-Type'] = 'application/json'
end
handle_itc_response(r.body)
end
+ # rubocop:disable Metrics/AbcSize
def submit_testflight_build_for_review!( # Required:
app_id: nil,
train: nil,
build_number: nil,
@@ -354,20 +525,35 @@
end
handle_itc_response(r.body)
end
end
+ # rubocop:enable Metrics/AbcSize
#####################################################
# @!group Submit for Review
#####################################################
- def send_app_submission(app_id, data, stage)
+ def prepare_app_submissions(app_id, version)
raise "app_id is required" unless app_id
+ raise "version is required" unless version
+ r = request(:get) do |req|
+ req.url "ra/apps/#{app_id}/versions/#{version}/submit/summary"
+ req.headers['Content-Type'] = 'application/json'
+ end
+
+ handle_itc_response(r.body)
+ parse_response(r, 'data')
+ end
+
+ def send_app_submission(app_id, data)
+ raise "app_id is required" unless app_id
+
+ # ra/apps/1039164429/version/submit/complete
r = request(:post) do |req|
- req.url "ra/apps/#{app_id}/version/submit/#{stage}"
+ req.url "ra/apps/#{app_id}/version/submit/complete"
req.body = data.to_json
req.headers['Content-Type'] = 'application/json'
end
handle_itc_response(r.body)
@@ -461,10 +647,25 @@
update_tester_from_app!(tester, app_id, false)
end
private
+ # the contentProviderIr found in the UserDetail instance
+ def 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
+ end
+
+ # the ssoTokenForVideo found in the AppVersionRef instance
+ def 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 = {
users: [
{
@@ -492,7 +693,7 @@
data = parse_response(r, 'data')
handle_itc_response(data)
end
end
+ # rubocop:enable Metrics/ClassLength
end
-# rubocop:enable Metrics/ClassLength