recce-nightly 1.2.0.20250506__py3-none-any.whl → 1.26.0.20251124__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.

Potentially problematic release.


This version of recce-nightly might be problematic. Click here for more details.

Files changed (213) hide show
  1. recce/VERSION +1 -1
  2. recce/__init__.py +27 -22
  3. recce/adapter/base.py +11 -14
  4. recce/adapter/dbt_adapter/__init__.py +810 -480
  5. recce/adapter/dbt_adapter/dbt_version.py +3 -0
  6. recce/adapter/sqlmesh_adapter.py +24 -35
  7. recce/apis/check_api.py +39 -28
  8. recce/apis/check_func.py +33 -27
  9. recce/apis/run_api.py +25 -19
  10. recce/apis/run_func.py +29 -23
  11. recce/artifact.py +119 -51
  12. recce/cli.py +1299 -323
  13. recce/config.py +42 -33
  14. recce/connect_to_cloud.py +138 -0
  15. recce/core.py +55 -47
  16. recce/data/404.html +1 -1
  17. recce/data/__next.__PAGE__.txt +10 -0
  18. recce/data/__next._full.txt +23 -0
  19. recce/data/__next._head.txt +8 -0
  20. recce/data/__next._index.txt +8 -0
  21. recce/data/__next._tree.txt +5 -0
  22. recce/data/_next/static/52aV_JrNUZU6dMFgvTQEO/_buildManifest.js +11 -0
  23. recce/data/_next/static/52aV_JrNUZU6dMFgvTQEO/_clientMiddlewareManifest.json +1 -0
  24. recce/data/_next/static/chunks/02b996c7f6a29a06.js +4 -0
  25. recce/data/_next/static/chunks/19c10d219a6a21ff.js +1 -0
  26. recce/data/_next/static/chunks/2df9ec28a061971d.js +11 -0
  27. recce/data/_next/static/chunks/3098c987393bda15.js +1 -0
  28. recce/data/_next/static/chunks/393dc43e483f717a.css +2 -0
  29. recce/data/_next/static/chunks/399e8d91a7e45073.js +2 -0
  30. recce/data/_next/static/chunks/4d0186f631230245.js +1 -0
  31. recce/data/_next/static/chunks/5794ba9e10a9c060.js +11 -0
  32. recce/data/_next/static/chunks/715761c929a3f28b.js +110 -0
  33. recce/data/_next/static/chunks/71f88fcc615bf282.js +1 -0
  34. recce/data/_next/static/chunks/80d2a95eaf1201ea.js +1 -0
  35. recce/data/_next/static/chunks/9979c6109bbbee35.js +1 -0
  36. recce/data/_next/static/chunks/99d638224186c118.js +1 -0
  37. recce/data/_next/static/chunks/d003eb36240e92f3.js +1 -0
  38. recce/data/_next/static/chunks/d3167cdfec4fc351.js +1 -0
  39. recce/data/_next/static/chunks/e124bccf574a3361.css +1 -0
  40. recce/data/_next/static/chunks/f40141db1bdb46f0.css +6 -0
  41. recce/data/_next/static/chunks/fcc53a88741a52f9.js +1 -0
  42. recce/data/_next/static/chunks/turbopack-b1920d28cfb1f28d.js +3 -0
  43. recce/data/_next/static/media/favicon.a8d38d84.ico +0 -0
  44. recce/data/_next/static/media/montserrat-cyrillic-800-normal.d80d830d.woff2 +0 -0
  45. recce/data/_next/static/media/montserrat-cyrillic-800-normal.f9d58125.woff +0 -0
  46. recce/data/_next/static/media/montserrat-cyrillic-ext-800-normal.076c2a93.woff2 +0 -0
  47. recce/data/_next/static/media/montserrat-cyrillic-ext-800-normal.a4fa76b5.woff +0 -0
  48. recce/data/_next/static/media/montserrat-latin-800-normal.cde454cc.woff2 +0 -0
  49. recce/data/_next/static/media/montserrat-latin-800-normal.d5761935.woff +0 -0
  50. recce/data/_next/static/media/montserrat-latin-ext-800-normal.40ec0659.woff2 +0 -0
  51. recce/data/_next/static/media/montserrat-latin-ext-800-normal.b671449b.woff +0 -0
  52. recce/data/_next/static/media/montserrat-vietnamese-800-normal.9f7b8541.woff +0 -0
  53. recce/data/_next/static/media/montserrat-vietnamese-800-normal.f9eb854e.woff2 +0 -0
  54. recce/data/_next/static/media/reload-image.7aa931c7.svg +4 -0
  55. recce/data/_not-found/__next._full.txt +17 -0
  56. recce/data/_not-found/__next._head.txt +8 -0
  57. recce/data/_not-found/__next._index.txt +8 -0
  58. recce/data/_not-found/__next._not-found.__PAGE__.txt +5 -0
  59. recce/data/_not-found/__next._not-found.txt +4 -0
  60. recce/data/_not-found/__next._tree.txt +3 -0
  61. recce/data/_not-found.html +1 -0
  62. recce/data/_not-found.txt +17 -0
  63. recce/data/auth_callback.html +68 -0
  64. recce/data/imgs/reload-image.svg +4 -0
  65. recce/data/index.html +1 -27
  66. recce/data/index.txt +23 -7
  67. recce/diff.py +6 -12
  68. recce/event/__init__.py +86 -74
  69. recce/event/collector.py +33 -22
  70. recce/event/track.py +49 -27
  71. recce/exceptions.py +1 -1
  72. recce/git.py +7 -7
  73. recce/github.py +57 -53
  74. recce/mcp_server.py +716 -0
  75. recce/models/__init__.py +4 -1
  76. recce/models/check.py +6 -7
  77. recce/models/run.py +1 -0
  78. recce/models/types.py +131 -28
  79. recce/pull_request.py +27 -25
  80. recce/run.py +165 -121
  81. recce/server.py +303 -111
  82. recce/state/__init__.py +31 -0
  83. recce/state/cloud.py +632 -0
  84. recce/state/const.py +26 -0
  85. recce/state/local.py +56 -0
  86. recce/state/state.py +119 -0
  87. recce/state/state_loader.py +174 -0
  88. recce/summary.py +188 -143
  89. recce/tasks/__init__.py +19 -3
  90. recce/tasks/core.py +11 -13
  91. recce/tasks/dataframe.py +82 -18
  92. recce/tasks/histogram.py +69 -34
  93. recce/tasks/lineage.py +2 -2
  94. recce/tasks/profile.py +152 -86
  95. recce/tasks/query.py +139 -87
  96. recce/tasks/rowcount.py +37 -31
  97. recce/tasks/schema.py +18 -15
  98. recce/tasks/top_k.py +35 -35
  99. recce/tasks/valuediff.py +216 -152
  100. recce/util/__init__.py +3 -0
  101. recce/util/api_token.py +80 -0
  102. recce/util/breaking.py +87 -85
  103. recce/util/cll.py +274 -219
  104. recce/util/io.py +22 -17
  105. recce/util/lineage.py +65 -16
  106. recce/util/logger.py +1 -1
  107. recce/util/onboarding_state.py +45 -0
  108. recce/util/perf_tracking.py +85 -0
  109. recce/util/recce_cloud.py +322 -72
  110. recce/util/singleton.py +4 -4
  111. recce/yaml/__init__.py +7 -10
  112. recce_cloud/__init__.py +24 -0
  113. recce_cloud/api/__init__.py +17 -0
  114. recce_cloud/api/base.py +111 -0
  115. recce_cloud/api/client.py +150 -0
  116. recce_cloud/api/exceptions.py +26 -0
  117. recce_cloud/api/factory.py +63 -0
  118. recce_cloud/api/github.py +76 -0
  119. recce_cloud/api/gitlab.py +82 -0
  120. recce_cloud/artifact.py +57 -0
  121. recce_cloud/ci_providers/__init__.py +9 -0
  122. recce_cloud/ci_providers/base.py +82 -0
  123. recce_cloud/ci_providers/detector.py +147 -0
  124. recce_cloud/ci_providers/github_actions.py +136 -0
  125. recce_cloud/ci_providers/gitlab_ci.py +130 -0
  126. recce_cloud/cli.py +245 -0
  127. recce_cloud/upload.py +214 -0
  128. {recce_nightly-1.2.0.20250506.dist-info → recce_nightly-1.26.0.20251124.dist-info}/METADATA +68 -37
  129. recce_nightly-1.26.0.20251124.dist-info/RECORD +180 -0
  130. {recce_nightly-1.2.0.20250506.dist-info → recce_nightly-1.26.0.20251124.dist-info}/WHEEL +1 -1
  131. {recce_nightly-1.2.0.20250506.dist-info → recce_nightly-1.26.0.20251124.dist-info}/top_level.txt +1 -0
  132. tests/adapter/dbt_adapter/conftest.py +9 -5
  133. tests/adapter/dbt_adapter/dbt_test_helper.py +37 -22
  134. tests/adapter/dbt_adapter/test_dbt_adapter.py +0 -15
  135. tests/adapter/dbt_adapter/test_dbt_cll.py +656 -41
  136. tests/adapter/dbt_adapter/test_selector.py +22 -21
  137. tests/recce_cloud/__init__.py +0 -0
  138. tests/recce_cloud/test_ci_providers.py +351 -0
  139. tests/recce_cloud/test_cli.py +372 -0
  140. tests/recce_cloud/test_client.py +273 -0
  141. tests/recce_cloud/test_platform_clients.py +333 -0
  142. tests/tasks/conftest.py +1 -1
  143. tests/tasks/test_histogram.py +58 -66
  144. tests/tasks/test_lineage.py +36 -23
  145. tests/tasks/test_preset_checks.py +45 -31
  146. tests/tasks/test_profile.py +339 -15
  147. tests/tasks/test_query.py +46 -46
  148. tests/tasks/test_row_count.py +65 -46
  149. tests/tasks/test_schema.py +65 -42
  150. tests/tasks/test_top_k.py +22 -18
  151. tests/tasks/test_valuediff.py +43 -32
  152. tests/test_cli.py +174 -60
  153. tests/test_cli_mcp_optional.py +45 -0
  154. tests/test_cloud_listing_cli.py +324 -0
  155. tests/test_config.py +7 -9
  156. tests/test_connect_to_cloud.py +82 -0
  157. tests/test_core.py +151 -4
  158. tests/test_dbt.py +7 -7
  159. tests/test_mcp_server.py +332 -0
  160. tests/test_pull_request.py +1 -1
  161. tests/test_server.py +25 -19
  162. tests/test_summary.py +29 -17
  163. recce/data/_next/static/Kcbs3GEIyH2LxgLYat0es/_buildManifest.js +0 -1
  164. recce/data/_next/static/chunks/1f229bf6-d9fe92e56db8d93b.js +0 -1
  165. recce/data/_next/static/chunks/29e3cc0d-8c150e37dff9631b.js +0 -1
  166. recce/data/_next/static/chunks/368-7587b306577df275.js +0 -65
  167. recce/data/_next/static/chunks/36e1c10d-bb0210cbd6573a8d.js +0 -1
  168. recce/data/_next/static/chunks/3998a672-eaad84bdd88cc73e.js +0 -1
  169. recce/data/_next/static/chunks/3a92ee20-3b5d922d4157af5e.js +0 -1
  170. recce/data/_next/static/chunks/450c323b-1bb5db526e54435a.js +0 -1
  171. recce/data/_next/static/chunks/47d8844f-79a1b53c66a7d7ec.js +0 -1
  172. recce/data/_next/static/chunks/6dc81886-c94b9b91bc2c3caf.js +0 -1
  173. recce/data/_next/static/chunks/6ef81909-694dc38134099299.js +0 -1
  174. recce/data/_next/static/chunks/700-3b65fc3666820d00.js +0 -2
  175. recce/data/_next/static/chunks/7a8a3e83-d7fa409d97b38b2b.js +0 -1
  176. recce/data/_next/static/chunks/7f27ae6c-413f6b869a04183a.js +0 -1
  177. recce/data/_next/static/chunks/8d700b6a-f0b1f6b9e0d97ce2.js +0 -1
  178. recce/data/_next/static/chunks/9746af58-d74bef4d03eea6ab.js +0 -1
  179. recce/data/_next/static/chunks/a30376cd-7d806e1602f2dc3a.js +0 -1
  180. recce/data/_next/static/chunks/app/_not-found/page-8a886fa0855c3105.js +0 -1
  181. recce/data/_next/static/chunks/app/layout-9102e22cb73f74d6.js +0 -1
  182. recce/data/_next/static/chunks/app/page-cee661090afbd6aa.js +0 -1
  183. recce/data/_next/static/chunks/b63b1b3f-7395c74e11a14e95.js +0 -1
  184. recce/data/_next/static/chunks/c132bf7d-8102037f9ccf372a.js +0 -1
  185. recce/data/_next/static/chunks/c1ceaa8b-a1e442154d23515e.js +0 -1
  186. recce/data/_next/static/chunks/cd9f8d63-cf0d5a7b0f7a92e8.js +0 -54
  187. recce/data/_next/static/chunks/ce84277d-f42c2c58049cea2d.js +0 -1
  188. recce/data/_next/static/chunks/e24bf851-0f8cbc99656833e7.js +0 -1
  189. recce/data/_next/static/chunks/fee69bc6-f17d36c080742e74.js +0 -1
  190. recce/data/_next/static/chunks/framework-ded83d71b51ce901.js +0 -1
  191. recce/data/_next/static/chunks/main-a0859f1f36d0aa6c.js +0 -1
  192. recce/data/_next/static/chunks/main-app-0225a2255968e566.js +0 -1
  193. recce/data/_next/static/chunks/pages/_app-d5672bf3d8b6371b.js +0 -1
  194. recce/data/_next/static/chunks/pages/_error-ed75be3f25588548.js +0 -1
  195. recce/data/_next/static/chunks/webpack-567d72f0bc0820d5.js +0 -1
  196. recce/data/_next/static/css/c9ecb46a4b21c126.css +0 -14
  197. recce/data/_next/static/media/montserrat-cyrillic-800-normal.22628180.woff2 +0 -0
  198. recce/data/_next/static/media/montserrat-cyrillic-800-normal.31d693bb.woff +0 -0
  199. recce/data/_next/static/media/montserrat-cyrillic-ext-800-normal.7e2c1e62.woff +0 -0
  200. recce/data/_next/static/media/montserrat-cyrillic-ext-800-normal.94a63aea.woff2 +0 -0
  201. recce/data/_next/static/media/montserrat-latin-800-normal.6f8fa298.woff2 +0 -0
  202. recce/data/_next/static/media/montserrat-latin-800-normal.97e20d5e.woff +0 -0
  203. recce/data/_next/static/media/montserrat-latin-ext-800-normal.013b84f9.woff2 +0 -0
  204. recce/data/_next/static/media/montserrat-latin-ext-800-normal.aff52ab0.woff +0 -0
  205. recce/data/_next/static/media/montserrat-vietnamese-800-normal.5f21869b.woff +0 -0
  206. recce/data/_next/static/media/montserrat-vietnamese-800-normal.c0035377.woff2 +0 -0
  207. recce/state.py +0 -753
  208. recce_nightly-1.2.0.20250506.dist-info/RECORD +0 -142
  209. tests/test_state.py +0 -123
  210. /recce/data/_next/static/{Kcbs3GEIyH2LxgLYat0es → 52aV_JrNUZU6dMFgvTQEO}/_ssgManifest.js +0 -0
  211. /recce/data/_next/static/chunks/{polyfills-42372ed130431b0a.js → a6dad97d9634a72d.js} +0 -0
  212. {recce_nightly-1.2.0.20250506.dist-info → recce_nightly-1.26.0.20251124.dist-info}/entry_points.txt +0 -0
  213. {recce_nightly-1.2.0.20250506.dist-info → recce_nightly-1.26.0.20251124.dist-info}/licenses/LICENSE +0 -0
recce/models/__init__.py CHANGED
@@ -1,3 +1,6 @@
1
1
  from .check import CheckDAO
2
2
  from .run import RunDAO
3
- from .types import Check, Run, RunType, RunProgress
3
+ from .types import Check, Run, RunProgress, RunType
4
+
5
+ # Explicitly declare exports
6
+ __all__ = ["CheckDAO", "RunDAO", "Check", "Run", "RunProgress", "RunType"]
recce/models/check.py CHANGED
@@ -1,6 +1,7 @@
1
- from typing import Optional, List
1
+ from typing import List, Optional
2
2
 
3
3
  from recce.exceptions import RecceException
4
+
4
5
  from .types import Check
5
6
 
6
7
 
@@ -12,6 +13,7 @@ class CheckDAO:
12
13
  @property
13
14
  def _checks(self):
14
15
  from recce.core import default_context
16
+
15
17
  return default_context().checks
16
18
 
17
19
  def create(self, check) -> None:
@@ -38,10 +40,10 @@ class CheckDAO:
38
40
  def reorder(self, source: int, destination: int):
39
41
 
40
42
  if source < 0 or source >= len(self._checks):
41
- raise RecceException('Failed to reorder checks. Source index out of range')
43
+ raise RecceException("Failed to reorder checks. Source index out of range")
42
44
 
43
45
  if destination < 0 or destination >= len(self._checks):
44
- raise RecceException('Failed to reorder checks. Destination index out of range')
46
+ raise RecceException("Failed to reorder checks. Destination index out of range")
45
47
 
46
48
  check_to_move = self._checks.pop(source)
47
49
  self._checks.insert(destination, check_to_move)
@@ -50,7 +52,4 @@ class CheckDAO:
50
52
  self._checks.clear()
51
53
 
52
54
  def status(self):
53
- return {
54
- 'total': len(self._checks),
55
- 'approved': len([c for c in self._checks if c.is_checked])
56
- }
55
+ return {"total": len(self._checks), "approved": len([c for c in self._checks if c.is_checked])}
recce/models/run.py CHANGED
@@ -9,6 +9,7 @@ class RunDAO:
9
9
  @property
10
10
  def _runs(self):
11
11
  from recce.core import default_context
12
+
12
13
  return default_context().runs
13
14
 
14
15
  def create(self, run: Run):
recce/models/types.py CHANGED
@@ -1,26 +1,28 @@
1
1
  import uuid
2
2
  from datetime import datetime, timezone
3
3
  from enum import Enum
4
- from typing import Optional, Literal
4
+ from typing import Dict, List, Literal, Optional, Set
5
5
 
6
- from pydantic import BaseModel, UUID4, Field
6
+ from pydantic import UUID4, BaseModel, Field
7
+
8
+ from recce.util.pydantic_model import pydantic_model_dump
7
9
 
8
10
 
9
11
  class RunType(Enum):
10
- SIMPLE = 'simple'
12
+ SIMPLE = "simple"
11
13
  QUERY = "query"
12
- QUERY_BASE = 'query_base'
13
- QUERY_DIFF = 'query_diff'
14
- VALUE_DIFF = 'value_diff'
15
- VALUE_DIFF_DETAIL = 'value_diff_detail'
16
- SCHEMA_DIFF = 'schema_diff'
17
- PROFILE = 'profile'
18
- PROFILE_DIFF = 'profile_diff'
19
- ROW_COUNT = 'row_count'
20
- ROW_COUNT_DIFF = 'row_count_diff'
21
- LINEAGE_DIFF = 'lineage_diff'
22
- TOP_K_DIFF = 'top_k_diff'
23
- HISTOGRAM_DIFF = 'histogram_diff'
14
+ QUERY_BASE = "query_base"
15
+ QUERY_DIFF = "query_diff"
16
+ VALUE_DIFF = "value_diff"
17
+ VALUE_DIFF_DETAIL = "value_diff_detail"
18
+ SCHEMA_DIFF = "schema_diff"
19
+ PROFILE = "profile"
20
+ PROFILE_DIFF = "profile_diff"
21
+ ROW_COUNT = "row_count"
22
+ ROW_COUNT_DIFF = "row_count_diff"
23
+ LINEAGE_DIFF = "lineage_diff"
24
+ TOP_K_DIFF = "top_k_diff"
25
+ HISTOGRAM_DIFF = "histogram_diff"
24
26
 
25
27
  def __str__(self):
26
28
  return self.value
@@ -32,12 +34,10 @@ class RunProgress(BaseModel):
32
34
 
33
35
 
34
36
  class RunStatus(Enum):
35
- FINISHED = 'finished'
36
- FAILED = 'failed'
37
- CANCELLED = 'cancelled'
38
- RUNNING = 'running'
39
- # This is a special status only in v0.36.0. Replaced by FINISHED. To be removed in the future.
40
- SUCCESSFUL = 'successful'
37
+ FINISHED = "finished"
38
+ FAILED = "failed"
39
+ CANCELLED = "cancelled"
40
+ RUNNING = "running"
41
41
 
42
42
 
43
43
  class Run(BaseModel):
@@ -52,6 +52,39 @@ class Run(BaseModel):
52
52
  run_id: UUID4 = Field(default_factory=uuid.uuid4)
53
53
  run_at: str = Field(default_factory=lambda: datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ"))
54
54
 
55
+ def __init__(self, **data):
56
+ type = data.get("type")
57
+
58
+ if "result" in data and data["result"] is not None:
59
+ result = data.get("result")
60
+
61
+ if type in [RunType.QUERY.value, RunType.QUERY_BASE.value]:
62
+ from recce.tasks.query import QueryResult
63
+
64
+ data["result"] = pydantic_model_dump(QueryResult(**result))
65
+ elif type == RunType.QUERY_DIFF.value:
66
+ from recce.tasks.query import QueryDiffResult
67
+
68
+ data["result"] = pydantic_model_dump(QueryDiffResult(**result))
69
+ elif type == RunType.PROFILE.value:
70
+ from recce.tasks.profile import ProfileResult
71
+
72
+ data["result"] = pydantic_model_dump(ProfileResult(**result))
73
+ elif type == RunType.PROFILE_DIFF.value:
74
+ from recce.tasks.profile import ProfileDiffResult
75
+
76
+ data["result"] = pydantic_model_dump(ProfileDiffResult(**result))
77
+ elif type == RunType.VALUE_DIFF.value:
78
+ from recce.tasks.valuediff import ValueDiffResult
79
+
80
+ data["result"] = pydantic_model_dump(ValueDiffResult(**result))
81
+ elif type == RunType.VALUE_DIFF_DETAIL.value:
82
+ from recce.tasks.valuediff import ValueDiffDetailResult
83
+
84
+ data["result"] = pydantic_model_dump(ValueDiffDetailResult(**result))
85
+
86
+ super().__init__(**data)
87
+
55
88
 
56
89
  class Check(BaseModel):
57
90
  name: str
@@ -84,15 +117,15 @@ class Check(BaseModel):
84
117
 
85
118
 
86
119
  ChangeStatus = Literal[
87
- 'added',
88
- 'removed',
89
- 'modified',
120
+ "added",
121
+ "removed",
122
+ "modified",
90
123
  ]
91
124
  ChangeCategory = Literal[
92
- 'breaking',
93
- 'non_breaking',
94
- 'partial_breaking',
95
- 'unknown',
125
+ "breaking",
126
+ "non_breaking",
127
+ "partial_breaking",
128
+ "unknown",
96
129
  ]
97
130
 
98
131
 
@@ -110,3 +143,73 @@ class LineageDiff(BaseModel):
110
143
  base: dict
111
144
  current: dict
112
145
  diff: dict[str, NodeDiff]
146
+
147
+
148
+ # Column Level Linage
149
+ class CllColumnDep(BaseModel):
150
+ node: str
151
+ column: str
152
+
153
+
154
+ class CllColumn(BaseModel):
155
+ id: Optional[str] = None
156
+ table_id: Optional[str] = None
157
+ name: Optional[str] = None
158
+
159
+ # data type
160
+ type: Optional[str] = None
161
+
162
+ # transformation type
163
+ transformation_type: Literal["source", "passthrough", "renamed", "derived", "unknown"] = "unknown"
164
+
165
+ # change analysis
166
+ change_status: Optional[ChangeStatus] = None
167
+
168
+ # column-to-column dependencies
169
+ depends_on: List[CllColumnDep] = Field(default_factory=list)
170
+
171
+
172
+ class CllNode(BaseModel):
173
+ id: str
174
+ name: str
175
+ package_name: str
176
+ resource_type: str
177
+ raw_code: Optional[str] = None
178
+ source_name: Optional[str] = None
179
+
180
+ # change analysis
181
+ change_status: Optional[ChangeStatus] = None
182
+ change_category: Optional[ChangeCategory] = None
183
+
184
+ # Column to column dependencies
185
+ columns: Dict[str, CllColumn] = Field(default_factory=dict)
186
+
187
+ # If the node is impacted. Only used if option 'change_analysis' is set
188
+ impacted: Optional[bool] = None
189
+
190
+ @classmethod
191
+ def build_cll_node(cls, manifest, resource_key, node_id) -> Optional["CllNode"]:
192
+ resources = getattr(manifest, resource_key)
193
+ if node_id not in resources:
194
+ return None
195
+ n = resources[node_id]
196
+ if resource_key == "nodes" and n.resource_type not in ["model", "seed", "snapshot"]:
197
+ return None
198
+ cll_node = CllNode(
199
+ id=n.unique_id,
200
+ name=n.name,
201
+ package_name=n.package_name,
202
+ resource_type=n.resource_type,
203
+ )
204
+ if resource_key == "sources":
205
+ cll_node.source_name = n.source_name
206
+ elif resource_key == "nodes":
207
+ cll_node.raw_code = n.raw_code
208
+ return cll_node
209
+
210
+
211
+ class CllData(BaseModel):
212
+ nodes: Dict[str, CllNode] = Field(default_factory=dict)
213
+ columns: Dict[str, CllColumn] = Field(default_factory=dict)
214
+ parent_map: Dict[str, Set[str]] = Field(default_factory=dict)
215
+ child_map: Dict[str, Set[str]] = Field(default_factory=dict)
recce/pull_request.py CHANGED
@@ -28,14 +28,14 @@ def fetch_pr_metadata(**kwargs):
28
28
  # fetch from github action event path
29
29
  metadata = fetch_pr_metadata_from_event_path()
30
30
  if metadata is not None:
31
- pr_info.id = metadata.get('github_pr_id')
32
- pr_info.url = metadata.get('github_pr_url')
33
- pr_info.title = metadata.get('github_pr_title')
34
- pr_info.repository = metadata.get('github_repository')
31
+ pr_info.id = metadata.get("github_pr_id")
32
+ pr_info.url = metadata.get("github_pr_url")
33
+ pr_info.title = metadata.get("github_pr_title")
34
+ pr_info.repository = metadata.get("github_repository")
35
35
  else:
36
36
  repo = hosting_repo()
37
- pr, message = recce_pr_information(github_token=kwargs.get('github_token'))
38
- if kwargs.get('cloud') and message:
37
+ pr, message = recce_pr_information(github_token=kwargs.get("github_token"))
38
+ if kwargs.get("cloud") and message:
39
39
  print(message)
40
40
 
41
41
  if pr:
@@ -47,14 +47,14 @@ def fetch_pr_metadata(**kwargs):
47
47
  pr_info.branch = pr.head.ref
48
48
 
49
49
  # fetch from cli arguments
50
- if pr_info.url is None and 'github_pull_request_url' in kwargs:
51
- pr_info.url = kwargs.get('github_pull_request_url')
50
+ if pr_info.url is None and "github_pull_request_url" in kwargs:
51
+ pr_info.url = kwargs.get("github_pull_request_url")
52
52
 
53
53
  if pr_info.branch is None:
54
- pr_info.branch = kwargs.get('git_current_branch')
54
+ pr_info.branch = kwargs.get("git_current_branch")
55
55
 
56
56
  if pr_info.base_branch is None:
57
- pr_info.base_branch = kwargs.get('git_base_branch')
57
+ pr_info.base_branch = kwargs.get("git_base_branch")
58
58
 
59
59
  # fetch from env
60
60
  if pr_info.url is None:
@@ -65,17 +65,17 @@ def fetch_pr_metadata(**kwargs):
65
65
 
66
66
  def fetch_pr_metadata_from_event_path() -> Optional[dict]:
67
67
  """
68
- If recce run is running in a GitHub Action, this function will return the pull request metadata.
68
+ If recce run is running in a GitHub Action, this function will return the pull request metadata.
69
69
 
70
- Example:
71
- {
72
- "github_pr_id": 1,
73
- "github_pr_url": "https://github.com/xyz/abc/pull/1,
74
- "github_pr_title": "Update README.md",
75
- "github_repository": "xyz/abc"
76
- }
70
+ Example:
71
+ {
72
+ "github_pr_id": 1,
73
+ "github_pr_url": "https://github.com/xyz/abc/pull/1,
74
+ "github_pr_title": "Update README.md",
75
+ "github_repository": "xyz/abc"
76
+ }
77
77
 
78
- :return: dict
78
+ :return: dict
79
79
  """
80
80
 
81
81
  # get the event json from the path in GITHUB_EVENT_PATH
@@ -83,7 +83,7 @@ def fetch_pr_metadata_from_event_path() -> Optional[dict]:
83
83
  github_repository = os.getenv("GITHUB_REPOSITORY")
84
84
  if event_path:
85
85
  try:
86
- with open(event_path, "r") as event_file:
86
+ with open(event_path, "r", encoding="utf-8") as event_file:
87
87
  event_data = json.load(event_file)
88
88
 
89
89
  pr_id = event_data["number"]
@@ -92,10 +92,12 @@ def fetch_pr_metadata_from_event_path() -> Optional[dict]:
92
92
  pr_url = pull_request_data["_links"]["html"]["href"]
93
93
  pr_api = pull_request_data["_links"]["self"]["href"]
94
94
  pr_title = _fetch_pr_title(pr_api)
95
- return dict(github_pr_id=pr_id,
96
- github_pr_url=pr_url,
97
- github_pr_title=pr_title,
98
- github_repository=github_repository)
95
+ return dict(
96
+ github_pr_id=pr_id,
97
+ github_pr_url=pr_url,
98
+ github_pr_title=pr_title,
99
+ github_repository=github_repository,
100
+ )
99
101
  else:
100
102
  print("Not a pull request event, skip.")
101
103
  except Exception as e:
@@ -115,7 +117,7 @@ def _fetch_pr_title(endpoint) -> Optional[str]:
115
117
 
116
118
  if response.status_code == 200:
117
119
  pull_request_data = response.json()
118
- return pull_request_data.get('title')
120
+ return pull_request_data.get("title")
119
121
  except Exception as e:
120
122
  print("Cannot fetch PR title: ", e)
121
123