Skip to content

Commit

Permalink
Add missing fields for App Store Connect models (#383)
Browse files Browse the repository at this point in the history
  • Loading branch information
priitlatt authored Apr 26, 2024
1 parent a947b60 commit 0bed134
Show file tree
Hide file tree
Showing 11 changed files with 141 additions and 2 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ UNRELEASED
**Bugfixes**
- Fix App Store Connect API responses deserialization for cases when resource contains an empty relationship. [PR #401](https://github.com/codemagic-ci-cd/cli-tools/pull/401)

- **Development**
- Add missing attributes and relationships to `codemagic.apple.resources.App` and `codemagic.apple.resources.Build`. [PR #383](https://github.com/codemagic-ci-cd/cli-tools/pull/383)
- Define new enumerations `codemagic.apple.resources.enums.BuildAudienceType` and `codemagic.apple.resources.enums.SubscriptionStatusUrlVersion`. [PR #383](https://github.com/codemagic-ci-cd/cli-tools/pull/383)

Version 0.50.7
-------------

Expand Down
2 changes: 2 additions & 0 deletions src/codemagic/apple/resources/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from .device import Device
from .enums import AppStoreState
from .enums import BetaReviewState
from .enums import BuildAudienceType
from .enums import BuildProcessingState
from .enums import BundleIdPlatform
from .enums import CapabilityOptionKey
Expand All @@ -36,6 +37,7 @@
from .enums import ResourceType
from .enums import ReviewSubmissionItemState
from .enums import ReviewSubmissionState
from .enums import SubscriptionStatusUrlVersion
from .error_response import ErrorResponse
from .pre_release_version import PreReleaseVersion
from .profile import Profile
Expand Down
56 changes: 55 additions & 1 deletion src/codemagic/apple/resources/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

from .enums import ContentRightsDeclaration
from .enums import Locale
from .enums import SubscriptionStatusUrlVersion
from .resource import Relationship
from .resource import Resource

Expand All @@ -26,15 +27,49 @@ class Attributes(Resource.Attributes):
contentRightsDeclaration: ContentRightsDeclaration
isOrEverWasMadeForKids: bool

subscriptionStatusUrl: Optional[str] = None
subscriptionStatusUrlForSandbox: Optional[str] = None
subscriptionStatusUrlVersion: Optional[SubscriptionStatusUrlVersion] = None
subscriptionStatusUrlVersionForSandbox: Optional[SubscriptionStatusUrlVersion] = None

def __post_init__(self):
if isinstance(self.contentRightsDeclaration, str):
self.contentRightsDeclaration = ContentRightsDeclaration(self.contentRightsDeclaration)
if isinstance(self.primaryLocale, str):
self.primaryLocale = Locale(self.primaryLocale)
if isinstance(self.subscriptionStatusUrlVersion, str):
self.subscriptionStatusUrlVersion = SubscriptionStatusUrlVersion(self.subscriptionStatusUrlVersion)
if isinstance(self.subscriptionStatusUrlVersionForSandbox, str):
self.subscriptionStatusUrlVersionForSandbox = SubscriptionStatusUrlVersion(
self.subscriptionStatusUrlVersionForSandbox,
)

@dataclass
class Relationships(Resource.Relationships):
_OMIT_IF_NONE_KEYS = ("betaTesters", "ciProduct", "perfPowerMetrics")
_OMIT_IF_NONE_KEYS = (
"alternativeDistributionKey",
"analyticsReportRequests",
"appAvailability",
"appAvailabilityV2",
"appClips",
"appCustomProductPages",
"appEvents",
"appPricePoints",
"appPriceSchedule",
"appStoreVersionExperimentsV2",
"betaTesters",
"ciProduct",
"customerReviews",
"gameCenterDetail",
"inAppPurchasesV2",
"marketplaceSearchDetail",
"perfPowerMetrics",
"pricePoints",
"promotedPurchases",
"reviewSubmissions",
"subscriptionGracePeriod",
"subscriptionGroups",
)

appInfos: Relationship
appStoreVersions: Relationship
Expand All @@ -49,6 +84,25 @@ class Relationships(Resource.Relationships):
preOrder: Relationship
preReleaseVersions: Relationship

alternativeDistributionKey: Optional[Relationship] = None
analyticsReportRequests: Optional[Relationship] = None
appAvailability: Optional[Relationship] = None
appAvailabilityV2: Optional[Relationship] = None
appClips: Optional[Relationship] = None
appCustomProductPages: Optional[Relationship] = None
appEvents: Optional[Relationship] = None
appPricePoints: Optional[Relationship] = None
appPriceSchedule: Optional[Relationship] = None
appStoreVersionExperimentsV2: Optional[Relationship] = None
betaTesters: Optional[Relationship] = None
ciProduct: Optional[Relationship] = None
customerReviews: Optional[Relationship] = None
gameCenterDetail: Optional[Relationship] = None
inAppPurchasesV2: Optional[Relationship] = None
marketplaceSearchDetail: Optional[Relationship] = None
perfPowerMetrics: Optional[Relationship] = None
pricePoints: Optional[Relationship] = None
promotedPurchases: Optional[Relationship] = None
reviewSubmissions: Optional[Relationship] = None
subscriptionGracePeriod: Optional[Relationship] = None
subscriptionGroups: Optional[Relationship] = None
8 changes: 8 additions & 0 deletions src/codemagic/apple/resources/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from datetime import datetime
from typing import Optional

from .enums import BuildAudienceType
from .enums import BuildProcessingState
from .resource import DictSerializable
from .resource import Relationship
Expand Down Expand Up @@ -55,8 +56,15 @@ class Attributes(Resource.Attributes):
usesNonExemptEncryption: bool
uploadedDate: datetime
expirationDate: datetime
buildAudienceType: BuildAudienceType

computedMinMacOsVersion: Optional[str] = None
lsMinimumSystemVersion: Optional[str] = None
computedMinVisionOsVersion: Optional[str] = None

def __post_init__(self):
if isinstance(self.buildAudienceType, str):
self.buildAudienceType = BuildAudienceType(self.buildAudienceType)
if isinstance(self.processingState, str):
self.processingState = BuildProcessingState(self.processingState)
if isinstance(self.uploadedDate, str):
Expand Down
20 changes: 20 additions & 0 deletions src/codemagic/apple/resources/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,15 @@ class BuildProcessingState(ResourceEnum):
VALID = "VALID"


class BuildAudienceType(ResourceEnum):
"""
https://developer.apple.com/documentation/appstoreconnectapi/buildaudiencetype
"""

INTERNAL_ONLY = "INTERNAL_ONLY"
APP_STORE_ELIGIBLE = "APP_STORE_ELIGIBLE"


class BundleIdPlatform(ResourceEnum):
IOS = "IOS"
MAC_OS = "MAC_OS"
Expand Down Expand Up @@ -392,3 +401,14 @@ class Locale(ResourceEnum):
VI = "vi"
ZH_HANS = "zh-Hans"
ZH_HANT = "zh-Hant"


class SubscriptionStatusUrlVersion(ResourceEnum):
"""
https://developer.apple.com/documentation/appstoreconnectapi/subscriptionstatusurlversion
"""

V1 = "V1"
V2 = "V2"
v1 = "v1"
v2 = "v2"
3 changes: 2 additions & 1 deletion src/codemagic/apple/resources/resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,8 @@ def _format_attribute_name(self, name: str) -> str:
type_prefix = self.type.value.rstrip("s")
name = re.sub(f"{type_prefix}s?", "", name)
name = re.sub(r"([a-z])([A-Z])", r"\1 \2", name)
return name.lower().capitalize()
name = name.lower().capitalize()
return re.sub("(i|vision|mac) os ", r"\1OS", name)

def _hide_attribute_value(self, attribute_name: str) -> bool:
if not hasattr(self.attributes, "__dataclass_fields__"):
Expand Down
4 changes: 4 additions & 0 deletions tests/apple/resources/mocks/build.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,16 @@
"expirationDate":"2021-05-03T04:00:19-07:00",
"expired":false,
"minOsVersion":"10.2",
"lsMinimumSystemVersion":null,
"computedMinMacOsVersion":"11.0",
"computedMinVisionOsVersion":"1.0",
"iconAssetToken":{
"templateUrl":"https://is2-ssl.mzstatic.com/image/thumb/Purple114/v4/80/d3/39/80d339cc-1ce8-0987-e3c0-25fc8de3275a/Icon-83.5@2x.png.png/{w}x{h}bb.{f}",
"width":167,
"height":167
},
"processingState":"VALID",
"buildAudienceType":"APP_STORE_ELIGIBLE",
"usesNonExemptEncryption":false
},
"relationships":{
Expand Down
5 changes: 5 additions & 0 deletions tests/apple/resources/test_app_resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
from codemagic.apple.resources import App


def test_app_initialization(api_app):
app = App(api_app)
assert app.dict() == api_app


def test_app_with_empty_relationship(api_app):
api_app["relationships"]["ciProduct"] = {}
app = App(api_app)
Expand Down
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import os

import pytest
from codemagic.apple.resources import App
from codemagic.apple.resources import ResourceId
from codemagic.tools import AppStoreConnect


@pytest.mark.skipif(
not os.environ.get("RUN_LIVE_API_TESTS"),
reason="Live App Store Connect API access",
)
def test_get_app(app_store_connect: AppStoreConnect):
app = app_store_connect.get_app(ResourceId("1481211155"))
assert isinstance(app, App)
assert app.id == "1481211155"
25 changes: 25 additions & 0 deletions tests/tools/app_store_connect/conftest.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

import argparse
import os
import pathlib

Expand Down Expand Up @@ -42,3 +43,27 @@ def namespace_kwargs(mock_auth_key):
continue
os.environ.pop(arg.type.environment_variable_key, None)
return ns_kwargs


@pytest.fixture()
def app_store_connect(namespace_kwargs) -> AppStoreConnect:
args = AppStoreConnectArgument
if "TEST_APPLE_PRIVATE_KEY_PATH" in os.environ:
key_path = pathlib.Path(os.environ["TEST_APPLE_PRIVATE_KEY_PATH"])
private_key = key_path.expanduser().read_text()
key_identifier = os.environ["TEST_APPLE_KEY_IDENTIFIER"]
issuer_id = os.environ["TEST_APPLE_ISSUER_ID"]
elif "TEST_APPLE_PRIVATE_KEY_CONTENT" in os.environ:
private_key = os.environ["TEST_APPLE_PRIVATE_KEY_CONTENT"]
key_identifier = os.environ["TEST_APPLE_KEY_IDENTIFIER"]
issuer_id = os.environ["TEST_APPLE_ISSUER_ID"]
else:
raise RuntimeError("Missing App Store Connect authentication information")

ns = namespace_kwargs | {
args.ISSUER_ID.key: Types.IssuerIdArgument(issuer_id),
args.KEY_IDENTIFIER.key: Types.KeyIdentifierArgument(key_identifier),
args.PRIVATE_KEY.key: Types.PrivateKeyArgument(private_key),
}
cli_args = argparse.Namespace(**ns)
return AppStoreConnect.from_cli_args(cli_args)

0 comments on commit 0bed134

Please sign in to comment.