diff --git a/scripts/app_store_submit.py b/scripts/app_store_submit.py index 90edb39..9599959 100644 --- a/scripts/app_store_submit.py +++ b/scripts/app_store_submit.py @@ -468,14 +468,30 @@ def review_submissions(app_id): def withdraw(submission_id, app_id): - """Takes a submission that App Review has not started out of review.""" + """Takes a submission that App Review has not started out of review and + waits until its versions can be edited again.""" + items = call("GET", f"/v1/reviewSubmissions/{submission_id}/items", { + "include": "appStoreVersion", + })["data"] + version_ids = [ + item["relationships"]["appStoreVersion"]["data"]["id"] + for item in items + if item["relationships"].get("appStoreVersion", {}).get("data") + ] call("PATCH", f"/v1/reviewSubmissions/{submission_id}", body={"data": { "type": "reviewSubmissions", "id": submission_id, "attributes": {"canceled": True}, }}) for _ in range(40): - if all(s["id"] != submission_id for s in review_submissions(app_id)): + gone = all(s["id"] != submission_id for s in review_submissions(app_id)) + # App Store Connect makes the versions editable a little later. + if gone and all( + call("GET", f"/v1/appStoreVersions/{version_id}", { + "fields[appStoreVersions]": "appVersionState", + })["data"]["attributes"]["appVersionState"] in EDITABLE + for version_id in version_ids + ): print("Withdrew the version waiting for review; this release replaces it.") return time.sleep(15) diff --git a/tests/test_app_store_submit.py b/tests/test_app_store_submit.py index ce0d84a..4364712 100644 --- a/tests/test_app_store_submit.py +++ b/tests/test_app_store_submit.py @@ -71,6 +71,8 @@ def __init__(self, build=True, encryption=None, versions=None, submissions=None, self.beta_submissions = [] # Screenshot sets: display type -> [(id, contents, checksum)]. self.screenshot_sets = {} + # Versions that become editable at their next read. + self.withdrawn = [] self.uploads = {} self.spec = AppStoreSpec() @@ -303,6 +305,11 @@ def __call__(self, request): version["version"] = attributes.get("versionString", version["version"]) return 200, {"data": self.version(version_id)} self.polls += 1 + if version_id in self.withdrawn: + if version.get("read_once"): + version["state"] = "DEVELOPER_REJECTED" + self.withdrawn.remove(version_id) + version["read_once"] = True state = version["state"] if self.slow_ready and self.polls < 3: state = "PREPARE_FOR_SUBMISSION" @@ -351,7 +358,10 @@ def __call__(self, request): assert submission["state"] == "WAITING_FOR_REVIEW", submission submission["state"] = "COMPLETE" for version_id in submission["items"]: - self.versions[version_id]["state"] = "DEVELOPER_REJECTED" + # Editable only after the submission is gone, as in App + # Store Connect. + self.versions[version_id]["state"] = "WAITING_FOR_REVIEW" + self.withdrawn.append(version_id) return 200, {"data": {"type": "reviewSubmissions", "id": match.group(1), "attributes": {"state": "CANCELING"}}} submission["state"] = "WAITING_FOR_REVIEW"