b0VIM 8.12|`joshJoshs-Mac-mini.local~josh/Projects/fastlane/fastlane/supply/lib/supply/client.rbutf-8 3210#"! UtpgihV'adU gyxRD {* u < ! Z  q d J ' b  ^]5GonS ogfZYA%A6Z&%   # Editing something SCOPE = AndroidPublisher::AUTH_ANDROIDPUBLISHER SERVICE = AndroidPublisher::AndroidPublisherService class Client < AbstractGoogleServiceClient end end UI.user_error!("Google Api Error: #{e.message} - #{message}") end message = e.body else message = error["error"] && error["error"]["message"] if error end nil rescue JSON.parse(e.body) error = begin rescue Google::Apis::Error => e yield if block_given? def call_google_api private end self.client = service end service.root_url = params[:root_url] params[:root_url] << '/' unless params[:root_url].end_with?('/') # Google's client expects the root_url string to end with "/". if params[:root_url] service.authorization = auth_client service = self.class::SERVICE.new Google::Apis::RequestOptions.default.retries = 5 Google::Apis::ClientOptions.default.send_timeout_sec = params[:timeout] Google::Apis::ClientOptions.default.open_timeout_sec = params[:timeout] Google::Apis::ClientOptions.default.read_timeout_sec = params[:timeout] Google::Apis::ClientOptions.default.application_version = Fastlane::VERSION Google::Apis::ClientOptions.default.application_name = "fastlane (supply client)" end Google::Apis.logger.level = Logger::DEBUG if FastlaneCore::Env.truthy?("DEBUG") auth_client.fetch_access_token! UI.verbose("Fetching a new access token from Google...") auth_client = Google::Auth::ServiceAccountCredentials.make_creds(json_key_io: service_account_json, scope: self.class::SCOPE) def initialize(service_account_json: nil, params: nil) # @param service_account_json: The raw service account Json data # Initializes the service and its auth_client using the specified information end service_account_json end service_account_json = StringIO.new(params[:json_key_data]) elsif params[:json_key_data] service_account_json = File.open(File.expand_path(params[:json_key])) if params[:json_key] end end UI.user_error!("Could not load Google authentication. Make sure it has been added as an environment variable in 'json_key' or 'json_key_data'") else params[:json_key] = json_key_path UI.user_error!("Could not find service account json file at path '#{json_key_path}'") unless File.exist?(json_key_path) json_key_path = File.expand_path(json_key_path) json_key_path = UI.input("The service account json file used to authenticate with Google: ") UI.important("To not be asked about this value, you can specify it using 'json_key'") if UI.interactive? unless params[:json_key] || params[:json_key_data] def self.service_account_authentication(params: nil) # Supply authentication file end return self.new(service_account_json: service_account_data, params: params) service_account_data = self.service_account_authentication(params: params) params ||= Supply.config def self.make_from_config(params: nil) attr_accessor :client # Connecting with Google SERVICE = nil SCOPE = nil class AbstractGoogleServiceClientmodule Supply# rubocop:disable Metrics/ClassLengthrequire 'net/http'AndroidPublisher = Google::Apis::AndroidpublisherV3require 'google/apis/androidpublisher_v3'require 'googleauth'adJ<2*)fP@6,$# l L 1  r f \ R J I  k j H '  _ B ,  ~ ; `OED0('|{b@ xw=#" vldc rV7)!# rubocop:enable Metrics/ClassLengthend end end UI.user_error!("You need to have an active edit, make sure to call `begin_edit`") unless @current_edit def ensure_active_edit! private end end ) content_type: 'application/octet-stream' upload_source: obb_file_path, expansion_file_type, apk_version_code, current_edit.id, current_package_name, client.upload_edit_expansionfile( call_google_api do ensure_active_edit! def upload_obb(obb_file_path: nil, apk_version_code: nil, expansion_file_type: nil) end end ) image_type language, current_edit.id, current_package_name, client.deleteall_edit_image( call_google_api do ensure_active_edit! def clear_screenshots(image_type: nil, language: nil) end end ) content_type: 'image/*' upload_source: image_path, image_type, language, current_edit.id, current_package_name, client.upload_edit_image( call_google_api do ensure_active_edit! def upload_image(image_path: nil, image_type: nil, language: nil) # @param image_type (e.g. phoneScreenshots, sevenInchScreenshots, ...) end return images end full_url full_url = "#{url}=s0" # '=s0' param ensures full image size is returned (https://github.com/fastlane/fastlane/pull/14322#issuecomment-473012462) UI.verbose("URL after removing params: '#{clean_url}'") UI.verbose("Removed params ('#{uri.query}') from the URL") UI.verbose("Initial URL received: '#{url}'") ].join uri.path uri.port, uri.host, uri.userinfo, uri.scheme, clean_url = [ uri = URI.parse(url) images = urls.map do |url| urls = (result.images || []).map(&:url) end ) image_type language, current_edit.id, current_package_name, client.list_edit_images( result = call_google_api do ensure_active_edit! def fetch_images(image_type: nil, language: nil) ##################################################### # @!group Screenshots ##################################################### end end ) ) file_size: file_size references_version: references_version, AndroidPublisher::ExpansionFile.new( expansion_file_type, apk_version_code, current_edit.id, current_package_name, client.update_edit_expansionfile( call_google_api do ensure_active_edit! def update_obb(apk_version_code, expansion_file_type, references_version, file_size) end end ) track track_name, self.current_edit.id, current_package_name, client.update_edit_track( call_google_api do ensure_active_edit! def upload_changelogs(track, track_name) end end raise return [] if e.status_code == 404 && e.to_s.include?("trackEmpty") rescue Google::Apis::ClientError => e return result.releases || [] ) trackad Vkcb|{D<; i h D j  d f \ [ &cb utXPO}X*   language, current_edit.id, current_package_name, client.update_edit_listing( call_google_api do }) video: video short_description: short_description, full_description: full_description, title: title, language: language, listing = AndroidPublisher::Listing.new(**{ ensure_active_edit! def update_listing_for_language(language: nil, title: nil, short_description: nil, full_description: nil, video: nil) # Updates or creates the listing for the specified language ##################################################### # @!group Modifying data ##################################################### end return latest_version end latest_version = tracks.select { |t| t.track == track }.map(&:releases).flatten.reject { |r| r.name.nil? }.max_by(&:name) else UI.user_error!(%(Unable to find latest version information from "#{Supply::Tracks::DEFAULT}" track. Please specify track information by using the '--track' option.)) if latest_version.nil? && track == Supply::Tracks::DEFAULT # Check if user specified '--track' option if version information from 'production' track is nil latest_version = tracks.select { |t| t.track == Supply::Tracks::DEFAULT }.map(&:releases).flatten.reject { |r| r.name.nil? }.max_by(&:name) def latest_version(track) end end Supply::ReleaseListing.new(filtered_track, filtered_release.name, filtered_release.version_codes, row.language, row.text) return filtered_release.release_notes.map do |row| end return nil UI.user_error!("Version '#{version}' for '#{current_package_name}' does not seem to have any release notes. Nothing to download.") if filtered_release.release_notes.nil? # Since we can release on Alpha/Beta without release notes. filtered_release = filtered_track.releases.first { |r| !r.name.nil? && r.name == version } end UI.message("Found '#{version}' in '#{filtered_track.track}' track.") else return nil UI.user_error!("Unable to find version '#{version}' for '#{current_package_name}' in all tracks. Please double check the version number.") if filtered_track.nil? filtered_track = filtered_tracks.first end end filtered_tracks = filtered_tracks.select { |t| t.track == Supply::Tracks::BETA } # E.g.: A release might be in both Alpha & Beta (not sure if this is possible, just catching if it ever happens), giving Beta precedence. else filtered_tracks = filtered_tracks.select { |t| t.track == Supply::Tracks::DEFAULT } if filtered_tracks.any? { |t| t.track == Supply::Tracks::DEFAULT } # E.g.: A release might've been promoted from Alpha/Beta track. This means the release will be present in two or more tracks # Production track takes precedence if version is present in multiple tracks if filtered_tracks.length > 1 filtered_tracks = tracks.select { |t| !t.releases.nil? && t.releases.any? { |r| r.name == version } } # Verify that tracks have releases ensure_active_edit! def release_listings(version) end return Array(result.bundles).map(&:version_code) result = call_google_api { client.list_edit_bundles(current_package_name, current_edit.id) } ensure_active_edit! def aab_version_codes # Get a list of all AAB version codes - returns the list of version codes end return Array(result.apks).map(&:version_code) result = call_google_api { client.list_edit_apks(current_package_name, current_edit.id) }adWiS+*h3 A y o -   x p o 5 7 6 s X > = T5rU;:^C L%  |{HM2 ensure_active_edit! def apks_version_codes # Get a list of all APK version codes - returns the list of version codes end end raise return Listing.new(self, language) if e.status_code == 404 # create a new empty listing rescue Google::Apis::ClientError => e return Listing.new(self, language, result) ) language current_edit.id, current_package_name, result = client.get_edit_listing( begin ensure_active_edit! def listing_for_language(language) # Returns the listing for the given language filled with the current values if it already exists end end Listing.new(self, row.language, row) return result.listings.map do |row| result = call_google_api { client.list_edit_listings(current_package_name, current_edit.id) } ensure_active_edit! def listings # make sure to have an active edit # Get a list of all languages - returns the list ##################################################### # @!group Getting data ##################################################### end self.current_package_name = nil self.current_edit = nil call_google_api { client.commit_edit(current_package_name, current_edit.id) } ensure_active_edit! def commit_current_edit! # Commits the current edit saving all pending changes on Google Play end call_google_api { client.validate_edit(current_package_name, current_edit.id) } ensure_active_edit! def validate_current_edit! # Validates the current edit - does not change data on Google Play end self.current_package_name = nil self.current_edit = nil call_google_api { client.delete_edit(current_package_name, current_edit.id) } ensure_active_edit! def abort_current_edit # Aborts the current edit deleting all pending changes end self.current_package_name = package_name self.current_edit = call_google_api { client.insert_edit(package_name) } UI.user_error!("You currently have an active edit") if @current_edit def begin_edit(package_name: nil) # Begin modifying a certain package ##################################################### # @!group Handling the edit lifecycle ##################################################### end end UI.user_error!("No authentication parameters were specified. These must be provided in order to authenticate with Google") else service_account_json service_account_json = StringIO.new(JSON.dump(cred_json)) } client_email: params[:issuer] private_key: key.to_s, cred_json = { key = Google::APIClient::KeyUtils.load_from_pkcs12(File.expand_path(params[:key]), 'notasecret') UI.important("This type of authentication is deprecated. Please consider using JSON authentication instead") require 'google/api_client/auth/key_utils' elsif params[:key] && params[:issuer] super(params: params) if params[:json_key] || params[:json_key_data] def self.service_account_authentication(params: nil) ##################################################### # @!group Login ##################################################### attr_accessor :current_package_name # Package name of the currently edited element attr_accessor :current_edit # Reference to the entry we're currently editing. Might be nil if don't have one openad*mM-a  m : 0 & % f e L  b / %    h H "  ~ V N M  K3 bF,+v"qQ6  xP0eE*) current_edit.id, current_package_name, result = client.get_edit_track( begin ensure_active_edit! def track_releases(track) # Get list of release names for track end end raise return [] if e.status_code == 404 && (e.to_s.include?("trackEmpty") || e.to_s.include?("Track not found")) rescue Google::Apis::ClientError => e return result.releases.flat_map(&:version_codes) || [] ) track current_edit.id, current_package_name, result = client.get_edit_track( begin ensure_active_edit! def track_version_codes(track) # Get list of version codes for track end end ) track_object track_name, current_edit.id, current_package_name, client.update_edit_track( call_google_api do ensure_active_edit! def update_track(track_name, track_object) end return all_tracks end all_tracks = all_tracks.select { |track| tracknames.include?(track.track) } if tracknames.length > 0 all_tracks = [] unless all_tracks all_tracks = call_google_api { client.list_edit_tracks(current_package_name, current_edit.id) }.tracks ensure_active_edit! def tracks(*tracknames) # Get a list of all tracks - returns the list end return result_upload.download_url end ) content_type: "application/octet-stream" upload_source: path_to_aab, package_name, client.uploadbundle_internalappsharingartifact( result_upload = call_google_api do # NOTE: This Google API is a little different. It doesn't require an active edit. def upload_bundle_to_internal_app_sharing(package_name, path_to_aab) end return result_upload.version_code end ) ack_bundle_installation_warning: Supply.config[:ack_bundle_installation_warning] content_type: "application/octet-stream", upload_source: path_to_aab, self.current_edit.id, current_package_name, client.upload_edit_bundle( result_upload = call_google_api do ensure_active_edit! def upload_bundle(path_to_aab) end end ) content_type: "application/octet-stream" upload_source: path_to_mapping, extension == ".zip" ? "nativeCode" : "proguard", apk_version_code, current_edit.id, current_package_name, client.upload_edit_deobfuscationfile( call_google_api do extension = File.extname(path_to_mapping).downcase ensure_active_edit! def upload_mapping(path_to_mapping, apk_version_code) end return result_upload.download_url end ) content_type: "application/octet-stream" upload_source: path_to_apk, package_name, client.uploadapk_internalappsharingartifact( result_upload = call_google_api do # NOTE: This Google API is a little different. It doesn't require an active edit. def upload_apk_to_internal_app_sharing(package_name, path_to_apk) end return result_upload.version_code end ) upload_source: path_to_apk current_edit.id, current_package_name, client.upload_edit_apk( result_upload = call_google_api do ensure_active_edit! def upload_apk(path_to_apk) end end ) listing