clear-skies-cortex 2.0.3__py3-none-any.whl → 2.0.4__py3-none-any.whl

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: clear-skies-cortex
3
- Version: 2.0.3
3
+ Version: 2.0.4
4
4
  Summary: Cortex module for Clearskies
5
5
  Project-URL: Docs, https://https://clearskies.info/modules/clear-skies-cortex
6
6
  Project-URL: Repository, https://github.com/clearskies-py/cortex
@@ -0,0 +1,28 @@
1
+ clearskies_cortex/__init__.py,sha256=SwFkNfKLJgcq2qE6YJ_78_JfeoRGojlKuR_3DymZZSQ,142
2
+ clearskies_cortex/dataclasses.py,sha256=UQP-Myo9SaGHOXMaNv85k0Z_Xvm_nmycpeLwkZqIvho,5289
3
+ clearskies_cortex/backends/__init__.py,sha256=Ek74kpJLE7ERY-6yhH2KwFO25gm3Yjz51laUApnhgnU,179
4
+ clearskies_cortex/backends/cortex_backend.py,sha256=Q2xzusGA_iXXxeSxd8v1iSxxlUWKtGuhwnywuMu_7Jk,8162
5
+ clearskies_cortex/backends/cortex_team_relationship_backend.py,sha256=UiMZp0jcKvpx3tLbR8aMBS8-KIfECxJGiwM3fz8DmuM,7648
6
+ clearskies_cortex/columns/__init__.py,sha256=BpoVCEVXRtKcsyRFnAYLtlwYtHBjciUbzuzaBxmfH00,87
7
+ clearskies_cortex/columns/string_list.py,sha256=Ww9Bl1tuZlKhLbapYjjp_fy5RB4RQSquyh9tuC5PK4k,2104
8
+ clearskies_cortex/defaults/__init__.py,sha256=tulZSvFgp4YUKj_bnArIevmlpC8z_AQKO0okYs4hCDo,216
9
+ clearskies_cortex/defaults/default_cortex_auth.py,sha256=sWlWgXIoCXk0FkwK3Kz33TVohsaVpIOS9qj2Ef5_YN8,2135
10
+ clearskies_cortex/defaults/default_cortex_url.py,sha256=sKBDQVPJWc4ozBquZ7fKryTbUOXYfrH6hUcQuUtMXEU,1576
11
+ clearskies_cortex/models/__init__.py,sha256=zckJ4-KOjIcmtN7h7lXOESUu3d6JBsZZJq-s2MJ0NLQ,1145
12
+ clearskies_cortex/models/cortex_catalog_entity.py,sha256=nWKApnvcr3pmSijEJgZ4cuZgG9ck6d8ey3AyVb8vfXQ,6632
13
+ clearskies_cortex/models/cortex_catalog_entity_domain.py,sha256=AOa07Fq-SWc8zkg6g-fBi5x1GnGg42GhPM2GozrgC2c,2327
14
+ clearskies_cortex/models/cortex_catalog_entity_group.py,sha256=D5Je_kX4dBW8jkhmZBca02mFYT3cqK1walleMXTlcdg,1295
15
+ clearskies_cortex/models/cortex_catalog_entity_scorecard.py,sha256=XT08_865AzsiMGsybIhQPQ8jTR1HrCfAtRAME7MdDo0,2372
16
+ clearskies_cortex/models/cortex_catalog_entity_service.py,sha256=OhX4nBRPpHxrA77B7HYNNKO265xBWxQN0x5pm2oHtKI,4003
17
+ clearskies_cortex/models/cortex_catalog_entity_team.py,sha256=1Uk4ja9B5Pn-X2xxzs-R8LzfL20bsppRyeE2MRiDERw,2326
18
+ clearskies_cortex/models/cortex_catalog_entity_types.py,sha256=kmpHEpL2ydUnyb_V2aY2rGdaOYTZqG0KcwSDLDA_X3Q,1611
19
+ clearskies_cortex/models/cortex_entity_relationships.py,sha256=Zfv1DLf_vWsPELqPYaZcCMYh3K0qjIy8DpxNZfhMyHg,1815
20
+ clearskies_cortex/models/cortex_model.py,sha256=U4hRboM49duUpDqvTfJ-pSFRHZzZyiGOM1aniq-iWK8,778
21
+ clearskies_cortex/models/cortex_scorecard.py,sha256=aiM8_MkdMT_n-a6teG8y6zWSLmHuASW7IyXB-QKqU-Y,1712
22
+ clearskies_cortex/models/cortex_team.py,sha256=UUS1KHufPqhO29h4eKIfeZzUz75KjwkqaW5ItmdAKS4,4356
23
+ clearskies_cortex/models/cortex_team_category_tree.py,sha256=PRCIGeZrBoGiH1a7Q8ZsYYzSfj4cZF9tIRcBWEKAX60,1898
24
+ clearskies_cortex/models/cortex_team_department.py,sha256=_0Go97ZFr_GCd_ol0iBw7Px5F_gE94WtAWFGnzQ9y-M,1582
25
+ clear_skies_cortex-2.0.4.dist-info/METADATA,sha256=rhPhCyHqZqZ7yks_PDAaZX1aJ-MBL0kaG_oYh-NS_Bo,2117
26
+ clear_skies_cortex-2.0.4.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
27
+ clear_skies_cortex-2.0.4.dist-info/licenses/LICENSE,sha256=MkEX8JF8kZxdyBpTTcB0YTd-xZpWnHvbRlw-pQh8u58,1069
28
+ clear_skies_cortex-2.0.4.dist-info/RECORD,,
@@ -2,29 +2,113 @@ from typing import Any
2
2
 
3
3
  import clearskies
4
4
  import requests
5
- from clearskies import Column, configs
5
+ from clearskies import configs
6
6
  from clearskies.authentication import Authentication
7
7
  from clearskies.decorators import parameters_to_properties
8
8
  from clearskies.di import inject
9
9
  from clearskies.query import Query
10
- from clearskies.query.result import CountQueryResult
11
10
 
12
11
 
13
12
  class CortexBackend(clearskies.backends.ApiBackend):
14
- """Backend for Cortex.io."""
13
+ """
14
+ Backend for interacting with the Cortex.io API.
15
15
 
16
+ This backend extends the ApiBackend to provide seamless integration with the Cortex.io platform.
17
+ It handles the specific pagination and response format used by Cortex APIs, where pagination
18
+ information (`page`, `totalPages`, `total`) is returned in the response body rather than headers.
19
+
20
+ ## Usage
21
+
22
+ The CortexBackend is typically used with models that represent Cortex entities:
23
+
24
+ ```python
25
+ import clearskies
26
+ from clearskies_cortex.backends import CortexBackend
27
+
28
+
29
+ class CortexService(clearskies.Model):
30
+ backend = CortexBackend()
31
+
32
+ @classmethod
33
+ def destination_name(cls) -> str:
34
+ return "catalog/services"
35
+
36
+ tag = clearskies.columns.String()
37
+ name = clearskies.columns.String()
38
+ description = clearskies.columns.String()
39
+ ```
40
+
41
+ ## Authentication
42
+
43
+ By default, the backend uses the `cortex_auth` binding for authentication, which should be
44
+ configured in your application's dependency injection container. You can also provide a custom
45
+ authentication instance:
46
+
47
+ ```python
48
+ backend = CortexBackend(
49
+ authentication=clearskies.authentication.SecretBearer(
50
+ environment_key="CORTEX_API_KEY",
51
+ )
52
+ )
53
+ ```
54
+
55
+ ## Pagination
56
+
57
+ The Cortex API uses page-based pagination with the following response format:
58
+
59
+ ```json
60
+ {
61
+ "entities": [...],
62
+ "page": 1,
63
+ "totalPages": 5,
64
+ "total": 100
65
+ }
66
+ ```
67
+
68
+ The backend automatically handles extracting pagination data and provides the next page
69
+ information to clearskies for seamless iteration through results.
70
+ """
71
+
72
+ """
73
+ The base URL for the Cortex API.
74
+ """
16
75
  base_url = configs.String(default="https://api.getcortexapp.com/api/v1/")
76
+
77
+ """
78
+ The authentication instance to use for API requests.
79
+
80
+ By default, this uses the `cortex_auth` binding from the dependency injection container.
81
+ """
17
82
  authentication = inject.ByName("cortex_auth") # type: ignore[assignment]
83
+
84
+ """
85
+ The requests instance for making HTTP calls.
86
+ """
18
87
  requests = inject.Requests()
88
+
89
+ """
90
+ The casing style used by the Cortex API (camelCase by default).
91
+ """
19
92
  api_casing = configs.Select(["snake_case", "camelCase", "TitleCase"], default="camelCase")
20
93
 
21
94
  _auth_headers: dict[str, str] = {}
22
95
 
96
+ """
97
+ A mapping from API response keys to model column names.
98
+ """
23
99
  api_to_model_map = configs.AnyDict(default={})
100
+
101
+ """
102
+ The name of the pagination parameter used in requests.
103
+ """
24
104
  pagination_parameter_name = configs.String(default="page")
105
+
106
+ """
107
+ The name of the limit parameter used in requests.
108
+ """
25
109
  limit_parameter_name = configs.String(default="pageSize")
26
110
 
27
- can_count = True
111
+ can_count = False
28
112
 
29
113
  @parameters_to_properties
30
114
  def __init__(
@@ -40,22 +124,29 @@ class CortexBackend(clearskies.backends.ApiBackend):
40
124
  ):
41
125
  self.finalize_and_validate_configuration()
42
126
 
43
- def count(self, query: Query) -> CountQueryResult:
44
- """Return count of records matching query."""
45
- self.check_query(query)
46
- (url, method, body, headers) = self.build_records_request(query)
47
- response = self.execute_request(url, method, json=body, headers=headers)
48
- response.raise_for_status()
49
- data = response.json()
50
- if "total" in data:
51
- return CountQueryResult(count=data["total"])
52
- data = self.map_records_response(data, query)
53
- return CountQueryResult(count=len(data))
54
-
55
127
  def map_records_response(
56
128
  self, response_data: Any, query: Query, query_data: dict[str, Any] | None = None
57
129
  ) -> list[dict[str, Any]]:
58
- """Map api response to model fields."""
130
+ """
131
+ Map the Cortex API response to model fields.
132
+
133
+ The Cortex API returns responses in a specific format where the actual records are nested
134
+ within a dictionary alongside pagination metadata. This method extracts the records and
135
+ removes the pagination fields before passing to the parent implementation.
136
+
137
+ Example Cortex API response:
138
+
139
+ ```json
140
+ {
141
+ "entities": [{"tag": "service-1", "name": "My Service"}, ...],
142
+ "page": 1,
143
+ "totalPages": 5,
144
+ "total": 100
145
+ }
146
+ ```
147
+
148
+ This method will extract the `entities` list and pass it to the parent for further processing.
149
+ """
59
150
  if isinstance(response_data, dict):
60
151
  if "page" in response_data:
61
152
  del response_data["page"]
@@ -68,34 +159,78 @@ class CortexBackend(clearskies.backends.ApiBackend):
68
159
  return super().map_records_response(response_data[first_item], query, query_data)
69
160
  return super().map_records_response(response_data, query, query_data)
70
161
 
71
- def set_next_page_data_from_response(
162
+ def get_next_page_data_from_response(
72
163
  self,
73
- next_page_data: dict[str, Any],
74
164
  query: Query,
75
165
  response: "requests.Response", # type: ignore
76
- ) -> None:
166
+ ) -> dict[str, Any]:
77
167
  """
78
- Update the next_page_data dictionary with the appropriate data needed to fetch the next page of records.
168
+ Extract pagination data from the Cortex API response.
79
169
 
80
- This method has a very important job, which is to inform clearskies about how to make another API call to fetch the next
81
- page of records. The way this happens is by updating the `next_page_data` dictionary in place with whatever pagination
82
- information is necessary. Note that this relies on next_page_data being passed by reference, hence the need to update
83
- it in place. That means that you can do this:
170
+ The Cortex API includes pagination information in the response body:
84
171
 
85
- ```python
86
- next_page_data["some_key"] = "some_value"
87
- ```
88
-
89
- but if you do this:
172
+ - `page`: The current page number
173
+ - `totalPages`: The total number of pages available
174
+ - `total`: The total number of records
90
175
 
91
- ```python
92
- next_page_data = {"some_key": "some_value"}
93
- ```
176
+ This method checks if there are more pages available and returns the next page number
177
+ if so. It also extracts total count information for use in RecordsQueryResult.
178
+ The returned dictionary is used by clearskies to fetch subsequent pages and
179
+ populate count metadata.
94
180
 
95
- Then things simply won't work.
181
+ Returns:
182
+ A dictionary containing:
183
+ - The next page number if more pages exist
184
+ - total_count: The total number of records (if available)
185
+ - total_pages: The total number of pages (if available)
96
186
  """
97
- if isinstance(response.json(), dict):
98
- page = response.json().get("page", None)
99
- total_pages = response.json().get("totalPages", None)
100
- if page is not None and total_pages is not None and page < total_pages:
187
+ next_page_data: dict[str, Any] = {}
188
+
189
+ response_data = response.json() if response.content else {}
190
+
191
+ if isinstance(response_data, dict):
192
+ # Extract count information from response body
193
+ count_info = self.extract_count_from_response(None, response_data)
194
+ if count_info:
195
+ total_count, total_pages = count_info
196
+ if total_count is not None:
197
+ next_page_data["total_count"] = total_count
198
+ if total_pages is not None:
199
+ next_page_data["total_pages"] = total_pages
200
+
201
+ # Check if there are more pages
202
+ page = response_data.get("page", None)
203
+ total_pages_from_response = response_data.get("totalPages", None)
204
+ if page is not None and total_pages_from_response is not None and page < total_pages_from_response:
101
205
  next_page_data[self.pagination_parameter_name] = page + 1
206
+
207
+ return next_page_data
208
+
209
+ def extract_count_from_response(
210
+ self,
211
+ response_headers: dict[str, str] | None = None,
212
+ response_data: Any = None,
213
+ ) -> tuple[int | None, int | None]:
214
+ """
215
+ Extract count information from the Cortex API response body.
216
+
217
+ Unlike many APIs that return count information in headers, the Cortex API includes
218
+ this data in the response body:
219
+
220
+ - `total`: The total number of records matching the query
221
+ - `totalPages`: The total number of pages available
222
+
223
+ This method extracts these values and returns them as a tuple for use in
224
+ `RecordsQueryResult`.
225
+
226
+ Returns:
227
+ A tuple of (total_count, total_pages) where either value may be None
228
+ if not present in the response.
229
+ """
230
+ if not isinstance(response_data, dict):
231
+ return (None, None)
232
+
233
+ total_count = response_data.get("total", None)
234
+ total_pages = response_data.get("totalPages", None)
235
+
236
+ return (total_count, total_pages)
@@ -4,20 +4,64 @@ from clearskies.columns import String
4
4
 
5
5
 
6
6
  class StringList(String):
7
- """Column type for comma delimited string."""
7
+ """
8
+ Column type for comma-delimited string lists.
9
+
10
+ This column type extends the String column to handle values that are stored as
11
+ comma-delimited strings but should be represented as Python lists. It automatically
12
+ converts between the two formats when reading from and writing to the backend.
13
+
14
+ Use this column type when the API returns or expects comma-separated values that
15
+ you want to work with as lists in your application code.
16
+
17
+ ```python
18
+ from clearskies import Model
19
+ from clearskies_cortex.columns import StringList
20
+
21
+
22
+ class MyModel(Model):
23
+ # Define a column that stores ["tag1", "tag2"] as "tag1,tag2"
24
+ tags = StringList()
25
+
26
+
27
+ # When reading from the backend:
28
+ # API returns: {"tags": "tag1,tag2,tag3"}
29
+ # Model provides: model.tags = ["tag1", "tag2", "tag3"]
30
+
31
+ # When writing to the backend:
32
+ # Model has: model.tags = ["tag1", "tag2", "tag3"]
33
+ # API receives: {"tags": "tag1,tag2,tag3"}
34
+ ```
35
+ """
8
36
 
9
37
  def from_backend(self, value: str | list[str]) -> list[str]:
10
- """Return comma delimited string to list."""
38
+ """
39
+ Convert backend value to a Python list.
40
+
41
+ Handles both string (comma-delimited) and list inputs for flexibility.
42
+
43
+ Args:
44
+ value: Either a comma-delimited string or a list of strings.
45
+
46
+ Returns:
47
+ A list of strings.
48
+ """
11
49
  if isinstance(value, list):
12
50
  return value
13
51
  return value.split(",")
14
52
 
15
53
  def to_backend(self, data: dict[str, Any]) -> dict[str, Any]:
16
54
  """
17
- Make any changes needed to save the data to the backend.
55
+ Convert Python list to comma-delimited string for backend storage.
56
+
57
+ Transforms the list value back into a comma-separated string format
58
+ expected by the backend API.
59
+
60
+ Args:
61
+ data: Dictionary containing the column data.
18
62
 
19
- This typically means formatting changes - converting DateTime objects to database
20
- date strings, etc...
63
+ Returns:
64
+ Dictionary with the column value converted to a comma-delimited string.
21
65
  """
22
66
  if self.name not in data:
23
67
  return data
@@ -4,70 +4,240 @@ from typing import Any
4
4
 
5
5
  @dataclass
6
6
  class ServiceEntityHierarchy:
7
- """Dataclass for parent in service hierarchy."""
7
+ """
8
+ Dataclass representing the hierarchy structure of a service entity.
8
9
 
10
+ This dataclass is used to parse the hierarchy JSON data from Cortex catalog entities.
11
+ It contains both parent and child relationships for navigating the entity hierarchy.
12
+
13
+ ```python
14
+ from clearskies_cortex.models import CortexCatalogEntity
15
+
16
+ entity = CortexCatalogEntity().find("tag=my-service")
17
+ hierarchy = entity.parse_hierarchy()
18
+
19
+ # Access parents
20
+ for parent in hierarchy.parents:
21
+ print(f"Parent: {parent.name} ({parent.type})")
22
+
23
+ # Access children
24
+ for child in hierarchy.children:
25
+ print(f"Child: {child.name} ({child.type})")
26
+ ```
27
+ """
28
+
29
+ """
30
+ List of parent entities in the hierarchy (from immediate parent to root).
31
+ """
9
32
  parents: list["ServiceEntityHierarchyParent"]
33
+
34
+ """
35
+ List of child entities in the hierarchy.
36
+ """
10
37
  children: list["ServiceEntityHierarchyChild"]
11
38
 
12
39
 
13
40
  @dataclass
14
41
  class ServiceEntityHierarchyParent:
15
- """Dataclass for parent in hierarchy."""
42
+ """
43
+ Dataclass representing a parent entity in the hierarchy.
44
+
45
+ Contains information about a parent entity including its tag, type, name,
46
+ and recursively its own parents for traversing up the hierarchy tree.
47
+ """
16
48
 
49
+ """
50
+ The unique tag identifier of the parent entity.
51
+ """
17
52
  tag: str
53
+
54
+ """
55
+ The type of the parent entity (e.g., "domain", "team").
56
+ """
18
57
  type: str
58
+
59
+ """
60
+ The human-readable name of the parent entity.
61
+ """
19
62
  name: str
63
+
64
+ """
65
+ Optional description of the parent entity.
66
+ """
20
67
  description: str | None
68
+
69
+ """
70
+ Optional definition data for the parent entity.
71
+ """
21
72
  definition: None | dict[str, Any]
73
+
74
+ """
75
+ List of grandparent entities (recursive parent structure).
76
+ """
22
77
  parents: list["ServiceEntityHierarchyParent"]
78
+
79
+ """
80
+ Optional list of groups the parent entity belongs to.
81
+ """
23
82
  groups: list[str] | None
24
83
 
25
84
 
26
85
  @dataclass
27
86
  class ServiceEntityHierarchyChild:
28
- """Dataclass for child in hierarchy."""
87
+ """
88
+ Dataclass representing a child entity in the hierarchy.
29
89
 
90
+ Contains information about a child entity including its tag, type, name,
91
+ and recursively its own children for traversing down the hierarchy tree.
92
+ """
93
+
94
+ """
95
+ The unique tag identifier of the child entity.
96
+ """
30
97
  tag: str
98
+
99
+ """
100
+ The type of the child entity (e.g., "service", "domain").
101
+ """
31
102
  type: str
103
+
104
+ """
105
+ The human-readable name of the child entity.
106
+ """
32
107
  name: str
108
+
109
+ """
110
+ Optional description of the child entity.
111
+ """
33
112
  description: str | None
113
+
114
+ """
115
+ Optional definition data for the child entity.
116
+ """
34
117
  definition: None | dict[str, Any]
118
+
119
+ """
120
+ List of grandchild entities (recursive child structure).
121
+ """
35
122
  children: list["ServiceEntityHierarchyChild"]
123
+
124
+ """
125
+ Optional list of groups the child entity belongs to.
126
+ """
36
127
  groups: list[str] | None
37
128
 
38
129
 
39
130
  @dataclass
40
131
  class TeamCategory:
41
- """Dataclass for Team Category Tree."""
132
+ """
133
+ Dataclass representing a team category in the category tree.
134
+
135
+ Used for organizing teams into hierarchical categories with parent-child relationships.
136
+ """
42
137
 
138
+ """
139
+ The name of the category.
140
+ """
43
141
  name: str
142
+
143
+ """
144
+ The depth level in the category hierarchy (0 for root).
145
+ """
44
146
  level: int
147
+
148
+ """
149
+ The name of the parent category.
150
+ """
45
151
  parent_name: str
46
152
 
47
153
 
48
154
  @dataclass
49
155
  class EntityTeam:
50
- """Dataclass for team in Catalog Entity."""
156
+ """
157
+ Dataclass representing a team associated with a catalog entity.
158
+
159
+ Contains team information including ownership inheritance and provider details.
160
+ """
51
161
 
162
+ """
163
+ Optional description of the team.
164
+ """
52
165
  description: str | None
166
+
167
+ """
168
+ The inheritance type for ownership (e.g., "direct", "inherited").
169
+ """
53
170
  inheritance: str
171
+
172
+ """
173
+ Whether the team is archived.
174
+ """
54
175
  isArchived: bool # noqa: N815
176
+
177
+ """
178
+ The human-readable name of the team.
179
+ """
55
180
  name: str
181
+
182
+ """
183
+ The provider of the team data (e.g., "cortex", "external").
184
+ """
56
185
  provider: str
186
+
187
+ """
188
+ The unique tag identifier of the team.
189
+ """
57
190
  tag: str
58
191
 
59
192
 
60
193
  @dataclass
61
194
  class EntityIndividual:
62
- """Dataclass for individual in Catalog Entity."""
195
+ """
196
+ Dataclass representing an individual owner of a catalog entity.
197
+
198
+ Contains information about individual (non-team) owners.
199
+ """
63
200
 
201
+ """
202
+ Optional description of the individual.
203
+ """
64
204
  description: str | None
205
+
206
+ """
207
+ The email address of the individual.
208
+ """
65
209
  email: str
66
210
 
67
211
 
68
212
  @dataclass
69
213
  class EntityTeamOwner:
70
- """Dataclass for team owners in Catalog Entity."""
214
+ """
215
+ Dataclass representing the ownership information for a catalog entity.
216
+
217
+ Contains both team and individual owners. Used by `CortexCatalogEntity.parse_owners()`.
218
+
219
+ ```python
220
+ from clearskies_cortex.models import CortexCatalogEntity
71
221
 
222
+ entity = CortexCatalogEntity().find("tag=my-service")
223
+ owners = entity.parse_owners()
224
+
225
+ # Access team owners
226
+ for team in owners.teams:
227
+ print(f"Team owner: {team.name} ({team.tag})")
228
+
229
+ # Access individual owners
230
+ for individual in owners.individuals:
231
+ print(f"Individual owner: {individual.email}")
232
+ ```
233
+ """
234
+
235
+ """
236
+ List of teams that own the entity.
237
+ """
72
238
  teams: list[EntityTeam]
239
+
240
+ """
241
+ List of individuals that own the entity.
242
+ """
73
243
  individuals: list[EntityIndividual]
@@ -2,7 +2,53 @@ import clearskies
2
2
 
3
3
 
4
4
  class DefaultCortexAuth(clearskies.di.AdditionalConfigAutoImport):
5
+ """
6
+ Default authentication provider for Cortex API.
7
+
8
+ This class provides automatic configuration for Cortex API authentication using
9
+ the clearskies dependency injection system. It is auto-imported when the clearskies_cortex
10
+ package is used, making authentication configuration seamless.
11
+
12
+ The authentication can be configured in two ways:
13
+
14
+ 1. **Secret Path (recommended for production)**: Set the `CORTEX_AUTH_SECRET_PATH` environment
15
+ variable to point to a secret manager path containing the API key.
16
+
17
+ 2. **Direct Environment Key**: Set the `CORTEX_AUTH_KEY` environment variable directly
18
+ with the Cortex API key.
19
+
20
+ ```python
21
+ import clearskies
22
+ from clearskies_cortex.models import CortexTeam
23
+
24
+ # The authentication is automatically configured via environment variables
25
+ # Option 1: Using secret path
26
+ # export CORTEX_AUTH_SECRET_PATH=/path/to/secret
27
+
28
+ # Option 2: Using direct key
29
+ # export CORTEX_AUTH_KEY=your-api-key
30
+
31
+ # Then use models normally - auth is handled automatically
32
+ teams = CortexTeam()
33
+ for team in teams:
34
+ print(team.get_name())
35
+ ```
36
+ """
37
+
5
38
  def provide_cortex_auth(self, environment: clearskies.Environment):
39
+ """
40
+ Provide the Cortex authentication configuration.
41
+
42
+ Checks for `CORTEX_AUTH_SECRET_PATH` first, falling back to `CORTEX_AUTH_KEY`
43
+ if not set. Returns a SecretBearer authentication instance configured for
44
+ the Cortex API.
45
+
46
+ Args:
47
+ environment: The clearskies Environment instance for accessing env variables.
48
+
49
+ Returns:
50
+ A SecretBearer authentication instance configured for Cortex API.
51
+ """
6
52
  if environment.get("CORTEX_AUTH_SECRET_PATH", True):
7
53
  secret_key = environment.get("CORTEX_AUTH_SECRET_PATH")
8
54
  return clearskies.authentication.SecretBearer(secret_key=secret_key, header_prefix="Bearer ")