Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[spaceship] Use new app store statuses to filter app versions and app infos #21895

Merged
merged 8 commits into from May 3, 2024
4 changes: 2 additions & 2 deletions deliver/lib/deliver/submit_for_review.rb
Expand Up @@ -51,11 +51,11 @@ def create_review_submission(options, app, version, platform)
10.times do
version_with_latest_info = Spaceship::ConnectAPI::AppStoreVersion.get(app_store_version_id: version.id)

if version_with_latest_info.app_store_state == Spaceship::ConnectAPI::AppStoreVersion::AppStoreState::READY_FOR_REVIEW
if version_with_latest_info.app_version_state == Spaceship::ConnectAPI::AppStoreVersion::AppVersionState::READY_FOR_REVIEW
break
end

UI.message("Waiting for the state of the version to become #{Spaceship::ConnectAPI::AppStoreVersion::AppStoreState::READY_FOR_REVIEW}...")
UI.message("Waiting for the state of the version to become #{Spaceship::ConnectAPI::AppStoreVersion::AppVersionState::READY_FOR_REVIEW}...")

sleep(15)
end
Expand Down
4 changes: 2 additions & 2 deletions deliver/spec/submit_for_review_spec.rb
Expand Up @@ -14,13 +14,13 @@
let(:ready_for_review_version) do
double('ready_for_review_version',
id: '1',
app_store_state: "READY_FOR_REVIEW",
app_version_state: "READY_FOR_REVIEW",
version_string: "1.0.0")
end
let(:prepare_for_submission_version) do
double('prepare_for_submission_version',
id: '1',
app_store_state: "PREPARE_FOR_SUBMISSION",
app_version_state: "PREPARE_FOR_SUBMISSION",
version_string: "1.0.0")
end
let(:selected_build) { double('selected_build') }
Expand Down
5 changes: 3 additions & 2 deletions spaceship/docs/AppStoreConnect.md
Expand Up @@ -114,10 +114,10 @@ You can then go ahead and modify app metadata on the version objects:
v = app.get_edit_app_store_version

# Access information
v.app_store_state # => "Waiting for Review"
v.app_version_state # => "Waiting for Review"
v.version_string # => "0.9.14"

# Build is not always available in all app_store_state, e.g. not available in `Prepare for Submission`
# Build is not always available in all app_version_state, e.g. not available in `Prepare for Submission`
build_number = v.build.nil? ? nil : v.build.version

# Update app metadata
Expand Down Expand Up @@ -162,6 +162,7 @@ Available options:
attr_accessor :platform
attr_accessor :version_string
attr_accessor :app_store_state
attr_accessor :app_version_state
attr_accessor :store_icon
attr_accessor :watch_store_icon
attr_accessor :copyright
Expand Down
61 changes: 28 additions & 33 deletions spaceship/lib/spaceship/connect_api/models/app.rb
Expand Up @@ -116,34 +116,29 @@ def update(client: nil, attributes: nil, app_price_tier_id: nil, territory_ids:
def fetch_live_app_info(client: nil, includes: Spaceship::ConnectAPI::AppInfo::ESSENTIAL_INCLUDES)
client ||= Spaceship::ConnectAPI
states = [
Spaceship::ConnectAPI::AppInfo::AppStoreState::READY_FOR_SALE,
Spaceship::ConnectAPI::AppInfo::AppStoreState::PENDING_APPLE_RELEASE,
Spaceship::ConnectAPI::AppInfo::AppStoreState::PENDING_DEVELOPER_RELEASE,
Spaceship::ConnectAPI::AppInfo::AppStoreState::PROCESSING_FOR_APP_STORE,
Spaceship::ConnectAPI::AppInfo::AppStoreState::IN_REVIEW,
Spaceship::ConnectAPI::AppInfo::AppStoreState::DEVELOPER_REMOVED_FROM_SALE
Spaceship::ConnectAPI::AppInfo::State::READY_FOR_DISTRIBUTION,
Spaceship::ConnectAPI::AppInfo::State::PENDING_RELEASE,
Spaceship::ConnectAPI::AppInfo::State::IN_REVIEW
]

resp = client.get_app_infos(app_id: id, includes: includes)
return resp.to_models.select do |model|
states.include?(model.app_store_state)
states.include?(model.state)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had expected to see app_version_state here. Are these 2 fields identical / duplicated in the returned data?

Also appVersionState doesn't appear in the fixture data. Could we refresh the fixture data as well, to make it more up to date with real life data returned from the API? It would make it easier to review the code. Thanks

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added appVersionState values to fixtures in a new commit. A fixture for AppInfo is not present yet and should be added to code.

And about the state.
Those fields are not identical. app_version_state is an attribute of AppStoreVersion.
Filtering of AppInfo needs to be done based on state.
In the opan api spec, AppInfo doesn't have an app version state, but it has state with less values, e.g. there is no PENDING_APPLE_RELEASE or PENDING_DEVELOPER_RELEASE, but there is simply PENDING_RELEASE

For reference:
https://developer.apple.com/documentation/appstoreconnectapi/list_all_app_store_versions_for_an_app
https://developer.apple.com/documentation/appstoreconnectapi/appstoreversion/attributes
appStoreState: ACCEPTED, DEVELOPER_REMOVED_FROM_SALE, DEVELOPER_REJECTED, IN_REVIEW, INVALID_BINARY, METADATA_REJECTED, PENDING_APPLE_RELEASE, PENDING_CONTRACT, PENDING_DEVELOPER_RELEASE, PREPARE_FOR_SUBMISSION, PREORDER_READY_FOR_SALE, PROCESSING_FOR_APP_STORE, READY_FOR_REVIEW, READY_FOR_SALE, REJECTED, REMOVED_FROM_SALE, WAITING_FOR_EXPORT_COMPLIANCE, WAITING_FOR_REVIEW, REPLACED_WITH_NEW_VERSION, NOT_APPLICABLE

appVersionState: ACCEPTED, DEVELOPER_REJECTED, IN_REVIEW, INVALID_BINARY, METADATA_REJECTED, PENDING_APPLE_RELEASE, PENDING_DEVELOPER_RELEASE, PREPARE_FOR_SUBMISSION, PROCESSING_FOR_DISTRIBUTION, READY_FOR_DISTRIBUTION, READY_FOR_REVIEW, REJECTED, REPLACED_WITH_NEW_VERSION, WAITING_FOR_EXPORT_COMPLIANCE, WAITING_FOR_REVIEW

https://developer.apple.com/documentation/appstoreconnectapi/appinfo/attributes
appStoreState
state: ACCEPTED, DEVELOPER_REJECTED, IN_REVIEW, PENDING_RELEASE, PREPARE_FOR_SUBMISSION, READY_FOR_DISTRIBUTION, READY_FOR_REVIEW, REJECTED, REPLACED_WITH_NEW_INFO, WAITING_FOR_REVIEW

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added a fixture for AppInfo.

end.first
end

def fetch_edit_app_info(client: nil, includes: Spaceship::ConnectAPI::AppInfo::ESSENTIAL_INCLUDES)
client ||= Spaceship::ConnectAPI
states = [
Spaceship::ConnectAPI::AppInfo::AppStoreState::PREPARE_FOR_SUBMISSION,
Spaceship::ConnectAPI::AppInfo::AppStoreState::DEVELOPER_REJECTED,
Spaceship::ConnectAPI::AppInfo::AppStoreState::REJECTED,
Spaceship::ConnectAPI::AppInfo::AppStoreState::METADATA_REJECTED,
Spaceship::ConnectAPI::AppInfo::AppStoreState::WAITING_FOR_REVIEW,
Spaceship::ConnectAPI::AppInfo::AppStoreState::INVALID_BINARY
Spaceship::ConnectAPI::AppInfo::State::PREPARE_FOR_SUBMISSION,
Spaceship::ConnectAPI::AppInfo::State::DEVELOPER_REJECTED,
Spaceship::ConnectAPI::AppInfo::State::REJECTED,
Spaceship::ConnectAPI::AppInfo::State::WAITING_FOR_REVIEW
]

resp = client.get_app_infos(app_id: id, includes: includes)
return resp.to_models.select do |model|
states.include?(model.app_store_state)
states.include?(model.state)
end.first
end

Expand Down Expand Up @@ -182,11 +177,11 @@ def reject_version_if_possible!(client: nil, platform: nil)
client ||= Spaceship::ConnectAPI
platform ||= Spaceship::ConnectAPI::Platform::IOS
filter = {
appStoreState: [
Spaceship::ConnectAPI::AppStoreVersion::AppStoreState::PENDING_APPLE_RELEASE,
Spaceship::ConnectAPI::AppStoreVersion::AppStoreState::PENDING_DEVELOPER_RELEASE,
Spaceship::ConnectAPI::AppStoreVersion::AppStoreState::IN_REVIEW,
Spaceship::ConnectAPI::AppStoreVersion::AppStoreState::WAITING_FOR_REVIEW
appVersionState: [
Spaceship::ConnectAPI::AppStoreVersion::AppVersionState::PENDING_APPLE_RELEASE,
Spaceship::ConnectAPI::AppStoreVersion::AppVersionState::PENDING_DEVELOPER_RELEASE,
Spaceship::ConnectAPI::AppStoreVersion::AppVersionState::IN_REVIEW,
Spaceship::ConnectAPI::AppStoreVersion::AppVersionState::WAITING_FOR_REVIEW
].join(","),
platform: platform
}
Expand Down Expand Up @@ -240,9 +235,9 @@ def get_live_app_store_version(client: nil, platform: nil, includes: Spaceship::
client ||= Spaceship::ConnectAPI
platform ||= Spaceship::ConnectAPI::Platform::IOS
filter = {
appStoreState: [
Spaceship::ConnectAPI::AppStoreVersion::AppStoreState::READY_FOR_SALE,
Spaceship::ConnectAPI::AppStoreVersion::AppStoreState::DEVELOPER_REMOVED_FROM_SALE
appVersionState: [
Spaceship::ConnectAPI::AppStoreVersion::AppVersionState::READY_FOR_DISTRIBUTION,
Spaceship::ConnectAPI::AppStoreVersion::AppVersionState::PROCESSING_FOR_DISTRIBUTION
].join(","),
platform: platform
}
Expand All @@ -253,13 +248,13 @@ def get_edit_app_store_version(client: nil, platform: nil, includes: Spaceship::
client ||= Spaceship::ConnectAPI
platform ||= Spaceship::ConnectAPI::Platform::IOS
filter = {
appStoreState: [
Spaceship::ConnectAPI::AppStoreVersion::AppStoreState::PREPARE_FOR_SUBMISSION,
Spaceship::ConnectAPI::AppStoreVersion::AppStoreState::DEVELOPER_REJECTED,
Spaceship::ConnectAPI::AppStoreVersion::AppStoreState::REJECTED,
Spaceship::ConnectAPI::AppStoreVersion::AppStoreState::METADATA_REJECTED,
Spaceship::ConnectAPI::AppStoreVersion::AppStoreState::WAITING_FOR_REVIEW,
Spaceship::ConnectAPI::AppStoreVersion::AppStoreState::INVALID_BINARY
appVersionState: [
Spaceship::ConnectAPI::AppStoreVersion::AppVersionState::PREPARE_FOR_SUBMISSION,
Spaceship::ConnectAPI::AppStoreVersion::AppVersionState::DEVELOPER_REJECTED,
Spaceship::ConnectAPI::AppStoreVersion::AppVersionState::REJECTED,
Spaceship::ConnectAPI::AppStoreVersion::AppVersionState::METADATA_REJECTED,
Spaceship::ConnectAPI::AppStoreVersion::AppVersionState::WAITING_FOR_REVIEW,
Spaceship::ConnectAPI::AppStoreVersion::AppVersionState::INVALID_BINARY
].join(","),
platform: platform
}
Expand All @@ -274,7 +269,7 @@ def get_in_review_app_store_version(client: nil, platform: nil, includes: Spaces
client ||= Spaceship::ConnectAPI
platform ||= Spaceship::ConnectAPI::Platform::IOS
filter = {
appStoreState: Spaceship::ConnectAPI::AppStoreVersion::AppStoreState::IN_REVIEW,
appVersionState: Spaceship::ConnectAPI::AppStoreVersion::AppVersionState::IN_REVIEW,
platform: platform
}
return get_app_store_versions(client: client, filter: filter, includes: includes).first
Expand All @@ -284,9 +279,9 @@ def get_pending_release_app_store_version(client: nil, platform: nil, includes:
client ||= Spaceship::ConnectAPI
platform ||= Spaceship::ConnectAPI::Platform::IOS
filter = {
appStoreState: [
Spaceship::ConnectAPI::AppStoreVersion::AppStoreState::PENDING_APPLE_RELEASE,
Spaceship::ConnectAPI::AppStoreVersion::AppStoreState::PENDING_DEVELOPER_RELEASE
appVersionState: [
Spaceship::ConnectAPI::AppStoreVersion::AppVersionState::PENDING_APPLE_RELEASE,
Spaceship::ConnectAPI::AppStoreVersion::AppVersionState::PENDING_DEVELOPER_RELEASE
].join(','),
platform: platform
}
Expand Down
2 changes: 2 additions & 0 deletions spaceship/spec/connect_api/fixtures/testflight/apps.json
Expand Up @@ -1062,6 +1062,7 @@
"platform" : "IOS",
"versionString" : "1.0",
"appStoreState" : "PREPARE_FOR_SUBMISSION",
"appVersionState" : "PREPARE_FOR_SUBMISSION",
"copyright" : null,
"releaseType" : "AFTER_APPROVAL",
"earliestReleaseDate" : null,
Expand Down Expand Up @@ -1147,6 +1148,7 @@
"platform" : "IOS",
"versionString" : "1.0",
"appStoreState" : "PREPARE_FOR_SUBMISSION",
"appVersionState" : "PREPARE_FOR_SUBMISSION",
"copyright" : null,
"releaseType" : "AFTER_APPROVAL",
"earliestReleaseDate" : null,
Expand Down
137 changes: 137 additions & 0 deletions spaceship/spec/connect_api/fixtures/tunes/app_infos.json
@@ -0,0 +1,137 @@
{
"data": [
{
"type": "appInfos",
"id": "1111-11111",
"attributes": {
"appStoreState": "READY_FOR_SALE",
"state": "READY_FOR_DISTRIBUTION",
"appStoreAgeRating": "FOUR_PLUS",
"brazilAgeRating": "L",
"brazilAgeRatingV2": "SELF_RATED_L",
"kidsAgeBand": null
},
"relationships": {
"ageRatingDeclaration": {
"links": {
"self": "https://appstoreconnect.apple.com/iris/v1/appInfos/1111-11111/relationships/ageRatingDeclaration",
"related": "https://appstoreconnect.apple.com/iris/v1/appInfos/1111-11111/ageRatingDeclaration"
}
},
"appInfoLocalizations": {
"links": {
"self": "https://appstoreconnect.apple.com/iris/v1/appInfos/1111-11111/relationships/appInfoLocalizations",
"related": "https://appstoreconnect.apple.com/iris/v1/appInfos/1111-11111/appInfoLocalizations"
}
},
"primaryCategory": {
"links": {
"self": "https://appstoreconnect.apple.com/iris/v1/appInfos/1111-11111/relationships/primaryCategory",
"related": "https://appstoreconnect.apple.com/iris/v1/appInfos/1111-11111/primaryCategory"
}
},
"primarySubcategoryOne": {
"links": {
"self": "https://appstoreconnect.apple.com/iris/v1/appInfos/1111-11111/relationships/primarySubcategoryOne",
"related": "https://appstoreconnect.apple.com/iris/v1/appInfos/1111-11111/primarySubcategoryOne"
}
},
"primarySubcategoryTwo": {
"links": {
"self": "https://appstoreconnect.apple.com/iris/v1/appInfos/1111-11111/relationships/primarySubcategoryTwo",
"related": "https://appstoreconnect.apple.com/iris/v1/appInfos/1111-11111/primarySubcategoryTwo"
}
},
"secondaryCategory": {
"links": {
"self": "https://appstoreconnect.apple.com/iris/v1/appInfos/1111-11111/relationships/secondaryCategory",
"related": "https://appstoreconnect.apple.com/iris/v1/appInfos/1111-11111/secondaryCategory"
}
},
"secondarySubcategoryOne": {
"links": {
"self": "https://appstoreconnect.apple.com/iris/v1/appInfos/1111-11111/relationships/secondarySubcategoryOne",
"related": "https://appstoreconnect.apple.com/iris/v1/appInfos/1111-11111/secondarySubcategoryOne"
}
},
"secondarySubcategoryTwo": {
"links": {
"self": "https://appstoreconnect.apple.com/iris/v1/appInfos/1111-11111/relationships/secondarySubcategoryTwo",
"related": "https://appstoreconnect.apple.com/iris/v1/appInfos/1111-11111/secondarySubcategoryTwo"
}
}
},
"links": {
"self": "https://appstoreconnect.apple.com/iris/v1/appInfos/1111-11111"
}
},
{
"type": "appInfos",
"id": "1111-2222",
"attributes": {
"appStoreState": "PREPARE_FOR_SUBMISSION",
"state": "PREPARE_FOR_SUBMISSION",
"appStoreAgeRating": "FOUR_PLUS",
"brazilAgeRating": "L",
"brazilAgeRatingV2": "SELF_RATED_L",
"kidsAgeBand": null
},
"relationships": {
"ageRatingDeclaration": {
"links": {
"self": "https://appstoreconnect.apple.com/iris/v1/appInfos/1111-2222/relationships/ageRatingDeclaration",
"related": "https://appstoreconnect.apple.com/iris/v1/appInfos/1111-2222/ageRatingDeclaration"
}
},
"appInfoLocalizations": {
"links": {
"self": "https://appstoreconnect.apple.com/iris/v1/appInfos/1111-2222/relationships/appInfoLocalizations",
"related": "https://appstoreconnect.apple.com/iris/v1/appInfos/1111-2222/appInfoLocalizations"
}
},
"primaryCategory": {
"links": {
"self": "https://appstoreconnect.apple.com/iris/v1/appInfos/1111-2222/relationships/primaryCategory",
"related": "https://appstoreconnect.apple.com/iris/v1/appInfos/1111-2222/primaryCategory"
}
},
"primarySubcategoryOne": {
"links": {
"self": "https://appstoreconnect.apple.com/iris/v1/appInfos/1111-2222/relationships/primarySubcategoryOne",
"related": "https://appstoreconnect.apple.com/iris/v1/appInfos/1111-2222/primarySubcategoryOne"
}
},
"primarySubcategoryTwo": {
"links": {
"self": "https://appstoreconnect.apple.com/iris/v1/appInfos/1111-2222/relationships/primarySubcategoryTwo",
"related": "https://appstoreconnect.apple.com/iris/v1/appInfos/1111-2222/primarySubcategoryTwo"
}
},
"secondaryCategory": {
"links": {
"self": "https://appstoreconnect.apple.com/iris/v1/appInfos/1111-2222/relationships/secondaryCategory",
"related": "https://appstoreconnect.apple.com/iris/v1/appInfos/1111-2222/secondaryCategory"
}
},
"secondarySubcategoryOne": {
"links": {
"self": "https://appstoreconnect.apple.com/iris/v1/appInfos/1111-2222/relationships/secondarySubcategoryOne",
"related": "https://appstoreconnect.apple.com/iris/v1/appInfos/1111-2222/secondarySubcategoryOne"
}
},
"secondarySubcategoryTwo": {
"links": {
"self": "https://appstoreconnect.apple.com/iris/v1/appInfos/1111-2222/relationships/secondarySubcategoryTwo",
"related": "https://appstoreconnect.apple.com/iris/v1/appInfos/1111-2222/secondarySubcategoryTwo"
}
}
},
"links": {
"self": "https://appstoreconnect.apple.com/iris/v1/appInfos/1111-2222"
}
}
],
"links": {
"self": "https://appstoreconnect.apple.com/iris/v1/apps/123456789/appInfos"
}
}
Expand Up @@ -61,7 +61,8 @@
"isWatchOnly" : false,
"createdDate" : "2021-01-01T10:10:10-07:00",
"releaseType" : "MANUAL",
"appStoreState" : "READY_FOR_REVIEW"
"appStoreState" : "READY_FOR_REVIEW",
"appVersionState" : "READY_FOR_REVIEW"
},
"relationships" : {
"appStoreVersionLocalizations" : {
Expand Down
Expand Up @@ -46,7 +46,8 @@
"isWatchOnly" : false,
"createdDate" : "2021-01-01T10:10:10-07:00",
"releaseType" : "MANUAL",
"appStoreState" : "READY_FOR_REVIEW"
"appStoreState" : "READY_FOR_REVIEW",
"appVersionState" : "READY_FOR_REVIEW"
},
"relationships" : {
"appStoreVersionLocalizations" : {
Expand Down
22 changes: 22 additions & 0 deletions spaceship/spec/connect_api/models/app_spec.rb
Expand Up @@ -86,6 +86,28 @@
expect(model.bundle_id).to eq("com.joshholtz.FastlaneTest")
end

it('fetches live app info') do
ConnectAPIStubbing::Tunes.stub_get_app_infos
app = Spaceship::ConnectAPI::App.new("123456789", [])

info = app.fetch_live_app_info(includes: nil)
expect(info.id).to eq("1111-11111")
expect(info.app_store_age_rating).to eq("FOUR_PLUS")
expect(info.app_store_state).to eq("READY_FOR_SALE")
expect(info.state).to eq("READY_FOR_DISTRIBUTION")
end

it('fetches edit app info') do
ConnectAPIStubbing::Tunes.stub_get_app_infos
app = Spaceship::ConnectAPI::App.new("123456789", [])

info = app.fetch_edit_app_info(includes: nil)
expect(info.id).to eq("1111-2222")
expect(info.app_store_age_rating).to eq("FOUR_PLUS")
expect(info.app_store_state).to eq("PREPARE_FOR_SUBMISSION")
expect(info.state).to eq("PREPARE_FOR_SUBMISSION")
end

it 'creates beta group' do
app = Spaceship::ConnectAPI::App.find("com.joshholtz.FastlaneTest")

Expand Down
5 changes: 5 additions & 0 deletions spaceship/spec/connect_api/tunes/tunes_stubbing.rb
Expand Up @@ -14,6 +14,11 @@ def stub_request(*args)
WebMock::API.stub_request(*args)
end

def stub_get_app_infos
stub_request(:get, "https://appstoreconnect.apple.com/iris/v1/apps/123456789/appInfos").
to_return(status: 200, body: read_fixture_file('app_infos.json'), headers: { 'Content-Type' => 'application/json' })
end

def stub_app_store_version_release_request
stub_request(:post, "https://appstoreconnect.apple.com/iris/v1/appStoreVersionReleaseRequests").
to_return(status: 200, body: read_fixture_file('app_store_version_release_request.json'), headers: { 'Content-Type' => 'application/json' })
Expand Down