fastlane 2.13.0 → 2.14.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/credentials_manager/lib/credentials_manager.rb +1 -1
- data/fastlane/lib/fastlane/actions/ipa.rb +2 -1
- data/fastlane/lib/fastlane/actions/mailgun.rb +15 -2
- data/fastlane/lib/fastlane/actions/scan.rb +14 -0
- data/fastlane/lib/fastlane/documentation/docs_generator.rb +24 -1
- data/fastlane/lib/fastlane/environment_printer.rb +2 -1
- data/fastlane/lib/fastlane/fast_file.rb +4 -4
- data/fastlane/lib/fastlane/version.rb +1 -1
- data/fastlane_core/lib/fastlane_core.rb +1 -1
- data/fastlane_core/lib/fastlane_core/configuration/config_item.rb +44 -2
- data/fastlane_core/lib/fastlane_core/device_manager.rb +15 -0
- data/fastlane_core/lib/fastlane_core/helper.rb +1 -1
- data/fastlane_core/lib/fastlane_core/ui/disable_colors.rb +4 -4
- data/frameit/lib/frameit/config_parser.rb +8 -13
- data/frameit/lib/frameit/editor.rb +3 -2
- data/gym/lib/gym/options.rb +4 -2
- data/match/README.md +2 -2
- data/match/lib/match.rb +5 -19
- data/match/lib/match/generator.rb +8 -1
- data/match/lib/match/git_helper.rb +3 -1
- data/match/lib/match/nuke.rb +18 -14
- data/match/lib/match/options.rb +13 -2
- data/match/lib/match/runner.rb +20 -8
- data/match/lib/match/table_printer.rb +5 -4
- data/match/lib/match/utils.rb +12 -8
- data/scan/lib/scan/options.rb +24 -1
- data/scan/lib/scan/runner.rb +12 -11
- data/scan/lib/scan/test_command_generator.rb +10 -2
- data/sigh/lib/sigh/download_all.rb +1 -1
- data/sigh/lib/sigh/runner.rb +2 -2
- data/snapshot/lib/snapshot/options.rb +2 -1
- data/snapshot/lib/snapshot/runner.rb +12 -12
- data/spaceship/lib/spaceship.rb +1 -0
- data/spaceship/lib/spaceship/base.rb +27 -4
- data/spaceship/lib/spaceship/client.rb +3 -2
- data/spaceship/lib/spaceship/du/du_client.rb +26 -16
- data/spaceship/lib/spaceship/portal/app.rb +1 -1
- data/spaceship/lib/spaceship/portal/person.rb +53 -0
- data/spaceship/lib/spaceship/portal/persons.rb +49 -0
- data/spaceship/lib/spaceship/portal/portal.rb +2 -0
- data/spaceship/lib/spaceship/portal/portal_client.rb +69 -10
- data/spaceship/lib/spaceship/portal/provisioning_profile.rb +29 -1
- data/spaceship/lib/spaceship/tunes/application.rb +11 -1
- data/spaceship/lib/spaceship/tunes/iap.rb +113 -0
- data/spaceship/lib/spaceship/tunes/iap_detail.rb +185 -0
- data/spaceship/lib/spaceship/tunes/iap_families.rb +62 -0
- data/spaceship/lib/spaceship/tunes/iap_family_details.rb +59 -0
- data/spaceship/lib/spaceship/tunes/iap_family_list.rb +33 -0
- data/spaceship/lib/spaceship/tunes/iap_list.rb +72 -0
- data/spaceship/lib/spaceship/tunes/iap_status.rb +48 -0
- data/spaceship/lib/spaceship/tunes/iap_type.rb +45 -0
- data/spaceship/lib/spaceship/tunes/tunes.rb +1 -0
- data/spaceship/lib/spaceship/tunes/tunes_client.rb +163 -1
- metadata +21 -5
@@ -8,6 +8,7 @@ require 'spaceship/helper/net_http_generic_request'
|
|
8
8
|
require 'spaceship/helper/plist_middleware'
|
9
9
|
require 'spaceship/ui'
|
10
10
|
require 'tmpdir'
|
11
|
+
require 'cgi'
|
11
12
|
|
12
13
|
Faraday::Utils.default_params_encoder = Faraday::FlatParamsEncoder
|
13
14
|
|
@@ -78,8 +79,8 @@ module Spaceship
|
|
78
79
|
|
79
80
|
[
|
80
81
|
"Apple provided the following error info:",
|
81
|
-
@error_info['resultString'],
|
82
|
-
@error_info['userString']
|
82
|
+
CGI.unescapeHTML(@error_info['resultString']),
|
83
|
+
CGI.unescapeHTML(@error_info['userString'])
|
83
84
|
].compact.uniq # sometimes 'resultString' and 'userString' are the same value
|
84
85
|
end
|
85
86
|
end
|
@@ -15,49 +15,59 @@ module Spaceship
|
|
15
15
|
#####################################################
|
16
16
|
|
17
17
|
def upload_screenshot(app_version, upload_file, content_provider_id, sso_token_for_image, device, is_messages)
|
18
|
-
upload_file(app_version, upload_file, '/upload/image', content_provider_id, sso_token_for_image, screenshot_picture_type(device, is_messages))
|
18
|
+
upload_file(app_version: app_version, upload_file: upload_file, path: '/upload/image', content_provider_id: content_provider_id, sso_token: sso_token_for_image, du_validation_rule_set: screenshot_picture_type(device, is_messages))
|
19
|
+
end
|
20
|
+
|
21
|
+
def upload_purchase_review_screenshot(app_id, upload_file, content_provider_id, sso_token_for_image)
|
22
|
+
upload_file(app_id: app_id, upload_file: upload_file, path: '/upload/image', content_provider_id: content_provider_id, sso_token: sso_token_for_image, du_validation_rule_set: 'MZPFT.SortedScreenShot')
|
19
23
|
end
|
20
24
|
|
21
25
|
def upload_large_icon(app_version, upload_file, content_provider_id, sso_token_for_image)
|
22
|
-
upload_file(app_version, upload_file, '/upload/image', content_provider_id, sso_token_for_image, 'MZPFT.LargeApplicationIcon')
|
26
|
+
upload_file(app_version: app_version, upload_file: upload_file, path: '/upload/image', content_provider_id: content_provider_id, sso_token: sso_token_for_image, du_validation_rule_set: 'MZPFT.LargeApplicationIcon')
|
23
27
|
end
|
24
28
|
|
25
29
|
def upload_watch_icon(app_version, upload_file, content_provider_id, sso_token_for_image)
|
26
|
-
upload_file(app_version, upload_file, '/upload/image', content_provider_id, sso_token_for_image, 'MZPFT.GizmoAppIcon')
|
30
|
+
upload_file(app_version: app_version, upload_file: upload_file, path: '/upload/image', content_provider_id: content_provider_id, sso_token: sso_token_for_image, du_validation_rule_set: 'MZPFT.GizmoAppIcon')
|
27
31
|
end
|
28
32
|
|
29
33
|
def upload_geojson(app_version, upload_file, content_provider_id, sso_token_for_image)
|
30
|
-
upload_file(app_version, upload_file, '/upload/geo-json', content_provider_id, sso_token_for_image)
|
34
|
+
upload_file(app_version: app_version, upload_file: upload_file, path: '/upload/geo-json', content_provider_id: content_provider_id, sso_token: sso_token_for_image)
|
31
35
|
end
|
32
36
|
|
33
37
|
def upload_trailer(app_version, upload_file, content_provider_id, sso_token_for_video)
|
34
|
-
upload_file(app_version, upload_file, '/upload/purple-video', content_provider_id, sso_token_for_video)
|
38
|
+
upload_file(app_version: app_version, upload_file: upload_file, path: '/upload/purple-video', content_provider_id: content_provider_id, sso_token: sso_token_for_video)
|
35
39
|
end
|
36
40
|
|
37
41
|
def upload_trailer_preview(app_version, upload_file, content_provider_id, sso_token_for_image)
|
38
|
-
upload_file(app_version, upload_file, '/upload/app-screenshot-image', content_provider_id, sso_token_for_image)
|
42
|
+
upload_file(app_version: app_version, upload_file: upload_file, path: '/upload/app-screenshot-image', content_provider_id: content_provider_id, sso_token: sso_token_for_image)
|
39
43
|
end
|
40
44
|
|
41
45
|
private
|
42
46
|
|
43
|
-
def upload_file(app_version, upload_file, path, content_provider_id, sso_token, du_validation_rule_set
|
47
|
+
def upload_file(app_version: nil, upload_file: nil, path: nil, content_provider_id: nil, sso_token: nil, du_validation_rule_set: nil, app_id: nil)
|
44
48
|
raise "File #{upload_file.file_path} is empty" if upload_file.file_size == 0
|
45
49
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
50
|
+
if app_id
|
51
|
+
app_id = app_id
|
52
|
+
app_type = nil
|
53
|
+
version = nil
|
54
|
+
referrer = nil
|
55
|
+
else
|
56
|
+
version = app_version.version
|
57
|
+
app_id = app_version.application.apple_id
|
58
|
+
app_type = app_version.app_type
|
59
|
+
referrer = app_version.application.url
|
60
|
+
end
|
51
61
|
|
52
62
|
r = request(:post) do |req|
|
53
63
|
req.url "#{self.class.hostname}#{path}"
|
54
64
|
req.body = upload_file.bytes
|
55
65
|
req.headers['Accept'] = 'application/json, text/plain, */*'
|
56
66
|
req.headers['Content-Type'] = upload_file.content_type
|
57
|
-
req.headers['X-Apple-Upload-Referrer'] = referrer
|
58
|
-
req.headers['Referrer'] = referrer
|
67
|
+
req.headers['X-Apple-Upload-Referrer'] = referrer if referrer
|
68
|
+
req.headers['Referrer'] = referrer if referrer
|
59
69
|
req.headers['X-Apple-Upload-AppleId'] = app_id
|
60
|
-
req.headers['X-Apple-Jingle-Correlation-Key'] = "#{app_type}:AdamId=#{app_id}:Version=#{version}"
|
70
|
+
req.headers['X-Apple-Jingle-Correlation-Key'] = "#{app_type}:AdamId=#{app_id}:Version=#{version}" if app_type
|
61
71
|
req.headers['X-Apple-Upload-itctoken'] = sso_token
|
62
72
|
req.headers['X-Apple-Upload-ContentProviderId'] = content_provider_id
|
63
73
|
req.headers['X-Original-Filename'] = upload_file.file_name
|
@@ -67,7 +77,7 @@ module Spaceship
|
|
67
77
|
end
|
68
78
|
|
69
79
|
if r.status == 500 and r.body.include?("Server Error")
|
70
|
-
return upload_file(app_version, upload_file, path, content_provider_id, sso_token, du_validation_rule_set)
|
80
|
+
return upload_file(app_version: app_version, upload_file: upload_file, path: path, content_provider_id: content_provider_id, sso_token: sso_token, du_validation_rule_set: du_validation_rule_set, app_id: app_id)
|
71
81
|
end
|
72
82
|
|
73
83
|
parse_upload_response(r)
|
@@ -103,7 +103,7 @@ module Spaceship
|
|
103
103
|
# @return (App) The app you're looking for. This is nil if the app can't be found.
|
104
104
|
def find(bundle_id, mac: false)
|
105
105
|
all(mac: mac).find do |app|
|
106
|
-
app.bundle_id ==
|
106
|
+
return app if app.bundle_id.casecmp(bundle_id) == 0
|
107
107
|
end
|
108
108
|
end
|
109
109
|
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Spaceship
|
2
|
+
module Portal
|
3
|
+
class Person < PortalBase
|
4
|
+
# @return (String) Person Id
|
5
|
+
attr_accessor :person_id
|
6
|
+
|
7
|
+
# @return (String) First name
|
8
|
+
attr_accessor :firstname
|
9
|
+
|
10
|
+
# @return (String) Last name
|
11
|
+
attr_accessor :lastname
|
12
|
+
|
13
|
+
# @return (String) Email Address
|
14
|
+
attr_accessor :email_address
|
15
|
+
|
16
|
+
# @return (String) Developer status (active, inactive)
|
17
|
+
attr_accessor :developer_status
|
18
|
+
|
19
|
+
# @return (String) Joined Date
|
20
|
+
attr_accessor :joined
|
21
|
+
|
22
|
+
# @return (String) Id in Team scope
|
23
|
+
attr_accessor :team_member_id
|
24
|
+
|
25
|
+
# @return (String) Role (member, admin or agent)
|
26
|
+
attr_accessor :type
|
27
|
+
|
28
|
+
attr_mapping(
|
29
|
+
'personId' => :person_id,
|
30
|
+
'firstName' => :firstname,
|
31
|
+
'lastName' => :lastname,
|
32
|
+
'email' => :email_address,
|
33
|
+
'developerStatus' => :developer_status,
|
34
|
+
'dateJoined' => :joined,
|
35
|
+
'teamMemberId' => :team_member_id
|
36
|
+
)
|
37
|
+
|
38
|
+
class << self
|
39
|
+
def factory(attrs)
|
40
|
+
self.new(attrs)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def remove!
|
45
|
+
client.team_remove_member!(team_member_id)
|
46
|
+
end
|
47
|
+
|
48
|
+
def change_role(role)
|
49
|
+
client.team_set_role(team_member_id, role)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Spaceship
|
2
|
+
module Portal
|
3
|
+
class Persons < PortalBase
|
4
|
+
class << self
|
5
|
+
def all
|
6
|
+
members = client.team_members
|
7
|
+
all_members = []
|
8
|
+
member = factory_member(members["members"], "member")
|
9
|
+
admins = factory_member(members["admins"], "admin")
|
10
|
+
agent = factory_member(members["agent"], "agent")
|
11
|
+
|
12
|
+
all_members.concat(member)
|
13
|
+
all_members.concat(admins)
|
14
|
+
all_members << agent
|
15
|
+
|
16
|
+
return all_members
|
17
|
+
end
|
18
|
+
|
19
|
+
def factory_member(members, type)
|
20
|
+
if members.kind_of?(Hash)
|
21
|
+
attrs = members
|
22
|
+
attrs[:type] = type
|
23
|
+
return Spaceship::Portal::Person.factory(attrs)
|
24
|
+
end
|
25
|
+
final_members = []
|
26
|
+
members.each do |member|
|
27
|
+
attrs = member
|
28
|
+
attrs[:type] = type
|
29
|
+
final_members << Spaceship::Portal::Person.factory(attrs)
|
30
|
+
end
|
31
|
+
return final_members
|
32
|
+
end
|
33
|
+
|
34
|
+
def find(email)
|
35
|
+
all.each do |member|
|
36
|
+
if member.email_address == email
|
37
|
+
return member
|
38
|
+
end
|
39
|
+
end
|
40
|
+
return nil
|
41
|
+
end
|
42
|
+
|
43
|
+
def invite(email, role)
|
44
|
+
client.team_invite(email, role)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -236,6 +236,62 @@ module Spaceship
|
|
236
236
|
parse_response(r)
|
237
237
|
end
|
238
238
|
|
239
|
+
#####################################################
|
240
|
+
# @!group Team
|
241
|
+
#####################################################
|
242
|
+
def team_members
|
243
|
+
response = request(:post) do |req|
|
244
|
+
req.url "/services-account/#{PROTOCOL_VERSION}/account/getTeamMembers"
|
245
|
+
req.body = {
|
246
|
+
teamId: team_id
|
247
|
+
}.to_json
|
248
|
+
req.headers['Content-Type'] = 'application/json'
|
249
|
+
end
|
250
|
+
parse_response(response)
|
251
|
+
end
|
252
|
+
|
253
|
+
def team_set_role(team_member_id, role)
|
254
|
+
ensure_csrf(Spaceship::Portal::Persons)
|
255
|
+
response = request(:post) do |req|
|
256
|
+
req.url "/services-account/#{PROTOCOL_VERSION}/account/setTeamMemberRoles"
|
257
|
+
req.body = {
|
258
|
+
teamId: team_id,
|
259
|
+
role: role,
|
260
|
+
teamMemberIds: [team_member_id]
|
261
|
+
}.to_json
|
262
|
+
req.headers['Content-Type'] = 'application/json'
|
263
|
+
end
|
264
|
+
parse_response(response)
|
265
|
+
end
|
266
|
+
|
267
|
+
def team_remove_member!(team_member_id)
|
268
|
+
ensure_csrf(Spaceship::Portal::Persons)
|
269
|
+
response = request(:post) do |req|
|
270
|
+
req.url "/services-account/#{PROTOCOL_VERSION}/account/removeTeamMembers"
|
271
|
+
req.body = {
|
272
|
+
teamId: team_id,
|
273
|
+
teamMemberIds: [team_member_id]
|
274
|
+
}.to_json
|
275
|
+
req.headers['Content-Type'] = 'application/json'
|
276
|
+
end
|
277
|
+
parse_response(response)
|
278
|
+
end
|
279
|
+
|
280
|
+
def team_invite(email, role)
|
281
|
+
ensure_csrf(Spaceship::Portal::Persons)
|
282
|
+
response = request(:post) do |req|
|
283
|
+
req.url "/services-account/#{PROTOCOL_VERSION}/account/sendInvites"
|
284
|
+
req.body = {
|
285
|
+
invites: [
|
286
|
+
{ recipientEmail: email, recipientRole: role }
|
287
|
+
],
|
288
|
+
teamId: team_id
|
289
|
+
}.to_json
|
290
|
+
req.headers['Content-Type'] = 'application/json'
|
291
|
+
end
|
292
|
+
parse_response(response)
|
293
|
+
end
|
294
|
+
|
239
295
|
#####################################################
|
240
296
|
# @!group Devices
|
241
297
|
#####################################################
|
@@ -427,20 +483,23 @@ module Spaceship
|
|
427
483
|
parse_response(r)
|
428
484
|
end
|
429
485
|
|
430
|
-
def repair_provisioning_profile!(profile_id, name, distribution_method, app_id, certificate_ids, device_ids, mac: false)
|
486
|
+
def repair_provisioning_profile!(profile_id, name, distribution_method, app_id, certificate_ids, device_ids, mac: false, sub_platform: nil)
|
431
487
|
ensure_csrf(Spaceship::ProvisioningProfile) do
|
432
488
|
fetch_csrf_token_for_provisioning
|
433
489
|
end
|
434
490
|
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
}
|
491
|
+
params = {
|
492
|
+
teamId: team_id,
|
493
|
+
provisioningProfileId: profile_id,
|
494
|
+
provisioningProfileName: name,
|
495
|
+
appIdId: app_id,
|
496
|
+
distributionType: distribution_method,
|
497
|
+
certificateIds: certificate_ids.join(','),
|
498
|
+
deviceIds: device_ids
|
499
|
+
}
|
500
|
+
params[:subPlatform] = sub_platform if sub_platform
|
501
|
+
|
502
|
+
r = request(:post, "account/#{platform_slug(mac)}/profile/regenProvisioningProfile.action", params)
|
444
503
|
|
445
504
|
parse_response(r, 'provisioningProfile')
|
446
505
|
end
|
@@ -65,6 +65,11 @@ module Spaceship
|
|
65
65
|
# "ios"
|
66
66
|
attr_accessor :platform
|
67
67
|
|
68
|
+
# @return (String) The supported sub_platform for this profile
|
69
|
+
# @example
|
70
|
+
# "tvOS"
|
71
|
+
attr_accessor :sub_platform
|
72
|
+
|
68
73
|
# No information about this attribute
|
69
74
|
attr_accessor :managing_app
|
70
75
|
|
@@ -136,6 +141,7 @@ module Spaceship
|
|
136
141
|
'type' => :type,
|
137
142
|
'version' => :version,
|
138
143
|
'proProPlatform' => :platform,
|
144
|
+
'proProSubPlatform' => :sub_platform,
|
139
145
|
'managingApp' => :managing_app,
|
140
146
|
'appId' => :app
|
141
147
|
})
|
@@ -206,6 +212,8 @@ module Spaceship
|
|
206
212
|
app = Spaceship::App.find(bundle_id, mac: mac)
|
207
213
|
raise "Could not find app with bundle id '#{bundle_id}'" unless app
|
208
214
|
|
215
|
+
raise "Invalid sub_platform #{sub_platform}, valid values are tvOS" if !sub_platform.nil? and sub_platform != 'tvOS'
|
216
|
+
|
209
217
|
# Fill in sensible default values
|
210
218
|
name ||= [bundle_id, self.pretty_type].join(' ')
|
211
219
|
|
@@ -278,6 +286,20 @@ module Spaceship
|
|
278
286
|
end
|
279
287
|
end
|
280
288
|
|
289
|
+
# @return (Array) Returns all profiles registered for this account
|
290
|
+
# If you're calling this from a subclass (like AdHoc), this will
|
291
|
+
# only return the profiles that are of this type
|
292
|
+
def all_tvos
|
293
|
+
profiles = all(mac: false)
|
294
|
+
tv_os_profiles = []
|
295
|
+
profiles.each do |tv_os_profile|
|
296
|
+
if tv_os_profile.tvos?
|
297
|
+
tv_os_profiles << tv_os_profile
|
298
|
+
end
|
299
|
+
end
|
300
|
+
return tv_os_profiles
|
301
|
+
end
|
302
|
+
|
281
303
|
# @return (Array) Returns an array of provisioning
|
282
304
|
# profiles matching the bundle identifier
|
283
305
|
# Returns [] if no profiles were found
|
@@ -385,7 +407,8 @@ module Spaceship
|
|
385
407
|
app.app_id,
|
386
408
|
certificates.map(&:id),
|
387
409
|
devices.map(&:id),
|
388
|
-
mac: mac
|
410
|
+
mac: mac?,
|
411
|
+
sub_platform: tvos? ? 'tvOS' : nil
|
389
412
|
)
|
390
413
|
end
|
391
414
|
|
@@ -425,6 +448,11 @@ module Spaceship
|
|
425
448
|
platform == 'mac'
|
426
449
|
end
|
427
450
|
|
451
|
+
# @return (Bool) Is this a tvos provisioning profile?
|
452
|
+
def tvos?
|
453
|
+
sub_platform == 'tvOS'
|
454
|
+
end
|
455
|
+
|
428
456
|
def devices
|
429
457
|
fetch_details
|
430
458
|
|
@@ -63,7 +63,7 @@ module Spaceship
|
|
63
63
|
# as either the App ID or the bundle identifier
|
64
64
|
def find(identifier, mac: false)
|
65
65
|
all.find do |app|
|
66
|
-
(app.apple_id
|
66
|
+
((app.apple_id && app.apple_id.casecmp(identifier.to_s) == 0) || (app.bundle_id && app.bundle_id.casecmp(identifier.to_s) == 0)) &&
|
67
67
|
app.version_sets.any? { |v| (mac ? ["osx"] : ["ios", "appletvos"]).include?(v.platform) }
|
68
68
|
end
|
69
69
|
end
|
@@ -249,6 +249,16 @@ module Spaceship
|
|
249
249
|
client.availability(self.apple_id)
|
250
250
|
end
|
251
251
|
|
252
|
+
#####################################################
|
253
|
+
# @!group in_app_purchases
|
254
|
+
#####################################################
|
255
|
+
# Get base In-App-Purchases object
|
256
|
+
def in_app_purchases
|
257
|
+
attrs = {}
|
258
|
+
attrs[:application] = self
|
259
|
+
Tunes::IAP.factory(attrs)
|
260
|
+
end
|
261
|
+
|
252
262
|
#####################################################
|
253
263
|
# @!group Builds
|
254
264
|
#####################################################
|
@@ -0,0 +1,113 @@
|
|
1
|
+
require 'spaceship/tunes/iap_list'
|
2
|
+
require 'spaceship/tunes/iap_detail'
|
3
|
+
require 'spaceship/tunes/iap_status'
|
4
|
+
require 'spaceship/tunes/iap_type'
|
5
|
+
require 'spaceship/tunes/iap_family_list'
|
6
|
+
require 'spaceship/tunes/iap_families'
|
7
|
+
require 'spaceship/tunes/iap_family_details'
|
8
|
+
require 'spaceship/tunes/iap_families'
|
9
|
+
|
10
|
+
module Spaceship
|
11
|
+
module Tunes
|
12
|
+
class IAP < TunesBase
|
13
|
+
# @return (Spaceship::Tunes::Application) A reference to the application
|
14
|
+
attr_accessor :application
|
15
|
+
|
16
|
+
class << self
|
17
|
+
def factory(attrs)
|
18
|
+
return self.new(attrs)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# @return (Spaceship::Tunes::IAPFamilies) A reference to the familie list
|
23
|
+
def families
|
24
|
+
attrs = {}
|
25
|
+
attrs[:application] = self.application
|
26
|
+
Tunes::IAPFamilies.factory(attrs)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Creates a new In-App-Purchese on iTunes Connect
|
30
|
+
# if the In-App-Purchase already exists an exception is raised. Spaceship::TunesClient::ITunesConnectError
|
31
|
+
# @param type (String): The Type of the in-app-purchase (Spaceship::Tunes::IAPType::CONSUMABLE,Spaceship::Tunes::IAPType::NON_CONSUMABLE,Spaceship::Tunes::IAPType::RECURRING,Spaceship::Tunes::IAPType::NON_RENEW_SUBSCRIPTION)
|
32
|
+
# @param versions (Hash): a Hash of the languages
|
33
|
+
# @example: {
|
34
|
+
# 'de-DE': {
|
35
|
+
# name: "Name shown in AppStore",
|
36
|
+
# description: "Description of the In app Purchase"
|
37
|
+
#
|
38
|
+
# }
|
39
|
+
# }
|
40
|
+
# @param reference_name (String): iTC Reference Name
|
41
|
+
# @param product_id (String): A unique ID for your in-app-purchase
|
42
|
+
# @param bundle_id (String): The bundle ID must match the one you used in Xcode. It
|
43
|
+
# @param cleared_for_sale (Boolean): Is this In-App-Purchase Cleared for Sale
|
44
|
+
# @param review_notes (String): Review Notes
|
45
|
+
# @param review_screenshot (String): Path to the screenshot (should be 640x940 PNG)
|
46
|
+
# @param pricing_intervals (Hash): a Hash of the languages
|
47
|
+
# @example:
|
48
|
+
# [
|
49
|
+
# {
|
50
|
+
# country: "WW",
|
51
|
+
# begin_date: nil,
|
52
|
+
# end_date: nil,
|
53
|
+
# tier: 1
|
54
|
+
# }
|
55
|
+
# ]
|
56
|
+
# @param family_id (String) Only used on RECURRING purchases, assigns the In-App-Purchase to a specific familie
|
57
|
+
# @param subscription_free_trial (String) Free Trial duration (1w,1m,3m....)
|
58
|
+
# @param subscription_duration (String) 1w,1m.....
|
59
|
+
def create!(type: "consumable",
|
60
|
+
versions: nil,
|
61
|
+
reference_name: nil,
|
62
|
+
product_id: nil,
|
63
|
+
cleared_for_sale: true,
|
64
|
+
review_notes: nil,
|
65
|
+
review_screenshot: nil,
|
66
|
+
pricing_intervals: nil,
|
67
|
+
family_id: nil,
|
68
|
+
subscription_free_trial: nil,
|
69
|
+
subscription_duration: nil)
|
70
|
+
|
71
|
+
client.create_iap!(app_id: self.application.apple_id,
|
72
|
+
type: type,
|
73
|
+
versions: versions,
|
74
|
+
reference_name: reference_name,
|
75
|
+
product_id: product_id,
|
76
|
+
cleared_for_sale: cleared_for_sale,
|
77
|
+
review_notes: review_notes,
|
78
|
+
review_screenshot: review_screenshot,
|
79
|
+
pricing_intervals: pricing_intervals,
|
80
|
+
family_id: family_id,
|
81
|
+
subscription_duration: subscription_duration,
|
82
|
+
subscription_free_trial: subscription_free_trial)
|
83
|
+
end
|
84
|
+
|
85
|
+
# find a specific product
|
86
|
+
# @param product_id (String) Product Id
|
87
|
+
def find(product_id)
|
88
|
+
all.each do |product|
|
89
|
+
if product.product_id == product_id
|
90
|
+
return product
|
91
|
+
end
|
92
|
+
end
|
93
|
+
return nil
|
94
|
+
end
|
95
|
+
|
96
|
+
# return all available In-App-Purchase's of current app
|
97
|
+
# this is not paged inside iTC-API so if you have a lot if IAP's (2k+)
|
98
|
+
# it might take some time to load, same as it takes when you load the list via iTunes Connect
|
99
|
+
def all(include_deleted: false)
|
100
|
+
r = client.iaps(app_id: self.application.apple_id)
|
101
|
+
return_iaps = []
|
102
|
+
r.each do |product|
|
103
|
+
attrs = product
|
104
|
+
attrs[:application] = self.application
|
105
|
+
loaded_iap = Tunes::IAPList.factory(attrs)
|
106
|
+
next if loaded_iap.status == "deleted" && !include_deleted
|
107
|
+
return_iaps << loaded_iap
|
108
|
+
end
|
109
|
+
return_iaps
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|