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.
Files changed (82) hide show
  1. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/LICENSE +1 -1
  2. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/PKG-INFO +2 -2
  3. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/README.md +1 -1
  4. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/__init__.py +6 -2
  5. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/_api_client.py +13 -3
  6. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/_constant.py +3 -2
  7. contentstack_management-1.7.1/contentstack_management/_messages.py +131 -0
  8. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/aliases/aliases.py +2 -1
  9. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/assets/assets.py +11 -10
  10. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/auditlogs/auditlog.py +2 -1
  11. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/branches/branches.py +3 -2
  12. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/bulk_operations/bulk_operation.py +2 -1
  13. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/content_types/content_type.py +3 -2
  14. contentstack_management-1.7.1/contentstack_management/contentstack.py +182 -0
  15. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/delivery_token/delivery_token.py +2 -1
  16. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/entries/entry.py +19 -18
  17. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/entry_variants/entry_variants.py +4 -3
  18. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/environments/environment.py +2 -1
  19. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/extensions/extension.py +2 -1
  20. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/global_fields/global_fields.py +2 -1
  21. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/labels/label.py +2 -1
  22. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/locale/locale.py +2 -1
  23. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/management_token/management_token.py +2 -1
  24. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/metadata/metadata.py +2 -1
  25. contentstack_management-1.7.1/contentstack_management/oauth/__init__.py +5 -0
  26. contentstack_management-1.7.1/contentstack_management/oauth/oauth_handler.py +417 -0
  27. contentstack_management-1.7.1/contentstack_management/oauth/oauth_interceptor.py +189 -0
  28. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/organizations/organization.py +7 -6
  29. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/publish_queue/publish_queue.py +2 -1
  30. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/release_items/release_item.py +2 -1
  31. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/releases/release.py +2 -1
  32. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/roles/roles.py +2 -1
  33. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/stack/stack.py +11 -10
  34. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/taxonomies/taxonomy.py +2 -1
  35. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/terms/terms.py +4 -3
  36. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/user_session/user_session.py +4 -3
  37. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/variant_group/variant_group.py +2 -1
  38. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/variants/variants.py +4 -3
  39. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/webhooks/webhook.py +9 -8
  40. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/workflows/workflows.py +14 -13
  41. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management.egg-info/PKG-INFO +2 -2
  42. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management.egg-info/SOURCES.txt +4 -0
  43. contentstack_management-1.6.0/contentstack_management/contentstack.py +0 -98
  44. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/_errors.py +0 -0
  45. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/aliases/__init__.py +0 -0
  46. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/assets/__init__.py +0 -0
  47. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/auditlogs/__init__.py +0 -0
  48. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/branches/__init__.py +0 -0
  49. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/bulk_operations/__init__.py +0 -0
  50. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/common.py +0 -0
  51. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/content_types/__init__.py +0 -0
  52. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/delivery_token/__init__.py +0 -0
  53. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/entries/__init__.py +0 -0
  54. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/entry_variants/__init__.py +0 -0
  55. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/environments/__init__.py +0 -0
  56. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/extensions/__init__.py +0 -0
  57. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/global_fields/__init__.py +0 -0
  58. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/labels/__init__.py +0 -0
  59. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/locale/__init__.py +0 -0
  60. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/management_token/__init__.py +0 -0
  61. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/metadata/__init__.py +0 -0
  62. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/organizations/__init__.py +0 -0
  63. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/publish_queue/__init__.py +0 -0
  64. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/release_items/__init__.py +0 -0
  65. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/releases/__init__.py +0 -0
  66. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/roles/__init__.py +0 -0
  67. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/stack/__init__.py +0 -0
  68. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/taxonomies/__init__.py +0 -0
  69. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/terms/__init__.py +0 -0
  70. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/user_session/__init__.py +0 -0
  71. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/users/__init__.py +0 -0
  72. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/users/user.py +0 -0
  73. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/variant_group/__init__.py +0 -0
  74. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/variants/__init__.py +0 -0
  75. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/webhooks/__init__.py +0 -0
  76. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management/workflows/__init__.py +0 -0
  77. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management.egg-info/dependency_links.txt +0 -0
  78. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management.egg-info/requires.txt +0 -0
  79. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/contentstack_management.egg-info/top_level.txt +0 -0
  80. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/setup.cfg +0 -0
  81. {contentstack_management-1.6.0 → contentstack_management-1.7.1}/setup.py +0 -0
  82. {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 - 2025 Contentstack. All rights reserved.
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.6.0
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-2025 [Contentstack](https://www.contentstack.com/). All Rights Reserved
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-2025 [Contentstack](https://www.contentstack.com/). All Rights Reserved
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
 
@@ -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.6.0'
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
- # headers.update(self.headers)
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
 
@@ -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(_request_headers().__str__())
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('alias_uid is required')
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('asset_uid is required')
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('asset_uid is required')
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('asset_uid is required')
309
+ raise Exception(ASSET_UID_REQUIRED)
309
310
  if version_number is None:
310
- raise Exception('Version Number is required')
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('asset_uid is required')
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('asset_type is required')
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('asset_uid is required')
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('asset_uid is required')
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('asset_uid is required')
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('asset_uid is required')
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('Log item Uid is required')
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('branch_uid is required field')
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('branch_uid is required field')
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("job_uid", "job_uid cannot be None")
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('content_type_uid is required')
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('Content type uid is required')
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("Delivery Token Uid is required")
195
+ raise ArgumentException(DELIVERY_TOKEN_UID_REQUIRED)