pycharter 0.0.25__py3-none-any.whl → 0.0.26__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.
Files changed (462) hide show
  1. pycharter/__init__.py +6 -0
  2. pycharter/api/README.md +1 -1
  3. pycharter/api/dependencies/auth.py +158 -0
  4. pycharter/api/main.py +30 -2
  5. pycharter/api/models/etl.py +66 -0
  6. pycharter/api/routes/v1/__init__.py +4 -0
  7. pycharter/api/routes/v1/auth.py +97 -0
  8. pycharter/api/routes/v1/contracts.py +10 -8
  9. pycharter/api/routes/v1/etl.py +131 -0
  10. pycharter/cli.py +1 -1
  11. pycharter/config.py +69 -0
  12. pycharter/contract_builder/builder.py +32 -37
  13. pycharter/data/seed/compliance_frameworks.yaml +22 -0
  14. pycharter/data/seed/contracts.yaml +130 -0
  15. pycharter/data/seed/data_feeds.yaml +22 -0
  16. pycharter/data/seed/domains.yaml +13 -0
  17. pycharter/data/seed/environments.yaml +19 -0
  18. pycharter/data/seed/owners.yaml +21 -0
  19. pycharter/data/seed/systems.yaml +13 -0
  20. pycharter/data/seed/tags.yaml +25 -0
  21. pycharter/data/templates/contract/README.md +31 -14
  22. pycharter/data/templates/contract/template_contract.yaml +37 -0
  23. pycharter/data/templates/etl/README.md +1 -1
  24. pycharter/data/templates/etl/extract_with_validation.yaml +86 -0
  25. pycharter/data/templates/etl/load_with_validation.yaml +111 -0
  26. pycharter/data/templates/etl/settings.yaml +55 -0
  27. pycharter/db/cli.py +126 -4
  28. pycharter/db/migrations/versions/20260122000000_change_artifact_unique_constraints_to_title_version.py +2 -2
  29. pycharter/etl_generator/INTERFACES.md +6 -7
  30. pycharter/etl_generator/__init__.py +47 -11
  31. pycharter/etl_generator/config_models.py +673 -0
  32. pycharter/etl_generator/config_validator.py +133 -157
  33. pycharter/etl_generator/context.py +3 -0
  34. pycharter/etl_generator/database.py +5 -1
  35. pycharter/etl_generator/extractors/__init__.py +4 -2
  36. pycharter/etl_generator/extractors/cloud_storage.py +9 -9
  37. pycharter/etl_generator/extractors/database.py +2 -2
  38. pycharter/etl_generator/extractors/factory.py +15 -33
  39. pycharter/etl_generator/extractors/file.py +2 -2
  40. pycharter/etl_generator/extractors/http.py +2 -2
  41. pycharter/etl_generator/extractors/mongodb.py +393 -0
  42. pycharter/etl_generator/extractors/streaming.py +2 -2
  43. pycharter/etl_generator/loaders/__init__.py +15 -9
  44. pycharter/etl_generator/loaders/{cloud_storage_loader.py → cloud_storage.py} +95 -2
  45. pycharter/etl_generator/loaders/factory.py +16 -29
  46. pycharter/etl_generator/loaders/file.py +135 -1
  47. pycharter/etl_generator/loaders/mongodb.py +416 -0
  48. pycharter/etl_generator/pipeline.py +283 -164
  49. pycharter/etl_generator/result.py +16 -0
  50. pycharter/etl_generator/schemas/__init__.py +71 -42
  51. pycharter/etl_generator/transformers/config.py +3 -2
  52. pycharter/etl_generator/transformers/simple_operations.py +57 -4
  53. pycharter/etl_generator/validation.py +551 -0
  54. pycharter/runtime_validator/__init__.py +7 -0
  55. pycharter/runtime_validator/utils.py +33 -0
  56. pycharter/runtime_validator/validator.py +13 -10
  57. pycharter/ui/package-lock.json +50 -41
  58. pycharter/ui/package.json +2 -1
  59. pycharter/ui/static/404/index.html +1 -1
  60. pycharter/ui/static/404.html +1 -1
  61. pycharter/ui/static/__next.__PAGE__.txt +2 -2
  62. pycharter/ui/static/__next._full.txt +7 -7
  63. pycharter/ui/static/__next._head.txt +1 -1
  64. pycharter/ui/static/__next._index.txt +6 -6
  65. pycharter/ui/static/__next._tree.txt +2 -2
  66. pycharter/ui/static/_next/static/chunks/0fc1f70b787b8845.js +1 -0
  67. pycharter/ui/static/_next/static/chunks/17bb8075d7b75663.css +1 -0
  68. pycharter/ui/static/_next/static/chunks/381932864dcbfdb8.js +1 -0
  69. pycharter/ui/static/_next/static/chunks/4c951b8e4507e2b3.js +1 -0
  70. pycharter/ui/static/_next/static/chunks/68b87a6f65abd3ed.js +1 -0
  71. pycharter/ui/static/_next/static/chunks/78572617b8fae189.js +1 -0
  72. pycharter/ui/static/_next/static/chunks/8b7be2803e3fe184.js +1 -0
  73. pycharter/ui/static/_next/static/chunks/a8e529fd1e67f121.js +1 -0
  74. pycharter/ui/static/_next/static/chunks/c35d998f80be3ff5.js +1 -0
  75. pycharter/ui/static/_next/static/chunks/e453aa5d01c32c17.js +1 -0
  76. pycharter/ui/static/_next/static/chunks/f2d240eb057f898a.js +970 -0
  77. pycharter/ui/static/_next/static/chunks/f7722448f6040846.js +1 -0
  78. pycharter/ui/static/_not-found/__next._full.txt +12 -12
  79. pycharter/ui/static/_not-found/__next._head.txt +3 -3
  80. pycharter/ui/static/_not-found/__next._index.txt +8 -8
  81. pycharter/ui/static/_not-found/__next._not-found.__PAGE__.txt +2 -2
  82. pycharter/ui/static/_not-found/__next._not-found.txt +3 -3
  83. pycharter/ui/static/_not-found/__next._tree.txt +2 -2
  84. pycharter/ui/static/_not-found/index.html +1 -1
  85. pycharter/ui/static/_not-found/index.txt +12 -12
  86. pycharter/ui/static/contracts/__next._full.txt +7 -7
  87. pycharter/ui/static/contracts/__next._head.txt +1 -1
  88. pycharter/ui/static/contracts/__next._index.txt +6 -6
  89. pycharter/ui/static/contracts/__next._tree.txt +2 -2
  90. pycharter/ui/static/contracts/__next.contracts.__PAGE__.txt +2 -2
  91. pycharter/ui/static/contracts/__next.contracts.txt +1 -1
  92. pycharter/ui/static/contracts/index.html +1 -1
  93. pycharter/ui/static/contracts/index.txt +7 -7
  94. pycharter/ui/static/documentation/__next._full.txt +7 -7
  95. pycharter/ui/static/documentation/__next._head.txt +1 -1
  96. pycharter/ui/static/documentation/__next._index.txt +6 -6
  97. pycharter/ui/static/documentation/__next._tree.txt +2 -2
  98. pycharter/ui/static/documentation/__next.documentation.__PAGE__.txt +2 -2
  99. pycharter/ui/static/documentation/__next.documentation.txt +1 -1
  100. pycharter/ui/static/documentation/index.html +3 -3
  101. pycharter/ui/static/documentation/index.txt +7 -7
  102. pycharter/ui/static/etl/__next._full.txt +21 -0
  103. pycharter/ui/static/etl/__next._head.txt +7 -0
  104. pycharter/ui/static/etl/__next._index.txt +9 -0
  105. pycharter/ui/static/etl/__next._tree.txt +2 -0
  106. pycharter/ui/static/etl/__next.etl.__PAGE__.txt +9 -0
  107. pycharter/ui/static/etl/__next.etl.txt +4 -0
  108. pycharter/ui/static/etl/index.html +2 -0
  109. pycharter/ui/static/etl/index.txt +21 -0
  110. pycharter/ui/static/index.html +1 -1
  111. pycharter/ui/static/index.txt +7 -7
  112. pycharter/ui/static/metadata/__next._full.txt +7 -7
  113. pycharter/ui/static/metadata/__next._head.txt +1 -1
  114. pycharter/ui/static/metadata/__next._index.txt +6 -6
  115. pycharter/ui/static/metadata/__next._tree.txt +2 -2
  116. pycharter/ui/static/metadata/__next.metadata.__PAGE__.txt +2 -2
  117. pycharter/ui/static/metadata/__next.metadata.txt +1 -1
  118. pycharter/ui/static/metadata/index.html +1 -1
  119. pycharter/ui/static/metadata/index.txt +7 -7
  120. pycharter/ui/static/quality/__next._full.txt +7 -7
  121. pycharter/ui/static/quality/__next._head.txt +1 -1
  122. pycharter/ui/static/quality/__next._index.txt +6 -6
  123. pycharter/ui/static/quality/__next._tree.txt +2 -2
  124. pycharter/ui/static/quality/__next.quality.__PAGE__.txt +2 -2
  125. pycharter/ui/static/quality/__next.quality.txt +1 -1
  126. pycharter/ui/static/quality/index.html +2 -2
  127. pycharter/ui/static/quality/index.txt +7 -7
  128. pycharter/ui/static/rules/__next._full.txt +7 -7
  129. pycharter/ui/static/rules/__next._head.txt +1 -1
  130. pycharter/ui/static/rules/__next._index.txt +6 -6
  131. pycharter/ui/static/rules/__next._tree.txt +2 -2
  132. pycharter/ui/static/rules/__next.rules.__PAGE__.txt +2 -2
  133. pycharter/ui/static/rules/__next.rules.txt +1 -1
  134. pycharter/ui/static/rules/index.html +1 -1
  135. pycharter/ui/static/rules/index.txt +7 -7
  136. pycharter/ui/static/schemas/__next._full.txt +7 -7
  137. pycharter/ui/static/schemas/__next._head.txt +1 -1
  138. pycharter/ui/static/schemas/__next._index.txt +6 -6
  139. pycharter/ui/static/schemas/__next._tree.txt +2 -2
  140. pycharter/ui/static/schemas/__next.schemas.__PAGE__.txt +2 -2
  141. pycharter/ui/static/schemas/__next.schemas.txt +1 -1
  142. pycharter/ui/static/schemas/index.html +1 -1
  143. pycharter/ui/static/schemas/index.txt +7 -7
  144. pycharter/ui/static/settings/__next._full.txt +7 -7
  145. pycharter/ui/static/settings/__next._head.txt +1 -1
  146. pycharter/ui/static/settings/__next._index.txt +6 -6
  147. pycharter/ui/static/settings/__next._tree.txt +2 -2
  148. pycharter/ui/static/settings/__next.settings.__PAGE__.txt +2 -2
  149. pycharter/ui/static/settings/__next.settings.txt +1 -1
  150. pycharter/ui/static/settings/index.html +1 -1
  151. pycharter/ui/static/settings/index.txt +7 -7
  152. pycharter/ui/static/static/404/index.html +1 -1
  153. pycharter/ui/static/static/404.html +1 -1
  154. pycharter/ui/static/static/__next.__PAGE__.txt +1 -1
  155. pycharter/ui/static/static/__next._full.txt +1 -1
  156. pycharter/ui/static/static/__next._head.txt +1 -1
  157. pycharter/ui/static/static/__next._index.txt +1 -1
  158. pycharter/ui/static/static/__next._tree.txt +1 -1
  159. pycharter/ui/static/static/_not-found/__next._full.txt +1 -1
  160. pycharter/ui/static/static/_not-found/__next._head.txt +1 -1
  161. pycharter/ui/static/static/_not-found/__next._index.txt +1 -1
  162. pycharter/ui/static/static/_not-found/__next._not-found.__PAGE__.txt +1 -1
  163. pycharter/ui/static/static/_not-found/__next._not-found.txt +1 -1
  164. pycharter/ui/static/static/_not-found/__next._tree.txt +1 -1
  165. pycharter/ui/static/static/_not-found/index.html +1 -1
  166. pycharter/ui/static/static/_not-found/index.txt +1 -1
  167. pycharter/ui/static/static/contracts/__next._full.txt +2 -2
  168. pycharter/ui/static/static/contracts/__next._head.txt +1 -1
  169. pycharter/ui/static/static/contracts/__next._index.txt +1 -1
  170. pycharter/ui/static/static/contracts/__next._tree.txt +1 -1
  171. pycharter/ui/static/static/contracts/__next.contracts.__PAGE__.txt +2 -2
  172. pycharter/ui/static/static/contracts/__next.contracts.txt +1 -1
  173. pycharter/ui/static/static/contracts/index.html +1 -1
  174. pycharter/ui/static/static/contracts/index.txt +2 -2
  175. pycharter/ui/static/static/documentation/__next._full.txt +1 -1
  176. pycharter/ui/static/static/documentation/__next._head.txt +1 -1
  177. pycharter/ui/static/static/documentation/__next._index.txt +1 -1
  178. pycharter/ui/static/static/documentation/__next._tree.txt +1 -1
  179. pycharter/ui/static/static/documentation/__next.documentation.__PAGE__.txt +1 -1
  180. pycharter/ui/static/static/documentation/__next.documentation.txt +1 -1
  181. pycharter/ui/static/static/documentation/index.html +2 -2
  182. pycharter/ui/static/static/documentation/index.txt +1 -1
  183. pycharter/ui/static/static/index.html +1 -1
  184. pycharter/ui/static/static/index.txt +1 -1
  185. pycharter/ui/static/static/metadata/__next._full.txt +1 -1
  186. pycharter/ui/static/static/metadata/__next._head.txt +1 -1
  187. pycharter/ui/static/static/metadata/__next._index.txt +1 -1
  188. pycharter/ui/static/static/metadata/__next._tree.txt +1 -1
  189. pycharter/ui/static/static/metadata/__next.metadata.__PAGE__.txt +1 -1
  190. pycharter/ui/static/static/metadata/__next.metadata.txt +1 -1
  191. pycharter/ui/static/static/metadata/index.html +1 -1
  192. pycharter/ui/static/static/metadata/index.txt +1 -1
  193. pycharter/ui/static/static/quality/__next._full.txt +2 -2
  194. pycharter/ui/static/static/quality/__next._head.txt +1 -1
  195. pycharter/ui/static/static/quality/__next._index.txt +1 -1
  196. pycharter/ui/static/static/quality/__next._tree.txt +1 -1
  197. pycharter/ui/static/static/quality/__next.quality.__PAGE__.txt +2 -2
  198. pycharter/ui/static/static/quality/__next.quality.txt +1 -1
  199. pycharter/ui/static/static/quality/index.html +2 -2
  200. pycharter/ui/static/static/quality/index.txt +2 -2
  201. pycharter/ui/static/static/rules/__next._full.txt +1 -1
  202. pycharter/ui/static/static/rules/__next._head.txt +1 -1
  203. pycharter/ui/static/static/rules/__next._index.txt +1 -1
  204. pycharter/ui/static/static/rules/__next._tree.txt +1 -1
  205. pycharter/ui/static/static/rules/__next.rules.__PAGE__.txt +1 -1
  206. pycharter/ui/static/static/rules/__next.rules.txt +1 -1
  207. pycharter/ui/static/static/rules/index.html +1 -1
  208. pycharter/ui/static/static/rules/index.txt +1 -1
  209. pycharter/ui/static/static/schemas/__next._full.txt +1 -1
  210. pycharter/ui/static/static/schemas/__next._head.txt +1 -1
  211. pycharter/ui/static/static/schemas/__next._index.txt +1 -1
  212. pycharter/ui/static/static/schemas/__next._tree.txt +1 -1
  213. pycharter/ui/static/static/schemas/__next.schemas.__PAGE__.txt +1 -1
  214. pycharter/ui/static/static/schemas/__next.schemas.txt +1 -1
  215. pycharter/ui/static/static/schemas/index.html +1 -1
  216. pycharter/ui/static/static/schemas/index.txt +1 -1
  217. pycharter/ui/static/static/settings/__next._full.txt +1 -1
  218. pycharter/ui/static/static/settings/__next._head.txt +1 -1
  219. pycharter/ui/static/static/settings/__next._index.txt +1 -1
  220. pycharter/ui/static/static/settings/__next._tree.txt +1 -1
  221. pycharter/ui/static/static/settings/__next.settings.__PAGE__.txt +1 -1
  222. pycharter/ui/static/static/settings/__next.settings.txt +1 -1
  223. pycharter/ui/static/static/settings/index.html +1 -1
  224. pycharter/ui/static/static/settings/index.txt +1 -1
  225. pycharter/ui/static/static/static/404/index.html +1 -1
  226. pycharter/ui/static/static/static/404.html +1 -1
  227. pycharter/ui/static/static/static/__next.__PAGE__.txt +1 -1
  228. pycharter/ui/static/static/static/__next._full.txt +2 -2
  229. pycharter/ui/static/static/static/__next._head.txt +1 -1
  230. pycharter/ui/static/static/static/__next._index.txt +2 -2
  231. pycharter/ui/static/static/static/__next._tree.txt +2 -2
  232. pycharter/ui/static/static/static/_next/static/chunks/f7d1a90dd75d2572.js +1 -0
  233. pycharter/ui/static/static/static/_not-found/__next._full.txt +2 -2
  234. pycharter/ui/static/static/static/_not-found/__next._head.txt +1 -1
  235. pycharter/ui/static/static/static/_not-found/__next._index.txt +2 -2
  236. pycharter/ui/static/static/static/_not-found/__next._not-found.__PAGE__.txt +1 -1
  237. pycharter/ui/static/static/static/_not-found/__next._not-found.txt +1 -1
  238. pycharter/ui/static/static/static/_not-found/__next._tree.txt +2 -2
  239. pycharter/ui/static/static/static/_not-found/index.html +1 -1
  240. pycharter/ui/static/static/static/_not-found/index.txt +2 -2
  241. pycharter/ui/static/static/static/contracts/__next._full.txt +3 -3
  242. pycharter/ui/static/static/static/contracts/__next._head.txt +1 -1
  243. pycharter/ui/static/static/static/contracts/__next._index.txt +2 -2
  244. pycharter/ui/static/static/static/contracts/__next._tree.txt +2 -2
  245. pycharter/ui/static/static/static/contracts/__next.contracts.__PAGE__.txt +2 -2
  246. pycharter/ui/static/static/static/contracts/__next.contracts.txt +1 -1
  247. pycharter/ui/static/static/static/contracts/index.html +1 -1
  248. pycharter/ui/static/static/static/contracts/index.txt +3 -3
  249. pycharter/ui/static/static/static/documentation/__next._full.txt +3 -3
  250. pycharter/ui/static/static/static/documentation/__next._head.txt +1 -1
  251. pycharter/ui/static/static/static/documentation/__next._index.txt +2 -2
  252. pycharter/ui/static/static/static/documentation/__next._tree.txt +2 -2
  253. pycharter/ui/static/static/static/documentation/__next.documentation.__PAGE__.txt +2 -2
  254. pycharter/ui/static/static/static/documentation/__next.documentation.txt +1 -1
  255. pycharter/ui/static/static/static/documentation/index.html +2 -2
  256. pycharter/ui/static/static/static/documentation/index.txt +3 -3
  257. pycharter/ui/static/static/static/index.html +1 -1
  258. pycharter/ui/static/static/static/index.txt +2 -2
  259. pycharter/ui/static/static/static/metadata/__next._full.txt +2 -2
  260. pycharter/ui/static/static/static/metadata/__next._head.txt +1 -1
  261. pycharter/ui/static/static/static/metadata/__next._index.txt +2 -2
  262. pycharter/ui/static/static/static/metadata/__next._tree.txt +2 -2
  263. pycharter/ui/static/static/static/metadata/__next.metadata.__PAGE__.txt +1 -1
  264. pycharter/ui/static/static/static/metadata/__next.metadata.txt +1 -1
  265. pycharter/ui/static/static/static/metadata/index.html +1 -1
  266. pycharter/ui/static/static/static/metadata/index.txt +2 -2
  267. pycharter/ui/static/static/static/quality/__next._full.txt +2 -2
  268. pycharter/ui/static/static/static/quality/__next._head.txt +1 -1
  269. pycharter/ui/static/static/static/quality/__next._index.txt +2 -2
  270. pycharter/ui/static/static/static/quality/__next._tree.txt +2 -2
  271. pycharter/ui/static/static/static/quality/__next.quality.__PAGE__.txt +1 -1
  272. pycharter/ui/static/static/static/quality/__next.quality.txt +1 -1
  273. pycharter/ui/static/static/static/quality/index.html +2 -2
  274. pycharter/ui/static/static/static/quality/index.txt +2 -2
  275. pycharter/ui/static/static/static/rules/__next._full.txt +2 -2
  276. pycharter/ui/static/static/static/rules/__next._head.txt +1 -1
  277. pycharter/ui/static/static/static/rules/__next._index.txt +2 -2
  278. pycharter/ui/static/static/static/rules/__next._tree.txt +2 -2
  279. pycharter/ui/static/static/static/rules/__next.rules.__PAGE__.txt +1 -1
  280. pycharter/ui/static/static/static/rules/__next.rules.txt +1 -1
  281. pycharter/ui/static/static/static/rules/index.html +1 -1
  282. pycharter/ui/static/static/static/rules/index.txt +2 -2
  283. pycharter/ui/static/static/static/schemas/__next._full.txt +2 -2
  284. pycharter/ui/static/static/static/schemas/__next._head.txt +1 -1
  285. pycharter/ui/static/static/static/schemas/__next._index.txt +2 -2
  286. pycharter/ui/static/static/static/schemas/__next._tree.txt +2 -2
  287. pycharter/ui/static/static/static/schemas/__next.schemas.__PAGE__.txt +1 -1
  288. pycharter/ui/static/static/static/schemas/__next.schemas.txt +1 -1
  289. pycharter/ui/static/static/static/schemas/index.html +1 -1
  290. pycharter/ui/static/static/static/schemas/index.txt +2 -2
  291. pycharter/ui/static/static/static/settings/__next._full.txt +2 -2
  292. pycharter/ui/static/static/static/settings/__next._head.txt +1 -1
  293. pycharter/ui/static/static/static/settings/__next._index.txt +2 -2
  294. pycharter/ui/static/static/static/settings/__next._tree.txt +2 -2
  295. pycharter/ui/static/static/static/settings/__next.settings.__PAGE__.txt +1 -1
  296. pycharter/ui/static/static/static/settings/__next.settings.txt +1 -1
  297. pycharter/ui/static/static/static/settings/index.html +1 -1
  298. pycharter/ui/static/static/static/settings/index.txt +2 -2
  299. pycharter/ui/static/static/static/static/.gitkeep +0 -0
  300. pycharter/ui/static/static/static/static/404/index.html +1 -0
  301. pycharter/ui/static/static/static/static/404.html +1 -0
  302. pycharter/ui/static/static/static/static/__next.__PAGE__.txt +10 -0
  303. pycharter/ui/static/static/static/static/__next._full.txt +30 -0
  304. pycharter/ui/static/static/static/static/__next._head.txt +7 -0
  305. pycharter/ui/static/static/static/static/__next._index.txt +9 -0
  306. pycharter/ui/static/static/static/static/__next._tree.txt +2 -0
  307. pycharter/ui/static/static/static/static/_next/static/chunks/222442f6da32302a.js +1 -0
  308. pycharter/ui/static/static/static/static/_next/static/chunks/247eb132b7f7b574.js +1 -0
  309. pycharter/ui/static/static/static/static/_next/static/chunks/297d55555b71baba.js +1 -0
  310. pycharter/ui/static/static/static/static/_next/static/chunks/414e77373f8ff61c.js +1 -0
  311. pycharter/ui/static/static/static/static/_next/static/chunks/652ad0aa26265c47.js +2 -0
  312. pycharter/ui/static/static/static/static/_next/static/chunks/9c23f44fff36548a.js +1 -0
  313. pycharter/ui/static/static/static/static/_next/static/chunks/a6dad97d9634a72d.js +1 -0
  314. pycharter/ui/static/static/static/static/_next/static/chunks/b32a0963684b9933.js +4 -0
  315. pycharter/ui/static/static/static/static/_next/static/chunks/db913959c675cea6.js +1 -0
  316. pycharter/ui/static/static/static/static/_next/static/chunks/f2e7afeab1178138.js +1 -0
  317. pycharter/ui/static/static/static/static/_next/static/chunks/ff1a16fafef87110.js +1 -0
  318. pycharter/ui/static/static/static/static/_next/static/chunks/turbopack-ffcb7ab6794027ef.js +3 -0
  319. pycharter/ui/static/static/static/static/_next/static/tNTkVW6puVXC4bAm4WrHl/_buildManifest.js +11 -0
  320. pycharter/ui/static/static/static/static/_next/static/tNTkVW6puVXC4bAm4WrHl/_clientMiddlewareManifest.json +1 -0
  321. pycharter/ui/static/static/static/static/_next/static/tNTkVW6puVXC4bAm4WrHl/_ssgManifest.js +1 -0
  322. pycharter/ui/static/static/static/static/_not-found/__next._full.txt +17 -0
  323. pycharter/ui/static/static/static/static/_not-found/__next._head.txt +7 -0
  324. pycharter/ui/static/static/static/static/_not-found/__next._index.txt +9 -0
  325. pycharter/ui/static/static/static/static/_not-found/__next._not-found.__PAGE__.txt +5 -0
  326. pycharter/ui/static/static/static/static/_not-found/__next._not-found.txt +4 -0
  327. pycharter/ui/static/static/static/static/_not-found/__next._tree.txt +2 -0
  328. pycharter/ui/static/static/static/static/_not-found/index.html +1 -0
  329. pycharter/ui/static/static/static/static/_not-found/index.txt +17 -0
  330. pycharter/ui/static/static/static/static/contracts/__next._full.txt +21 -0
  331. pycharter/ui/static/static/static/static/contracts/__next._head.txt +7 -0
  332. pycharter/ui/static/static/static/static/contracts/__next._index.txt +9 -0
  333. pycharter/ui/static/static/static/static/contracts/__next._tree.txt +2 -0
  334. pycharter/ui/static/static/static/static/contracts/__next.contracts.__PAGE__.txt +9 -0
  335. pycharter/ui/static/static/static/static/contracts/__next.contracts.txt +4 -0
  336. pycharter/ui/static/static/static/static/contracts/index.html +1 -0
  337. pycharter/ui/static/static/static/static/contracts/index.txt +21 -0
  338. pycharter/ui/static/static/static/static/documentation/__next._full.txt +21 -0
  339. pycharter/ui/static/static/static/static/documentation/__next._head.txt +7 -0
  340. pycharter/ui/static/static/static/static/documentation/__next._index.txt +9 -0
  341. pycharter/ui/static/static/static/static/documentation/__next._tree.txt +2 -0
  342. pycharter/ui/static/static/static/static/documentation/__next.documentation.__PAGE__.txt +9 -0
  343. pycharter/ui/static/static/static/static/documentation/__next.documentation.txt +4 -0
  344. pycharter/ui/static/static/static/static/documentation/index.html +93 -0
  345. pycharter/ui/static/static/static/static/documentation/index.txt +21 -0
  346. pycharter/ui/static/static/static/static/index.html +1 -0
  347. pycharter/ui/static/static/static/static/index.txt +30 -0
  348. pycharter/ui/static/static/static/static/metadata/__next._full.txt +21 -0
  349. pycharter/ui/static/static/static/static/metadata/__next._head.txt +7 -0
  350. pycharter/ui/static/static/static/static/metadata/__next._index.txt +9 -0
  351. pycharter/ui/static/static/static/static/metadata/__next._tree.txt +2 -0
  352. pycharter/ui/static/static/static/static/metadata/__next.metadata.__PAGE__.txt +9 -0
  353. pycharter/ui/static/static/static/static/metadata/__next.metadata.txt +4 -0
  354. pycharter/ui/static/static/static/static/metadata/index.html +1 -0
  355. pycharter/ui/static/static/static/static/metadata/index.txt +21 -0
  356. pycharter/ui/static/static/static/static/quality/__next._full.txt +21 -0
  357. pycharter/ui/static/static/static/static/quality/__next._head.txt +7 -0
  358. pycharter/ui/static/static/static/static/quality/__next._index.txt +9 -0
  359. pycharter/ui/static/static/static/static/quality/__next._tree.txt +2 -0
  360. pycharter/ui/static/static/static/static/quality/__next.quality.__PAGE__.txt +9 -0
  361. pycharter/ui/static/static/static/static/quality/__next.quality.txt +4 -0
  362. pycharter/ui/static/static/static/static/quality/index.html +2 -0
  363. pycharter/ui/static/static/static/static/quality/index.txt +21 -0
  364. pycharter/ui/static/static/static/static/rules/__next._full.txt +21 -0
  365. pycharter/ui/static/static/static/static/rules/__next._head.txt +7 -0
  366. pycharter/ui/static/static/static/static/rules/__next._index.txt +9 -0
  367. pycharter/ui/static/static/static/static/rules/__next._tree.txt +2 -0
  368. pycharter/ui/static/static/static/static/rules/__next.rules.__PAGE__.txt +9 -0
  369. pycharter/ui/static/static/static/static/rules/__next.rules.txt +4 -0
  370. pycharter/ui/static/static/static/static/rules/index.html +1 -0
  371. pycharter/ui/static/static/static/static/rules/index.txt +21 -0
  372. pycharter/ui/static/static/static/static/schemas/__next._full.txt +21 -0
  373. pycharter/ui/static/static/static/static/schemas/__next._head.txt +7 -0
  374. pycharter/ui/static/static/static/static/schemas/__next._index.txt +9 -0
  375. pycharter/ui/static/static/static/static/schemas/__next._tree.txt +2 -0
  376. pycharter/ui/static/static/static/static/schemas/__next.schemas.__PAGE__.txt +9 -0
  377. pycharter/ui/static/static/static/static/schemas/__next.schemas.txt +4 -0
  378. pycharter/ui/static/static/static/static/schemas/index.html +1 -0
  379. pycharter/ui/static/static/static/static/schemas/index.txt +21 -0
  380. pycharter/ui/static/static/static/static/settings/__next._full.txt +21 -0
  381. pycharter/ui/static/static/static/static/settings/__next._head.txt +7 -0
  382. pycharter/ui/static/static/static/static/settings/__next._index.txt +9 -0
  383. pycharter/ui/static/static/static/static/settings/__next._tree.txt +2 -0
  384. pycharter/ui/static/static/static/static/settings/__next.settings.__PAGE__.txt +9 -0
  385. pycharter/ui/static/static/static/static/settings/__next.settings.txt +4 -0
  386. pycharter/ui/static/static/static/static/settings/index.html +1 -0
  387. pycharter/ui/static/static/static/static/settings/index.txt +21 -0
  388. pycharter/ui/static/static/static/static/validation/__next._full.txt +21 -0
  389. pycharter/ui/static/static/static/static/validation/__next._head.txt +7 -0
  390. pycharter/ui/static/static/static/static/validation/__next._index.txt +9 -0
  391. pycharter/ui/static/static/static/static/validation/__next._tree.txt +2 -0
  392. pycharter/ui/static/static/static/static/validation/__next.validation.__PAGE__.txt +9 -0
  393. pycharter/ui/static/static/static/static/validation/__next.validation.txt +4 -0
  394. pycharter/ui/static/static/static/static/validation/index.html +1 -0
  395. pycharter/ui/static/static/static/static/validation/index.txt +21 -0
  396. pycharter/ui/static/static/static/validation/__next._full.txt +2 -2
  397. pycharter/ui/static/static/static/validation/__next._head.txt +1 -1
  398. pycharter/ui/static/static/static/validation/__next._index.txt +2 -2
  399. pycharter/ui/static/static/static/validation/__next._tree.txt +2 -2
  400. pycharter/ui/static/static/static/validation/__next.validation.__PAGE__.txt +1 -1
  401. pycharter/ui/static/static/static/validation/__next.validation.txt +1 -1
  402. pycharter/ui/static/static/static/validation/index.html +1 -1
  403. pycharter/ui/static/static/static/validation/index.txt +2 -2
  404. pycharter/ui/static/static/validation/__next._full.txt +2 -2
  405. pycharter/ui/static/static/validation/__next._head.txt +1 -1
  406. pycharter/ui/static/static/validation/__next._index.txt +1 -1
  407. pycharter/ui/static/static/validation/__next._tree.txt +1 -1
  408. pycharter/ui/static/static/validation/__next.validation.__PAGE__.txt +2 -2
  409. pycharter/ui/static/static/validation/__next.validation.txt +1 -1
  410. pycharter/ui/static/static/validation/index.html +1 -1
  411. pycharter/ui/static/static/validation/index.txt +2 -2
  412. pycharter/ui/static/validation/__next._full.txt +7 -7
  413. pycharter/ui/static/validation/__next._head.txt +1 -1
  414. pycharter/ui/static/validation/__next._index.txt +6 -6
  415. pycharter/ui/static/validation/__next._tree.txt +2 -2
  416. pycharter/ui/static/validation/__next.validation.__PAGE__.txt +2 -2
  417. pycharter/ui/static/validation/__next.validation.txt +1 -1
  418. pycharter/ui/static/validation/index.html +1 -1
  419. pycharter/ui/static/validation/index.txt +7 -7
  420. {pycharter-0.0.25.dist-info → pycharter-0.0.26.dist-info}/METADATA +57 -26
  421. pycharter-0.0.26.dist-info/RECORD +702 -0
  422. pycharter/etl_generator/config_loader.py +0 -394
  423. pycharter/etl_generator/loaders/cloud.py +0 -87
  424. pycharter/etl_generator/loaders/file_loader.py +0 -130
  425. pycharter/etl_generator/schemas/extract.json +0 -234
  426. pycharter/etl_generator/schemas/load.json +0 -202
  427. pycharter/etl_generator/schemas/pipeline.json +0 -94
  428. pycharter/etl_generator/schemas/transform.json +0 -171
  429. pycharter-0.0.25.dist-info/RECORD +0 -572
  430. /pycharter/ui/static/_next/static/{2gKjNv6YvE6BcIdFthBLs → YCnlK66gA7FV5vvcixspB}/_buildManifest.js +0 -0
  431. /pycharter/ui/static/_next/static/{2gKjNv6YvE6BcIdFthBLs → YCnlK66gA7FV5vvcixspB}/_clientMiddlewareManifest.json +0 -0
  432. /pycharter/ui/static/_next/static/{2gKjNv6YvE6BcIdFthBLs → YCnlK66gA7FV5vvcixspB}/_ssgManifest.js +0 -0
  433. /pycharter/ui/static/static/_next/static/{0rYA78L88aUyD2Uh38hhX → 2gKjNv6YvE6BcIdFthBLs}/_buildManifest.js +0 -0
  434. /pycharter/ui/static/static/_next/static/{0rYA78L88aUyD2Uh38hhX → 2gKjNv6YvE6BcIdFthBLs}/_clientMiddlewareManifest.json +0 -0
  435. /pycharter/ui/static/static/_next/static/{0rYA78L88aUyD2Uh38hhX → 2gKjNv6YvE6BcIdFthBLs}/_ssgManifest.js +0 -0
  436. /pycharter/ui/static/{_next → static/_next}/static/chunks/26dfc590f7714c03.js +0 -0
  437. /pycharter/ui/static/{_next → static/_next}/static/chunks/34d289e6db2ef551.js +0 -0
  438. /pycharter/ui/static/{_next → static/_next}/static/chunks/99508d9d5869cc27.js +0 -0
  439. /pycharter/ui/static/{_next → static/_next}/static/chunks/b313c35a6ba76574.js +0 -0
  440. /pycharter/ui/static/static/static/_next/static/{tNTkVW6puVXC4bAm4WrHl → 0rYA78L88aUyD2Uh38hhX}/_buildManifest.js +0 -0
  441. /pycharter/ui/static/static/static/_next/static/{tNTkVW6puVXC4bAm4WrHl → 0rYA78L88aUyD2Uh38hhX}/_clientMiddlewareManifest.json +0 -0
  442. /pycharter/ui/static/static/static/_next/static/{tNTkVW6puVXC4bAm4WrHl → 0rYA78L88aUyD2Uh38hhX}/_ssgManifest.js +0 -0
  443. /pycharter/ui/static/{_next → static/static/_next}/static/chunks/13d4a0fbd74c1ee4.js +0 -0
  444. /pycharter/ui/static/{_next → static/static/_next}/static/chunks/2edb43b48432ac04.js +0 -0
  445. /pycharter/ui/static/static/{_next → static/_next}/static/chunks/c4fa4f4114b7c352.js +0 -0
  446. /pycharter/ui/static/{_next → static/static/_next}/static/chunks/d2363397e1b2bcab.css +0 -0
  447. /pycharter/ui/static/{_next → static/static/static/_next}/static/chunks/2ab439ce003cd691.js +0 -0
  448. /pycharter/ui/static/{_next → static/static/static/_next}/static/chunks/49ca65abd26ae49e.js +0 -0
  449. /pycharter/ui/static/static/static/{_next → static/_next}/static/chunks/4e310fe5005770a3.css +0 -0
  450. /pycharter/ui/static/static/{_next → static/static/_next}/static/chunks/5e04d10c4a7b58a3.js +0 -0
  451. /pycharter/ui/static/static/static/{_next → static/_next}/static/chunks/5fc14c00a2779dc5.js +0 -0
  452. /pycharter/ui/static/static/{_next → static/static/_next}/static/chunks/75d88a058d8ffaa6.js +0 -0
  453. /pycharter/ui/static/static/{_next → static/static/_next}/static/chunks/8c89634cf6bad76f.js +0 -0
  454. /pycharter/ui/static/{_next → static/static/static/_next}/static/chunks/9667e7a3d359eb39.js +0 -0
  455. /pycharter/ui/static/static/static/{_next → static/_next}/static/chunks/b584574fdc8ab13e.js +0 -0
  456. /pycharter/ui/static/{_next → static/static/static/_next}/static/chunks/c69f6cba366bd988.js +0 -0
  457. /pycharter/ui/static/static/static/{_next → static/_next}/static/chunks/d5989c94d3614b3a.js +0 -0
  458. /pycharter/ui/static/{_next → static/static/static/_next}/static/chunks/f061a4be97bfc3b3.js +0 -0
  459. {pycharter-0.0.25.dist-info → pycharter-0.0.26.dist-info}/WHEEL +0 -0
  460. {pycharter-0.0.25.dist-info → pycharter-0.0.26.dist-info}/entry_points.txt +0 -0
  461. {pycharter-0.0.25.dist-info → pycharter-0.0.26.dist-info}/licenses/LICENSE +0 -0
  462. {pycharter-0.0.25.dist-info → pycharter-0.0.26.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,673 @@
1
+ """
2
+ Pydantic models for ETL pipeline configuration.
3
+
4
+ Replaces JSON Schema files with type-safe Python models for:
5
+ - Extract configuration (HTTP, file, database, cloud storage)
6
+ - Transform configuration (simple ops, JSONata, custom function)
7
+ - Load configuration (Postgres, SQLite, file, cloud storage)
8
+ - Settings (shared pipeline config)
9
+ - Validation (source/target schema validation)
10
+ - DLQ (dead letter queue)
11
+ """
12
+
13
+ from enum import Enum
14
+ from typing import Any, Dict, List, Literal, Optional, Union
15
+
16
+ from pydantic import BaseModel, ConfigDict, Field
17
+
18
+
19
+ # =============================================================================
20
+ # ENUMS
21
+ # =============================================================================
22
+
23
+
24
+ class ExtractType(str, Enum):
25
+ """Extract source types."""
26
+
27
+ HTTP = "http"
28
+ FILE = "file"
29
+ DATABASE = "database"
30
+ CLOUD_STORAGE = "cloud_storage"
31
+
32
+
33
+ class LoadType(str, Enum):
34
+ """Load destination types."""
35
+
36
+ POSTGRES = "postgres"
37
+ POSTGRESQL = "postgresql"
38
+ DATABASE = "database"
39
+ SQLITE = "sqlite"
40
+ FILE = "file"
41
+ CLOUD_STORAGE = "cloud_storage"
42
+
43
+
44
+ class CloudProvider(str, Enum):
45
+ """Cloud storage providers."""
46
+
47
+ S3 = "s3"
48
+ GCS = "gcs"
49
+ AZURE = "azure"
50
+
51
+
52
+ class WriteMethod(str, Enum):
53
+ """Database write methods."""
54
+
55
+ INSERT = "insert"
56
+ UPSERT = "upsert"
57
+ REPLACE = "replace"
58
+ UPDATE = "update"
59
+ DELETE = "delete"
60
+ TRUNCATE_AND_LOAD = "truncate_and_load"
61
+
62
+
63
+ class FileFormat(str, Enum):
64
+ """File formats."""
65
+
66
+ JSON = "json"
67
+ JSONL = "jsonl"
68
+ CSV = "csv"
69
+ TSV = "tsv"
70
+ PARQUET = "parquet"
71
+ EXCEL = "excel"
72
+ XML = "xml"
73
+
74
+
75
+ class WriteMode(str, Enum):
76
+ """File write modes."""
77
+
78
+ OVERWRITE = "overwrite"
79
+ APPEND = "append"
80
+
81
+
82
+ class OnErrorAction(str, Enum):
83
+ """Actions on validation error."""
84
+
85
+ FAIL = "fail"
86
+ WARN = "warn"
87
+ SKIP = "skip"
88
+ QUARANTINE = "quarantine"
89
+
90
+
91
+ class DLQBackend(str, Enum):
92
+ """DLQ storage backends."""
93
+
94
+ FILE = "file"
95
+ DATABASE = "database"
96
+ MEMORY = "memory"
97
+
98
+
99
+ class MetadataStoreType(str, Enum):
100
+ """Metadata store types."""
101
+
102
+ POSTGRES = "postgres"
103
+ SQLITE = "sqlite"
104
+ MONGODB = "mongodb"
105
+ REDIS = "redis"
106
+ MEMORY = "memory"
107
+
108
+
109
+ class FilterOperator(str, Enum):
110
+ """Filter comparison operators."""
111
+
112
+ EQ = "eq"
113
+ NE = "ne"
114
+ GT = "gt"
115
+ GTE = "gte"
116
+ LT = "lt"
117
+ LTE = "lte"
118
+ IN = "in"
119
+ NOT_IN = "not_in"
120
+ CONTAINS = "contains"
121
+ NOT_CONTAINS = "not_contains"
122
+ IS_NULL = "is_null"
123
+ IS_NOT_NULL = "is_not_null"
124
+
125
+
126
+ class PaginationStrategy(str, Enum):
127
+ """HTTP pagination strategies."""
128
+
129
+ PAGE = "page"
130
+ OFFSET = "offset"
131
+ CURSOR = "cursor"
132
+ LINK = "link"
133
+
134
+
135
+ class HTTPMethod(str, Enum):
136
+ """HTTP methods."""
137
+
138
+ GET = "GET"
139
+ POST = "POST"
140
+ PUT = "PUT"
141
+ PATCH = "PATCH"
142
+ DELETE = "DELETE"
143
+
144
+
145
+ class ResponseFormat(str, Enum):
146
+ """HTTP response formats."""
147
+
148
+ JSON = "json"
149
+ TEXT = "text"
150
+ XML = "xml"
151
+
152
+
153
+ class ConvertType(str, Enum):
154
+ """Type conversion targets."""
155
+
156
+ INT = "int"
157
+ INTEGER = "integer"
158
+ FLOAT = "float"
159
+ NUMBER = "number"
160
+ STR = "str"
161
+ STRING = "string"
162
+ BOOL = "bool"
163
+ BOOLEAN = "boolean"
164
+ DATETIME = "datetime"
165
+ DATE = "date"
166
+
167
+
168
+ class JSONataMode(str, Enum):
169
+ """JSONata application mode."""
170
+
171
+ RECORD = "record"
172
+ BATCH = "batch"
173
+
174
+
175
+ # =============================================================================
176
+ # SHARED CONFIG MODELS
177
+ # =============================================================================
178
+
179
+
180
+ class SSHTunnelConfig(BaseModel):
181
+ """SSH tunnel configuration for database connections."""
182
+
183
+ model_config = ConfigDict(extra="forbid")
184
+
185
+ host: str
186
+ port: int = 22
187
+ username: str
188
+ private_key_path: Optional[str] = None
189
+ remote_host: Optional[str] = None
190
+ remote_port: Optional[int] = None
191
+
192
+
193
+ class DatabaseConfig(BaseModel):
194
+ """Database connection configuration."""
195
+
196
+ model_config = ConfigDict(extra="forbid")
197
+
198
+ url: str
199
+
200
+
201
+ class CSVOptions(BaseModel):
202
+ """CSV file options."""
203
+
204
+ model_config = ConfigDict(extra="forbid")
205
+
206
+ delimiter: str = ","
207
+ quote_char: str = '"'
208
+ escape_char: Optional[str] = None
209
+ has_header: bool = True
210
+ include_header: bool = True # for writing
211
+ encoding: str = "utf-8"
212
+
213
+
214
+ class JSONOptions(BaseModel):
215
+ """JSON file options."""
216
+
217
+ model_config = ConfigDict(extra="forbid")
218
+
219
+ encoding: str = "utf-8"
220
+ lines: bool = False # for reading JSONL
221
+ indent: Optional[int] = None # for writing
222
+ ensure_ascii: bool = False # for writing
223
+
224
+
225
+ class CloudStorageConfig(BaseModel):
226
+ """Cloud storage configuration."""
227
+
228
+ model_config = ConfigDict(extra="forbid")
229
+
230
+ provider: CloudProvider
231
+ bucket: Optional[str] = None # S3, GCS
232
+ container: Optional[str] = None # Azure
233
+ path: Optional[str] = None
234
+ prefix: Optional[str] = None
235
+ credentials: Optional[Dict[str, Any]] = None
236
+
237
+
238
+ # =============================================================================
239
+ # DLQ CONFIG
240
+ # =============================================================================
241
+
242
+
243
+ class DLQConfig(BaseModel):
244
+ """Dead Letter Queue configuration."""
245
+
246
+ model_config = ConfigDict(extra="forbid")
247
+
248
+ enabled: bool = True
249
+ backend: DLQBackend = DLQBackend.FILE
250
+ path: Optional[str] = None # for file backend
251
+ connection_string: Optional[str] = None # for database backend
252
+ schema_name: Optional[str] = Field(None, alias="schema") # DB schema
253
+ table: Optional[str] = None # DB table name
254
+
255
+
256
+ # =============================================================================
257
+ # VALIDATION CONFIG
258
+ # =============================================================================
259
+
260
+
261
+ class ContractRef(BaseModel):
262
+ """Reference to a data contract in database."""
263
+
264
+ model_config = ConfigDict(extra="forbid")
265
+
266
+ type: Literal["database"] = "database"
267
+ name: str
268
+ version: Optional[str] = None
269
+
270
+
271
+ class ExtractValidationConfig(BaseModel):
272
+ """Validation config for extract stage (source schema)."""
273
+
274
+ model_config = ConfigDict(extra="forbid", populate_by_name=True)
275
+
276
+ schema_path: Optional[str] = Field(None, alias="schema")
277
+ on_error: OnErrorAction = OnErrorAction.FAIL
278
+ dlq: Optional[Union[bool, DLQConfig]] = None
279
+
280
+
281
+ class LoadValidationConfig(BaseModel):
282
+ """Validation config for load stage (target contract)."""
283
+
284
+ model_config = ConfigDict(extra="forbid")
285
+
286
+ contract: Optional[Union[str, ContractRef]] = None # file path or db ref
287
+ use_contract: bool = False # use settings.yaml contract
288
+ on_error: OnErrorAction = OnErrorAction.FAIL
289
+ dlq: Optional[Union[bool, DLQConfig]] = None
290
+
291
+
292
+ # =============================================================================
293
+ # METADATA STORE CONFIG
294
+ # =============================================================================
295
+
296
+
297
+ class MetadataStoreConfig(BaseModel):
298
+ """Metadata store configuration for database-based contracts."""
299
+
300
+ model_config = ConfigDict(extra="forbid")
301
+
302
+ type: MetadataStoreType
303
+ connection_string: str
304
+
305
+
306
+ # =============================================================================
307
+ # SETTINGS CONFIG
308
+ # =============================================================================
309
+
310
+
311
+ class SettingsConfig(BaseModel):
312
+ """Shared pipeline settings (settings.yaml)."""
313
+
314
+ model_config = ConfigDict(extra="forbid")
315
+
316
+ name: Optional[str] = None
317
+ metadata_store: Optional[MetadataStoreConfig] = None
318
+ dlq: Optional[DLQConfig] = None
319
+ contract: Optional[str] = None # default contract name
320
+
321
+
322
+ # =============================================================================
323
+ # EXTRACT CONFIGS
324
+ # =============================================================================
325
+
326
+
327
+ class PaginationConfig(BaseModel):
328
+ """HTTP pagination configuration."""
329
+
330
+ model_config = ConfigDict(extra="forbid")
331
+
332
+ enabled: bool = False
333
+ strategy: Optional[PaginationStrategy] = None
334
+ page_param: Optional[str] = None
335
+ offset_param: Optional[str] = None
336
+ limit_param: Optional[str] = None
337
+ cursor_param: Optional[str] = None
338
+ cursor_path: Optional[str] = None
339
+ next_link_path: Optional[str] = None
340
+ page_size: Optional[int] = Field(None, ge=1)
341
+ max_pages: Optional[int] = Field(None, ge=1)
342
+
343
+
344
+ class RetryConfig(BaseModel):
345
+ """HTTP retry configuration."""
346
+
347
+ model_config = ConfigDict(extra="forbid")
348
+
349
+ max_attempts: int = Field(3, ge=1)
350
+ backoff_factor: float = Field(2.0, ge=0)
351
+ retry_on_status: List[int] = Field(
352
+ default_factory=lambda: [429, 500, 502, 503, 504]
353
+ )
354
+
355
+
356
+ class TimeoutConfig(BaseModel):
357
+ """HTTP timeout configuration."""
358
+
359
+ model_config = ConfigDict(extra="forbid")
360
+
361
+ connect: Optional[float] = Field(None, ge=0)
362
+ read: Optional[float] = Field(None, ge=0)
363
+ write: Optional[float] = Field(None, ge=0)
364
+
365
+
366
+ class HTTPExtractConfig(BaseModel):
367
+ """HTTP extraction configuration."""
368
+
369
+ model_config = ConfigDict(extra="forbid")
370
+
371
+ type: Literal["http"] = "http"
372
+ url: Optional[str] = None
373
+ base_url: Optional[str] = None
374
+ endpoint: Optional[str] = None
375
+ method: HTTPMethod = HTTPMethod.GET
376
+ headers: Optional[Dict[str, str]] = None
377
+ params: Optional[Dict[str, Any]] = None
378
+ body: Optional[Dict[str, Any]] = None
379
+ response_format: ResponseFormat = ResponseFormat.JSON
380
+ response_path: Optional[str] = None
381
+ pagination: Optional[PaginationConfig] = None
382
+ retry: Optional[RetryConfig] = None
383
+ timeout: Optional[TimeoutConfig] = None
384
+ rate_limit_delay: Optional[float] = Field(None, ge=0)
385
+ batch_size: int = Field(1000, ge=1)
386
+ validation: Optional[ExtractValidationConfig] = None
387
+
388
+
389
+ class FileExtractConfig(BaseModel):
390
+ """File extraction configuration."""
391
+
392
+ model_config = ConfigDict(extra="forbid")
393
+
394
+ type: Literal["file"] = "file"
395
+ path: str
396
+ format: Optional[FileFormat] = None
397
+ csv_options: Optional[CSVOptions] = None
398
+ json_options: Optional[JSONOptions] = None
399
+ batch_size: int = Field(1000, ge=1)
400
+ validation: Optional[ExtractValidationConfig] = None
401
+
402
+
403
+ class DatabaseExtractConfig(BaseModel):
404
+ """Database extraction configuration."""
405
+
406
+ model_config = ConfigDict(extra="forbid", populate_by_name=True)
407
+
408
+ type: Literal["database"] = "database"
409
+ query: str
410
+ connection_string: Optional[str] = None
411
+ database: Optional[DatabaseConfig] = None
412
+ query_params: Optional[Dict[str, Any]] = Field(None, alias="params")
413
+ ssh_tunnel: Optional[SSHTunnelConfig] = None
414
+ batch_size: int = Field(1000, ge=1)
415
+ validation: Optional[ExtractValidationConfig] = None
416
+
417
+
418
+ class CloudStorageExtractConfig(BaseModel):
419
+ """Cloud storage extraction configuration."""
420
+
421
+ model_config = ConfigDict(extra="forbid")
422
+
423
+ type: Literal["cloud_storage"] = "cloud_storage"
424
+ storage: CloudStorageConfig
425
+ format: Optional[FileFormat] = None
426
+ batch_size: int = Field(1000, ge=1)
427
+ validation: Optional[ExtractValidationConfig] = None
428
+
429
+
430
+ # Union type for extract config - use discriminator on 'type'
431
+ ExtractConfig = Union[
432
+ HTTPExtractConfig,
433
+ FileExtractConfig,
434
+ DatabaseExtractConfig,
435
+ CloudStorageExtractConfig,
436
+ ]
437
+
438
+
439
+ # =============================================================================
440
+ # LOAD CONFIGS
441
+ # =============================================================================
442
+
443
+
444
+ class PostgresLoadConfig(BaseModel):
445
+ """PostgreSQL load configuration."""
446
+
447
+ model_config = ConfigDict(extra="forbid", populate_by_name=True)
448
+
449
+ type: Literal["postgres", "postgresql", "database"]
450
+ table: str
451
+ schema_name: Optional[str] = Field("public", alias="schema")
452
+ write_method: WriteMethod = WriteMethod.INSERT
453
+ primary_key: Optional[Union[str, List[str]]] = None
454
+ update_columns: Optional[List[str]] = None
455
+ connection_string: Optional[str] = None
456
+ database: Optional[DatabaseConfig] = None
457
+ ssh_tunnel: Optional[SSHTunnelConfig] = None
458
+ batch_size: int = Field(1000, ge=1)
459
+ validation: Optional[LoadValidationConfig] = None
460
+
461
+
462
+ class SQLiteLoadConfig(BaseModel):
463
+ """SQLite load configuration."""
464
+
465
+ model_config = ConfigDict(extra="forbid")
466
+
467
+ type: Literal["sqlite"] = "sqlite"
468
+ table: str
469
+ path: str
470
+ write_method: WriteMethod = WriteMethod.INSERT
471
+ primary_key: Optional[Union[str, List[str]]] = None
472
+ batch_size: int = Field(1000, ge=1)
473
+ validation: Optional[LoadValidationConfig] = None
474
+
475
+
476
+ class FileLoadConfig(BaseModel):
477
+ """File load configuration."""
478
+
479
+ model_config = ConfigDict(extra="forbid")
480
+
481
+ type: Literal["file"] = "file"
482
+ path: str
483
+ format: FileFormat = FileFormat.JSON
484
+ write_mode: WriteMode = WriteMode.OVERWRITE
485
+ csv_options: Optional[CSVOptions] = None
486
+ json_options: Optional[JSONOptions] = None
487
+ batch_size: int = Field(1000, ge=1)
488
+ validation: Optional[LoadValidationConfig] = None
489
+
490
+
491
+ class CloudStorageLoadConfig(BaseModel):
492
+ """Cloud storage load configuration."""
493
+
494
+ model_config = ConfigDict(extra="forbid")
495
+
496
+ type: Literal["cloud_storage"] = "cloud_storage"
497
+ storage: CloudStorageConfig
498
+ format: FileFormat = FileFormat.JSON
499
+ write_mode: WriteMode = WriteMode.OVERWRITE
500
+ partition_by: Optional[List[str]] = None
501
+ batch_size: int = Field(1000, ge=1)
502
+ validation: Optional[LoadValidationConfig] = None
503
+
504
+
505
+ # Union type for load config
506
+ LoadConfig = Union[
507
+ PostgresLoadConfig,
508
+ SQLiteLoadConfig,
509
+ FileLoadConfig,
510
+ CloudStorageLoadConfig,
511
+ ]
512
+
513
+
514
+ # =============================================================================
515
+ # TRANSFORM CONFIG
516
+ # =============================================================================
517
+
518
+
519
+ class FilterConfig(BaseModel):
520
+ """Filter operation configuration."""
521
+
522
+ model_config = ConfigDict(extra="forbid")
523
+
524
+ expression: Optional[str] = None
525
+ field: Optional[str] = None
526
+ operator: Optional[FilterOperator] = None
527
+ value: Optional[Any] = None
528
+
529
+
530
+ class MapConfig(BaseModel):
531
+ """Map (lookup) operation configuration."""
532
+
533
+ model_config = ConfigDict(extra="forbid")
534
+
535
+ field: str
536
+ mapping: Dict[str, Any]
537
+ default: Optional[Any] = None
538
+
539
+
540
+ class JSONataConfig(BaseModel):
541
+ """JSONata transformation configuration."""
542
+
543
+ model_config = ConfigDict(extra="forbid")
544
+
545
+ expression: str
546
+ mode: JSONataMode = JSONataMode.RECORD
547
+
548
+
549
+ class CustomFunctionConfig(BaseModel):
550
+ """Custom Python function configuration."""
551
+
552
+ model_config = ConfigDict(extra="forbid")
553
+
554
+ module: str
555
+ function: str
556
+ kwargs: Optional[Dict[str, Any]] = None
557
+ mode: JSONataMode = JSONataMode.BATCH
558
+
559
+
560
+ class SimpleOpsConfig(BaseModel):
561
+ """Simple transform operations (object format)."""
562
+
563
+ model_config = ConfigDict(extra="forbid")
564
+
565
+ rename: Optional[Dict[str, str]] = None
566
+ convert: Optional[Dict[str, ConvertType]] = None
567
+ defaults: Optional[Dict[str, Any]] = None
568
+ add: Optional[Dict[str, Any]] = None
569
+ select: Optional[List[str]] = None
570
+ drop: Optional[List[str]] = None
571
+ filter: Optional[FilterConfig] = None
572
+
573
+
574
+ class TransformConfig(BaseModel):
575
+ """Transform configuration (simple ops, JSONata, custom function)."""
576
+
577
+ model_config = ConfigDict(extra="forbid")
578
+
579
+ # Simple ops: can be list (ordered) or dict (legacy)
580
+ transform: Optional[Union[SimpleOpsConfig, List[Dict[str, Any]]]] = None
581
+ # JSONata transformation
582
+ jsonata: Optional[JSONataConfig] = None
583
+ # Custom Python function
584
+ custom_function: Optional[CustomFunctionConfig] = None
585
+
586
+
587
+ # =============================================================================
588
+ # PIPELINE CONFIG (single-file format)
589
+ # =============================================================================
590
+
591
+
592
+ class PipelineConfig(BaseModel):
593
+ """Complete pipeline configuration (single-file format)."""
594
+
595
+ model_config = ConfigDict(extra="forbid")
596
+
597
+ name: Optional[str] = None
598
+ version: Optional[str] = None
599
+ description: Optional[str] = None
600
+
601
+ # Core ETL steps (validated separately based on type)
602
+ extract: Dict[str, Any]
603
+ transform: Optional[Union[Dict[str, Any], List[Dict[str, Any]]]] = None
604
+ jsonata: Optional[JSONataConfig] = None
605
+ custom_function: Optional[CustomFunctionConfig] = None
606
+ load: Dict[str, Any]
607
+
608
+ # Inline settings (for single-file format)
609
+ metadata_store: Optional[MetadataStoreConfig] = None
610
+ dlq: Optional[DLQConfig] = None
611
+ contract: Optional[str] = None
612
+
613
+
614
+ # =============================================================================
615
+ # HELPER FUNCTIONS
616
+ # =============================================================================
617
+
618
+
619
+ def parse_extract_config(config: Dict[str, Any]) -> ExtractConfig:
620
+ """Parse extract config dict into typed model based on 'type' field."""
621
+ extract_type = config.get("type", "").lower()
622
+
623
+ if extract_type == "http":
624
+ return HTTPExtractConfig(**config)
625
+ elif extract_type == "file":
626
+ return FileExtractConfig(**config)
627
+ elif extract_type == "database":
628
+ return DatabaseExtractConfig(**config)
629
+ elif extract_type == "cloud_storage":
630
+ return CloudStorageExtractConfig(**config)
631
+ else:
632
+ raise ValueError(
633
+ f"Unknown extract type: '{extract_type}'. "
634
+ f"Supported types: http, file, database, cloud_storage"
635
+ )
636
+
637
+
638
+ def parse_load_config(config: Dict[str, Any]) -> LoadConfig:
639
+ """Parse load config dict into typed model based on 'type' field."""
640
+ load_type = config.get("type", "").lower()
641
+
642
+ if load_type in ("postgres", "postgresql", "database"):
643
+ return PostgresLoadConfig(**config)
644
+ elif load_type == "sqlite":
645
+ return SQLiteLoadConfig(**config)
646
+ elif load_type == "file":
647
+ return FileLoadConfig(**config)
648
+ elif load_type == "cloud_storage":
649
+ return CloudStorageLoadConfig(**config)
650
+ else:
651
+ raise ValueError(
652
+ f"Unknown load type: '{load_type}'. "
653
+ f"Supported types: postgres, postgresql, database, sqlite, file, cloud_storage"
654
+ )
655
+
656
+
657
+ def parse_transform_config(
658
+ config: Optional[Union[Dict[str, Any], List[Dict[str, Any]]]]
659
+ ) -> Optional[TransformConfig]:
660
+ """Parse transform config into typed model."""
661
+ if config is None:
662
+ return None
663
+
664
+ # Handle top-level transform that's just a list or simple ops dict
665
+ if isinstance(config, list):
666
+ return TransformConfig(transform=config)
667
+
668
+ return TransformConfig(**config)
669
+
670
+
671
+ def parse_settings_config(config: Dict[str, Any]) -> SettingsConfig:
672
+ """Parse settings config into typed model."""
673
+ return SettingsConfig(**config)