Skip to content

Commit dd4ea6a

Browse files
author
Dale Myers
committed
Add reviews client
1 parent e3e85bc commit dd4ea6a

File tree

7 files changed

+143
-5
lines changed

7 files changed

+143
-5
lines changed

asconnect/client.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from asconnect.app_info_client import AppInfoClient
1212
from asconnect.beta_review_client import BetaReviewClient
1313
from asconnect.build_client import BuildClient
14+
from asconnect.reviews_client import ReviewsClient
1415
from asconnect.screenshot_client import ScreenshotClient
1516
from asconnect.users_client import UsersClient
1617
from asconnect.version_client import VersionClient
@@ -28,6 +29,7 @@ class Client:
2829
app_info: AppInfoClient
2930
beta_review: BetaReviewClient
3031
build: BuildClient
32+
reviews: ReviewsClient
3133
screenshots: ScreenshotClient
3234
users: UsersClient
3335
version: VersionClient
@@ -61,6 +63,7 @@ def __init__(
6163
self.app_info = AppInfoClient(http_client=self.http_client, log=self.log)
6264
self.beta_review = BetaReviewClient(http_client=self.http_client, log=self.log)
6365
self.build = BuildClient(http_client=self.http_client, log=self.log)
66+
self.reviews = ReviewsClient(http_client=self.http_client, log=self.log)
6467
self.screenshots = ScreenshotClient(http_client=self.http_client, log=self.log)
6568
self.users = UsersClient(http_client=self.http_client, log=self.log)
6669
self.version = VersionClient(http_client=self.http_client, log=self.log)

asconnect/models/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,6 @@
1010
from asconnect.models.builds import *
1111
from asconnect.models.idfa import *
1212
from asconnect.models.localization import *
13+
from asconnect.models.reviews import *
1314
from asconnect.models.screenshots import *
1415
from asconnect.models.users import *

asconnect/models/apps.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,7 @@ class ContentRightsDeclaration(enum.Enum):
1414
USES_THIRD_PARTY_CONTENT = "USES_THIRD_PARTY_CONTENT"
1515

1616

17-
@deserialize.key("bundle_id", "bundleId")
18-
@deserialize.key("primary_locale", "primaryLocale")
19-
@deserialize.key("available_in_new_territories", "availableInNewTerritories")
20-
@deserialize.key("content_rights_declaration", "contentRightsDeclaration")
21-
@deserialize.key("is_or_ever_was_made_for_kids", "isOrEverWasMadeForKids")
17+
@deserialize.auto_snake()
2218
class AppAttributes(BaseAttributes):
2319
"""Represents app attributes."""
2420

@@ -29,6 +25,11 @@ class AppAttributes(BaseAttributes):
2925
available_in_new_territories: bool | None
3026
content_rights_declaration: ContentRightsDeclaration | None
3127
is_or_ever_was_made_for_kids: bool
28+
subscription_status_url: str | None
29+
subscription_status_url_version: str | None
30+
subscription_status_url_for_sandbox: str | None
31+
subscription_status_url_version_for_sandbox: str | None
32+
streamlined_purchasing_enabled: bool | None
3233

3334

3435
@deserialize.key("identifier", "id")

asconnect/models/reviews.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
"""User Models for the API"""
2+
3+
import datetime
4+
5+
import deserialize
6+
7+
from asconnect.models.common import BaseAttributes, Links, Relationship, Resource
8+
9+
10+
@deserialize.auto_snake()
11+
@deserialize.parser("created_date", datetime.datetime.fromisoformat)
12+
class CustomerReviewAttributes(BaseAttributes):
13+
"""Represents build attributes."""
14+
15+
body: str
16+
created_date: datetime.datetime
17+
rating: int
18+
reviewer_nickname: str
19+
title: str
20+
territory: str
21+
22+
23+
@deserialize.key("identifier", "id")
24+
class CustomerReview(Resource):
25+
"""Represents a user."""
26+
27+
identifier: str
28+
attributes: CustomerReviewAttributes
29+
relationships: dict[str, Relationship] | None
30+
links: Links

asconnect/reviews_client.py

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
"""Wrapper around the Apple App Store Connect APIs."""
2+
3+
# Copyright (c) Microsoft Corporation.
4+
# Licensed under the MIT license.
5+
6+
import logging
7+
from typing import Iterator
8+
9+
from asconnect.httpclient import HttpClient
10+
11+
from asconnect.models import CustomerReview
12+
from asconnect.sorting import CustomerReviewSort
13+
from asconnect.utilities import update_query_parameters
14+
15+
16+
class ReviewsClient:
17+
"""Wrapper class around the ASC API."""
18+
19+
log: logging.Logger
20+
http_client: HttpClient
21+
22+
def __init__(
23+
self,
24+
*,
25+
http_client: HttpClient,
26+
log: logging.Logger,
27+
) -> None:
28+
"""Construct a new client object.
29+
30+
:param http_client: The API HTTP client
31+
:param log: Any base logger to be used (one will be created if not supplied)
32+
"""
33+
34+
self.http_client = http_client
35+
self.log = log.getChild("reviews")
36+
37+
def get_reviews(
38+
self,
39+
app_id: str,
40+
sort_order: CustomerReviewSort | None = None,
41+
territory_filter: list[str] | None = None,
42+
published_response: bool | None = None,
43+
) -> Iterator[CustomerReview]:
44+
"""Get customer reviews for an app.
45+
46+
:param app_id: The app ID to get reviews for
47+
:param sort_order: The order to sort the reviews in. Defaults to None.
48+
:param territory_filter: The territory to filter the reviews by. Defaults to None.
49+
:param published_response: If set to True, only reviews with a published response will be
50+
returned. If set to False, only reviews without a published
51+
response will be returned. Defaults to None which returns all.
52+
53+
:yields: An iterator of reviews
54+
"""
55+
56+
self.log.info("Getting users...")
57+
58+
url = self.http_client.generate_url(f"apps/{app_id}/customerReviews")
59+
60+
query_parameters = {}
61+
62+
if sort_order:
63+
query_parameters["sort"] = sort_order.value
64+
65+
if territory_filter:
66+
if isinstance(territory_filter, str):
67+
territory_filter = [territory_filter]
68+
query_parameters["filter[territory]"] = ",".join(territory_filter)
69+
70+
if published_response is not None:
71+
query_parameters["exists[publishedResponse]"] = str(published_response).lower()
72+
73+
url = update_query_parameters(url, query_parameters)
74+
75+
yield from self.http_client.get(url=url, data_type=list[CustomerReview])

asconnect/sorting.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,12 @@ class BuildsSort(enum.Enum):
1212
UPLOADED_DATE_REVERSED = "-uploadedDate"
1313
VERSION = "version"
1414
VERSION_REVERSED = "-version"
15+
16+
17+
class CustomerReviewSort(enum.Enum):
18+
"""Orders that customer reviews can be sorted."""
19+
20+
RATING_ASC = "rating"
21+
RATING_DESC = "-rating"
22+
CREATED_DATE_ASC = "createdDate"
23+
CREATED_DATE_DESC = "-createdDate"

tests/test_reviews.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Copyright (c) Microsoft Corporation.
2+
# Licensed under the MIT license.
3+
4+
"""Tests for the package."""
5+
6+
import os
7+
import sys
8+
9+
sys.path.insert(0, os.path.abspath(os.path.join(os.path.abspath(__file__), "..", "..")))
10+
import asconnect # pylint: disable=wrong-import-order
11+
12+
13+
def test_get_reviews(client: asconnect.Client, app_id: str) -> None:
14+
"""Test that we can wait for a build."""
15+
16+
counter = 0
17+
for review in client.reviews.get_reviews(app_id=app_id):
18+
counter += 1
19+
print(review.attributes.body)

0 commit comments

Comments
 (0)