elements-api 2025.11.4__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 (159) hide show
  1. elements_api-2025.11.4/LICENSE +7 -0
  2. elements_api-2025.11.4/PKG-INFO +11 -0
  3. elements_api-2025.11.4/README.md +7 -0
  4. elements_api-2025.11.4/pyproject.toml +3 -0
  5. elements_api-2025.11.4/setup.cfg +4 -0
  6. elements_api-2025.11.4/setup.py +29 -0
  7. elements_api-2025.11.4/src/py/elements_api/__init__.py +8 -0
  8. elements_api-2025.11.4/src/py/elements_api/async_client.py +43 -0
  9. elements_api-2025.11.4/src/py/elements_api/async_interceptors.py +105 -0
  10. elements_api-2025.11.4/src/py/elements_api/elements_base_api.py +454 -0
  11. elements_api-2025.11.4/src/py/elements_api/models/__init__.py +47 -0
  12. elements_api-2025.11.4/src/py/elements_api/models/algorithm_computation_execution_pb2.py +48 -0
  13. elements_api-2025.11.4/src/py/elements_api/models/algorithm_computation_execution_pb2.pyi +84 -0
  14. elements_api-2025.11.4/src/py/elements_api/models/algorithm_computation_pb2.py +61 -0
  15. elements_api-2025.11.4/src/py/elements_api/models/algorithm_computation_pb2.pyi +134 -0
  16. elements_api-2025.11.4/src/py/elements_api/models/algorithm_config_pb2.py +90 -0
  17. elements_api-2025.11.4/src/py/elements_api/models/algorithm_config_pb2.pyi +185 -0
  18. elements_api-2025.11.4/src/py/elements_api/models/algorithm_manifest_pb2.py +43 -0
  19. elements_api-2025.11.4/src/py/elements_api/models/algorithm_manifest_pb2.pyi +25 -0
  20. elements_api-2025.11.4/src/py/elements_api/models/algorithm_pb2.py +57 -0
  21. elements_api-2025.11.4/src/py/elements_api/models/algorithm_pb2.pyi +96 -0
  22. elements_api-2025.11.4/src/py/elements_api/models/algorithm_version_pb2.py +88 -0
  23. elements_api-2025.11.4/src/py/elements_api/models/algorithm_version_pb2.pyi +233 -0
  24. elements_api-2025.11.4/src/py/elements_api/models/analysis_computation_pb2.py +69 -0
  25. elements_api-2025.11.4/src/py/elements_api/models/analysis_computation_pb2.pyi +159 -0
  26. elements_api-2025.11.4/src/py/elements_api/models/analysis_config_pb2.py +127 -0
  27. elements_api-2025.11.4/src/py/elements_api/models/analysis_config_pb2.pyi +180 -0
  28. elements_api-2025.11.4/src/py/elements_api/models/analysis_pb2.py +57 -0
  29. elements_api-2025.11.4/src/py/elements_api/models/analysis_pb2.pyi +98 -0
  30. elements_api-2025.11.4/src/py/elements_api/models/analysis_version_pb2.py +77 -0
  31. elements_api-2025.11.4/src/py/elements_api/models/analysis_version_pb2.pyi +141 -0
  32. elements_api-2025.11.4/src/py/elements_api/models/aoi_catalog_pb2.py +71 -0
  33. elements_api-2025.11.4/src/py/elements_api/models/aoi_catalog_pb2.pyi +162 -0
  34. elements_api-2025.11.4/src/py/elements_api/models/aoi_collection_pb2.py +67 -0
  35. elements_api-2025.11.4/src/py/elements_api/models/aoi_collection_pb2.pyi +146 -0
  36. elements_api-2025.11.4/src/py/elements_api/models/aoi_export_pb2.py +48 -0
  37. elements_api-2025.11.4/src/py/elements_api/models/aoi_export_pb2.pyi +62 -0
  38. elements_api-2025.11.4/src/py/elements_api/models/aoi_pb2.py +66 -0
  39. elements_api-2025.11.4/src/py/elements_api/models/aoi_pb2.pyi +191 -0
  40. elements_api-2025.11.4/src/py/elements_api/models/aoi_transaction_pb2.py +52 -0
  41. elements_api-2025.11.4/src/py/elements_api/models/aoi_transaction_pb2.pyi +56 -0
  42. elements_api-2025.11.4/src/py/elements_api/models/aoi_version_pb2.py +63 -0
  43. elements_api-2025.11.4/src/py/elements_api/models/aoi_version_pb2.pyi +171 -0
  44. elements_api-2025.11.4/src/py/elements_api/models/common_models_pb2.py +36 -0
  45. elements_api-2025.11.4/src/py/elements_api/models/common_models_pb2.pyi +15 -0
  46. elements_api-2025.11.4/src/py/elements_api/models/credit_pb2.py +79 -0
  47. elements_api-2025.11.4/src/py/elements_api/models/credit_pb2.pyi +216 -0
  48. elements_api-2025.11.4/src/py/elements_api/models/data_source_pb2.py +53 -0
  49. elements_api-2025.11.4/src/py/elements_api/models/data_source_pb2.pyi +86 -0
  50. elements_api-2025.11.4/src/py/elements_api/models/data_tracking_pb2.py +60 -0
  51. elements_api-2025.11.4/src/py/elements_api/models/data_tracking_pb2.pyi +127 -0
  52. elements_api-2025.11.4/src/py/elements_api/models/data_type_pb2.py +51 -0
  53. elements_api-2025.11.4/src/py/elements_api/models/data_type_pb2.pyi +74 -0
  54. elements_api-2025.11.4/src/py/elements_api/models/filter_pb2.py +63 -0
  55. elements_api-2025.11.4/src/py/elements_api/models/filter_pb2.pyi +133 -0
  56. elements_api-2025.11.4/src/py/elements_api/models/forgot_password_pb2.py +42 -0
  57. elements_api-2025.11.4/src/py/elements_api/models/forgot_password_pb2.pyi +19 -0
  58. elements_api-2025.11.4/src/py/elements_api/models/notification_pb2.py +82 -0
  59. elements_api-2025.11.4/src/py/elements_api/models/notification_pb2.pyi +180 -0
  60. elements_api-2025.11.4/src/py/elements_api/models/order_pb2.py +68 -0
  61. elements_api-2025.11.4/src/py/elements_api/models/order_pb2.pyi +181 -0
  62. elements_api-2025.11.4/src/py/elements_api/models/order_recommendation_pb2.py +53 -0
  63. elements_api-2025.11.4/src/py/elements_api/models/order_recommendation_pb2.pyi +99 -0
  64. elements_api-2025.11.4/src/py/elements_api/models/permission_pb2.py +84 -0
  65. elements_api-2025.11.4/src/py/elements_api/models/permission_pb2.pyi +177 -0
  66. elements_api-2025.11.4/src/py/elements_api/models/project_analysis_config_pb2.py +154 -0
  67. elements_api-2025.11.4/src/py/elements_api/models/project_analysis_config_pb2.pyi +76 -0
  68. elements_api-2025.11.4/src/py/elements_api/models/project_aoi_pb2.py +66 -0
  69. elements_api-2025.11.4/src/py/elements_api/models/project_aoi_pb2.pyi +118 -0
  70. elements_api-2025.11.4/src/py/elements_api/models/project_collaborator_pb2.py +54 -0
  71. elements_api-2025.11.4/src/py/elements_api/models/project_collaborator_pb2.pyi +74 -0
  72. elements_api-2025.11.4/src/py/elements_api/models/project_filter_pb2.py +55 -0
  73. elements_api-2025.11.4/src/py/elements_api/models/project_filter_pb2.pyi +61 -0
  74. elements_api-2025.11.4/src/py/elements_api/models/project_group_pb2.py +198 -0
  75. elements_api-2025.11.4/src/py/elements_api/models/project_group_pb2.pyi +119 -0
  76. elements_api-2025.11.4/src/py/elements_api/models/project_pb2.py +217 -0
  77. elements_api-2025.11.4/src/py/elements_api/models/project_pb2.pyi +314 -0
  78. elements_api-2025.11.4/src/py/elements_api/models/project_result_pb2.py +123 -0
  79. elements_api-2025.11.4/src/py/elements_api/models/project_result_pb2.pyi +221 -0
  80. elements_api-2025.11.4/src/py/elements_api/models/provider_quotas_pb2.py +42 -0
  81. elements_api-2025.11.4/src/py/elements_api/models/provider_quotas_pb2.pyi +33 -0
  82. elements_api-2025.11.4/src/py/elements_api/models/resource_pb2.py +64 -0
  83. elements_api-2025.11.4/src/py/elements_api/models/resource_pb2.pyi +133 -0
  84. elements_api-2025.11.4/src/py/elements_api/models/result_pb2.py +65 -0
  85. elements_api-2025.11.4/src/py/elements_api/models/result_pb2.pyi +158 -0
  86. elements_api-2025.11.4/src/py/elements_api/models/s3_pb2.py +52 -0
  87. elements_api-2025.11.4/src/py/elements_api/models/s3_pb2.pyi +49 -0
  88. elements_api-2025.11.4/src/py/elements_api/models/subject_pb2.py +36 -0
  89. elements_api-2025.11.4/src/py/elements_api/models/subject_pb2.pyi +11 -0
  90. elements_api-2025.11.4/src/py/elements_api/models/system_pb2.py +47 -0
  91. elements_api-2025.11.4/src/py/elements_api/models/system_pb2.pyi +28 -0
  92. elements_api-2025.11.4/src/py/elements_api/models/tasking_order_pb2.py +75 -0
  93. elements_api-2025.11.4/src/py/elements_api/models/tasking_order_pb2.pyi +156 -0
  94. elements_api-2025.11.4/src/py/elements_api/models/tile_pb2.py +52 -0
  95. elements_api-2025.11.4/src/py/elements_api/models/tile_pb2.pyi +77 -0
  96. elements_api-2025.11.4/src/py/elements_api/models/toi_pb2.py +67 -0
  97. elements_api-2025.11.4/src/py/elements_api/models/toi_pb2.pyi +138 -0
  98. elements_api-2025.11.4/src/py/elements_api/models/token_pb2.py +42 -0
  99. elements_api-2025.11.4/src/py/elements_api/models/token_pb2.pyi +23 -0
  100. elements_api-2025.11.4/src/py/elements_api/models/user_collection_pb2.py +78 -0
  101. elements_api-2025.11.4/src/py/elements_api/models/user_collection_pb2.pyi +172 -0
  102. elements_api-2025.11.4/src/py/elements_api/models/user_pb2.py +86 -0
  103. elements_api-2025.11.4/src/py/elements_api/models/user_pb2.pyi +183 -0
  104. elements_api-2025.11.4/src/py/elements_api/models/visualization_pb2.py +70 -0
  105. elements_api-2025.11.4/src/py/elements_api/models/visualization_pb2.pyi +187 -0
  106. elements_api-2025.11.4/src/py/elements_api/stubs/__init__.py +47 -0
  107. elements_api-2025.11.4/src/py/elements_api/stubs/algorithm_computation_execution_pb2_grpc.py +111 -0
  108. elements_api-2025.11.4/src/py/elements_api/stubs/algorithm_computation_pb2_grpc.py +251 -0
  109. elements_api-2025.11.4/src/py/elements_api/stubs/algorithm_config_pb2_grpc.py +377 -0
  110. elements_api-2025.11.4/src/py/elements_api/stubs/algorithm_manifest_pb2_grpc.py +99 -0
  111. elements_api-2025.11.4/src/py/elements_api/stubs/algorithm_pb2_grpc.py +239 -0
  112. elements_api-2025.11.4/src/py/elements_api/stubs/algorithm_version_pb2_grpc.py +336 -0
  113. elements_api-2025.11.4/src/py/elements_api/stubs/analysis_computation_pb2_grpc.py +239 -0
  114. elements_api-2025.11.4/src/py/elements_api/stubs/analysis_config_pb2_grpc.py +283 -0
  115. elements_api-2025.11.4/src/py/elements_api/stubs/analysis_pb2_grpc.py +192 -0
  116. elements_api-2025.11.4/src/py/elements_api/stubs/analysis_version_pb2_grpc.py +198 -0
  117. elements_api-2025.11.4/src/py/elements_api/stubs/aoi_catalog_pb2_grpc.py +273 -0
  118. elements_api-2025.11.4/src/py/elements_api/stubs/aoi_collection_pb2_grpc.py +327 -0
  119. elements_api-2025.11.4/src/py/elements_api/stubs/aoi_export_pb2_grpc.py +144 -0
  120. elements_api-2025.11.4/src/py/elements_api/stubs/aoi_pb2_grpc.py +241 -0
  121. elements_api-2025.11.4/src/py/elements_api/stubs/aoi_transaction_pb2_grpc.py +145 -0
  122. elements_api-2025.11.4/src/py/elements_api/stubs/aoi_version_pb2_grpc.py +192 -0
  123. elements_api-2025.11.4/src/py/elements_api/stubs/common_models_pb2_grpc.py +24 -0
  124. elements_api-2025.11.4/src/py/elements_api/stubs/credit_pb2_grpc.py +418 -0
  125. elements_api-2025.11.4/src/py/elements_api/stubs/data_source_pb2_grpc.py +144 -0
  126. elements_api-2025.11.4/src/py/elements_api/stubs/data_tracking_pb2_grpc.py +140 -0
  127. elements_api-2025.11.4/src/py/elements_api/stubs/data_type_pb2_grpc.py +189 -0
  128. elements_api-2025.11.4/src/py/elements_api/stubs/filter_pb2_grpc.py +234 -0
  129. elements_api-2025.11.4/src/py/elements_api/stubs/forgot_password_pb2_grpc.py +100 -0
  130. elements_api-2025.11.4/src/py/elements_api/stubs/notification_pb2_grpc.py +414 -0
  131. elements_api-2025.11.4/src/py/elements_api/stubs/order_pb2_grpc.py +279 -0
  132. elements_api-2025.11.4/src/py/elements_api/stubs/order_recommendation_pb2_grpc.py +183 -0
  133. elements_api-2025.11.4/src/py/elements_api/stubs/permission_pb2_grpc.py +282 -0
  134. elements_api-2025.11.4/src/py/elements_api/stubs/project_analysis_config_pb2_grpc.py +99 -0
  135. elements_api-2025.11.4/src/py/elements_api/stubs/project_aoi_pb2_grpc.py +279 -0
  136. elements_api-2025.11.4/src/py/elements_api/stubs/project_collaborator_pb2_grpc.py +189 -0
  137. elements_api-2025.11.4/src/py/elements_api/stubs/project_filter_pb2_grpc.py +144 -0
  138. elements_api-2025.11.4/src/py/elements_api/stubs/project_group_pb2_grpc.py +234 -0
  139. elements_api-2025.11.4/src/py/elements_api/stubs/project_pb2_grpc.py +504 -0
  140. elements_api-2025.11.4/src/py/elements_api/stubs/project_result_pb2_grpc.py +234 -0
  141. elements_api-2025.11.4/src/py/elements_api/stubs/provider_quotas_pb2_grpc.py +99 -0
  142. elements_api-2025.11.4/src/py/elements_api/stubs/resource_pb2_grpc.py +228 -0
  143. elements_api-2025.11.4/src/py/elements_api/stubs/result_pb2_grpc.py +99 -0
  144. elements_api-2025.11.4/src/py/elements_api/stubs/s3_pb2_grpc.py +99 -0
  145. elements_api-2025.11.4/src/py/elements_api/stubs/subject_pb2_grpc.py +24 -0
  146. elements_api-2025.11.4/src/py/elements_api/stubs/system_pb2_grpc.py +142 -0
  147. elements_api-2025.11.4/src/py/elements_api/stubs/tasking_order_pb2_grpc.py +279 -0
  148. elements_api-2025.11.4/src/py/elements_api/stubs/tile_pb2_grpc.py +99 -0
  149. elements_api-2025.11.4/src/py/elements_api/stubs/toi_pb2_grpc.py +281 -0
  150. elements_api-2025.11.4/src/py/elements_api/stubs/token_pb2_grpc.py +100 -0
  151. elements_api-2025.11.4/src/py/elements_api/stubs/user_collection_pb2_grpc.py +370 -0
  152. elements_api-2025.11.4/src/py/elements_api/stubs/user_pb2_grpc.py +559 -0
  153. elements_api-2025.11.4/src/py/elements_api/stubs/visualization_pb2_grpc.py +234 -0
  154. elements_api-2025.11.4/src/py/elements_api/sync_client.py +34 -0
  155. elements_api-2025.11.4/src/py/elements_api.egg-info/PKG-INFO +11 -0
  156. elements_api-2025.11.4/src/py/elements_api.egg-info/SOURCES.txt +157 -0
  157. elements_api-2025.11.4/src/py/elements_api.egg-info/dependency_links.txt +1 -0
  158. elements_api-2025.11.4/src/py/elements_api.egg-info/requires.txt +3 -0
  159. elements_api-2025.11.4/src/py/elements_api.egg-info/top_level.txt +1 -0
@@ -0,0 +1,7 @@
1
+ Copyright 2022 Orbital Insight, Inc.
2
+
3
+ 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:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,11 @@
1
+ Metadata-Version: 2.4
2
+ Name: elements-api
3
+ Version: 2025.11.4
4
+ Summary: Elements API Client
5
+ License-File: LICENSE
6
+ Requires-Dist: grpcio
7
+ Requires-Dist: grpcio-status
8
+ Requires-Dist: requests
9
+ Dynamic: license-file
10
+ Dynamic: requires-dist
11
+ Dynamic: summary
@@ -0,0 +1,7 @@
1
+ # Orbital Insight Platform API (OI Papi)
2
+
3
+ PAPI (Platform API) is the gateway to Orbital Insight's Terra platform. PAPI provides a single, unified API for developers to build geospatial analytics solutions tailed to their specific domains. PAPI provides both gRPC and HTTP Rest endpoints.
4
+
5
+ For more information about PAPI endpoints please refer to the [API documentation](https://sphinx.core3.orbitalinsight.io/oi_papi/latest/api-docs/).
6
+
7
+ If you are developer of PAPI itself, please refer to the [Local Setup and Testing](docs/local_setup_and_testing.md) document to get started.
@@ -0,0 +1,3 @@
1
+ [build-system]
2
+ requires = ["setuptools>=42", "wheel", "PyYAML>=6.0"]
3
+ build-backend = "setuptools.build_meta"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,29 @@
1
+ """
2
+ A setuptools based setup module.
3
+ Based on:
4
+ https://packaging.python.org/en/latest/distributing.html
5
+ https://github.com/pypa/sampleproject
6
+ """
7
+
8
+ import yaml
9
+ import setuptools # type: ignore
10
+
11
+
12
+ def get_version(version_file_loc):
13
+ with open(version_file_loc, 'r') as stream:
14
+ data = yaml.load(stream, yaml.SafeLoader)
15
+ return (data.get('version'))
16
+
17
+
18
+ setuptools.setup(
19
+ name='elements-api',
20
+ version=get_version('ops/conda-recipe/conda_build_config.yaml'),
21
+ description='Elements API Client',
22
+ package_dir={'': 'src/py'},
23
+ packages=setuptools.find_packages('src/py', exclude=[
24
+ 'mocked_services',
25
+ 'oi_papi',
26
+ 'oi_papi.*'
27
+ ]),
28
+ install_requires=['grpcio', 'grpcio-status', 'requests']
29
+ )
@@ -0,0 +1,8 @@
1
+ # Copyright 2017-2021 Orbital Insight Inc., all rights reserved.
2
+ # Contains confidential and trade secret information.
3
+ # Government Users: Commercial Computer Software - Use governed by
4
+ # terms of Orbital Insight commercial license agreement.
5
+
6
+
7
+ from elements_api.sync_client import ElementsSyncClient # noqa
8
+ from elements_api.async_client import ElementsAsyncClient # noqa
@@ -0,0 +1,43 @@
1
+ # Copyright 2017-2021 Orbital Insight Inc., all rights reserved.
2
+ # Contains confidential and trade secret information.
3
+ # Government Users: Commercial Computer Software - Use governed by
4
+ # terms of Orbital Insight commercial license agreement.
5
+
6
+
7
+ import grpc
8
+ from elements_api.elements_base_api import ElementsBaseApi
9
+ from elements_api import models
10
+ from elements_api.async_interceptors import (
11
+ ContextInjectorUnaryUnary, ContextInjectorStreamUnary, ContextInjectorUnaryStream, ContextInjectorStreamStream
12
+ )
13
+
14
+
15
+ class ElementsAsyncClient:
16
+ def __init__(self, oi_papi_url, port=443, secure=True, api_token=None, root_certificates_file=None):
17
+ self.api = ElementsAsyncApi(oi_papi_url, port, secure, api_token=api_token,
18
+ root_certificates_file=root_certificates_file)
19
+ self.models = models
20
+
21
+
22
+ class ElementsAsyncApi(ElementsBaseApi):
23
+ def __init__(self, oi_papi_url, port=443, secure=True, api_token=None, root_certificates_file=None):
24
+ self.context_injecting_interceptors = [
25
+ # materialize distributed context from request headers
26
+ ContextInjectorUnaryUnary(), ContextInjectorUnaryStream(),
27
+ ContextInjectorStreamUnary(), ContextInjectorStreamStream()
28
+ ]
29
+ super(ElementsAsyncApi, self).__init__(oi_papi_url, port, secure, api_token,
30
+ root_certificates_file)
31
+
32
+ def _get_channel(self, channel_credentials: grpc.ChannelCredentials):
33
+ token_creds = grpc.access_token_call_credentials(self.access_token)
34
+ creds = grpc.composite_channel_credentials(channel_credentials, token_creds)
35
+ return grpc.aio.secure_channel(f"{self._oi_papi_url}:{self._port}",
36
+ creds,
37
+ options=self.options,
38
+ compression=None,
39
+ interceptors=self.context_injecting_interceptors)
40
+
41
+ # TODO: async destructor
42
+ def __adel__(self):
43
+ self._channel.close()
@@ -0,0 +1,105 @@
1
+ import importlib
2
+ from typing import Callable, Union
3
+
4
+ import grpc
5
+ from grpc.aio import \
6
+ UnaryUnaryClientInterceptor, \
7
+ UnaryStreamClientInterceptor, \
8
+ StreamUnaryClientInterceptor, \
9
+ StreamStreamClientInterceptor, \
10
+ ClientCallDetails
11
+ from grpc.aio._call import \
12
+ UnaryUnaryCall, \
13
+ UnaryStreamCall, \
14
+ StreamUnaryCall, \
15
+ StreamStreamCall
16
+
17
+ from grpc.aio._typing import RequestType, ResponseIterableType, ResponseType, \
18
+ RequestIterableType
19
+
20
+ if importlib.util.find_spec('oi_tracing'):
21
+ from oi_tracing import inject_current_span
22
+ else:
23
+ def inject_current_span(_headers):
24
+ pass
25
+
26
+ if importlib.util.find_spec('mercury_client'):
27
+ from mercury_client.context import context_as_dict
28
+ else:
29
+ def context_as_dict():
30
+ return {}
31
+
32
+
33
+ def inject_current_context_into_header(client_call_details: ClientCallDetails)\
34
+ -> ClientCallDetails:
35
+
36
+ if client_call_details.metadata is None: # grpc.aio._metadata.Metadata
37
+ client_call_details.metadata = grpc.aio._metadata.Metadata()
38
+
39
+ new_headers = context_as_dict()
40
+ # Remove any Auth headers and let this be controlled by Papi's token setting
41
+ for key in ('Authorization', 'X-Orbitalinsight-Auth-Token'):
42
+ new_headers.pop(key, None)
43
+
44
+ inject_current_span(new_headers)
45
+ # inject_current_auth(new_headers)
46
+
47
+ for k, v in new_headers.items():
48
+ client_call_details.metadata.add(k, str(v))
49
+ # todo not clear if it is safe to modify or a new copy needs to be made
50
+ # at this point returning modified original object
51
+ return client_call_details
52
+
53
+
54
+ class ContextInjectorUnaryUnary(UnaryUnaryClientInterceptor):
55
+
56
+ async def intercept_unary_unary(
57
+ self, continuation: Callable[[ClientCallDetails, RequestType],
58
+ UnaryUnaryCall],
59
+ client_call_details: ClientCallDetails,
60
+ request: RequestType) \
61
+ -> Union[UnaryUnaryCall, ResponseType]:
62
+ new_details = inject_current_context_into_header(client_call_details)
63
+ response = continuation(new_details, request)
64
+ return await response
65
+
66
+
67
+ class ContextInjectorUnaryStream(UnaryStreamClientInterceptor):
68
+
69
+ async def intercept_unary_stream(
70
+ self,
71
+ continuation: Callable[[ClientCallDetails, RequestType],
72
+ UnaryStreamCall],
73
+ client_call_details: ClientCallDetails,
74
+ request: RequestType) \
75
+ -> Union[ResponseIterableType, UnaryStreamCall]:
76
+ new_details = inject_current_context_into_header(client_call_details)
77
+ response = continuation(new_details, request)
78
+ return await response
79
+
80
+
81
+ class ContextInjectorStreamUnary(StreamUnaryClientInterceptor):
82
+
83
+ async def intercept_stream_unary(
84
+ self,
85
+ continuation: Callable[[ClientCallDetails, RequestType],
86
+ StreamUnaryCall],
87
+ client_call_details: ClientCallDetails,
88
+ request_iterator: RequestIterableType,
89
+ ) -> StreamUnaryCall:
90
+ new_details = inject_current_context_into_header(client_call_details)
91
+ response = continuation(new_details, request_iterator)
92
+ return await response
93
+
94
+
95
+ class ContextInjectorStreamStream(StreamStreamClientInterceptor):
96
+ async def intercept_stream_stream(
97
+ self,
98
+ continuation: Callable[[ClientCallDetails, RequestType],
99
+ StreamStreamCall],
100
+ client_call_details: ClientCallDetails,
101
+ request_iterator: RequestIterableType,
102
+ ) -> Union[ResponseIterableType, StreamStreamCall]:
103
+ new_details = inject_current_context_into_header(client_call_details)
104
+ response = continuation(new_details, request_iterator)
105
+ return await response
@@ -0,0 +1,454 @@
1
+ # Copyright 2017-2021 Orbital Insight Inc., all rights reserved.
2
+ # Contains confidential and trade secret information.
3
+ # Government Users: Commercial Computer Software - Use governed by
4
+ # terms of Orbital Insight commercial license agreement.
5
+
6
+ import os
7
+ from abc import ABC, abstractmethod
8
+ import json
9
+ import time
10
+ import base64
11
+ from typing import Optional
12
+ from functools import lru_cache, wraps
13
+ import requests
14
+
15
+ import grpc
16
+
17
+
18
+ API_TOKEN = 'TERRASCOPE_API_TOKEN'
19
+
20
+
21
+ @lru_cache(maxsize=128)
22
+ def json_from_jwt(jwt_token: str) -> dict[str, object]:
23
+ # see also similar function in user svc client
24
+ # returns claims dict
25
+ try:
26
+ [_header, payload, _signature] = jwt_token.split('.')
27
+ payload_text = base64.b64decode(payload + "===", '-_')
28
+ payload_json = json.loads(payload_text)
29
+ return payload_json
30
+ except BaseException:
31
+ return {}
32
+
33
+
34
+ def is_token_expiring(jwt_token_str, within_secs=300) -> bool:
35
+ claims = json_from_jwt(jwt_token_str)
36
+ return claims.get('exp') < time.time() + within_secs
37
+
38
+
39
+ def is_offline_token(jwt_token_str) -> bool:
40
+ claims = json_from_jwt(jwt_token_str)
41
+ return claims.get('typ') == 'Offline'
42
+
43
+
44
+ def check_access_token(func) -> bool:
45
+ # decorator
46
+ @wraps(func)
47
+ def wrapper(*args, **kwargs): # The instance is passed as the first argument
48
+ self = args[0]
49
+ # check if current access token is expired
50
+ self.refresh_access_token_and_recreate_stubs()
51
+ result = func(self)
52
+ return result
53
+ return wrapper
54
+
55
+
56
+ class ElementsBaseApi(ABC):
57
+
58
+ def __init__(self, oi_papi_url: str, port: int = 443, secure=True, api_token: str = None, root_certificates_file=None):
59
+ self._oi_papi_url = oi_papi_url
60
+ self._port = port
61
+ self._secure = secure
62
+ self._root_certificates_file = root_certificates_file
63
+ self._channel = None
64
+ self.api_token: Optional[str] = os.environ.get(API_TOKEN, "").strip() if api_token is None else api_token.strip()
65
+
66
+ # check if this is an offline token, which is a refresh token, and obtain access tokens from it periodically
67
+ self.is_offline_token: bool = is_offline_token(self.api_token)
68
+ self.access_token: Optional[str] = self.api_token if not is_offline_token else None
69
+ self.access_token_expiration_threshold = int(os.getenv('ACCESS_TOKEN_EXPIRATION_THRESHOLD', '300'))
70
+ # (token will be renewed when it has less than this threshhold before expiry)
71
+ self.options = [('grpc.max_send_message_length', -1),
72
+ ('grpc.max_receive_message_length', -1),
73
+ ('grpc.max_metadata_size', 16000)]
74
+ service_config_json: str = json.dumps({
75
+ "methodConfig": [{
76
+ "name": [{}], # Apply retry to all methods by using [{}]
77
+ "retryPolicy": {
78
+ "maxAttempts": 5,
79
+ "initialBackoff": "1.0s",
80
+ "maxBackoff": "60s",
81
+ "backoffMultiplier": 2,
82
+ "retryableStatusCodes": ["UNAVAILABLE"],
83
+ },
84
+ }]
85
+ })
86
+ self.options.append(("grpc.service_config", service_config_json))
87
+
88
+ self.refresh_access_token()
89
+ self.new_channel()
90
+ self.recreate_stubs()
91
+
92
+ def recreate_stubs(self):
93
+ from elements_api import stubs
94
+ # unauthenticated users can access forgot_password
95
+ self._forgot_password = stubs.forgot_password.ForgotPasswordApiStub(self._channel)
96
+
97
+ if self.api_token:
98
+ self._algorithm = stubs.algorithm.AlgorithmApiStub(self._channel)
99
+ self._algorithm_version = stubs.algorithm_version.AlgorithmVersionApiStub(self._channel)
100
+ self._algorithm_config = stubs.algorithm_config.AlgorithmConfigApiStub(self._channel)
101
+ self._aoi = stubs.aoi.AOIApiStub(self._channel)
102
+ self._aoi_collection = stubs.aoi_collection.AOICollectionApiStub(self._channel)
103
+ self._aoi_version = stubs.aoi_version.AOIVersionApiStub(self._channel)
104
+ self._aoi_catalog = stubs.aoi_catalog.AOICatalogApiStub(self._channel)
105
+ self._aoi_transaction = stubs.aoi_transaction.AOITransactionApiStub(self._channel)
106
+ self._aoi_export = stubs.aoi_export.AOIExportApiStub(self._channel)
107
+ self._algorithm_computation = stubs.algorithm_computation.AlgorithmComputationApiStub(self._channel)
108
+ self._algorithm_computation_execution = \
109
+ stubs.algorithm_computation_execution.AlgorithmComputationExecutionApiStub(self._channel)
110
+ self._analysis = stubs.analysis.AnalysisApiStub(self._channel)
111
+ self._analysis_version = stubs.analysis_version.AnalysisVersionApiStub(self._channel)
112
+ self._credit = stubs.credit.CreditApiStub(self._channel)
113
+ self._result = stubs.result.ResultApiStub(self._channel)
114
+ self._analysis_config = stubs.analysis_config.AnalysisConfigApiStub(self._channel)
115
+ self._analysis_computation = stubs.analysis_computation.AnalysisComputationApiStub(self._channel)
116
+ self._system = stubs.system.SystemApiStub(self._channel)
117
+ self._toi = stubs.toi.TOIApiStub(self._channel)
118
+ self._permission = stubs.permission.PermissionApiStub(self._channel)
119
+ self._user = stubs.user.UserApiStub(self._channel)
120
+ self._user_collection = stubs.user_collection.UserCollectionApiStub(self._channel)
121
+ self._token = stubs.token.TokenApiStub(self._channel)
122
+ self._visualization = stubs.visualization.VisualizationApiStub(self._channel)
123
+ self._tile = stubs.tile.TileApiStub(self._channel)
124
+ self._notification = stubs.notification.NotificationApiStub(self._channel)
125
+ self._data_source = stubs.data_source.DataSourceAPIStub(self._channel)
126
+ self._data_type = stubs.data_type.DataTypeAPIStub(self._channel)
127
+ self._data_tracking = stubs.data_tracking.DataTrackingApiStub(self._channel)
128
+ self._project = stubs.project.ProjectApiStub(self._channel)
129
+ self._project_analysis_config = stubs.project_analysis_config.ProjectAnalysisConfigApiStub(self._channel)
130
+ self._project_collaborator = stubs.project_collaborator.ProjectCollaboratorApiStub(self._channel)
131
+ self._project_group = stubs.project_group.ProjectGroupApiStub(self._channel)
132
+ self._project_result = stubs.project_result.ProjectResultApiStub(self._channel)
133
+ self._project_filter = stubs.project_filter.ProjectFilterApiStub(self._channel)
134
+ self._filter = stubs.filter.FilterApiStub(self._channel)
135
+ self._project_aoi = stubs.project_aoi.ProjectAOIApiStub(self._channel)
136
+ self._order = stubs.order.OrderApiStub(self._channel)
137
+ self._order_recommendation = stubs.order_recommendation.OrderRecommendationApiStub(self._channel)
138
+ self._tasking_order = stubs.tasking_order.TaskingOrderApiStub(self._channel)
139
+ self._provider_quotas = stubs.provider_quotas.ProviderQuotasApiStub(self._channel)
140
+ self._resource = stubs.resource.ResourceApiStub(self._channel)
141
+ self._s3 = stubs.s3.S3ApiStub(self._channel)
142
+
143
+ def new_channel(self):
144
+ # if self._channel is not None:
145
+ # self._channel.close() # todo this is async, and channel.__del__ takes care of it
146
+ self._channel = self._get_channel(
147
+ self._get_ssl_channel_credentials(
148
+ self._secure,
149
+ root_certificates_file=self._root_certificates_file))
150
+ return self._channel
151
+
152
+ def refresh_access_token(self) -> bool:
153
+ """
154
+ Refreshes the access token if it is expired.
155
+ returns True if the access token was refreshed, False otherwise.
156
+ """
157
+ # check if current access token is expired
158
+ if self.api_token and not is_offline_token(self.api_token):
159
+ return False # in which case it is an access token, and it was not refreshed
160
+ # check expiration on the access_token
161
+
162
+ if self.access_token and not is_token_expiring(
163
+ self.access_token,
164
+ within_secs=self.access_token_expiration_threshold
165
+ ):
166
+ return False
167
+
168
+ oidc_base_url = os.getenv(
169
+ 'OIDC_BASE_URL',
170
+ 'https://keycloak.orbitalinsight.com')
171
+ oidc_realm = os.getenv('OIDC_REALM', 'elements')
172
+ oidc_token_endpoint = os.getenv(
173
+ 'OIDC_TOKEN_ENDPOINT',
174
+ f'{oidc_base_url}/realms/{oidc_realm}/protocol/openid-connect/token')
175
+
176
+ # note: api token is an offline token, which is a special refresh token.
177
+ oidc_client_id = os.getenv('OIDC_CLIENT_ID', 'login-ui')
178
+ oidc_client_secret = os.getenv('OIDC_CLIENT_SECRET')
179
+
180
+ request_json = {
181
+ 'refresh_token': self.api_token,
182
+ 'client_id': oidc_client_id,
183
+ 'grant_type': 'refresh_token',
184
+ 'scope': 'openid offline_access'
185
+ }
186
+ if oidc_client_secret:
187
+ request_json['client_secret'] = oidc_client_secret
188
+
189
+ response = requests.post(
190
+ oidc_token_endpoint,
191
+ data=request_json,
192
+ headers={'Content-Type': 'application/x-www-form-urlencoded'})
193
+ if not response.ok:
194
+ raise Exception(f"Failed to refresh token: {response.text}")
195
+
196
+ self.access_token = response.json().get('access_token')
197
+ return True
198
+
199
+ def refresh_access_token_and_recreate_stubs(self) -> bool:
200
+ # check if current access token is expired
201
+ if not self.refresh_access_token():
202
+ return False
203
+ self.new_channel()
204
+ self.recreate_stubs()
205
+ return True
206
+ # individual api accessors are properties, since underlying channel may need to change
207
+ # at any time due to its token change. Do not store property result in a variable,
208
+ # as it will not be refreshed automatically when the token changes
209
+ # token freshness is checked in every call, then if it expired and changed,
210
+ # (all) stubs are regenerated. A new channel is created with that token in each stub
211
+
212
+ @property
213
+ @check_access_token
214
+ def forgot_password(self):
215
+ return self._forgot_password
216
+
217
+ @property
218
+ @check_access_token
219
+ def algorithm(self):
220
+ return self._algorithm
221
+
222
+ @property
223
+ @check_access_token
224
+ def algorithm_version(self):
225
+ return self._algorithm_version
226
+
227
+ @property
228
+ @check_access_token
229
+ def algorithm_config(self):
230
+ return self._algorithm_config
231
+
232
+ @property
233
+ @check_access_token
234
+ def aoi(self):
235
+ return self._aoi
236
+
237
+ @property
238
+ @check_access_token
239
+ def aoi_collection(self):
240
+ return self._aoi_collection
241
+
242
+ @property
243
+ @check_access_token
244
+ def aoi_version(self):
245
+ return self._aoi_version
246
+
247
+ @property
248
+ @check_access_token
249
+ def aoi_catalog(self):
250
+ return self._aoi_catalog
251
+
252
+ @property
253
+ @check_access_token
254
+ def aoi_transaction(self):
255
+ return self._aoi_transaction
256
+
257
+ @property
258
+ @check_access_token
259
+ def aoi_export(self):
260
+ return self._aoi_export
261
+
262
+ @property
263
+ @check_access_token
264
+ def algorithm_computation(self):
265
+ return self._algorithm_computation
266
+
267
+ @property
268
+ @check_access_token
269
+ def algorithm_computation_execution(self):
270
+ return self._algorithm_computation_execution
271
+
272
+ @property
273
+ @check_access_token
274
+ def analysis(self):
275
+ return self._analysis
276
+
277
+ @property
278
+ @check_access_token
279
+ def analysis_version(self):
280
+ return self._analysis_version
281
+
282
+ @property
283
+ @check_access_token
284
+ def credit(self):
285
+ return self._credit
286
+
287
+ @property
288
+ @check_access_token
289
+ def result(self):
290
+ return self._result
291
+
292
+ @property
293
+ @check_access_token
294
+ def analysis_config(self):
295
+ return self._analysis_config
296
+
297
+ @property
298
+ @check_access_token
299
+ def analysis_computation(self):
300
+ return self._analysis_computation
301
+
302
+ @property
303
+ @check_access_token
304
+ def system(self):
305
+ return self._system
306
+
307
+ @property
308
+ @check_access_token
309
+ def toi(self):
310
+ return self._toi
311
+
312
+ @property
313
+ @check_access_token
314
+ def permission(self):
315
+ return self._permission
316
+
317
+ @property
318
+ @check_access_token
319
+ def user(self):
320
+ return self._user
321
+
322
+ @property
323
+ @check_access_token
324
+ def user_collection(self):
325
+ return self._user_collection
326
+
327
+ @property
328
+ @check_access_token
329
+ def token(self):
330
+ return self._token
331
+
332
+ @property
333
+ @check_access_token
334
+ def visualization(self):
335
+ return self._visualization
336
+
337
+ @property
338
+ @check_access_token
339
+ def tile(self):
340
+ return self._tile
341
+
342
+ @property
343
+ @check_access_token
344
+ def notification(self):
345
+ return self._notification
346
+
347
+ @property
348
+ @check_access_token
349
+ def data_source(self):
350
+ return self._data_source
351
+
352
+ @property
353
+ @check_access_token
354
+ def data_type(self):
355
+ return self._data_type
356
+
357
+ @property
358
+ @check_access_token
359
+ def data_tracking(self):
360
+ return self._data_tracking
361
+
362
+ @property
363
+ @check_access_token
364
+ def project(self):
365
+ return self._project
366
+
367
+ @property
368
+ @check_access_token
369
+ def project_analysis_config(self):
370
+ return self._project_analysis_config
371
+
372
+ @property
373
+ @check_access_token
374
+ def project_collaborator(self):
375
+ return self._project_collaborator
376
+
377
+ @property
378
+ @check_access_token
379
+ def project_group(self):
380
+ return self._project_group
381
+
382
+ @property
383
+ @check_access_token
384
+ def project_result(self):
385
+ return self._project_result
386
+
387
+ @property
388
+ @check_access_token
389
+ def project_filter(self):
390
+ return self._project_filter
391
+
392
+ @property
393
+ @check_access_token
394
+ def filter(self):
395
+ return self._filter
396
+
397
+ @property
398
+ @check_access_token
399
+ def project_aoi(self):
400
+ return self._project_aoi
401
+
402
+ @property
403
+ @check_access_token
404
+ def order(self):
405
+ return self._order
406
+
407
+ @property
408
+ @check_access_token
409
+ def order_recommendation(self):
410
+ return self._order_recommendation
411
+
412
+ @property
413
+ @check_access_token
414
+ def tasking_order(self):
415
+ return self._tasking_order
416
+
417
+ @property
418
+ @check_access_token
419
+ def provider_quotas(self):
420
+ return self._provider_quotas
421
+
422
+ @property
423
+ @check_access_token
424
+ def provider(self):
425
+ return self._provider
426
+
427
+ @property
428
+ @check_access_token
429
+ def resource(self):
430
+ return self._resource
431
+
432
+ @property
433
+ @check_access_token
434
+ def s3(self):
435
+ return self._s3
436
+
437
+ def _get_ssl_channel_credentials(self, secure, root_certificates_file=None):
438
+ # note: with all defaults, gRPC will search for cert as described here:
439
+ # https://github.com/grpc/grpc/blob/7a63bd5407d5e14b30f19a5aaf4b6cd1b80f00e1/include/grpc/grpc_security.h#L287
440
+ # for local env, use local_channel_credentials:
441
+ # https://grpc.github.io/grpc/python/grpc.html#grpc.local_channel_credentials
442
+ if secure:
443
+ root_certificates = None
444
+ if root_certificates_file:
445
+ with open(root_certificates_file, 'rb') as file:
446
+ root_certificates = file.read()
447
+
448
+ return grpc.ssl_channel_credentials(root_certificates=root_certificates,
449
+ private_key=None, certificate_chain=None)
450
+ return grpc.local_channel_credentials()
451
+
452
+ @abstractmethod
453
+ def _get_channel(self, channel_credentials: grpc.ChannelCredentials):
454
+ ...