Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions Tests/ScriptsTests/test_submit_app_store_review.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,28 @@ def request(self, method, path, params=None, body=None, allowed=(200,)):
raise AssertionError(f"unexpected request: {method} {path}")


class SubmittedReviewItemClient(FakeASCClient):
def __init__(self):
super().__init__()
self.canceled_paths: list[str] = []

def request(self, method, path, params=None, body=None, allowed=(200,)):
if method == "DELETE" and path == "/reviewSubmissionItems/item-1":
raise submit_app_store_review.AppStoreConnectError(
"App Store Connect request failed: DELETE /reviewSubmissionItems/item-1",
status=409,
payload={"errors": [{"detail": "Item was already submitted"}]},
)
if method == "GET" and path == "/appStoreVersionSubmissions":
self.requests.append((method, path, params, body, allowed))
return {"data": [{"id": "submitted-version-review-1"}]}
if method == "DELETE" and path == "/appStoreVersionSubmissions/submitted-version-review-1":
self.requests.append((method, path, params, body, allowed))
self.canceled_paths.append(path)
return {}
return super().request(method, path, params, body, allowed)


class RemoveActiveReviewVersionTests(unittest.TestCase):
def test_deletes_matching_item(self):
client = FakeASCClient()
Expand All @@ -87,6 +109,13 @@ def test_deletes_matching_item(self):

self.assertEqual(client.deleted_paths, ["/reviewSubmissionItems/item-1"])

def test_cancels_submitted_version_review_when_review_item_is_already_submitted(self):
client = SubmittedReviewItemClient()

submit_app_store_review.remove_active_review_version(client, "app-id", "1.0.13")

self.assertEqual(client.canceled_paths, ["/appStoreVersionSubmissions/submitted-version-review-1"])

def test_dry_run_does_not_delete(self):
client = FakeASCClient()

Expand Down
45 changes: 42 additions & 3 deletions scripts/submit-app-store-review.py
Original file line number Diff line number Diff line change
Expand Up @@ -241,9 +241,16 @@ def remove_active_review_version(client: ASCClient, app_id: str, version_string:
if dry_run:
print(f"Dry run: would remove App Store version {version_string} from review submission: {item_id}")
return
client.request("DELETE", f"/reviewSubmissionItems/{item_id}", allowed=(204,))
print(f"Removed App Store version {version_string} from review submission: {item_id}")
return
try:
client.request("DELETE", f"/reviewSubmissionItems/{item_id}", allowed=(204,))
print(f"Removed App Store version {version_string} from review submission: {item_id}")
return
except AppStoreConnectError as error:
if not is_submitted_review_item_conflict(error):
raise
print(f"Review item {item_id} was already submitted; canceling submitted version review instead")
cancel_app_store_version_submission(client, version_id, version_string, dry_run=False)
return
state = version_state(version)
if state in BLOCKING_REVIEW_VERSION_STATES:
raise AppStoreConnectError(
Expand All @@ -252,13 +259,45 @@ def remove_active_review_version(client: ASCClient, app_id: str, version_string:
print(f"No active review submission item found for App Store version {version_string}")


def cancel_app_store_version_submission(
client: ASCClient, version_id: str, version_string: str, dry_run: bool = False
) -> None:
payload = client.request(
"GET",
"/appStoreVersionSubmissions",
{
"filter[appStoreVersion]": version_id,
"fields[appStoreVersionSubmissions]": "appStoreVersion",
"limit": 10,
},
)
submissions = payload.get("data") or []
if not submissions:
raise AppStoreConnectError(
f"App Store version {version_string} was already submitted, but no submitted version review was found to cancel"
)
submission_id = submissions[0]["id"]
if dry_run:
print(f"Dry run: would cancel submitted App Store version {version_string} review: {submission_id}")
return
client.request("DELETE", f"/appStoreVersionSubmissions/{submission_id}", allowed=(204,))
print(f"Canceled submitted App Store version {version_string} review: {submission_id}")


def is_version_creation_state_conflict(error: AppStoreConnectError) -> bool:
if error.status != 409:
return False
text = json.dumps(error.payload or {}, sort_keys=True).lower()
return "current state" in text or "another version" in text or "new version" in text


def is_submitted_review_item_conflict(error: AppStoreConnectError) -> bool:
if error.status != 409:
return False
text = json.dumps(error.payload or {}, sort_keys=True).lower()
return "item was already submitted" in text


def create_app_store_version(client: ASCClient, app_id: str, args: argparse.Namespace) -> dict[str, Any]:
return client.request(
"POST",
Expand Down