# -*- coding: utf-8 -*-
from setuptools import setup

packages = \
['applaud', 'applaud.endpoints', 'applaud.schemas']

package_data = \
{'': ['*']}

install_requires = \
['Deprecated>=1.2.13,<2.0.0',
 'authlib>=0.15.5,<0.16.0',
 'pydantic[email]>=1.9.0,<2.0.0',
 'requests>=2.27.0,<3.0.0']

setup_kwargs = {
    'name': 'applaud',
    'version': '0.9.0',
    'description': 'The Python SDK to work with the App Store Connect API from Apple.',
    'long_description': '# applaud\n\n`applaud` is a Python client library for accessing [App Store Connect API](https://developer.apple.com/documentation/appstoreconnectapi), generated by [applaudgen](https://github.com/codinn/applaudgen).\n\n## Features\n\n- [x] Support App Store Connect API latest version 1.6\n- [x] Support `filter`, `fileds`, `include`, `limit`, `sort`, `exists` and other query parameters\n- [x] All endpoints / paths are implemented, include, but not limited to: App Information, TestFlight, Users and Roles, Sales and Finances\n- [x] Pythonic, all `camelCase` schema fields are represented as `snake_case` class attributes\n- [x] Embrace [Python type hints](https://www.python.org/dev/peps/pep-0483/)\n- [x] Use [Python Requests](https://docs.python-requests.org/en/latest/) to hanlde HTTP sessions\n- [x] [ErrorResponse](https://developer.apple.com/documentation/appstoreconnectapi/errorresponse) can be catched as exception\n\n## Installation\n\nInstall with `pip`:\n\n```\npip install applaud\n```\n\nInstall with [Poetry](https://python-poetry.org/):\n```\npoetry add applaud\n```\n\nInstall with [Pipenv](https://pipenv.pypa.io/en/latest/):\n```\npipenv install applaud\n```\n\n## Usage\n\nCalls to the API require authorization, so before we get started, you obtain keys to create the tokens from your organization’s App Store Connect account. See [Creating API Keys for App Store Connect API](https://developer.apple.com/documentation/appstoreconnectapi/creating_api_keys_for_app_store_connect_api) to create your keys and tokens.\n\n\n### Connection\n\n`Connection` is the core class of `applaud`, it holds a connection between client and remote service, [generate a new token](https://developer.apple.com/documentation/appstoreconnectapi/generating_tokens_for_api_requests#3878467) before it expires.\n\n```python\nfrom applaud.connection import Connection\n\n# Create a connection object using API keys\nconnection = Connection(APPSTORE_ISSUER_ID, APPSTORE_KEY_ID, APPSTORE_PRIVATE_KEY)\n```\n\nIn most of cases, all tasks you\'d like to perform on remote service should be initiated from a `Connection` object. `Connection` has a bunch of functions help you create `…Endpoint` objects:\n\n```python\n# Return an AppListEndpoint object\nconnection.apps()\n```\n\n### Endpoint\n\nA `…Endpoint` class encapsulates all operations you can perform on a specific resource. For example, this snippet fetches first two (sort by app name) apps that "ready for sale" and have game center enabled versions:\n\n```python\n# Return an AppsResponse object\nconnection.apps().filter(\n    app_store_versions_app_store_state=AppStoreVersionState.READY_FOR_SALE\n).exists(\n    game_center_enabled_versions=True\n).limit(\n    2\n).sort(\n    name: SortOrder.ASC\n).get()\n```\n\n#### `…Endpoint.get()`\n\nThe `get()` operation initiates a HTTP `GET` request on the endpoint\'s path. For example, the URL of [List Apps](https://developer.apple.com/documentation/appstoreconnectapi/list_apps) service endpoint is:\n```\nGET https://api.appstoreconnect.apple.com/v1/apps\n```\n\nThe corresponding code in `applaud`:\n```python\n# Return an AppsResponse object\nresponse = connection.apps().get()\n\nfor app in response.data:\n    print(f\'{app.attributes.name}: {app.attributes.bundle_id}\')\n```\n\nUnlike other operations (`create()`, `update()` and `delete()`), `get()` operation can be chained by query parameters functions:\n\n**`filter()`**\n\nYou use `filter()` function to extract matching resources. For example:\n```\nfilter[bundleId]  Attributes, relationships, and IDs by which to filter.\n        [string]\n```\n\nThe corresponding code in `applaud`:\n```python\nresponse = connection.apps().filter(\n    bundle_id="com.exmaple.app1"\n).get()\n# or\nconnection.apps().filter(\n    bundle_id=["com.exmaple.app1", "com.exmaple.app2"]\n).get()\n\nfor app in response.data:\n    print(f\'{app.attributes.name}: {app.attributes.bundle_id}\')\n```\n\n**`include()`**\n\nYou use `include()` function to ask relationship data to include in the response. For example:\n```\n include  Relationship data to include in the response.\n[string]  Possible values: appClips, appInfos, appStoreVersions,\n          availableTerritories, betaAppLocalizations,\n          betaAppReviewDetail, betaGroups, betaLicenseAgreement,\n          builds, ciProduct, endUserLicenseAgreement,\n          gameCenterEnabledVersions, inAppPurchases, preOrder,\n          preReleaseVersions, prices\n```\n\nThe corresponding code in `applaud`:\n```python\nresponse = connection.apps().include(\n    AppListEndpoint.Include.BETA_LICENSE_AGREEMENT\n).get()\n# or\nresponse = connection.apps().include(\n    [AppListEndpoint.Include.BETA_LICENSE_AGREEMENT, AppListEndpoint.Include.PRICES]\n).get()\n```\n\n**`fields()`**\n\nYou use `fields()` function to ask fields data to return for included related resources in a `get()` operation. Related resources specified in `fields()` function *MUST* be included explicitly in `include()` function, otherwise, the remote service may not return the fields data that you expect. For example:\n```\nfields[betaLicenseAgreements]  Fields to return for included related types.\n                     [string]  Possible values: agreementText, app\n```\n\nThe corresponding code in `applaud`:\n```python\nconnection.apps().include(\n    AppListEndpoint.Include.BETA_LICENSE_AGREEMENT\n).fields(\n    beta_license_agreement=[BetaLicenseAgreementField.AGREEMENT_TEXT, BetaLicenseAgreementField.APP]\n).get()\n```\n\n**`limit()`**\n\nYou use `limit()` function to restrict the maximum number of resources to return in a `get()` operation. For example:\n```\n  limit  Number of resources to return.\ninteger  Maximum Value: 200\n```\n\nThe corresponding code in `applaud`:\n```python\n# Return a response contains 10 apps at most\nconnection.apps().limit(10).get()\n\n# Raise a ValueError exception, the maxinmu allowed value is 400\nconnection.apps().limit(400).get()\n```\n\nYou can also included limit the number of related resources to return, as in `fields()` function, you *MUST* also specify the related resources explicitly in `include()` function. For example:\n```\nlimit[appStoreVersions]  integer\n                         Maximum Value: 50\n```\n\nThe corresponding code in `applaud`:\n```python\n# All returned apps have 5 related app store version at most\nconnection.apps().include(\n    AppListEndpoint.Include.APP_STORE_VERSIONS\n).limit(app_store_versions=5).get()\n\n# Raise a ValueError exception, the maxinmu allowed value is 50\nconnection.apps().include(\n    AppListEndpoint.Include.APP_STORE_VERSIONS\n).limit(app_store_versions=100).get()\n```\n\nBy leverage `limit()` function with `sort()` function, your script can be more responsive.\n\n**`sort()`**\n\nYou use `sort()` function to sort the returned resources by attributes in ascending or descending order. For example:\n```\n    sort  Attributes by which to sort.\n[string]  Possible values: bundleId, -bundleId, name, -name, sku, -sku\n```\n\nThe corresponding code in `applaud`:\n```python\nconnection.apps().sort(name=SortOrder.ASC, bundleId=SortOrder.DESC).get()\n```\n\n**`exists()`**\n\n`exists()` is a special type of filter – filter by existence or non-existence of related resource. For example:\n```\nexists[gameCenterEnabledVersions]  [string]\n```\n\nThe corresponding code in `applaud`:\n```python\nconnection.apps().exists(game_center_enabled_versions=True).get()\n```\n\n#### `…Endpoint.create()`\n\nThe `create()` operation initiates a HTTP `POST` request on the endpoint\'s path. For example, the URL of [Create an App Store Version](https://developer.apple.com/documentation/appstoreconnectapi/create_an_app_store_version) service endpoint is:\n```\nPOST https://api.appstoreconnect.apple.com/v1/appStoreVersions\n```\n\nThe corresponding code in `applaud`:\n```python\nrequest = AppStoreVersionCreateRequest(\n            data = AppStoreVersionCreateRequest.Data(\n                relationships = AppStoreVersionCreateRequest.Data.Relationships(\n                    app = AppStoreVersionCreateRequest.Data.Relationships.App(\n                        data = AppStoreVersionCreateRequest.Data.Relationships.App.Data(\n                            id = \'com.exmaple.app1\'\n                        )\n                    )\n                ),\n                attributes = AppStoreVersionCreateRequest.Data.Attributes(\n                    version_string = \'1.6\',\n                    platform = Platform.IOS,\n                    copyright = f\'Copyright © 2021 Codinn Technologies. All rights reserved.\',\n                    release_type = AppStoreVersionReleaseType.AFTER_APPROVAL\n                )\n            )\n        )\n\n# Return an AppStoreVersionResponse object\nreponse = connection.app_store_versions().create(request)\n\nversion = response.data\nprint(f\'{version.version_string}: {version.created_date}, {version.app_store_state}\')\n```\n\n#### `…Endpoint.update()`\n\nThe `update()` operation initiates a HTTP `PATCH` request on the endpoint\'s path. For example, the URL of [Modify an App Store Version](https://developer.apple.com/documentation/appstoreconnectapi/modify_an_app_store_version) service endpoint is:\n```\nPATCH https://api.appstoreconnect.apple.com/v1/appStoreVersions/{id}\n```\n\nThe corresponding code in `applaud`:\n```python\n# Get the version id created in previous example\nversion_id = version.data.id\n\n# Update version\'s information\nrequest = AppStoreVersionUpdateRequest(\n            data = AppStoreVersionUpdateRequest.Data(\n                id = version.data.id,\n\n                attributes = AppStoreVersionUpdateRequest.Data.Attributes(\n                    version_string = \'1.6.1\',\n                    platform = Platform.IOS,\n                    copyright = f\'Copyright © 2022 Codinn Technologies. All rights reserved.\',\n                    release_type = AppStoreVersionReleaseType.AFTER_APPROVAL\n                )\n            )\n        )\n\n# Return an AppStoreVersionResponse object\nreponse = connection.app_store_version(version_id).update(request)\n\nversion = response.data\nprint(f\'{version.version_string}: {version.copyright}, {version.app_store_state}\')\n```\n\n#### `…Endpoint.delete()`\n\nThe `delete()` operation initiates a HTTP `DELETE` request on the endpoint\'s path. For example, the URL of [Delete an App Store Version](https://developer.apple.com/documentation/appstoreconnectapi/delete_an_app_store_version) service endpoint is:\n```\nDELETE https://api.appstoreconnect.apple.com/v1/appStoreVersions/{id}\n```\n\nThe corresponding code in `applaud`:\n```python\n# Get the version id created in previous example\nversion_id = version.data.id\n\nconnection.app_store_version(version_id).delete()\n```\n\n### Exceptions\n\n`…Endpoint.get()`, `…Endpoint.create()`, `…Endpoint.update()` and `…Endpoint.delete()` may raise two types of exceptions:\n\n1. HTTP request exceptions raised by [Python Requests](https://docs.python-requests.org/en/latest/api/#exceptions)\n2. Remote service returns an [ErrorResponse](https://developer.apple.com/documentation/appstoreconnectapi/errorresponse)\n\nFor the second case, `applaud` raises an `EndpointException` exception, and attaches all [`ErrorResponse.Error`](https://developer.apple.com/documentation/appstoreconnectapi/errorresponse/errors) objects in `EndpointException.errors` attribute.\n\nSome errors are harmless, for example, App Store Connect has no API for you to tell whether a tester has accepted beta test invitation or not. When you trying to resend invitations to testers, you may encounter an `ALREADY_ACCEPTED` error, it\'s safe to just ignore such error:\n\n```python\ntry:\n    # Send / resend beta test invitations\n    response = connection.beta_tester_invitations().create(…)\nexcept EndpointException as err:\n    already_accepted_error = False\n    for e in err.errors:\n        if e.code == \'STATE_ERROR.TESTER_INVITE.ALREADY_ACCEPTED\':\n            # silent this error\n            already_accepted_error = True\n            break\n\n    if not already_accepted_error:\n        raise err\n```\n\n## Caveats\n\n- Query parameters functions (`filter()`, `include`, `fields` …) play a role only when using with `…Endpoint.get()`. Though there is no side effects if chain it with `…Endpoint.create()`, `…Endpoint.update()` and `…Endpoint.delete()` operations, it is not adviced.\n\n',
    'author': 'Yang.Y',
    'author_email': 'yang@yangyubo.com',
    'maintainer': None,
    'maintainer_email': None,
    'url': 'https://github.com/codinn/applaud',
    'packages': packages,
    'package_data': package_data,
    'install_requires': install_requires,
    'python_requires': '>=3.9,<4.0',
}


setup(**setup_kwargs)
