clear-skies-cortex 2.0.1__tar.gz → 2.0.3__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 (49) hide show
  1. {clear_skies_cortex-2.0.1 → clear_skies_cortex-2.0.3}/.copier-answers.yml +1 -1
  2. {clear_skies_cortex-2.0.1 → clear_skies_cortex-2.0.3}/.github/workflows/create-version.yaml +1 -1
  3. {clear_skies_cortex-2.0.1 → clear_skies_cortex-2.0.3}/.github/workflows/run-tests.yml +2 -1
  4. {clear_skies_cortex-2.0.1 → clear_skies_cortex-2.0.3}/.github/workflows/tests-matrix.yaml +6 -19
  5. {clear_skies_cortex-2.0.1 → clear_skies_cortex-2.0.3}/.pre-commit-config.yaml +1 -7
  6. clear_skies_cortex-2.0.3/.vscode/settings.json +21 -0
  7. {clear_skies_cortex-2.0.1 → clear_skies_cortex-2.0.3}/CHANGELOG.md +22 -0
  8. clear_skies_cortex-2.0.3/LATEST_CHANGELOG.md +6 -0
  9. {clear_skies_cortex-2.0.1 → clear_skies_cortex-2.0.3}/PKG-INFO +2 -2
  10. {clear_skies_cortex-2.0.1 → clear_skies_cortex-2.0.3}/pyproject.toml +3 -17
  11. clear_skies_cortex-2.0.3/src/clearskies_cortex/backends/cortex_backend.py +101 -0
  12. {clear_skies_cortex-2.0.1 → clear_skies_cortex-2.0.3}/src/clearskies_cortex/backends/cortex_team_relationship_backend.py +28 -30
  13. {clear_skies_cortex-2.0.1 → clear_skies_cortex-2.0.3}/src/clearskies_cortex/dataclasses.py +2 -2
  14. {clear_skies_cortex-2.0.1 → clear_skies_cortex-2.0.3}/src/clearskies_cortex/models/__init__.py +2 -0
  15. {clear_skies_cortex-2.0.1 → clear_skies_cortex-2.0.3}/src/clearskies_cortex/models/cortex_catalog_entity.py +31 -3
  16. clear_skies_cortex-2.0.3/src/clearskies_cortex/models/cortex_catalog_entity_domain.py +38 -0
  17. clear_skies_cortex-2.0.3/src/clearskies_cortex/models/cortex_catalog_entity_service.py +76 -0
  18. clear_skies_cortex-2.0.3/src/clearskies_cortex/models/cortex_catalog_entity_team.py +38 -0
  19. {clear_skies_cortex-2.0.1 → clear_skies_cortex-2.0.3}/src/clearskies_cortex/models/cortex_team.py +10 -3
  20. clear_skies_cortex-2.0.3/uv.lock +983 -0
  21. clear_skies_cortex-2.0.1/LATEST_CHANGELOG.md +0 -16
  22. clear_skies_cortex-2.0.1/src/clearskies_cortex/backends/cortex_backend.py +0 -45
  23. clear_skies_cortex-2.0.1/src/clearskies_cortex/models/cortex_catalog_entity_domain.py +0 -25
  24. clear_skies_cortex-2.0.1/src/clearskies_cortex/models/cortex_catalog_entity_service.py +0 -92
  25. clear_skies_cortex-2.0.1/uv.lock +0 -956
  26. {clear_skies_cortex-2.0.1 → clear_skies_cortex-2.0.3}/.editorconfig +0 -0
  27. {clear_skies_cortex-2.0.1 → clear_skies_cortex-2.0.3}/.github/workflows/docs.yaml +0 -0
  28. {clear_skies_cortex-2.0.1 → clear_skies_cortex-2.0.3}/.github/workflows/tests.yaml +0 -0
  29. {clear_skies_cortex-2.0.1 → clear_skies_cortex-2.0.3}/.gitignore +0 -0
  30. {clear_skies_cortex-2.0.1 → clear_skies_cortex-2.0.3}/.python-version +0 -0
  31. {clear_skies_cortex-2.0.1 → clear_skies_cortex-2.0.3}/LICENSE +0 -0
  32. {clear_skies_cortex-2.0.1 → clear_skies_cortex-2.0.3}/README.md +0 -0
  33. {clear_skies_cortex-2.0.1 → clear_skies_cortex-2.0.3}/cliff.toml +0 -0
  34. {clear_skies_cortex-2.0.1 → clear_skies_cortex-2.0.3}/ruff.toml +0 -0
  35. {clear_skies_cortex-2.0.1 → clear_skies_cortex-2.0.3}/src/clearskies_cortex/__init__.py +0 -0
  36. {clear_skies_cortex-2.0.1 → clear_skies_cortex-2.0.3}/src/clearskies_cortex/backends/__init__.py +0 -0
  37. {clear_skies_cortex-2.0.1 → clear_skies_cortex-2.0.3}/src/clearskies_cortex/columns/__init__.py +0 -0
  38. {clear_skies_cortex-2.0.1 → clear_skies_cortex-2.0.3}/src/clearskies_cortex/columns/string_list.py +0 -0
  39. {clear_skies_cortex-2.0.1 → clear_skies_cortex-2.0.3}/src/clearskies_cortex/defaults/__init__.py +0 -0
  40. {clear_skies_cortex-2.0.1 → clear_skies_cortex-2.0.3}/src/clearskies_cortex/defaults/default_cortex_auth.py +0 -0
  41. {clear_skies_cortex-2.0.1 → clear_skies_cortex-2.0.3}/src/clearskies_cortex/defaults/default_cortex_url.py +0 -0
  42. {clear_skies_cortex-2.0.1 → clear_skies_cortex-2.0.3}/src/clearskies_cortex/models/cortex_catalog_entity_group.py +0 -0
  43. {clear_skies_cortex-2.0.1 → clear_skies_cortex-2.0.3}/src/clearskies_cortex/models/cortex_catalog_entity_scorecard.py +0 -0
  44. {clear_skies_cortex-2.0.1 → clear_skies_cortex-2.0.3}/src/clearskies_cortex/models/cortex_catalog_entity_types.py +0 -0
  45. {clear_skies_cortex-2.0.1 → clear_skies_cortex-2.0.3}/src/clearskies_cortex/models/cortex_entity_relationships.py +0 -0
  46. {clear_skies_cortex-2.0.1 → clear_skies_cortex-2.0.3}/src/clearskies_cortex/models/cortex_model.py +0 -0
  47. {clear_skies_cortex-2.0.1 → clear_skies_cortex-2.0.3}/src/clearskies_cortex/models/cortex_scorecard.py +0 -0
  48. {clear_skies_cortex-2.0.1 → clear_skies_cortex-2.0.3}/src/clearskies_cortex/models/cortex_team_category_tree.py +0 -0
  49. {clear_skies_cortex-2.0.1 → clear_skies_cortex-2.0.3}/src/clearskies_cortex/models/cortex_team_department.py +0 -0
@@ -1,5 +1,5 @@
1
1
  # Changes here will be overwritten by Copier; NEVER EDIT MANUALLY
2
- _commit: v0.0.35
2
+ _commit: v0.0.45
3
3
  _src_path: gh:clearskies-py/clearskies-module-template
4
4
  author_email: tom.nijboer@cimpress.com
5
5
  author_name: Tom Nijboer
@@ -79,7 +79,7 @@ jobs:
79
79
  # STEP 2: Commit all file changes together
80
80
  - name: Commit All Changes
81
81
  run: |
82
- git add pyproject.toml CHANGELOG.md
82
+ git add pyproject.toml CHANGELOG.md uv.lock
83
83
  git commit -m "chore(release): bump version to ${{ steps.bump.outputs.tag }}"
84
84
 
85
85
  # STEP 3: Create the tag for the final commit
@@ -67,7 +67,7 @@ jobs:
67
67
  python-version: ${{ matrix.python-version }}
68
68
  run-mypy: ${{ needs.changes.outputs.tests == 'true' }}
69
69
  run-pytest: ${{ needs.changes.outputs.tests == 'true' }}
70
- run-black: ${{ needs.changes.outputs.tests == 'true' }}
70
+ run-ruff-format: ${{ needs.changes.outputs.tests == 'true' }}
71
71
  run-ruff-check: ${{ needs.changes.outputs.tests == 'true' }}
72
72
  secrets: inherit
73
73
  strategy:
@@ -76,6 +76,7 @@ jobs:
76
76
  - '3.11'
77
77
  - '3.12'
78
78
  - '3.13'
79
+ - '3.14'
79
80
  fail-fast: false
80
81
 
81
82
  status:
@@ -14,7 +14,7 @@ on:
14
14
  run-pytest:
15
15
  required: true
16
16
  type: boolean
17
- run-black:
17
+ run-ruff-format:
18
18
  required: true
19
19
  type: boolean
20
20
  run-ruff-check:
@@ -66,27 +66,14 @@ jobs:
66
66
  - run: uv run pytest -v
67
67
  - run: git diff --exit-code --stat HEAD
68
68
 
69
- black:
70
- name: black
69
+ ruff-format:
70
+ name: ruff-format
71
71
  runs-on: ${{ inputs.runner }}
72
- if: inputs.run-black
72
+ if: inputs.run-ruff-format
73
73
  steps:
74
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
75
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
76
- with:
77
- persist-credentials: false
78
-
79
- - name: Install uv and set the python version
80
- uses: astral-sh/setup-uv@v6
81
- with:
82
- python-version: ${{ inputs.python-version }}
83
- enable-cache: true
84
- - name: Install dependencies
85
- run: uv sync --locked --all-extras --dev
86
- - uses: psf/black@stable
74
+ - uses: astral-sh/ruff-action@v3
87
75
  with:
88
- options: "--check --verbose"
89
- src: "./src"
76
+ args: "format --diff"
90
77
 
91
78
  ruff-check:
92
79
  name: ruff-check
@@ -38,12 +38,6 @@ repos:
38
38
  hooks:
39
39
  - id: uv-lock
40
40
 
41
- - repo: https://github.com/psf/black
42
- rev: 25.1.0
43
- hooks:
44
- - id: black
45
- files: \.py$
46
-
47
41
  - repo: https://github.com/astral-sh/ruff-pre-commit
48
42
  rev: v0.12.1
49
43
  hooks:
@@ -55,7 +49,7 @@ repos:
55
49
  rev: v1.16.1
56
50
  hooks:
57
51
  - id: mypy
58
- additional_dependencies: [types-requests, clear-skies]
52
+ additional_dependencies: [types-requests]
59
53
  files: \.py$
60
54
 
61
55
  - repo: https://github.com/adrienverge/yamllint
@@ -0,0 +1,21 @@
1
+ {
2
+ "python.testing.pytestArgs": ["tests"],
3
+ "python.testing.unittestEnabled": false,
4
+ "python.testing.pytestEnabled": true,
5
+ "python.defaultInterpreterPath": "${workspaceFolder}/.venv/bin/python",
6
+ "ruff.interpreter": ["${workspaceFolder}/.venv/bin/python"],
7
+ "ruff.path": ["${workspaceFolder}/.venv/bin/ruff"],
8
+ "ruff.configuration": "${workspaceFolder}/ruff.toml",
9
+ "ruff.nativeServer": "on",
10
+ "[python]": {
11
+ "editor.formatOnSave": true,
12
+ "editor.codeActionsOnSave": {
13
+ "source.fixAll": "explicit",
14
+ "source.organizeImports": "explicit"
15
+ },
16
+ "editor.defaultFormatter": "charliermarsh.ruff"
17
+ },
18
+ "mypy-type-checker.args": [
19
+ "--config-file=${workspaceFolder}/pyproject.toml"
20
+ ]
21
+ }
@@ -5,12 +5,31 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [2.0.3] - 2026-01-27
9
+
10
+ ### Changed
11
+ - Udpate to QueryResult
12
+ - Update to latest copier version
13
+
14
+ ## [2.0.2] - 2026-01-06
15
+
16
+ ### Added
17
+ - Add teams
18
+
19
+ ### Changed
20
+ - Bump version to v2.0.2 by @github-actions[bot]
21
+ - Make it all work with the api
22
+
23
+ ### Fixed
24
+ - Use the built-in functions of clearskies now
25
+
8
26
  ## [2.0.1] - 2025-11-27
9
27
 
10
28
  ### Added
11
29
  - Add classes
12
30
 
13
31
  ### Changed
32
+ - Bump version to v2.0.1 by @github-actions[bot]
14
33
  - Merge pull request #1 from clearskies-py/di-fix by @tnijboer in [#1](https://github.com/clearskies-py/cortex/pull/1)
15
34
  - Initial project setup
16
35
 
@@ -18,8 +37,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
18
37
  - Pass dependencies and build the tree by @cmancone
19
38
 
20
39
  ## New Contributors
40
+ * @github-actions[bot] made their first contribution
21
41
  * @tnijboer made their first contribution in [#1](https://github.com/clearskies-py/cortex/pull/1)
22
42
  * @cmancone made their first contribution
23
43
  * @ made their first contribution
44
+ [2.0.3]: https://github.com/clearskies-py/cortex/compare/v2.0.2..v2.0.3
45
+ [2.0.2]: https://github.com/clearskies-py/cortex/compare/v2.0.1..v2.0.2
24
46
 
25
47
  <!-- generated by git-cliff -->
@@ -0,0 +1,6 @@
1
+ ## [2.0.3] - 2026-01-27
2
+
3
+ ### Changed
4
+ - Udpate to QueryResult
5
+ - Update to latest copier version
6
+
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: clear-skies-cortex
3
- Version: 2.0.1
3
+ Version: 2.0.3
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
@@ -14,7 +14,7 @@ Classifier: Intended Audience :: Developers
14
14
  Classifier: License :: OSI Approved :: MIT License
15
15
  Classifier: Programming Language :: Python :: 3
16
16
  Requires-Python: <4.0,>=3.11
17
- Requires-Dist: clear-skies<3.0.0,>=2.0.0
17
+ Requires-Dist: clear-skies<3.0.0,>=2.0.37
18
18
  Requires-Dist: dacite>=1.9.2
19
19
  Provides-Extra: dev
20
20
  Requires-Dist: types-requests>=2.32.4; extra == 'dev'
@@ -1,13 +1,13 @@
1
1
  [project]
2
2
  name = "clear-skies-cortex"
3
3
  description = "Cortex module for Clearskies"
4
- version = "2.0.1"
4
+ version = "2.0.3"
5
5
  license = "MIT"
6
6
  readme = "./README.md"
7
7
  authors = [{name = "Tom Nijboer", email = "tom.nijboer@cimpress.com"}]
8
8
  requires-python = ">=3.11,<4.0"
9
9
  dependencies = [
10
- "clear-skies>=2.0.0,<3.0.0",
10
+ "clear-skies>=2.0.37,<3.0.0",
11
11
  "dacite>=1.9.2",
12
12
  ]
13
13
  classifiers = [
@@ -44,19 +44,6 @@ exclude = [
44
44
  "tests/**"
45
45
  ]
46
46
 
47
- [tool.black]
48
- line-length = 120
49
- skip-magic-trailing-comma = false
50
- preview = false
51
-
52
- [tool.mypy]
53
- python_version = "3.11"
54
-
55
- exclude = [
56
- ".*_test\\.py$",
57
- "docs/.*"
58
- ]
59
-
60
47
  [tool.pytest.ini_options]
61
48
  minversion = "6.0"
62
49
  addopts = "-ra -q"
@@ -65,14 +52,13 @@ testpaths = [
65
52
  "integration",
66
53
  ]
67
54
  pythonpath = [
68
- "app"
55
+ "src"
69
56
  ]
70
57
 
71
58
  # This is the dedicated section for uv"s configuration
72
59
  [dependency-groups]
73
60
  dev = [
74
61
  "akeyless>=5.0.8",
75
- "black>=25.1.0",
76
62
  "mypy>=1.18.2",
77
63
  "pre-commit>=4.3.0",
78
64
  "pytest>=8.4.1",
@@ -0,0 +1,101 @@
1
+ from typing import Any
2
+
3
+ import clearskies
4
+ import requests
5
+ from clearskies import Column, configs
6
+ from clearskies.authentication import Authentication
7
+ from clearskies.decorators import parameters_to_properties
8
+ from clearskies.di import inject
9
+ from clearskies.query import Query
10
+ from clearskies.query.result import CountQueryResult
11
+
12
+
13
+ class CortexBackend(clearskies.backends.ApiBackend):
14
+ """Backend for Cortex.io."""
15
+
16
+ base_url = configs.String(default="https://api.getcortexapp.com/api/v1/")
17
+ authentication = inject.ByName("cortex_auth") # type: ignore[assignment]
18
+ requests = inject.Requests()
19
+ api_casing = configs.Select(["snake_case", "camelCase", "TitleCase"], default="camelCase")
20
+
21
+ _auth_headers: dict[str, str] = {}
22
+
23
+ api_to_model_map = configs.AnyDict(default={})
24
+ pagination_parameter_name = configs.String(default="page")
25
+ limit_parameter_name = configs.String(default="pageSize")
26
+
27
+ can_count = True
28
+
29
+ @parameters_to_properties
30
+ def __init__(
31
+ self,
32
+ base_url: str | None = "https://api.getcortexapp.com/api/v1/",
33
+ authentication: Authentication | None = None,
34
+ model_casing: str = "snake_case",
35
+ api_casing: str = "camelCase",
36
+ api_to_model_map: dict[str, str | list[str]] = {},
37
+ pagination_parameter_name: str = "page",
38
+ pagination_parameter_type: str = "int",
39
+ limit_parameter_name: str = "pageSize",
40
+ ):
41
+ self.finalize_and_validate_configuration()
42
+
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
+ def map_records_response(
56
+ self, response_data: Any, query: Query, query_data: dict[str, Any] | None = None
57
+ ) -> list[dict[str, Any]]:
58
+ """Map api response to model fields."""
59
+ if isinstance(response_data, dict):
60
+ if "page" in response_data:
61
+ del response_data["page"]
62
+ del response_data["totalPages"]
63
+ del response_data["total"]
64
+ first_item = next(iter(response_data))
65
+ if isinstance(response_data[first_item], list) and all(
66
+ isinstance(item, dict) for item in response_data[first_item]
67
+ ):
68
+ return super().map_records_response(response_data[first_item], query, query_data)
69
+ return super().map_records_response(response_data, query, query_data)
70
+
71
+ def set_next_page_data_from_response(
72
+ self,
73
+ next_page_data: dict[str, Any],
74
+ query: Query,
75
+ response: "requests.Response", # type: ignore
76
+ ) -> None:
77
+ """
78
+ Update the next_page_data dictionary with the appropriate data needed to fetch the next page of records.
79
+
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:
84
+
85
+ ```python
86
+ next_page_data["some_key"] = "some_value"
87
+ ```
88
+
89
+ but if you do this:
90
+
91
+ ```python
92
+ next_page_data = {"some_key": "some_value"}
93
+ ```
94
+
95
+ Then things simply won't work.
96
+ """
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:
101
+ next_page_data[self.pagination_parameter_name] = page + 1
@@ -8,7 +8,8 @@ from clearskies import Configurable, Model, configs
8
8
  from clearskies.backends.memory_backend import MemoryBackend, MemoryTable
9
9
  from clearskies.columns import String, Uuid
10
10
  from clearskies.di import inject
11
- from clearskies.query.query import Query
11
+ from clearskies.query import Condition, Query
12
+ from clearskies.query.result import RecordsQueryResult
12
13
 
13
14
  from clearskies_cortex.backends import cortex_backend as rest_backend
14
15
 
@@ -34,7 +35,7 @@ class CortexTeamRelationshipBackend(MemoryBackend, Configurable):
34
35
  # or we need to let the di system build the CortexBackend. This change does both:
35
36
  self.cortex_backend = cortex_backend
36
37
 
37
- def records(self, query: Query, next_page_data: dict[str, str | int] | None = None) -> list[dict[str, Any]]:
38
+ def records(self, query: Query) -> RecordsQueryResult:
38
39
  """Accept either a model or a model class and creates a "table" for it."""
39
40
  table_name = query.model_class.destination_name()
40
41
  if table_name not in self._tables:
@@ -45,7 +46,7 @@ class CortexTeamRelationshipBackend(MemoryBackend, Configurable):
45
46
  # we don't need since we built the data ourselves. In short, it will be a lot slower, so I cheat.
46
47
  self._tables[table_name]._rows = records # type: ignore[assignment]
47
48
  self._tables[table_name]._id_index = id_index # type: ignore[assignment]
48
- return super().records(query, next_page_data)
49
+ return super().records(query)
49
50
 
50
51
  def _fetch_and_map_relationship_data(self, table_name: str) -> tuple[list[dict[str, str | int]], dict[str, int]]:
51
52
  class RelationshipModel(Model):
@@ -61,20 +62,6 @@ class CortexTeamRelationshipBackend(MemoryBackend, Configurable):
61
62
  def destination_name(cls) -> str:
62
63
  return "teams/relationships"
63
64
 
64
- try:
65
- relationship_data = self._get_cortex_backend().records(
66
- Query(
67
- model_class=RelationshipModel,
68
- ),
69
- {},
70
- )[0]
71
-
72
- except IndexError:
73
- relationship_data = {"edges": []}
74
-
75
- # this should match up to exactly what backend.records() will return
76
- # relationship_data = example_data["edges"]
77
-
78
65
  # we need to map this to the kind of row structure expected by the category_tree column
79
66
  # (see https://github.com/clearskies-py/clearskies/blob/main/src/clearskies/columns/category_tree.py)
80
67
  # This takes slightly more time up front but makes for quick lookups in both directions (and we'll
@@ -89,9 +76,17 @@ class CortexTeamRelationshipBackend(MemoryBackend, Configurable):
89
76
  root_categories: dict[str, str] = {}
90
77
  known_children: dict[str, str] = {}
91
78
  relationships: dict[str, set[str]] = {}
92
- for relationship in relationship_data["edges"]:
93
- child_category = relationship["childTeamTag"]
94
- parent_category = relationship["parentTeamTag"]
79
+ for relationship in (
80
+ self._get_cortex_backend()
81
+ .records(
82
+ Query(
83
+ model_class=RelationshipModel,
84
+ ),
85
+ )
86
+ .records
87
+ ):
88
+ child_category = relationship["child_team_tag"]
89
+ parent_category = relationship["parent_team_tag"]
95
90
  # Skip if either parent or child is archived
96
91
  if parent_category not in relationships:
97
92
  relationships[parent_category] = set()
@@ -115,8 +110,8 @@ class CortexTeamRelationshipBackend(MemoryBackend, Configurable):
115
110
  for idx, ancestor in enumerate(ancestors):
116
111
  if (
117
112
  not self.all_teams().get(node_name)
118
- or self.all_teams().get(node_name, {}).get("isArchived")
119
- or self.all_teams().get(ancestor, {}).get("isArchived")
113
+ # or self.all_teams().get(node_name, {}).get("is_archived")
114
+ # or self.all_teams().get(ancestor, {}).get("is_archived")
120
115
  ):
121
116
  continue
122
117
  mapped.append(
@@ -162,13 +157,16 @@ class CortexTeamRelationshipBackend(MemoryBackend, Configurable):
162
157
  from clearskies_cortex.models.cortex_team import CortexTeam
163
158
 
164
159
  teams: dict[str, dict[str, Any]] = {}
165
- team_result = self._get_cortex_backend().records(
166
- Query(
167
- model_class=CortexTeam,
168
- ),
169
- {},
170
- )[0]
171
- for team in team_result["teams"]:
172
- teams[team["teamTag"]] = team
160
+ for team in (
161
+ self._get_cortex_backend()
162
+ .records(
163
+ Query(
164
+ model_class=CortexTeam,
165
+ conditions=[Condition("include_teams_without_members=true")],
166
+ ),
167
+ )
168
+ .records
169
+ ):
170
+ teams[team["team_tag"]] = team
173
171
  self._cached_teams = teams
174
172
  return teams
@@ -20,7 +20,7 @@ class ServiceEntityHierarchyParent:
20
20
  description: str | None
21
21
  definition: None | dict[str, Any]
22
22
  parents: list["ServiceEntityHierarchyParent"]
23
- groups: list[str]
23
+ groups: list[str] | None
24
24
 
25
25
 
26
26
  @dataclass
@@ -33,7 +33,7 @@ class ServiceEntityHierarchyChild:
33
33
  description: str | None
34
34
  definition: None | dict[str, Any]
35
35
  children: list["ServiceEntityHierarchyChild"]
36
- groups: list[str]
36
+ groups: list[str] | None
37
37
 
38
38
 
39
39
  @dataclass
@@ -3,6 +3,7 @@ from clearskies_cortex.models.cortex_catalog_entity_domain import CortexCatalogE
3
3
  from clearskies_cortex.models.cortex_catalog_entity_group import CortexCatalogEntityGroup
4
4
  from clearskies_cortex.models.cortex_catalog_entity_scorecard import CortexCatalogEntityScorecard
5
5
  from clearskies_cortex.models.cortex_catalog_entity_service import CortexCatalogEntityService
6
+ from clearskies_cortex.models.cortex_catalog_entity_team import CortexCatalogEntityTeam
6
7
  from clearskies_cortex.models.cortex_scorecard import CortexScorecard
7
8
  from clearskies_cortex.models.cortex_team import CortexTeam
8
9
  from clearskies_cortex.models.cortex_team_category_tree import CortexTeamCategoryTree
@@ -14,6 +15,7 @@ __all__ = [
14
15
  "CortexCatalogEntityGroup",
15
16
  "CortexCatalogEntityScorecard",
16
17
  "CortexCatalogEntityService",
18
+ "CortexCatalogEntityTeam",
17
19
  "CortexScorecard",
18
20
  "CortexTeam",
19
21
  "CortexTeamCategoryTree",
@@ -1,11 +1,19 @@
1
- from typing import Self
1
+ from collections.abc import Iterator
2
+ from typing import Any, Self
2
3
 
3
4
  from clearskies import Model
4
5
  from clearskies.columns import Boolean, Datetime, HasMany, Json, String
6
+ from clearskies.di import inject
7
+ from clearskies.query import Query
8
+ from dacite import from_dict
5
9
 
10
+ from clearskies_cortex import dataclasses
6
11
  from clearskies_cortex.backends import CortexBackend
7
12
  from clearskies_cortex.columns import StringList
8
- from clearskies_cortex.models import cortex_catalog_entity_group, cortex_catalog_entity_scorecard
13
+ from clearskies_cortex.models import (
14
+ cortex_catalog_entity_group,
15
+ cortex_catalog_entity_scorecard,
16
+ )
9
17
 
10
18
 
11
19
  class CortexCatalogEntity(Model):
@@ -25,7 +33,7 @@ class CortexCatalogEntity(Model):
25
33
  groups = StringList("groups")
26
34
  owners = Json()
27
35
  ownership = Json()
28
- ownersV2 = Json()
36
+ owners_v2 = Json()
29
37
  description = String()
30
38
  git = Json()
31
39
  hierarchy = Json()
@@ -46,6 +54,22 @@ class CortexCatalogEntity(Model):
46
54
  foreign_column_name="entity_tag",
47
55
  )
48
56
 
57
+ # search columns
58
+ hierarchy_depth = String(is_searchable=True, is_temporary=True)
59
+ git_repositories = StringList(is_searchable=True, is_temporary=True)
60
+ types = StringList(is_searchable=True, is_temporary=True)
61
+ query = String(is_searchable=True, is_temporary=True)
62
+ include_archived = Boolean(is_searchable=True, is_temporary=True)
63
+ include_metadata = Boolean(is_searchable=True, is_temporary=True)
64
+ include_links = Boolean(is_searchable=True, is_temporary=True)
65
+ include_owners = Boolean(is_searchable=True)
66
+ include_nested_fields = StringList(is_searchable=True, is_temporary=True)
67
+ include_hierarchy_fields = StringList(is_searchable=True, is_temporary=True)
68
+
69
+ def parse_hierarchy(self) -> dataclasses.ServiceEntityHierarchy:
70
+ """Parse the hierarchy column into a dictionary."""
71
+ return from_dict(dataclasses.ServiceEntityHierarchy, data=self.hierarchy)
72
+
49
73
  def parse_groups(self) -> dict[str, str]:
50
74
  """
51
75
  Parse the strings of groups.
@@ -60,3 +84,7 @@ class CortexCatalogEntity(Model):
60
84
  if len(splitted) > 1:
61
85
  parsed[splitted[0]] = splitted[1]
62
86
  return parsed
87
+
88
+ def parse_owners(self) -> dataclasses.EntityTeamOwner:
89
+ """Parse the owners column into a dictionary."""
90
+ return from_dict(dataclasses.EntityTeamOwner, data=self.owners)
@@ -0,0 +1,38 @@
1
+ from typing import Any, Self
2
+
3
+ from clearskies import Column
4
+ from clearskies.query import Condition, Query
5
+
6
+ from clearskies_cortex.models import cortex_catalog_entity
7
+
8
+
9
+ class CortexCatalogEntityDomain(cortex_catalog_entity.CortexCatalogEntity):
10
+ """Model for domain entities."""
11
+
12
+ def get_final_query(self) -> Query:
13
+ return (
14
+ self.get_query()
15
+ .add_where(Condition("types=domain"))
16
+ .add_where(Condition("include_nested_fields=team:members"))
17
+ .add_where(Condition("include_owners=true"))
18
+ .add_where(Condition("include_metadata=true"))
19
+ .add_where(Condition("include_hierarchy_fields=groups"))
20
+ )
21
+
22
+ def get_top_level_domain(self: Self) -> Self:
23
+ """Get the upper domain of this service if set."""
24
+ hierarchy = self.parse_hierarchy()
25
+ if hierarchy.parents:
26
+ parent = hierarchy.parents[0]
27
+ while parent.parents:
28
+ parent = parent.parents[0]
29
+ return self.as_query().find(f"tag={parent.tag}")
30
+ return self.empty()
31
+
32
+ def get_parent(self: Self) -> Self:
33
+ """Get the first domain of this service if set."""
34
+ hierarchy = self.parse_hierarchy()
35
+ if hierarchy.parents:
36
+ container = hierarchy.parents[0]
37
+ return self.as_query().find(f"tag={container.tag}")
38
+ return self.empty()
@@ -0,0 +1,76 @@
1
+ import logging
2
+ from typing import Any, Iterator, Self, cast
3
+
4
+ from clearskies import Column
5
+ from clearskies.di import inject
6
+ from clearskies.query import Condition, Query
7
+ from dacite import from_dict
8
+
9
+ from clearskies_cortex import dataclasses
10
+ from clearskies_cortex.backends import CortexBackend
11
+ from clearskies_cortex.models import (
12
+ cortex_catalog_entity,
13
+ cortex_catalog_entity_domain,
14
+ cortex_team,
15
+ )
16
+
17
+
18
+ class CortexCatalogEntityService(cortex_catalog_entity.CortexCatalogEntity):
19
+ """Model for domain entities."""
20
+
21
+ backend = CortexBackend()
22
+
23
+ teams = inject.ByClass(cortex_team.CortexTeam)
24
+ entity_domains = inject.ByClass(cortex_catalog_entity_domain.CortexCatalogEntityDomain)
25
+
26
+ def get_final_query(self) -> Query:
27
+ return (
28
+ self.get_query()
29
+ .add_where(Condition("types=service"))
30
+ .add_where(Condition("include_nested_fields=team:members"))
31
+ .add_where(Condition("include_owners=true"))
32
+ .add_where(Condition("include_metadata=true"))
33
+ .add_where(Condition("include_hierarchy_fields=groups"))
34
+ )
35
+
36
+ def get_top_level_team(self: Self) -> cortex_team.CortexTeam:
37
+ """Find the top level team based on the team ownership."""
38
+ team = self.get_team()
39
+ if team:
40
+ return team.find_top_level_team()
41
+
42
+ return team
43
+
44
+ def get_team(self: Self) -> cortex_team.CortexTeam:
45
+ """Find the team based on the team ownership."""
46
+ team = self.teams.empty()
47
+ self.logger.debug(f"EntityService: owners {self.owners}")
48
+ if not self.owners:
49
+ return team
50
+ owners = self.parse_owners()
51
+ self.logger.debug(f"Parsed owners: {owners}")
52
+ if not owners.teams:
53
+ return team
54
+
55
+ entity_team = owners.teams[0]
56
+ self.logger.debug(f"Found entity team: {entity_team}")
57
+
58
+ return self.teams.find(f"team_tag={entity_team.tag}")
59
+
60
+ def get_top_level_domain(self: Self) -> cortex_catalog_entity_domain.CortexCatalogEntityDomain:
61
+ """Get the upper domain of this service if set."""
62
+ hierarchy = self.parse_hierarchy()
63
+ if hierarchy.parents:
64
+ parent = hierarchy.parents[0]
65
+ while parent.parents:
66
+ parent = parent.parents[0]
67
+ return self.entity_domains.find(f"tag={parent.tag}")
68
+ return self.entity_domains.empty()
69
+
70
+ def get_parent_domain(self: Self) -> cortex_catalog_entity_domain.CortexCatalogEntityDomain:
71
+ """Get the first domain of this service if set."""
72
+ hierarchy = self.parse_hierarchy()
73
+ if hierarchy.parents:
74
+ container = hierarchy.parents[0]
75
+ return self.entity_domains.find(f"tag={container.tag}")
76
+ return self.entity_domains.empty()