contentstack-management 1.6.0__tar.gz → 1.7.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/LICENSE +1 -1
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/PKG-INFO +2 -2
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/README.md +1 -1
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/__init__.py +6 -2
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/_api_client.py +13 -3
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/_constant.py +3 -2
- contentstack_management-1.7.1/contentstack_management/_messages.py +131 -0
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/aliases/aliases.py +2 -1
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/assets/assets.py +11 -10
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/auditlogs/auditlog.py +2 -1
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/branches/branches.py +3 -2
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/bulk_operations/bulk_operation.py +2 -1
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/content_types/content_type.py +3 -2
- contentstack_management-1.7.1/contentstack_management/contentstack.py +182 -0
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/delivery_token/delivery_token.py +2 -1
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/entries/entry.py +19 -18
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/entry_variants/entry_variants.py +4 -3
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/environments/environment.py +2 -1
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/extensions/extension.py +2 -1
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/global_fields/global_fields.py +2 -1
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/labels/label.py +2 -1
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/locale/locale.py +2 -1
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/management_token/management_token.py +2 -1
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/metadata/metadata.py +2 -1
- contentstack_management-1.7.1/contentstack_management/oauth/__init__.py +5 -0
- contentstack_management-1.7.1/contentstack_management/oauth/oauth_handler.py +417 -0
- contentstack_management-1.7.1/contentstack_management/oauth/oauth_interceptor.py +189 -0
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/organizations/organization.py +7 -6
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/publish_queue/publish_queue.py +2 -1
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/release_items/release_item.py +2 -1
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/releases/release.py +2 -1
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/roles/roles.py +2 -1
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/stack/stack.py +11 -10
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/taxonomies/taxonomy.py +2 -1
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/terms/terms.py +4 -3
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/user_session/user_session.py +4 -3
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/variant_group/variant_group.py +2 -1
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/variants/variants.py +4 -3
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/webhooks/webhook.py +9 -8
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/workflows/workflows.py +14 -13
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management.egg-info/PKG-INFO +2 -2
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management.egg-info/SOURCES.txt +4 -0
- contentstack_management-1.6.0/contentstack_management/contentstack.py +0 -98
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/_errors.py +0 -0
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/aliases/__init__.py +0 -0
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/assets/__init__.py +0 -0
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/auditlogs/__init__.py +0 -0
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/branches/__init__.py +0 -0
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/bulk_operations/__init__.py +0 -0
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/common.py +0 -0
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/content_types/__init__.py +0 -0
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/delivery_token/__init__.py +0 -0
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/entries/__init__.py +0 -0
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/entry_variants/__init__.py +0 -0
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/environments/__init__.py +0 -0
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/extensions/__init__.py +0 -0
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/global_fields/__init__.py +0 -0
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/labels/__init__.py +0 -0
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/locale/__init__.py +0 -0
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/management_token/__init__.py +0 -0
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/metadata/__init__.py +0 -0
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/organizations/__init__.py +0 -0
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/publish_queue/__init__.py +0 -0
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/release_items/__init__.py +0 -0
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/releases/__init__.py +0 -0
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/roles/__init__.py +0 -0
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/stack/__init__.py +0 -0
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/taxonomies/__init__.py +0 -0
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/terms/__init__.py +0 -0
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/user_session/__init__.py +0 -0
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/users/__init__.py +0 -0
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/users/user.py +0 -0
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/variant_group/__init__.py +0 -0
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/variants/__init__.py +0 -0
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/webhooks/__init__.py +0 -0
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/workflows/__init__.py +0 -0
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management.egg-info/dependency_links.txt +0 -0
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management.egg-info/requires.txt +0 -0
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management.egg-info/top_level.txt +0 -0
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/setup.cfg +0 -0
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/setup.py +0 -0
- {contentstack_management-1.6.0 → contentstack_management-1.7.1}/tests/test_contentstack.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
MIT License
|
|
2
2
|
|
|
3
|
-
Copyright (c) 2012 -
|
|
3
|
+
Copyright (c) 2012 - 2026 Contentstack. All rights reserved.
|
|
4
4
|
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: contentstack-management
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.7.1
|
|
4
4
|
Summary: Contentstack API Client Library for Python
|
|
5
5
|
Home-page: https://github.com/contentstack/contentstack-management-python
|
|
6
6
|
Author: dev-ex
|
|
@@ -157,7 +157,7 @@ chmod +x .git/hooks/pre-commit .git/hooks/pre-push
|
|
|
157
157
|
- [Content Management API Docs](https://www.contentstack.com/docs/developers/apis/content-management-api)
|
|
158
158
|
|
|
159
159
|
### The MIT License (MIT)
|
|
160
|
-
Copyright © 2012-
|
|
160
|
+
Copyright © 2012-2026 [Contentstack](https://www.contentstack.com/). All Rights Reserved
|
|
161
161
|
|
|
162
162
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
163
163
|
|
|
@@ -123,7 +123,7 @@ chmod +x .git/hooks/pre-commit .git/hooks/pre-push
|
|
|
123
123
|
- [Content Management API Docs](https://www.contentstack.com/docs/developers/apis/content-management-api)
|
|
124
124
|
|
|
125
125
|
### The MIT License (MIT)
|
|
126
|
-
Copyright © 2012-
|
|
126
|
+
Copyright © 2012-2026 [Contentstack](https://www.contentstack.com/). All Rights Reserved
|
|
127
127
|
|
|
128
128
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
129
129
|
|
{contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/__init__.py
RENAMED
|
@@ -34,6 +34,8 @@ from .publish_queue.publish_queue import PublishQueue
|
|
|
34
34
|
from .extensions.extension import Extension
|
|
35
35
|
from .variant_group.variant_group import VariantGroup
|
|
36
36
|
from .variants.variants import Variants
|
|
37
|
+
from .oauth.oauth_handler import OAuthHandler
|
|
38
|
+
from .oauth.oauth_interceptor import OAuthInterceptor
|
|
37
39
|
|
|
38
40
|
|
|
39
41
|
__all__ = (
|
|
@@ -71,14 +73,16 @@ __all__ = (
|
|
|
71
73
|
"PublishQueue",
|
|
72
74
|
"Extension",
|
|
73
75
|
"VariantGroup",
|
|
74
|
-
"Variants"
|
|
76
|
+
"Variants",
|
|
77
|
+
"OAuthHandler",
|
|
78
|
+
"OAuthInterceptor"
|
|
75
79
|
)
|
|
76
80
|
|
|
77
81
|
__title__ = 'contentstack-management-python'
|
|
78
82
|
__author__ = 'dev-ex'
|
|
79
83
|
__status__ = 'debug'
|
|
80
84
|
__region__ = 'na'
|
|
81
|
-
__version__ = '1.
|
|
85
|
+
__version__ = '1.7.1'
|
|
82
86
|
__host__ = 'api.contentstack.io'
|
|
83
87
|
__protocol__ = 'https://'
|
|
84
88
|
__api_version__ = 'v3'
|
|
@@ -2,7 +2,7 @@ import requests
|
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
class _APIClient:
|
|
5
|
-
def __init__(self, endpoint, headers, timeout=30, max_retries: int = 5):
|
|
5
|
+
def __init__(self, endpoint, headers, timeout=30, max_retries: int = 5, oauth_interceptor=None):
|
|
6
6
|
"""
|
|
7
7
|
The function is a constructor that initializes the endpoint, headers, timeout, and max_retries
|
|
8
8
|
attributes of an object.
|
|
@@ -25,6 +25,8 @@ class _APIClient:
|
|
|
25
25
|
self.headers = headers
|
|
26
26
|
self.timeout = timeout
|
|
27
27
|
self.max_retries = max_retries
|
|
28
|
+
self.oauth_interceptor = oauth_interceptor
|
|
29
|
+
self.oauth = {} # OAuth token storage
|
|
28
30
|
pass
|
|
29
31
|
|
|
30
32
|
def _call_request(self, method, url, headers: dict = None, params=None, data=None, json_data=None, files=None):
|
|
@@ -52,9 +54,17 @@ class _APIClient:
|
|
|
52
54
|
:return: the JSON response from the HTTP request.
|
|
53
55
|
"""
|
|
54
56
|
|
|
55
|
-
|
|
57
|
+
if self.oauth_interceptor and self.oauth_interceptor.is_oauth_configured():
|
|
58
|
+
return self.oauth_interceptor.execute_request(
|
|
59
|
+
method, url, headers=headers, params=params, data=data,
|
|
60
|
+
json=json_data, files=files, timeout=self.timeout
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
if headers is None:
|
|
64
|
+
headers = {}
|
|
65
|
+
headers.update(self.headers) # Merge client headers (including authtoken) with request headers
|
|
56
66
|
response = requests.request(
|
|
57
|
-
method, url, headers=headers, params=params, data=data, json=json_data, files=files)
|
|
67
|
+
method, url, headers=headers, params=params, data=data, json=json_data, files=files, timeout=self.timeout)
|
|
58
68
|
# response.raise_for_status()
|
|
59
69
|
return response
|
|
60
70
|
|
{contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/_constant.py
RENAMED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import platform
|
|
2
|
-
import sys
|
|
2
|
+
import sys
|
|
3
|
+
from ._messages import REQUEST_HEADERS_INVALID
|
|
3
4
|
|
|
4
5
|
|
|
5
6
|
def _default_user_agent():
|
|
@@ -55,4 +56,4 @@ def _request_headers():
|
|
|
55
56
|
|
|
56
57
|
|
|
57
58
|
if __name__ == '__main__':
|
|
58
|
-
print(
|
|
59
|
+
print(REQUEST_HEADERS_INVALID)
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Centralized error and UI messages for Contentstack Management SDK.
|
|
3
|
+
|
|
4
|
+
This module contains all user-facing messages used throughout the SDK
|
|
5
|
+
to ensure consistency and easy maintenance.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
# General messages
|
|
9
|
+
REQUEST_HEADERS_INVALID = "Request headers are invalid. Provide valid headers and try again."
|
|
10
|
+
|
|
11
|
+
# Alias messages
|
|
12
|
+
ALIAS_UID_REQUIRED = "Alias UID is required. Provide a valid Alias UID and try again."
|
|
13
|
+
|
|
14
|
+
# Asset messages
|
|
15
|
+
ASSET_UID_REQUIRED = "Asset UID is required. Provide a valid Asset UID and try again."
|
|
16
|
+
ASSET_TYPE_REQUIRED = "Asset Type is required. Provide a valid Asset Type and try again."
|
|
17
|
+
ASSET_VERSION_NUMBER_REQUIRED = "Version Number is required. Provide a valid Version Number and try again."
|
|
18
|
+
|
|
19
|
+
# Audit log messages
|
|
20
|
+
LOG_ITEM_UID_REQUIRED = "Log Item UID is required. Provide a valid Log Item UID and try again."
|
|
21
|
+
|
|
22
|
+
# Branch messages
|
|
23
|
+
BRANCH_UID_REQUIRED = "Branch UID is required. Provide a valid Branch UID and try again."
|
|
24
|
+
|
|
25
|
+
# Bulk operation messages
|
|
26
|
+
JOB_UID_REQUIRED = "Job UID is required. Provide a valid Job UID and try again."
|
|
27
|
+
|
|
28
|
+
# Content type messages
|
|
29
|
+
CONTENT_TYPE_UID_REQUIRED = "Content Type UID is required. Provide a valid Content Type UID and try again."
|
|
30
|
+
|
|
31
|
+
# Delivery token messages
|
|
32
|
+
DELIVERY_TOKEN_UID_REQUIRED = "Delivery Token UID is required. Provide a valid Delivery Token UID and try again."
|
|
33
|
+
|
|
34
|
+
# Entry messages
|
|
35
|
+
ENTRY_UID_REQUIRED = "Entry UID is required. Provide a valid Entry UID and try again."
|
|
36
|
+
ENTRY_VERSION_NUMBER_REQUIRED = "Version Number is required. Provide a valid Version Number and try again."
|
|
37
|
+
ENTRY_BODY_REQUIRED = "Body is required. Provide a valid Body value and try again."
|
|
38
|
+
ENTRY_FILE_PATH_REQUIRED = "File Path is required. Provide a valid File Path and try again."
|
|
39
|
+
|
|
40
|
+
# Entry variant messages
|
|
41
|
+
ENTRY_VARIANT_CONTENT_TYPE_UID_REQUIRED = "Content Type UID is required. Provide a valid Content Type UID and try again."
|
|
42
|
+
ENTRY_VARIANT_ENTRY_UID_REQUIRED = "Entry UID is required. Provide a valid Entry UID and try again."
|
|
43
|
+
ENTRY_VARIANT_UID_REQUIRED = "Variant UID is required. Provide a valid Variant UID and try again."
|
|
44
|
+
|
|
45
|
+
# Environment messages
|
|
46
|
+
ENVIRONMENT_UID_REQUIRED = "Environment UID is required. Provide a valid Environment UID and try again."
|
|
47
|
+
|
|
48
|
+
# Extension messages
|
|
49
|
+
EXTENSION_UID_REQUIRED = "Extension UID is required. Provide a valid Extension UID and try again."
|
|
50
|
+
|
|
51
|
+
# Global field messages
|
|
52
|
+
GLOBAL_FIELD_UID_REQUIRED = "Global Field UID is required. Provide a valid Global Field UID and try again."
|
|
53
|
+
|
|
54
|
+
# Label messages
|
|
55
|
+
LABEL_UID_REQUIRED = "Label UID is required. Provide a valid Label UID and try again."
|
|
56
|
+
|
|
57
|
+
# Locale messages
|
|
58
|
+
LOCALE_CODE_REQUIRED = "Locale Code is required. Provide a valid Locale Code and try again."
|
|
59
|
+
|
|
60
|
+
# Management token messages
|
|
61
|
+
MANAGEMENT_TOKEN_UID_REQUIRED = "Management Token UID is required. Provide a valid Management Token UID and try again."
|
|
62
|
+
|
|
63
|
+
# Metadata messages
|
|
64
|
+
METADATA_UID_REQUIRED = "Metadata UID is required. Provide a valid Metadata UID and try again."
|
|
65
|
+
|
|
66
|
+
# OAuth messages
|
|
67
|
+
OAUTH_ACCESS_TOKEN_EXPIRED = "Access token expired. Refreshing the token."
|
|
68
|
+
OAUTH_ACCESS_TOKEN_NOT_AVAILABLE = "Access token is not available. Authenticate to continue."
|
|
69
|
+
OAUTH_TOKENS_NOT_AVAILABLE = "OAuth tokens are not available. Please authenticate first."
|
|
70
|
+
OAUTH_REFRESH_TOKEN_NOT_AVAILABLE = "Refresh token is not available. Please authenticate to get a new token."
|
|
71
|
+
OAUTH_NOT_CONFIGURED = "OAuth is not configured. Set up OAuth to continue."
|
|
72
|
+
OAUTH_AUTHORIZATION_CODE_NOT_FOUND = "Authorization code was not found in the redirect URL. Provide a valid code and try again."
|
|
73
|
+
OAUTH_TOKEN_EXCHANGE_FAILED = "Token exchange failed. Please try authenticating again."
|
|
74
|
+
OAUTH_TOKEN_REFRESH_FAILED = "Token refresh failed due to {error}. Review the error and try again."
|
|
75
|
+
OAUTH_BASE_URL_NOT_SET = "OAuth Base URL is not set. Provide a valid OAuth Base URL and try again."
|
|
76
|
+
OAUTH_AUTHORIZING = "Authorizing using app ID {app_id} and client ID {client_id}."
|
|
77
|
+
OAUTH_AUTHORIZATION_URL_GENERATED = "OAuth handler generated the final authorization URL at {final_url}."
|
|
78
|
+
OAUTH_AUTHORIZATION_URL_GENERATION_FAILED = "Authorization URL generation failed due to {error}. Review the error and try again."
|
|
79
|
+
OAUTH_AUTHORIZATION_CODE_EMPTY = "Authorization code is required. Provide a valid code and try again."
|
|
80
|
+
OAUTH_TOKEN_EXCHANGE_ERROR = "Token exchange failed due to {error}. Review the error and try again."
|
|
81
|
+
OAUTH_TOKEN_REFRESH_ERROR = "Token refresh failed due to {error}. Review the error and try again."
|
|
82
|
+
OAUTH_TOKEN_REFRESH_FAILED_AFTER_401 = "Token refresh failed after a 401 response due to {error}. Review the error and try again."
|
|
83
|
+
OAUTH_VISIT_URL_TO_AUTHORIZE = "Visit {auth_url} to authorize."
|
|
84
|
+
|
|
85
|
+
# Organization messages
|
|
86
|
+
ORGANIZATION_UID_REQUIRED = "Organization UID is required. Provide a valid Organization UID and try again."
|
|
87
|
+
|
|
88
|
+
# Publish queue messages
|
|
89
|
+
PUBLISH_QUEUE_UID_REQUIRED = "Publish Queue UID is required. Provide a valid Publish Queue UID and try again."
|
|
90
|
+
|
|
91
|
+
# Release messages
|
|
92
|
+
RELEASE_UID_REQUIRED = "Release UID is required. Provide a valid Release UID and try again."
|
|
93
|
+
|
|
94
|
+
# Role messages
|
|
95
|
+
ROLE_UID_REQUIRED = "Role UID is required. Provide a valid Release UID and try again."
|
|
96
|
+
|
|
97
|
+
# Stack messages
|
|
98
|
+
API_KEY_REQUIRED = "API Key is required. Provide a valid API Key and try again."
|
|
99
|
+
USER_ID_REQUIRED = "User ID is required. Provide a valid User ID and try again."
|
|
100
|
+
OWNERSHIP_TOKEN_REQUIRED = "Ownership Token is required. Provide a valid Ownership Token and try again."
|
|
101
|
+
|
|
102
|
+
# Taxonomy messages
|
|
103
|
+
TAXONOMY_UID_REQUIRED = "Taxonomy UID is required. Provide a valid Taxonomy UID and try again."
|
|
104
|
+
|
|
105
|
+
# Terms messages
|
|
106
|
+
TERMS_UID_REQUIRED = "Terms UID is required. Provide a valid Terms UID and try again."
|
|
107
|
+
TERM_STRING_REQUIRED = "Term String is required. Provide a valid Term String and try again."
|
|
108
|
+
|
|
109
|
+
# User session messages
|
|
110
|
+
EMAIL_ID_REQUIRED = "Email ID is required. Provide a valid Email ID and try again."
|
|
111
|
+
PASSWORD_REQUIRED = "Password is required. Provide a valid Password and try again."
|
|
112
|
+
|
|
113
|
+
# Variant group messages
|
|
114
|
+
VARIANT_GROUP_UID_REQUIRED = "Variant Group UID is required. Provide a valid Variant Group UID and try again."
|
|
115
|
+
|
|
116
|
+
# Variant messages
|
|
117
|
+
VARIANT_UIDS_NON_EMPTY_LIST_REQUIRED = "Variant UIDs must be a non-empty list. Provide at least one Variant UID and try again."
|
|
118
|
+
VARIANT_UID_REQUIRED = "Variant UID is required. Provide a valid Variant UID and try again."
|
|
119
|
+
|
|
120
|
+
# Webhook messages
|
|
121
|
+
WEBHOOK_UID_REQUIRED = "Webhook UID is required. Provide a valid Webhook UID and try again."
|
|
122
|
+
WEBHOOK_FILE_PATH_REQUIRED = "File Path is required. Provide a valid File Path and try again."
|
|
123
|
+
WEBHOOK_EXECUTION_UID_REQUIRED = "Execution UID is required. Provide a valid Execution UID and try again."
|
|
124
|
+
|
|
125
|
+
# Workflow messages
|
|
126
|
+
WORKFLOW_UID_REQUIRED = "Workflow UID is required. Provide a valid Workflow UID and try again."
|
|
127
|
+
WORKFLOW_CONTENT_TYPE_UID_REQUIRED = "Content Type UID is required. Provide a valid Content Type UID and try again."
|
|
128
|
+
WORKFLOW_ENTRY_UID_REQUIRED = "Entry UID is required. Provide a valid Entry UID and try again."
|
|
129
|
+
WORKFLOW_RULE_UID_REQUIRED = "Rule UID is required. Provide a valid Rule UID and try again."
|
|
130
|
+
|
|
131
|
+
|
|
@@ -3,6 +3,7 @@ frontend code to pull content from the target branch associated with an alias.""
|
|
|
3
3
|
|
|
4
4
|
import json
|
|
5
5
|
from ..common import Parameter
|
|
6
|
+
from .._messages import ALIAS_UID_REQUIRED
|
|
6
7
|
_path = 'stacks/branch_aliases'
|
|
7
8
|
|
|
8
9
|
|
|
@@ -47,7 +48,7 @@ class Alias(Parameter):
|
|
|
47
48
|
--------------------------------
|
|
48
49
|
"""
|
|
49
50
|
if self.alias_uid is None or '':
|
|
50
|
-
raise Exception(
|
|
51
|
+
raise Exception(ALIAS_UID_REQUIRED)
|
|
51
52
|
url = f"{_path}/{self.alias_uid}"
|
|
52
53
|
return self.client.get(url, headers=self.client.headers, params=self.params)
|
|
53
54
|
|
|
@@ -8,6 +8,7 @@ import json
|
|
|
8
8
|
from ..common import Parameter
|
|
9
9
|
import mimetypes
|
|
10
10
|
import os
|
|
11
|
+
from .._messages import ASSET_UID_REQUIRED, ASSET_TYPE_REQUIRED, ASSET_VERSION_NUMBER_REQUIRED
|
|
11
12
|
|
|
12
13
|
class Assets(Parameter):
|
|
13
14
|
"""
|
|
@@ -62,7 +63,7 @@ class Assets(Parameter):
|
|
|
62
63
|
"""
|
|
63
64
|
|
|
64
65
|
if self.asset_uid is None or '':
|
|
65
|
-
raise Exception(
|
|
66
|
+
raise Exception(ASSET_UID_REQUIRED)
|
|
66
67
|
url = f"assets/{self.asset_uid}"
|
|
67
68
|
return self.client.get(url, headers = self.client.headers, params = self.params)
|
|
68
69
|
|
|
@@ -284,7 +285,7 @@ class Assets(Parameter):
|
|
|
284
285
|
"""
|
|
285
286
|
|
|
286
287
|
if self.asset_uid is None or '':
|
|
287
|
-
raise Exception(
|
|
288
|
+
raise Exception(ASSET_UID_REQUIRED)
|
|
288
289
|
url = f"assets/{self.asset_uid}/versions"
|
|
289
290
|
return self.client.get(url, headers = self.client.headers, params = self.params)
|
|
290
291
|
|
|
@@ -305,9 +306,9 @@ class Assets(Parameter):
|
|
|
305
306
|
"""
|
|
306
307
|
|
|
307
308
|
if self.asset_uid is None or '':
|
|
308
|
-
raise Exception(
|
|
309
|
+
raise Exception(ASSET_UID_REQUIRED)
|
|
309
310
|
if version_number is None:
|
|
310
|
-
raise Exception(
|
|
311
|
+
raise Exception(ASSET_VERSION_NUMBER_REQUIRED)
|
|
311
312
|
url = f"assets/{self.asset_uid}/versions/{version_number}/name"
|
|
312
313
|
return self.client.delete(url, headers = self.client.headers, params = self.params)
|
|
313
314
|
|
|
@@ -326,7 +327,7 @@ class Assets(Parameter):
|
|
|
326
327
|
"""
|
|
327
328
|
|
|
328
329
|
if self.asset_uid is None or '':
|
|
329
|
-
raise Exception(
|
|
330
|
+
raise Exception(ASSET_UID_REQUIRED)
|
|
330
331
|
url = f"assets/{self.asset_uid}/references"
|
|
331
332
|
return self.client.get(url, headers = self.client.headers, params = self.params)
|
|
332
333
|
|
|
@@ -346,7 +347,7 @@ class Assets(Parameter):
|
|
|
346
347
|
"""
|
|
347
348
|
|
|
348
349
|
if asset_type is None or '':
|
|
349
|
-
raise Exception(
|
|
350
|
+
raise Exception(ASSET_TYPE_REQUIRED)
|
|
350
351
|
url = f"assets/{asset_type}"
|
|
351
352
|
return self.client.get(url, headers = self.client.headers, params = self.params)
|
|
352
353
|
|
|
@@ -376,7 +377,7 @@ class Assets(Parameter):
|
|
|
376
377
|
|
|
377
378
|
data = json.dumps(data)
|
|
378
379
|
if self.asset_uid is None or '':
|
|
379
|
-
raise Exception(
|
|
380
|
+
raise Exception(ASSET_UID_REQUIRED)
|
|
380
381
|
url = f"assets/{self.asset_uid}"
|
|
381
382
|
return self.client.put(url, headers = self.client.headers, params = self.params, data = data)
|
|
382
383
|
|
|
@@ -404,7 +405,7 @@ class Assets(Parameter):
|
|
|
404
405
|
|
|
405
406
|
data = json.dumps(data)
|
|
406
407
|
if self.asset_uid is None or '':
|
|
407
|
-
raise Exception(
|
|
408
|
+
raise Exception(ASSET_UID_REQUIRED)
|
|
408
409
|
url = f"assets/{self.asset_uid}"
|
|
409
410
|
Parameter.add_header(self, "Content-Type", "multipart/form-data")
|
|
410
411
|
return self.client.put(url, headers = self.client.headers, params = self.params, data = data)
|
|
@@ -439,7 +440,7 @@ class Assets(Parameter):
|
|
|
439
440
|
|
|
440
441
|
data = json.dumps(data)
|
|
441
442
|
if self.asset_uid is None or '':
|
|
442
|
-
raise Exception(
|
|
443
|
+
raise Exception(ASSET_UID_REQUIRED)
|
|
443
444
|
url = f"assets/{self.asset_uid}/publish"
|
|
444
445
|
return self.client.post(url, headers = self.client.headers, data = data)
|
|
445
446
|
|
|
@@ -474,7 +475,7 @@ class Assets(Parameter):
|
|
|
474
475
|
|
|
475
476
|
data = json.dumps(data)
|
|
476
477
|
if self.asset_uid is None or '':
|
|
477
|
-
raise Exception(
|
|
478
|
+
raise Exception(ASSET_UID_REQUIRED)
|
|
478
479
|
url = f"assets/{self.asset_uid}/unpublish"
|
|
479
480
|
return self.client.post(url, headers = self.client.headers, data = data)
|
|
480
481
|
|
|
@@ -5,6 +5,7 @@ the CRUD operations that can be performed on the API """
|
|
|
5
5
|
|
|
6
6
|
from ..common import Parameter
|
|
7
7
|
from .._errors import ArgumentException
|
|
8
|
+
from .._messages import LOG_ITEM_UID_REQUIRED
|
|
8
9
|
|
|
9
10
|
class Auditlog(Parameter):
|
|
10
11
|
"""
|
|
@@ -58,6 +59,6 @@ class Auditlog(Parameter):
|
|
|
58
59
|
|
|
59
60
|
def validate_uid(self):
|
|
60
61
|
if self.log_item_uid is None or '':
|
|
61
|
-
raise ArgumentException(
|
|
62
|
+
raise ArgumentException(LOG_ITEM_UID_REQUIRED)
|
|
62
63
|
|
|
63
64
|
|
|
@@ -6,6 +6,7 @@ organized, and structured manner without impacting each other."""
|
|
|
6
6
|
import json
|
|
7
7
|
|
|
8
8
|
from contentstack_management.common import Parameter
|
|
9
|
+
from contentstack_management._messages import BRANCH_UID_REQUIRED
|
|
9
10
|
|
|
10
11
|
_path = 'stacks/branches'
|
|
11
12
|
|
|
@@ -55,7 +56,7 @@ class Branch(Parameter):
|
|
|
55
56
|
--------------------------------
|
|
56
57
|
"""
|
|
57
58
|
if self.branch_uid is None or '':
|
|
58
|
-
raise Exception(
|
|
59
|
+
raise Exception(BRANCH_UID_REQUIRED)
|
|
59
60
|
url = f"{_path}/{self.branch_uid}"
|
|
60
61
|
return self.client.get(url, headers=self.client.headers, params=self.params)
|
|
61
62
|
|
|
@@ -101,6 +102,6 @@ class Branch(Parameter):
|
|
|
101
102
|
--------------------------------
|
|
102
103
|
"""
|
|
103
104
|
if self.branch_uid is None or '':
|
|
104
|
-
raise Exception(
|
|
105
|
+
raise Exception(BRANCH_UID_REQUIRED)
|
|
105
106
|
url = f"{_path}/{self.branch_uid}"
|
|
106
107
|
return self.client.delete(url, headers=self.client.headers, params=self.params)
|
|
@@ -7,6 +7,7 @@ import json
|
|
|
7
7
|
from ..common import Parameter
|
|
8
8
|
from urllib.parse import quote
|
|
9
9
|
from .._errors import ArgumentException
|
|
10
|
+
from .._messages import JOB_UID_REQUIRED
|
|
10
11
|
|
|
11
12
|
class BulkOperation(Parameter):
|
|
12
13
|
"""
|
|
@@ -313,7 +314,7 @@ class BulkOperation(Parameter):
|
|
|
313
314
|
-------------------------------
|
|
314
315
|
"""
|
|
315
316
|
if job_uid is None:
|
|
316
|
-
raise ArgumentException(
|
|
317
|
+
raise ArgumentException(JOB_UID_REQUIRED)
|
|
317
318
|
if headers is not None:
|
|
318
319
|
self.client.headers.update(headers)
|
|
319
320
|
url = f"{self.path}/jobs/{quote(job_uid)}"
|
|
@@ -6,6 +6,7 @@ import json
|
|
|
6
6
|
|
|
7
7
|
from contentstack_management.common import Parameter
|
|
8
8
|
from ..entries import entry
|
|
9
|
+
from .._messages import CONTENT_TYPE_UID_REQUIRED
|
|
9
10
|
|
|
10
11
|
_path = 'content_types'
|
|
11
12
|
|
|
@@ -163,7 +164,7 @@ class ContentType(Parameter):
|
|
|
163
164
|
"""
|
|
164
165
|
data = json.dumps(data)
|
|
165
166
|
if self.content_type_uid is None or '':
|
|
166
|
-
raise Exception(
|
|
167
|
+
raise Exception(CONTENT_TYPE_UID_REQUIRED)
|
|
167
168
|
url = f"{_path}/{self.content_type_uid}"
|
|
168
169
|
return self.client.put(url, headers=self.client.headers, params=self.params, data=data)
|
|
169
170
|
|
|
@@ -347,5 +348,5 @@ class ContentType(Parameter):
|
|
|
347
348
|
|
|
348
349
|
def entry(self, entry_uid: str =None):
|
|
349
350
|
if self.content_type_uid is None:
|
|
350
|
-
raise Exception(
|
|
351
|
+
raise Exception(CONTENT_TYPE_UID_REQUIRED)
|
|
351
352
|
return entry.Entry(self.client, self.content_type_uid, entry_uid)
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
import os
|
|
3
|
+
import pyotp
|
|
4
|
+
from ._api_client import _APIClient
|
|
5
|
+
from contentstack_management.organizations import organization
|
|
6
|
+
from contentstack_management.stack import stack
|
|
7
|
+
from contentstack_management.user_session import user_session
|
|
8
|
+
from contentstack_management.users import user
|
|
9
|
+
from contentstack_management.oauth.oauth_handler import OAuthHandler
|
|
10
|
+
|
|
11
|
+
version = '0.0.1'
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class Region(Enum):
|
|
15
|
+
US = "us"
|
|
16
|
+
EU = "eu"
|
|
17
|
+
AU = "au"
|
|
18
|
+
AZURE_EU = "azure-eu"
|
|
19
|
+
AZURE_NA = "azure-na"
|
|
20
|
+
GCP_NA = "gcp-na"
|
|
21
|
+
GCP_EU = "gcp-eu"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def user_agents(headers=None):
|
|
25
|
+
if headers is None:
|
|
26
|
+
headers = {}
|
|
27
|
+
local_headers = {'X-User-Agent': f'contentstack-management-python/v{version}',
|
|
28
|
+
"Content-Type": "application/json"}
|
|
29
|
+
headers.update(local_headers)
|
|
30
|
+
return headers
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class Client:
|
|
34
|
+
|
|
35
|
+
# TODO: DefaultCSCredential(), needs to be implemented
|
|
36
|
+
def __init__(self, host: str = 'api.contentstack.io', scheme: str = 'https://',
|
|
37
|
+
authtoken: str = None , management_token=None, headers: dict = None,
|
|
38
|
+
region: Region = Region.US.value, version='v3', timeout=2, max_retries: int = 18, early_access: list = None,
|
|
39
|
+
oauth_config: dict = None, **kwargs):
|
|
40
|
+
self.endpoint = 'https://api.contentstack.io/v3/'
|
|
41
|
+
|
|
42
|
+
if region is not None and region is not Region.US.value:
|
|
43
|
+
if host is not None and host != 'api.contentstack.io':
|
|
44
|
+
self.endpoint = f'{scheme}{region}-api.{host}/{version}/'
|
|
45
|
+
else:
|
|
46
|
+
host = 'api.contentstack.com'
|
|
47
|
+
self.endpoint = f'{scheme}{region}-{host}/{version}/'
|
|
48
|
+
elif host is not None and host != 'api.contentstack.io':
|
|
49
|
+
self.endpoint = f'{scheme}{host}/{version}/'
|
|
50
|
+
if headers is None:
|
|
51
|
+
headers = {}
|
|
52
|
+
if early_access is not None:
|
|
53
|
+
early_access_str = ', '.join(early_access)
|
|
54
|
+
headers['x-header-ea'] = early_access_str
|
|
55
|
+
|
|
56
|
+
if authtoken is not None:
|
|
57
|
+
headers['authtoken'] = authtoken
|
|
58
|
+
|
|
59
|
+
if management_token is not None:
|
|
60
|
+
headers['authorization'] = management_token
|
|
61
|
+
headers = user_agents(headers)
|
|
62
|
+
self.client = _APIClient(endpoint=self.endpoint, headers=headers, timeout=timeout, max_retries=max_retries)
|
|
63
|
+
|
|
64
|
+
# Initialize OAuth if configuration is provided
|
|
65
|
+
self.oauth_handler = None
|
|
66
|
+
if oauth_config:
|
|
67
|
+
self.oauth_handler = OAuthHandler(
|
|
68
|
+
app_id=oauth_config.get('app_id'),
|
|
69
|
+
client_id=oauth_config.get('client_id'),
|
|
70
|
+
redirect_uri=oauth_config.get('redirect_uri'),
|
|
71
|
+
response_type=oauth_config.get('response_type', 'code'),
|
|
72
|
+
client_secret=oauth_config.get('client_secret'),
|
|
73
|
+
scope=oauth_config.get('scope'),
|
|
74
|
+
api_client=self.client
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
"""
|
|
78
|
+
:param host: Optional hostname for the API endpoint.
|
|
79
|
+
:param authtoken: Optional authentication token for API requests
|
|
80
|
+
:param headers: Optional headers to be included with API requests
|
|
81
|
+
:param authorization: Optional headers to be included in the api request
|
|
82
|
+
:param region: optional region to be included in API requests,
|
|
83
|
+
We have region support options for na, eu, azure-eu, azure-na
|
|
84
|
+
:param scheme: optional scheme to be included in API requests
|
|
85
|
+
:param version: optional version to be included in API request path url,
|
|
86
|
+
:param timeout: Optional timeout value for API requests
|
|
87
|
+
:param max_requests:Optional maximum number of requests to be made
|
|
88
|
+
:param retry_on_error: Optional boolean value indicating whether to retry API requests on error.
|
|
89
|
+
:return: A client object for performing API operations.
|
|
90
|
+
-------------------------------
|
|
91
|
+
[Example:]
|
|
92
|
+
|
|
93
|
+
>>> import contentstack_management
|
|
94
|
+
>>> contentstack_client = contentstack_management.Client()
|
|
95
|
+
-------------------------------
|
|
96
|
+
"""
|
|
97
|
+
|
|
98
|
+
def login(self, email: str, password: str, tfa_token: str = None, mfa_secret: str = None):
|
|
99
|
+
"""
|
|
100
|
+
Login to Contentstack with optional TOTP support.
|
|
101
|
+
|
|
102
|
+
:param email: User's email address
|
|
103
|
+
:param password: User's password
|
|
104
|
+
:param tfa_token: Optional two-factor authentication token
|
|
105
|
+
:param mfa_secret: Optional MFA secret for automatic TOTP generation.
|
|
106
|
+
If not provided, will check MFA_SECRET environment variable
|
|
107
|
+
:return: Response object from the login request
|
|
108
|
+
"""
|
|
109
|
+
final_tfa_token = tfa_token
|
|
110
|
+
|
|
111
|
+
if not mfa_secret:
|
|
112
|
+
mfa_secret = os.getenv('MFA_SECRET')
|
|
113
|
+
|
|
114
|
+
if mfa_secret and not tfa_token:
|
|
115
|
+
final_tfa_token = self._generate_totp(mfa_secret)
|
|
116
|
+
|
|
117
|
+
return user_session.UserSession(self.client).login(email, password, final_tfa_token)
|
|
118
|
+
|
|
119
|
+
def _generate_totp(self, secret: str) -> str:
|
|
120
|
+
"""
|
|
121
|
+
Generate a Time-Based One-Time Password (TOTP) from the provided secret.
|
|
122
|
+
|
|
123
|
+
:param secret: The MFA secret key for TOTP generation
|
|
124
|
+
:return: The current TOTP code as a string
|
|
125
|
+
"""
|
|
126
|
+
totp = pyotp.TOTP(secret)
|
|
127
|
+
return totp.now()
|
|
128
|
+
|
|
129
|
+
def logout(self):
|
|
130
|
+
return user_session.UserSession(client=self.client).logout()
|
|
131
|
+
|
|
132
|
+
@property
|
|
133
|
+
def authtoken(self):
|
|
134
|
+
return self.client.headers['authtoken']
|
|
135
|
+
|
|
136
|
+
def user(self):
|
|
137
|
+
return user.User(self.client)
|
|
138
|
+
|
|
139
|
+
def organizations(self, organization_uid: str = None):
|
|
140
|
+
return organization.Organization(self.client, organization_uid)
|
|
141
|
+
|
|
142
|
+
def stack(self, api_key: str = None):
|
|
143
|
+
return stack.Stack(self.client, api_key)
|
|
144
|
+
|
|
145
|
+
def oauth(self, app_id: str, client_id: str, redirect_uri: str,
|
|
146
|
+
response_type: str = "code", client_secret: str = None,
|
|
147
|
+
scope: list = None):
|
|
148
|
+
"""
|
|
149
|
+
Create an OAuth handler for OAuth 2.0 authentication.
|
|
150
|
+
|
|
151
|
+
Args:
|
|
152
|
+
app_id: Your registered App ID
|
|
153
|
+
client_id: Your OAuth Client ID
|
|
154
|
+
redirect_uri: The URL where the user is redirected after login and consent
|
|
155
|
+
response_type: OAuth response type (default: "code")
|
|
156
|
+
client_secret: Client secret for standard OAuth flows (optional for PKCE)
|
|
157
|
+
scope: Permissions requested (optional)
|
|
158
|
+
|
|
159
|
+
Returns:
|
|
160
|
+
OAuthHandler instance
|
|
161
|
+
|
|
162
|
+
Example:
|
|
163
|
+
>>> import contentstack_management
|
|
164
|
+
>>> client = contentstack_management.Client()
|
|
165
|
+
>>> oauth_handler = client.oauth(
|
|
166
|
+
... app_id='your-app-id',
|
|
167
|
+
... client_id='your-client-id',
|
|
168
|
+
... redirect_uri='http://localhost:3000/callback'
|
|
169
|
+
... )
|
|
170
|
+
>>> auth_url = oauth_handler.authorize()
|
|
171
|
+
>>> from contentstack_management._messages import OAUTH_VISIT_URL_TO_AUTHORIZE
|
|
172
|
+
>>> print(OAUTH_VISIT_URL_TO_AUTHORIZE.format(auth_url=auth_url))
|
|
173
|
+
"""
|
|
174
|
+
return OAuthHandler(
|
|
175
|
+
app_id=app_id,
|
|
176
|
+
client_id=client_id,
|
|
177
|
+
redirect_uri=redirect_uri,
|
|
178
|
+
response_type=response_type,
|
|
179
|
+
client_secret=client_secret,
|
|
180
|
+
scope=scope,
|
|
181
|
+
api_client=self.client
|
|
182
|
+
)
|
|
@@ -6,6 +6,7 @@ the CRUD operations that can be performed on the API """
|
|
|
6
6
|
import json
|
|
7
7
|
from ..common import Parameter
|
|
8
8
|
from .._errors import ArgumentException
|
|
9
|
+
from .._messages import DELIVERY_TOKEN_UID_REQUIRED
|
|
9
10
|
|
|
10
11
|
class DeliveryToken(Parameter):
|
|
11
12
|
"""
|
|
@@ -191,4 +192,4 @@ class DeliveryToken(Parameter):
|
|
|
191
192
|
|
|
192
193
|
def validate_uid(self):
|
|
193
194
|
if self.delivery_token_uid is None or '':
|
|
194
|
-
raise ArgumentException(
|
|
195
|
+
raise ArgumentException(DELIVERY_TOKEN_UID_REQUIRED)
|