module Spaceship module Tunes class Tester < TunesBase # @return (String) The identifier of this tester, provided by iTunes Connect # @example # "60f858b4-60a8-428a-963a-f943a3d68d17" attr_accessor :tester_id # @return (String) The email of this tester # @example # "tester@spaceship.com" attr_accessor :email # @return (String) The first name of this tester # @example # "Cary" attr_accessor :first_name # @return (String) The last name of this tester # @example # "Bennett" attr_accessor :last_name # @return (Array) an array of associated groups # @example # [{ # "id": "e031e048-4f0f-4c1e-8d8a-a5341a267986", # "name": { # "value": "My App Testers" # } # }] attr_accessor :groups # @return (Array) An array of registered devices for this user # @example # [{ # "model": "iPhone 6", # "os": "iOS", # "osVersion": "8.3", # "name": null # }] attr_accessor :devices # Information about the most recent beta install # @return [Integer] The ID of the most recently installed app attr_accessor :latest_install_app_id # @return [Integer] Date of the last install of this tester # Number of seconds since 1970 attr_accessor :latest_install_date # @return [Integer] The build number of the last installed build attr_accessor :latest_installed_build_number # @return [Integer] The version number of the last installed build attr_accessor :latest_installed_version_number attr_mapping( 'testerId' => :tester_id, 'emailAddress.value' => :email, 'firstName.value' => :first_name, 'lastName.value' => :last_name, 'groups' => :groups, 'devices' => :devices, 'latestInstalledAppAdamId' => :latest_install_app_id, 'latestInstalledDate' => :latest_install_date, 'latestInstalledVersion' => :latest_installed_version_number, 'latestInstalledShortVersion' => :latest_installed_build_number ) class << self # @return (Hash) All urls for the ITC used for web requests def url raise "You have to use a subclass: Internal or External" end # @return (Array) Returns all beta testers available for this account def all client.testers(self).map { |tester| self.factory(tester) } end # @return (Spaceship::Tunes::Tester) Returns the tester matching the parameter # as either the Tester id or email # @param identifier (String) (required): Value used to filter the tester, case insensitive def find(identifier) all.find do |tester| (tester.tester_id.to_s.casecmp(identifier.to_s).zero? or tester.email.to_s.casecmp(identifier.to_s).zero?) end end def groups client.groups end # Create new tester in iTunes Connect # @param email (String) (required): The email of the new tester # @param first_name (String) (optional): The first name of the new tester # @param last_name (String) (optional): The last name of the new tester # @param groups (Array) (option): Names/IDs of existing groups for the new tester # @example # Spaceship::Tunes::Tester.external.create!(email: "tester@mathiascarignani.com", first_name: "Cary", last_name: "Bennett", groups: ["Testers"]) # @return (Tester): The newly created tester def create!(email: nil, first_name: nil, last_name: nil, groups: nil) client.create_tester!(tester: self, email: email, first_name: first_name, last_name: last_name, groups: groups) # We don't need to do additional parsing here, as `create_tester!` automatically # re-fetches the newly created tester end ##################################################### # @!group App ##################################################### # @return (Array) Returns all beta testers available for this account filtered by app # @param app_id (String) (required): The app id to filter the testers def all_by_app(app_id) client.testers_by_app(self, app_id).map { |tester| self.factory(tester) } end # @return (Spaceship::Tunes::Tester) Returns the tester matching the parameter # as either the Tester id or email # @param app_id (String) (required): The app id to filter the testers # @param identifier (String) (required): Value used to filter the tester, case insensitive def find_by_app(app_id, identifier) all_by_app(app_id).find do |tester| (tester.tester_id.to_s.casecmp(identifier.to_s).zero? or tester.email.to_s.casecmp(identifier.to_s).zero?) end end # Add all testers to the app received # @param app_id (String) (required): The app id to filter the testers def add_all_to_app!(app_id) all.each do |tester| begin tester.add_to_app!(app_id) rescue => ex if ex.to_s.include? "testerEmailExistsInternal" or ex.to_s.include? "duplicate.email" # That's a non-relevant error message by iTC # ignore that else raise ex end end end end end def setup self.devices ||= [] # by default, an empty array instead of nil end ##################################################### # @!group Subclasses ##################################################### class External < Tester def self.url(app_id = nil) { index: "ra/users/pre/ext", index_by_app: "ra/user/externalTesters/#{app_id}/", create: "ra/users/pre/create", delete: "ra/users/pre/ext/delete", update_by_app: "ra/user/externalTesters/#{app_id}/" } end end class Internal < Tester def self.url(app_id = nil) { index: "ra/users/pre/int", index_by_app: "ra/user/internalTesters/#{app_id}/", create: nil, delete: nil, update_by_app: "ra/user/internalTesters/#{app_id}/" } end end # Delete current tester def delete! client.delete_tester!(self) end ##################################################### # @!group App ##################################################### # Add current tester to list of the app testers # @param app_id (String) (required): The id of the application to which want to modify the list def add_to_app!(app_id) raise "`[tester].add_to_app!` got removed from spaceship as the TestFlight API changed, please use `app.default_external_group.add_tester!(tester)` instead" end # Remove current tester from list of the app testers # @param app_id (String) (required): The id of the application to which want to modify the list def remove_from_app!(app_id) raise "`[tester].remove_from_app!` got removed from spaceship as the TestFlight API changed, please use `app.default_external_group.remove_tester!(tester)` instead" end ##################################################### # @!group Helpers ##################################################### # Return a list of the Tester's group, if any # @return def groups_list(separator = ', ') if groups group_names = groups.map { |group| group["name"]["value"] } group_names.join(separator) end end end class SandboxTester < TunesBase # @return (String) The email of this tester # @example # "tester@spaceship.com" attr_accessor :email # @return (String) The first name of this tester # @example # "Cary" attr_accessor :first_name # @return (String) The last name of this tester # @example # "Bennett" attr_accessor :last_name # @return (String) The two-letter country code of this tester # @example # "US" attr_accessor :country attr_mapping( 'emailAddress.value' => :email, 'firstName.value' => :first_name, 'lastName.value' => :last_name, 'storeFront.value' => :country ) def self.url { index: "ra/users/iap", create: "ra/users/iap/add", delete: "ra/users/iap/delete" } end def self.all client.sandbox_testers(self).map { |tester| self.new(tester) } end def self.create!(email: nil, password: nil, first_name: 'Test', last_name: 'Test', country: 'US') data = client.create_sandbox_tester!( tester_class: self, email: email, password: password, first_name: first_name, last_name: last_name, country: country ) self.new(data) end def self.delete!(emails) client.delete_sandbox_testers!(self, emails) end def self.delete_all! delete!(self.all.map(&:email)) end end end end