recurvedata-lib 0.1.487__py2.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 recurvedata-lib might be problematic. Click here for more details.

Files changed (333) hide show
  1. recurvedata/__init__.py +0 -0
  2. recurvedata/__version__.py +1 -0
  3. recurvedata/client/__init__.py +3 -0
  4. recurvedata/client/client.py +150 -0
  5. recurvedata/client/server_client.py +91 -0
  6. recurvedata/config.py +99 -0
  7. recurvedata/connectors/__init__.py +20 -0
  8. recurvedata/connectors/_register.py +46 -0
  9. recurvedata/connectors/base.py +111 -0
  10. recurvedata/connectors/config_schema.py +1575 -0
  11. recurvedata/connectors/connectors/__init__.py +0 -0
  12. recurvedata/connectors/connectors/aliyun_access_key.py +30 -0
  13. recurvedata/connectors/connectors/auth.py +44 -0
  14. recurvedata/connectors/connectors/azure_blob.py +89 -0
  15. recurvedata/connectors/connectors/azure_synapse.py +79 -0
  16. recurvedata/connectors/connectors/bigquery.py +359 -0
  17. recurvedata/connectors/connectors/clickhouse.py +219 -0
  18. recurvedata/connectors/connectors/dingtalk.py +61 -0
  19. recurvedata/connectors/connectors/doris.py +215 -0
  20. recurvedata/connectors/connectors/es.py +62 -0
  21. recurvedata/connectors/connectors/feishu.py +65 -0
  22. recurvedata/connectors/connectors/ftp.py +50 -0
  23. recurvedata/connectors/connectors/generic.py +49 -0
  24. recurvedata/connectors/connectors/google_cloud_storage.py +115 -0
  25. recurvedata/connectors/connectors/google_service_account.py +225 -0
  26. recurvedata/connectors/connectors/hive.py +207 -0
  27. recurvedata/connectors/connectors/impala.py +210 -0
  28. recurvedata/connectors/connectors/jenkins.py +51 -0
  29. recurvedata/connectors/connectors/mail.py +89 -0
  30. recurvedata/connectors/connectors/microsoft_fabric.py +284 -0
  31. recurvedata/connectors/connectors/mongo.py +79 -0
  32. recurvedata/connectors/connectors/mssql.py +131 -0
  33. recurvedata/connectors/connectors/mysql.py +191 -0
  34. recurvedata/connectors/connectors/n8n.py +141 -0
  35. recurvedata/connectors/connectors/oss.py +74 -0
  36. recurvedata/connectors/connectors/owncloud.py +36 -0
  37. recurvedata/connectors/connectors/phoenix.py +36 -0
  38. recurvedata/connectors/connectors/postgres.py +230 -0
  39. recurvedata/connectors/connectors/python.py +50 -0
  40. recurvedata/connectors/connectors/redshift.py +187 -0
  41. recurvedata/connectors/connectors/s3.py +93 -0
  42. recurvedata/connectors/connectors/sftp.py +87 -0
  43. recurvedata/connectors/connectors/slack.py +35 -0
  44. recurvedata/connectors/connectors/spark.py +99 -0
  45. recurvedata/connectors/connectors/starrocks.py +175 -0
  46. recurvedata/connectors/connectors/tencent_cos.py +40 -0
  47. recurvedata/connectors/connectors/tidb.py +49 -0
  48. recurvedata/connectors/const.py +315 -0
  49. recurvedata/connectors/datasource.py +189 -0
  50. recurvedata/connectors/dbapi.py +469 -0
  51. recurvedata/connectors/fs.py +66 -0
  52. recurvedata/connectors/ftp.py +40 -0
  53. recurvedata/connectors/object_store.py +60 -0
  54. recurvedata/connectors/pigeon.py +172 -0
  55. recurvedata/connectors/proxy.py +104 -0
  56. recurvedata/connectors/service.py +223 -0
  57. recurvedata/connectors/utils.py +47 -0
  58. recurvedata/consts.py +49 -0
  59. recurvedata/core/__init__.py +0 -0
  60. recurvedata/core/config.py +46 -0
  61. recurvedata/core/configurable.py +27 -0
  62. recurvedata/core/consts.py +2 -0
  63. recurvedata/core/templating.py +206 -0
  64. recurvedata/core/tracing.py +223 -0
  65. recurvedata/core/transformer.py +186 -0
  66. recurvedata/core/translation.py +91 -0
  67. recurvedata/dbt/client.py +97 -0
  68. recurvedata/dbt/consts.py +99 -0
  69. recurvedata/dbt/cosmos_utils.py +275 -0
  70. recurvedata/dbt/error_codes.py +18 -0
  71. recurvedata/dbt/schemas.py +98 -0
  72. recurvedata/dbt/service.py +451 -0
  73. recurvedata/dbt/utils.py +246 -0
  74. recurvedata/error_codes.py +71 -0
  75. recurvedata/exceptions.py +72 -0
  76. recurvedata/executors/__init__.py +4 -0
  77. recurvedata/executors/cli/__init__.py +7 -0
  78. recurvedata/executors/cli/connector.py +117 -0
  79. recurvedata/executors/cli/dbt.py +118 -0
  80. recurvedata/executors/cli/main.py +82 -0
  81. recurvedata/executors/cli/parameters.py +18 -0
  82. recurvedata/executors/client.py +190 -0
  83. recurvedata/executors/consts.py +50 -0
  84. recurvedata/executors/debug_executor.py +100 -0
  85. recurvedata/executors/executor.py +300 -0
  86. recurvedata/executors/link_executor.py +189 -0
  87. recurvedata/executors/models.py +34 -0
  88. recurvedata/executors/schemas.py +222 -0
  89. recurvedata/executors/service/__init__.py +0 -0
  90. recurvedata/executors/service/connector.py +380 -0
  91. recurvedata/executors/utils.py +172 -0
  92. recurvedata/filestorage/__init__.py +11 -0
  93. recurvedata/filestorage/_factory.py +33 -0
  94. recurvedata/filestorage/backends/__init__.py +0 -0
  95. recurvedata/filestorage/backends/fsspec.py +45 -0
  96. recurvedata/filestorage/backends/local.py +67 -0
  97. recurvedata/filestorage/backends/oss.py +56 -0
  98. recurvedata/filestorage/interface.py +84 -0
  99. recurvedata/operators/__init__.py +10 -0
  100. recurvedata/operators/base.py +28 -0
  101. recurvedata/operators/config.py +21 -0
  102. recurvedata/operators/context.py +255 -0
  103. recurvedata/operators/dbt_operator/__init__.py +2 -0
  104. recurvedata/operators/dbt_operator/model_pipeline_link_operator.py +55 -0
  105. recurvedata/operators/dbt_operator/operator.py +353 -0
  106. recurvedata/operators/link_operator/__init__.py +1 -0
  107. recurvedata/operators/link_operator/operator.py +120 -0
  108. recurvedata/operators/models.py +55 -0
  109. recurvedata/operators/notify_operator/__init__.py +1 -0
  110. recurvedata/operators/notify_operator/operator.py +180 -0
  111. recurvedata/operators/operator.py +119 -0
  112. recurvedata/operators/python_operator/__init__.py +1 -0
  113. recurvedata/operators/python_operator/operator.py +132 -0
  114. recurvedata/operators/sensor_operator/__init__.py +1 -0
  115. recurvedata/operators/sensor_operator/airflow_utils.py +63 -0
  116. recurvedata/operators/sensor_operator/operator.py +172 -0
  117. recurvedata/operators/spark_operator/__init__.py +1 -0
  118. recurvedata/operators/spark_operator/operator.py +200 -0
  119. recurvedata/operators/spark_operator/spark_sample.py +47 -0
  120. recurvedata/operators/sql_operator/__init__.py +1 -0
  121. recurvedata/operators/sql_operator/operator.py +90 -0
  122. recurvedata/operators/task.py +211 -0
  123. recurvedata/operators/transfer_operator/__init__.py +40 -0
  124. recurvedata/operators/transfer_operator/const.py +10 -0
  125. recurvedata/operators/transfer_operator/dump_aliyun_sls.py +82 -0
  126. recurvedata/operators/transfer_operator/dump_sheet_task_base.py +292 -0
  127. recurvedata/operators/transfer_operator/dump_task_cass.py +155 -0
  128. recurvedata/operators/transfer_operator/dump_task_dbapi.py +209 -0
  129. recurvedata/operators/transfer_operator/dump_task_es.py +113 -0
  130. recurvedata/operators/transfer_operator/dump_task_feishu_sheet.py +114 -0
  131. recurvedata/operators/transfer_operator/dump_task_ftp.py +234 -0
  132. recurvedata/operators/transfer_operator/dump_task_google_sheet.py +66 -0
  133. recurvedata/operators/transfer_operator/dump_task_mongodb.py +168 -0
  134. recurvedata/operators/transfer_operator/dump_task_oss.py +285 -0
  135. recurvedata/operators/transfer_operator/dump_task_python.py +212 -0
  136. recurvedata/operators/transfer_operator/dump_task_s3.py +270 -0
  137. recurvedata/operators/transfer_operator/dump_task_sftp.py +229 -0
  138. recurvedata/operators/transfer_operator/load_task_aliyun_oss.py +107 -0
  139. recurvedata/operators/transfer_operator/load_task_azure_blob.py +115 -0
  140. recurvedata/operators/transfer_operator/load_task_azure_synapse.py +90 -0
  141. recurvedata/operators/transfer_operator/load_task_clickhouse.py +167 -0
  142. recurvedata/operators/transfer_operator/load_task_doris.py +164 -0
  143. recurvedata/operators/transfer_operator/load_task_email.py +188 -0
  144. recurvedata/operators/transfer_operator/load_task_es.py +86 -0
  145. recurvedata/operators/transfer_operator/load_task_filebrowser.py +151 -0
  146. recurvedata/operators/transfer_operator/load_task_ftp.py +19 -0
  147. recurvedata/operators/transfer_operator/load_task_google_bigquery.py +90 -0
  148. recurvedata/operators/transfer_operator/load_task_google_cloud_storage.py +127 -0
  149. recurvedata/operators/transfer_operator/load_task_google_sheet.py +130 -0
  150. recurvedata/operators/transfer_operator/load_task_hive.py +158 -0
  151. recurvedata/operators/transfer_operator/load_task_microsoft_fabric.py +105 -0
  152. recurvedata/operators/transfer_operator/load_task_mssql.py +153 -0
  153. recurvedata/operators/transfer_operator/load_task_mysql.py +157 -0
  154. recurvedata/operators/transfer_operator/load_task_owncloud.py +135 -0
  155. recurvedata/operators/transfer_operator/load_task_postgresql.py +109 -0
  156. recurvedata/operators/transfer_operator/load_task_qcloud_cos.py +119 -0
  157. recurvedata/operators/transfer_operator/load_task_recurve_data_prep.py +75 -0
  158. recurvedata/operators/transfer_operator/load_task_redshift.py +95 -0
  159. recurvedata/operators/transfer_operator/load_task_s3.py +150 -0
  160. recurvedata/operators/transfer_operator/load_task_sftp.py +90 -0
  161. recurvedata/operators/transfer_operator/load_task_starrocks.py +169 -0
  162. recurvedata/operators/transfer_operator/load_task_yicrowds.py +97 -0
  163. recurvedata/operators/transfer_operator/mixin.py +31 -0
  164. recurvedata/operators/transfer_operator/operator.py +231 -0
  165. recurvedata/operators/transfer_operator/task.py +223 -0
  166. recurvedata/operators/transfer_operator/utils.py +134 -0
  167. recurvedata/operators/ui.py +80 -0
  168. recurvedata/operators/utils/__init__.py +51 -0
  169. recurvedata/operators/utils/file_factory.py +150 -0
  170. recurvedata/operators/utils/fs.py +10 -0
  171. recurvedata/operators/utils/lineage.py +265 -0
  172. recurvedata/operators/web_init.py +15 -0
  173. recurvedata/pigeon/connector/__init__.py +294 -0
  174. recurvedata/pigeon/connector/_registry.py +17 -0
  175. recurvedata/pigeon/connector/aliyun_oss.py +80 -0
  176. recurvedata/pigeon/connector/awss3.py +123 -0
  177. recurvedata/pigeon/connector/azure_blob.py +176 -0
  178. recurvedata/pigeon/connector/azure_synapse.py +51 -0
  179. recurvedata/pigeon/connector/cass.py +151 -0
  180. recurvedata/pigeon/connector/clickhouse.py +403 -0
  181. recurvedata/pigeon/connector/clickhouse_native.py +351 -0
  182. recurvedata/pigeon/connector/dbapi.py +571 -0
  183. recurvedata/pigeon/connector/doris.py +166 -0
  184. recurvedata/pigeon/connector/es.py +176 -0
  185. recurvedata/pigeon/connector/feishu.py +1135 -0
  186. recurvedata/pigeon/connector/ftp.py +163 -0
  187. recurvedata/pigeon/connector/google_bigquery.py +283 -0
  188. recurvedata/pigeon/connector/google_cloud_storage.py +130 -0
  189. recurvedata/pigeon/connector/hbase_phoenix.py +108 -0
  190. recurvedata/pigeon/connector/hdfs.py +204 -0
  191. recurvedata/pigeon/connector/hive_impala.py +383 -0
  192. recurvedata/pigeon/connector/microsoft_fabric.py +95 -0
  193. recurvedata/pigeon/connector/mongodb.py +56 -0
  194. recurvedata/pigeon/connector/mssql.py +467 -0
  195. recurvedata/pigeon/connector/mysql.py +175 -0
  196. recurvedata/pigeon/connector/owncloud.py +92 -0
  197. recurvedata/pigeon/connector/postgresql.py +267 -0
  198. recurvedata/pigeon/connector/power_bi.py +179 -0
  199. recurvedata/pigeon/connector/qcloud_cos.py +79 -0
  200. recurvedata/pigeon/connector/redshift.py +123 -0
  201. recurvedata/pigeon/connector/sftp.py +73 -0
  202. recurvedata/pigeon/connector/sqlite.py +42 -0
  203. recurvedata/pigeon/connector/starrocks.py +144 -0
  204. recurvedata/pigeon/connector/tableau.py +162 -0
  205. recurvedata/pigeon/const.py +21 -0
  206. recurvedata/pigeon/csv.py +172 -0
  207. recurvedata/pigeon/docs/datasources-example.json +82 -0
  208. recurvedata/pigeon/docs/images/pigeon_design.png +0 -0
  209. recurvedata/pigeon/docs/lightweight-data-sync-solution.md +111 -0
  210. recurvedata/pigeon/dumper/__init__.py +171 -0
  211. recurvedata/pigeon/dumper/aliyun_sls.py +415 -0
  212. recurvedata/pigeon/dumper/base.py +141 -0
  213. recurvedata/pigeon/dumper/cass.py +213 -0
  214. recurvedata/pigeon/dumper/dbapi.py +346 -0
  215. recurvedata/pigeon/dumper/es.py +112 -0
  216. recurvedata/pigeon/dumper/ftp.py +64 -0
  217. recurvedata/pigeon/dumper/mongodb.py +103 -0
  218. recurvedata/pigeon/handler/__init__.py +4 -0
  219. recurvedata/pigeon/handler/base.py +153 -0
  220. recurvedata/pigeon/handler/csv_handler.py +290 -0
  221. recurvedata/pigeon/loader/__init__.py +87 -0
  222. recurvedata/pigeon/loader/base.py +83 -0
  223. recurvedata/pigeon/loader/csv_to_azure_synapse.py +214 -0
  224. recurvedata/pigeon/loader/csv_to_clickhouse.py +152 -0
  225. recurvedata/pigeon/loader/csv_to_doris.py +215 -0
  226. recurvedata/pigeon/loader/csv_to_es.py +51 -0
  227. recurvedata/pigeon/loader/csv_to_google_bigquery.py +169 -0
  228. recurvedata/pigeon/loader/csv_to_hive.py +468 -0
  229. recurvedata/pigeon/loader/csv_to_microsoft_fabric.py +242 -0
  230. recurvedata/pigeon/loader/csv_to_mssql.py +174 -0
  231. recurvedata/pigeon/loader/csv_to_mysql.py +180 -0
  232. recurvedata/pigeon/loader/csv_to_postgresql.py +248 -0
  233. recurvedata/pigeon/loader/csv_to_redshift.py +240 -0
  234. recurvedata/pigeon/loader/csv_to_starrocks.py +233 -0
  235. recurvedata/pigeon/meta.py +116 -0
  236. recurvedata/pigeon/row_factory.py +42 -0
  237. recurvedata/pigeon/schema/__init__.py +124 -0
  238. recurvedata/pigeon/schema/types.py +13 -0
  239. recurvedata/pigeon/sync.py +283 -0
  240. recurvedata/pigeon/transformer.py +146 -0
  241. recurvedata/pigeon/utils/__init__.py +134 -0
  242. recurvedata/pigeon/utils/bloomfilter.py +181 -0
  243. recurvedata/pigeon/utils/date_time.py +323 -0
  244. recurvedata/pigeon/utils/escape.py +15 -0
  245. recurvedata/pigeon/utils/fs.py +266 -0
  246. recurvedata/pigeon/utils/json.py +44 -0
  247. recurvedata/pigeon/utils/keyed_tuple.py +85 -0
  248. recurvedata/pigeon/utils/mp.py +156 -0
  249. recurvedata/pigeon/utils/sql.py +328 -0
  250. recurvedata/pigeon/utils/timing.py +155 -0
  251. recurvedata/provider_manager.py +0 -0
  252. recurvedata/providers/__init__.py +0 -0
  253. recurvedata/providers/dbapi/__init__.py +0 -0
  254. recurvedata/providers/flywheel/__init__.py +0 -0
  255. recurvedata/providers/mysql/__init__.py +0 -0
  256. recurvedata/schedulers/__init__.py +1 -0
  257. recurvedata/schedulers/airflow.py +974 -0
  258. recurvedata/schedulers/airflow_db_process.py +331 -0
  259. recurvedata/schedulers/airflow_operators.py +61 -0
  260. recurvedata/schedulers/airflow_plugin.py +9 -0
  261. recurvedata/schedulers/airflow_trigger_dag_patch.py +117 -0
  262. recurvedata/schedulers/base.py +99 -0
  263. recurvedata/schedulers/cli.py +228 -0
  264. recurvedata/schedulers/client.py +56 -0
  265. recurvedata/schedulers/consts.py +52 -0
  266. recurvedata/schedulers/debug_celery.py +62 -0
  267. recurvedata/schedulers/model.py +63 -0
  268. recurvedata/schedulers/schemas.py +97 -0
  269. recurvedata/schedulers/service.py +20 -0
  270. recurvedata/schedulers/system_dags.py +59 -0
  271. recurvedata/schedulers/task_status.py +279 -0
  272. recurvedata/schedulers/utils.py +73 -0
  273. recurvedata/schema/__init__.py +0 -0
  274. recurvedata/schema/field.py +88 -0
  275. recurvedata/schema/schema.py +55 -0
  276. recurvedata/schema/types.py +17 -0
  277. recurvedata/schema.py +0 -0
  278. recurvedata/server/__init__.py +0 -0
  279. recurvedata/server/app.py +7 -0
  280. recurvedata/server/connector/__init__.py +0 -0
  281. recurvedata/server/connector/api.py +79 -0
  282. recurvedata/server/connector/schemas.py +28 -0
  283. recurvedata/server/data_service/__init__.py +0 -0
  284. recurvedata/server/data_service/api.py +126 -0
  285. recurvedata/server/data_service/client.py +18 -0
  286. recurvedata/server/data_service/consts.py +1 -0
  287. recurvedata/server/data_service/schemas.py +68 -0
  288. recurvedata/server/data_service/service.py +218 -0
  289. recurvedata/server/dbt/__init__.py +0 -0
  290. recurvedata/server/dbt/api.py +116 -0
  291. recurvedata/server/error_code.py +49 -0
  292. recurvedata/server/exceptions.py +19 -0
  293. recurvedata/server/executor/__init__.py +0 -0
  294. recurvedata/server/executor/api.py +37 -0
  295. recurvedata/server/executor/schemas.py +30 -0
  296. recurvedata/server/executor/service.py +220 -0
  297. recurvedata/server/main.py +32 -0
  298. recurvedata/server/schedulers/__init__.py +0 -0
  299. recurvedata/server/schedulers/api.py +252 -0
  300. recurvedata/server/schedulers/schemas.py +50 -0
  301. recurvedata/server/schemas.py +50 -0
  302. recurvedata/utils/__init__.py +15 -0
  303. recurvedata/utils/_typer.py +61 -0
  304. recurvedata/utils/attrdict.py +19 -0
  305. recurvedata/utils/command_helper.py +20 -0
  306. recurvedata/utils/compat.py +12 -0
  307. recurvedata/utils/compression.py +203 -0
  308. recurvedata/utils/crontab.py +42 -0
  309. recurvedata/utils/crypto_util.py +305 -0
  310. recurvedata/utils/dataclass.py +11 -0
  311. recurvedata/utils/date_time.py +464 -0
  312. recurvedata/utils/dispatch.py +114 -0
  313. recurvedata/utils/email_util.py +104 -0
  314. recurvedata/utils/files.py +386 -0
  315. recurvedata/utils/helpers.py +170 -0
  316. recurvedata/utils/httputil.py +117 -0
  317. recurvedata/utils/imports.py +132 -0
  318. recurvedata/utils/json.py +80 -0
  319. recurvedata/utils/log.py +117 -0
  320. recurvedata/utils/log_capture.py +153 -0
  321. recurvedata/utils/mp.py +178 -0
  322. recurvedata/utils/normalizer.py +102 -0
  323. recurvedata/utils/redis_lock.py +474 -0
  324. recurvedata/utils/registry.py +54 -0
  325. recurvedata/utils/shell.py +15 -0
  326. recurvedata/utils/singleton.py +33 -0
  327. recurvedata/utils/sql.py +6 -0
  328. recurvedata/utils/timeout.py +28 -0
  329. recurvedata/utils/tracing.py +14 -0
  330. recurvedata_lib-0.1.487.dist-info/METADATA +605 -0
  331. recurvedata_lib-0.1.487.dist-info/RECORD +333 -0
  332. recurvedata_lib-0.1.487.dist-info/WHEEL +5 -0
  333. recurvedata_lib-0.1.487.dist-info/entry_points.txt +6 -0
@@ -0,0 +1,79 @@
1
+ try:
2
+ from pymongo import MongoClient
3
+ except ImportError:
4
+ pass
5
+
6
+ from recurvedata.connectors._register import register_connector_class
7
+ from recurvedata.connectors.dbapi import DBAPIBase, with_ssh_tunnel
8
+ from recurvedata.consts import ConnectorGroup
9
+ from recurvedata.core.translation import _l
10
+
11
+ CONNECTION_TYPE = "mongodb"
12
+ UI_CONNECTION_TYPE = "MongoDB"
13
+
14
+
15
+ @register_connector_class([CONNECTION_TYPE, UI_CONNECTION_TYPE])
16
+ class MongoDB(DBAPIBase):
17
+ SYSTEM_DATABASES = [
18
+ "admin",
19
+ "config",
20
+ "local",
21
+ ]
22
+ setup_extras_require = ["pymongo"]
23
+ connection_type = CONNECTION_TYPE
24
+ ui_connection_type = UI_CONNECTION_TYPE
25
+ group = [ConnectorGroup.DESTINATION]
26
+
27
+ config_schema = {
28
+ "type": "object",
29
+ "properties": {
30
+ "host": {
31
+ "type": "string",
32
+ "title": _l("Host Address"),
33
+ "default": "127.0.0.1",
34
+ },
35
+ "port": {
36
+ "type": "number",
37
+ "title": _l("Port Number"),
38
+ "default": 27017,
39
+ },
40
+ "user": {"type": "string", "title": _l("Username")},
41
+ "password": {"type": "string", "title": _l("Password")},
42
+ "authSource": {
43
+ "type": "string",
44
+ "title": _l("Authentication Database"),
45
+ "default": "admin",
46
+ },
47
+ },
48
+ "order": ["host", "port", "user", "password", "authSource"],
49
+ "required": ["host", "port", "user", "password"],
50
+ "secret": ["password"],
51
+ }
52
+
53
+ def test_connection(self):
54
+ client = MongoClient(
55
+ host=self.conf["host"],
56
+ port=self.conf["port"],
57
+ username=self.conf["user"],
58
+ password=self.conf["password"],
59
+ authSource=self.conf["authSource"],
60
+ serverSelectionTimeoutMS=5000,
61
+ )
62
+ client.admin.command("ping")
63
+
64
+ @classmethod
65
+ def get_sql_operator_types(cls) -> list[str]:
66
+ return []
67
+
68
+ @with_ssh_tunnel
69
+ def get_databases(self) -> list[str]:
70
+ client = MongoClient(
71
+ host=self.conf["host"],
72
+ port=self.conf["port"],
73
+ username=self.conf["user"],
74
+ password=self.conf["password"],
75
+ authSource=self.conf["authSource"],
76
+ serverSelectionTimeoutMS=5000,
77
+ )
78
+ databases = client.list_database_names()
79
+ return [d for d in databases if d not in self.SYSTEM_DATABASES]
@@ -0,0 +1,131 @@
1
+ from sqlalchemy.engine import URL
2
+
3
+ from recurvedata.connectors._register import register_connector_class
4
+ from recurvedata.connectors.dbapi import DBAPIBase
5
+ from recurvedata.consts import ConnectorGroup
6
+ from recurvedata.core.translation import _l
7
+
8
+ CONNECTION_TYPE = "mssql"
9
+ UI_CONNECTION_TYPE = "Microsoft SQL Server"
10
+
11
+
12
+ @register_connector_class([CONNECTION_TYPE, UI_CONNECTION_TYPE])
13
+ class MssqlConnector(DBAPIBase):
14
+ SYSTEM_DATABASES = ["master", "model", "msdb", "tempdb"]
15
+ connection_type = CONNECTION_TYPE
16
+ ui_connection_type = UI_CONNECTION_TYPE
17
+ group = [ConnectorGroup.DESTINATION]
18
+
19
+ setup_extras_require = ["pyodbc"]
20
+ driver = "mssql+pyodbc"
21
+
22
+ config_schema = {
23
+ "type": "object",
24
+ "properties": {
25
+ "host": {
26
+ "type": "string",
27
+ "title": _l("Host Address"),
28
+ "default": "127.0.0.1",
29
+ },
30
+ "port": {
31
+ "type": "number",
32
+ "title": _l("Port Number"),
33
+ "default": 1433,
34
+ },
35
+ "user": {"type": "string", "title": _l("Username")},
36
+ "password": {"type": "string", "title": _l("Password")},
37
+ "database": {
38
+ "type": "string",
39
+ "title": _l("Database Name"),
40
+ "description": _l("The name of the database to connect to"),
41
+ "default": "default",
42
+ },
43
+ "odbc_driver": {
44
+ "type": "string",
45
+ "title": _l("ODBC Driver"),
46
+ "default": "ODBC Driver 18 for SQL Server",
47
+ },
48
+ "encrypt": {
49
+ "type": "boolean",
50
+ "title": _l("Encrypt Connection"),
51
+ "description": _l("Whether to encrypt the connection"),
52
+ "default": True,
53
+ },
54
+ "trust_server_certificate": {
55
+ "type": "boolean",
56
+ "title": _l("Trust Server Certificate"),
57
+ "default": True,
58
+ },
59
+ },
60
+ "order": ["host", "port", "user", "password", "database", "odbc_driver", "encrypt", "trust_server_certificate"],
61
+ "required": [
62
+ "host",
63
+ ],
64
+ "secret": ["password"],
65
+ }
66
+
67
+ @property
68
+ def odbc_driver(self):
69
+ return self.conf["odbc_driver"]
70
+
71
+ @property
72
+ def sqlalchemy_url(self):
73
+ return URL(
74
+ self.driver,
75
+ self.user,
76
+ self.password,
77
+ self.host,
78
+ self.port,
79
+ self.database,
80
+ query={
81
+ "driver": self.odbc_driver,
82
+ "TrustServerCertificate": "yes" if self.conf.get("trust_server_certificate", False) else "no",
83
+ "Encrypt": "yes" if self.conf.get("encrypt", True) else "no",
84
+ },
85
+ )
86
+
87
+ @classmethod
88
+ def get_sql_operator_types(cls):
89
+ return [cls.connection_type, "azure_mssql"]
90
+
91
+ @classmethod
92
+ def order_sql(cls, sql: str, orders: list[dict[str, str]] | None = None, return_sql: bool = True) -> str:
93
+ """Format SQL with ORDER BY clause for MSSQL.
94
+
95
+ Args:
96
+ sql: The SQL query to add ORDER BY to
97
+ orders: List of dicts with column and order direction
98
+ return_sql: Whether to return the formatted SQL
99
+
100
+ Returns:
101
+ SQL query with ORDER BY clause
102
+ """
103
+ if not orders:
104
+ return sql
105
+
106
+ order_clauses = [f"{order['field']} {order['order']}" for order in orders]
107
+ order_by = f" ORDER BY {', '.join(order_clauses)}"
108
+ return f"{sql}{order_by}"
109
+
110
+ @classmethod
111
+ def limit_sql(
112
+ cls, sql: str, limit: int = 100, orders: list[dict[str, str]] | None = None, offset: int | None = None
113
+ ) -> str:
114
+ """Add TOP/OFFSET-FETCH clause to SQL query for MSSQL.
115
+
116
+ Args:
117
+ sql: The SQL query to add limit to
118
+ limit: Maximum number of rows to return
119
+ orders: List of order by clauses
120
+ offset: Number of rows to skip
121
+
122
+ Returns:
123
+ SQL query with TOP/OFFSET-FETCH clause
124
+ """
125
+ sub_query_name = "_recurve_limit_subquery"
126
+ sql = cls.order_sql(sql, orders)
127
+
128
+ if offset:
129
+ return f"{sql} OFFSET {offset} ROWS FETCH NEXT {limit} ROWS ONLY"
130
+
131
+ return f"SELECT TOP {limit} * FROM ({sql}) AS {sub_query_name}"
@@ -0,0 +1,191 @@
1
+ from functools import cached_property
2
+ from typing import Any
3
+
4
+ from recurvedata.connectors._register import register_connector_class
5
+ from recurvedata.connectors.datasource import DataSourceWrapper
6
+ from recurvedata.connectors.dbapi import DBAPIBase, with_ssh_tunnel
7
+ from recurvedata.consts import ConnectorGroup
8
+ from recurvedata.core.translation import _l
9
+
10
+ CONNECTION_TYPE = "mysql"
11
+ UI_CONNECTION_TYPE = "MySQL"
12
+
13
+
14
+ @register_connector_class([CONNECTION_TYPE, UI_CONNECTION_TYPE])
15
+ class MysqlConnector(DBAPIBase):
16
+ SYSTEM_DATABASES = ["information_schema", "mysql", "performance_schema", "sys"]
17
+ connection_type = CONNECTION_TYPE
18
+ ui_connection_type = UI_CONNECTION_TYPE
19
+ group = [ConnectorGroup.DESTINATION]
20
+
21
+ setup_extras_require = ["pymysql"]
22
+ driver = "mysql+pymysql"
23
+ config_schema = {
24
+ "type": "object",
25
+ "properties": {
26
+ "host": {
27
+ "type": "string",
28
+ "title": _l("Host Address"),
29
+ "default": "127.0.0.1",
30
+ },
31
+ "port": {
32
+ "type": "number",
33
+ "title": _l("Port Number"),
34
+ "default": 3306,
35
+ },
36
+ "user": {"type": "string", "title": _l("Username")},
37
+ "password": {"type": "string", "title": _l("Password")},
38
+ "database": {
39
+ "type": "string",
40
+ "title": _l("Database Name"),
41
+ "description": _l("The name of the database to connect to"),
42
+ },
43
+ # 'ssh_tunnel': SSH_TUNNEL_CONFIG_SCHEMA
44
+ },
45
+ "order": [
46
+ "host",
47
+ "port",
48
+ "user",
49
+ "password",
50
+ "database",
51
+ ],
52
+ "required": [
53
+ "host",
54
+ ],
55
+ "secret": ["password"],
56
+ }
57
+
58
+ column_type_mapping = {
59
+ "integer": ["mediumint", "year", "long"],
60
+ "string": ["tinytext", "mediumtext", "longtext", "enum", "set", "blob"],
61
+ "binary": ["tinyblob", "mediumblob", "longblob"],
62
+ }
63
+
64
+ available_column_types = DBAPIBase.available_column_types + [
65
+ # Integer types
66
+ "tinyint",
67
+ "tinyint unsigned",
68
+ "bit",
69
+ "mediumint",
70
+ "int unsigned",
71
+ "integer", # alias for int
72
+ "bigint unsigned",
73
+ # Floating-point types
74
+ "numeric", # alias for decimal
75
+ "fixed", # alias for decimal
76
+ "double precision", # alias for double
77
+ "float4", # alias for float
78
+ "float8", # alias for double
79
+ # String types
80
+ "tinytext",
81
+ "text",
82
+ "mediumtext",
83
+ "longtext",
84
+ "long", # alias for mediumtext
85
+ "character", # alias for char
86
+ "long varchar", # alias for mediumtext
87
+ "enum",
88
+ "set",
89
+ # Binary string types
90
+ "binary",
91
+ "varbinary",
92
+ "tinyblob",
93
+ "blob",
94
+ "mediumblob",
95
+ "longblob",
96
+ # Date and Time types
97
+ "datetime",
98
+ "time",
99
+ "year",
100
+ # Boolean type
101
+ "bool", # alias for boolean
102
+ ]
103
+
104
+ @with_ssh_tunnel
105
+ def get_columns(self, table, database=None):
106
+ database = database or self.database
107
+ query = f"""
108
+ SELECT column_name,
109
+ data_type,
110
+ is_nullable,
111
+ column_default,
112
+ column_comment
113
+ FROM INFORMATION_SCHEMA.COLUMNS
114
+ WHERE TABLE_SCHEMA = '{database}' AND TABLE_NAME = '{table}';
115
+ """
116
+ result = self.fetchall(query)
117
+ column_metas = []
118
+ for row in result:
119
+ column_metas.append(
120
+ {
121
+ "name": row[0],
122
+ "type": row[1].lower() if row[1] else "",
123
+ "nullable": row[2] == "YES",
124
+ "default": row[3],
125
+ "comment": row[4],
126
+ }
127
+ )
128
+ return column_metas
129
+
130
+ @cached_property
131
+ @with_ssh_tunnel
132
+ def type_code_mapping(self) -> dict:
133
+ """
134
+ type_code from sqlalchemy's cursor.description -> database's dialect data type name
135
+ """
136
+ return {}
137
+
138
+ def sqlalchemy_column_type_code_to_name(self, type_code: Any, size: int | None = None) -> str:
139
+ from pymysql.constants import FIELD_TYPE
140
+
141
+ mapping = {type_code: name.lower() for name, type_code in vars(FIELD_TYPE).items() if not name.startswith("__")}
142
+ type_mapping = {
143
+ FIELD_TYPE.TINY: "tinyint",
144
+ FIELD_TYPE.SHORT: "smallint",
145
+ FIELD_TYPE.LONG: "int",
146
+ FIELD_TYPE.LONGLONG: "bigint",
147
+ FIELD_TYPE.INT24: "mediumint",
148
+ FIELD_TYPE.NEWDECIMAL: "decimal",
149
+ FIELD_TYPE.TINY_BLOB: "tinyblob",
150
+ FIELD_TYPE.MEDIUM_BLOB: "mediumblob",
151
+ FIELD_TYPE.LONG_BLOB: "longblob",
152
+ FIELD_TYPE.VAR_STRING: "varchar",
153
+ FIELD_TYPE.STRING: "char",
154
+ }
155
+ return type_mapping.get(type_code, mapping.get(type_code, "varchar"))
156
+
157
+ @classmethod
158
+ def order_sql(cls, sql: str, orders: list[dict[str, str]] = None):
159
+ base_sql = f"SELECT * FROM ({sql}) AS _recurve_limit_subquery"
160
+ if orders:
161
+ order_clauses = [f"{order['field']} {order['order']}" for order in orders]
162
+ base_sql += " ORDER BY " + ", ".join(order_clauses)
163
+ return base_sql
164
+
165
+ @classmethod
166
+ def limit_sql(cls, sql: str, limit: int = 100, orders: list[dict[str, str]] | None = None, offset: int = 0) -> str:
167
+ """
168
+ the sqlglot will convert `timestamp` to `datetime`,
169
+ which cause this sql: `cast(field as timestamp) as field` to be error in dbt build but success in preview.
170
+ args:
171
+ sql: the sql to be limited
172
+ limit: the limit of the sql
173
+ orders: the orders of the sql(list[dict[str, str]]), each dict contains `field` and `order`, used in data service preview
174
+ """
175
+ base_sql = cls.order_sql(sql, orders)
176
+ if offset:
177
+ return f"{base_sql} LIMIT {offset}, {limit}"
178
+
179
+ return f"{base_sql} LIMIT {limit}"
180
+
181
+ def convert_config_to_cube_config(
182
+ self, database: str, schema: str = None, datasource: DataSourceWrapper = None
183
+ ) -> dict:
184
+ return {
185
+ "type": "mysql",
186
+ "host": self.host,
187
+ "port": self.port,
188
+ "user": datasource.user,
189
+ "password": datasource.password,
190
+ "database": database or self.database,
191
+ }
@@ -0,0 +1,141 @@
1
+ import base64
2
+ from contextlib import contextmanager
3
+ from urllib.parse import urlparse
4
+
5
+ import httpx
6
+
7
+ from recurvedata.connectors._register import register_connector_class
8
+ from recurvedata.connectors.base import RecurveConnectorBase
9
+ from recurvedata.connectors.proxy import HTTP_PROXY_CONFIG_SCHEMA, HttpProxyMixin
10
+ from recurvedata.consts import ConnectorGroup
11
+ from recurvedata.core.translation import _l
12
+
13
+ CONNECTION_TYPE = "n8n"
14
+ UI_CONNECTION_TYPE = "n8n"
15
+
16
+
17
+ @register_connector_class([CONNECTION_TYPE, UI_CONNECTION_TYPE])
18
+ class N8N(HttpProxyMixin, RecurveConnectorBase):
19
+ connection_type = CONNECTION_TYPE
20
+ ui_connection_type = UI_CONNECTION_TYPE
21
+ group = [ConnectorGroup.DESTINATION]
22
+
23
+ config_schema = {
24
+ "type": "object",
25
+ "properties": {
26
+ "url": {
27
+ "type": "string",
28
+ "title": _l("API Address"),
29
+ "description": _l("The URL of the n8n API, e.g. https://localhost:5678/api/v1"),
30
+ },
31
+ "api_key": {"type": "string", "title": _l("API KEY")},
32
+ "timeout": {
33
+ "type": "number",
34
+ "title": _l("Timeout"),
35
+ "description": _l("The timeout of the n8n API, e.g. 60"),
36
+ "default": 60,
37
+ },
38
+ "webhook_credential": {
39
+ "type": "object",
40
+ "title": _l("Webhook Trigger Node Credential"),
41
+ "description": _l("The credential of the n8n webhook trigger node"),
42
+ "properties": {
43
+ "credential_type": {
44
+ "type": "string",
45
+ "title": _l("Credential Type"),
46
+ "enum": ["Basic Auth", "Header Auth", "JWT Auth", "None"],
47
+ "enumNames": ["Basic Auth", "Header Auth", "JWT Auth", "None"],
48
+ "default": "None",
49
+ },
50
+ "basic_auth": {
51
+ "ui:hidden": '{{parentFormData.credential_type !== "Basic Auth"}}',
52
+ "type": "object",
53
+ "title": _l("Basic Auth"),
54
+ "description": _l("The basic auth of the n8n webhook trigger node"),
55
+ "properties": {
56
+ "username": {"type": "string", "title": _l("Username")},
57
+ "password": {"type": "string", "title": _l("Password")},
58
+ },
59
+ },
60
+ "header_auth": {
61
+ "ui:hidden": '{{parentFormData.credential_type !== "Header Auth"}}',
62
+ "type": "object",
63
+ "title": _l("Header Auth"),
64
+ "description": _l("The header auth of the n8n webhook trigger node"),
65
+ "properties": {
66
+ "header_name": {"type": "string", "title": _l("Header Name")},
67
+ "header_value": {"type": "string", "title": _l("Header Value")},
68
+ },
69
+ },
70
+ "jwt_auth": {
71
+ "ui:hidden": '{{parentFormData.credential_type !== "JWT Auth"}}',
72
+ "type": "object",
73
+ "title": _l("JWT Auth"),
74
+ "description": _l("The jwt auth of the n8n webhook trigger node"),
75
+ "properties": {
76
+ "jwt_token": {"type": "string", "title": _l("JWT Token")},
77
+ },
78
+ },
79
+ },
80
+ "order": ["credential_type", "basic_auth", "header_auth", "jwt_auth"],
81
+ },
82
+ "proxies": HTTP_PROXY_CONFIG_SCHEMA["proxies"],
83
+ },
84
+ "order": ["url", "api_key", "timeout", "webhook_credential", "proxies"],
85
+ "required": ["url", "api_key"],
86
+ "secret": [
87
+ "api_key",
88
+ "webhook_credential.basic_auth.password",
89
+ "webhook_credential.header_auth.header_value",
90
+ "webhook_credential.jwt_auth.jwt_token",
91
+ ],
92
+ }
93
+
94
+ def test_connection(self):
95
+ pass
96
+
97
+ @contextmanager
98
+ def _n8n_client(self) -> httpx.Client:
99
+ with self._init_proxy_manager():
100
+ yield httpx.Client(
101
+ base_url=f"{self.url}", headers={"X-N8N-API-KEY": f"{self.api_key}"}, timeout=self.timeout
102
+ )
103
+
104
+ def get_workflows(self) -> list[dict]:
105
+ path = "/workflows"
106
+ workflows = []
107
+ cursor = None
108
+ with self._n8n_client() as client:
109
+ response = client.get(path)
110
+ workflows.extend(response.json()["data"])
111
+ if response.json()["nextCursor"] and response.json()["nextCursor"] != cursor:
112
+ cursor = response.json()["nextCursor"]
113
+ while cursor:
114
+ response = client.get(path, params={"cursor": cursor})
115
+ workflows.extend(response.json()["data"])
116
+ cursor = response.json()["nextCursor"]
117
+ return workflows
118
+
119
+ def _trigger_workflow_via_webhook(self, webhook_id: str, payload: dict) -> dict:
120
+ main_url = f"{urlparse(self.url).scheme}://{urlparse(self.url).netloc}"
121
+ webhook_url = f"{main_url}/webhook/{webhook_id}"
122
+ headers = {}
123
+ credential_type = self.webhook_credential.get("credential_type")
124
+ basic_auth = self.webhook_credential.get("basic_auth", {})
125
+ header_auth = self.webhook_credential.get("header_auth", {})
126
+ jwt_auth = self.webhook_credential.get("jwt_auth", {})
127
+ with self._init_proxy_manager():
128
+ if credential_type == "Basic Auth":
129
+ username = basic_auth.get("username", "")
130
+ password = basic_auth.get("password", "")
131
+ headers["Authorization"] = f'Basic {base64.b64encode(f"{username}:{password}".encode()).decode()}'
132
+ elif credential_type == "Header Auth":
133
+ header_name = header_auth.get("header_name", "")
134
+ header_value = header_auth.get("header_value", "")
135
+ headers[header_name] = header_value
136
+ elif credential_type == "JWT Auth":
137
+ jwt_token = jwt_auth.get("jwt_token", "")
138
+ headers["Authorization"] = f"Bearer {jwt_token}"
139
+ response = httpx.post(url=webhook_url, headers=headers, timeout=self.timeout, json=payload)
140
+
141
+ return response.json()
@@ -0,0 +1,74 @@
1
+ try:
2
+ from ossfs import OSSFileSystem
3
+ except ImportError:
4
+ OSSFileSystem = None
5
+
6
+ from recurvedata.connectors._register import register_connector_class
7
+ from recurvedata.connectors.object_store import ObjectStoreMixin
8
+ from recurvedata.connectors.proxy import HTTP_PROXY_CONFIG_SCHEMA, HttpProxyMixin
9
+ from recurvedata.connectors.utils import juice_sync_process_special_character_within_secret
10
+ from recurvedata.consts import ConnectorGroup
11
+ from recurvedata.core.translation import _l
12
+
13
+ CONNECTION_TYPE = "oss"
14
+ UI_CONNECTION_TYPE = "Aliyun OSS"
15
+
16
+
17
+ @register_connector_class([CONNECTION_TYPE, UI_CONNECTION_TYPE])
18
+ class OSS(HttpProxyMixin, ObjectStoreMixin):
19
+ connection_type = CONNECTION_TYPE
20
+ ui_connection_type = UI_CONNECTION_TYPE
21
+ group = [ConnectorGroup.DESTINATION]
22
+ setup_extras_require = ["ossfs"]
23
+
24
+ config_schema = {
25
+ "type": "object",
26
+ "properties": {
27
+ "access_key_id": {
28
+ "type": "string",
29
+ "title": _l("Aliyun Access Key ID"),
30
+ "description": _l("The AccessKey ID for authenticating with Aliyun OSS"),
31
+ },
32
+ "secret_access_key": {
33
+ "type": "string",
34
+ "title": _l("Aliyun Secret Access Key"),
35
+ "description": _l("The AccessKey Secret for authenticating with Aliyun OSS"),
36
+ },
37
+ "endpoint": {
38
+ "type": "string",
39
+ "title": _l("OSS Endpoint"),
40
+ "description": _l("The endpoint URL for your OSS bucket region"),
41
+ "default": "oss-cn-hangzhou.aliyuncs.com",
42
+ },
43
+ "bucket": {"type": "string", "title": _l("Bucket Name")},
44
+ "proxies": HTTP_PROXY_CONFIG_SCHEMA["proxies"],
45
+ },
46
+ "order": [
47
+ "access_key_id",
48
+ "secret_access_key",
49
+ "endpoint",
50
+ "bucket",
51
+ "proxies",
52
+ ],
53
+ "required": ["access_key_id", "secret_access_key", "endpoint", "bucket"],
54
+ "secret": ["secret_access_key"],
55
+ }
56
+
57
+ def init_connection(self, conf) -> OSSFileSystem:
58
+ con = OSSFileSystem(
59
+ key=conf["access_key_id"],
60
+ secret=conf["secret_access_key"],
61
+ endpoint=conf["endpoint"],
62
+ )
63
+ self.connector = con
64
+ return con
65
+
66
+ juice_sync_able = True
67
+
68
+ def juice_sync_path(self, path: str) -> tuple[str, str]:
69
+ secret_part = f"{self.access_key_id}:{self.secret_access_key}"
70
+ secret_part = juice_sync_process_special_character_within_secret(secret_part)
71
+ common_part = f'{self.bucket}.{self.endpoint}/{path.lstrip("/")}'
72
+ secret_path = f"oss://{secret_part}@{common_part}"
73
+ non_secret_path = f"oss://{common_part}"
74
+ return secret_path, non_secret_path
@@ -0,0 +1,36 @@
1
+ from recurvedata.connectors._register import register_connector_class
2
+ from recurvedata.connectors.base import RecurveConnectorBase
3
+ from recurvedata.consts import ConnectionCategory, ConnectorGroup
4
+ from recurvedata.core.translation import _l
5
+
6
+ CONNECTION_TYPE = "owncloud"
7
+ UI_CONNECTION_TYPE = "OwnCloud"
8
+
9
+
10
+ @register_connector_class([CONNECTION_TYPE, UI_CONNECTION_TYPE])
11
+ class OwnCloud(RecurveConnectorBase):
12
+ connection_type = CONNECTION_TYPE
13
+ ui_connection_type = UI_CONNECTION_TYPE
14
+ category = [ConnectionCategory.STORAGE]
15
+ group = [ConnectorGroup.DESTINATION]
16
+
17
+ config_schema = {
18
+ "type": "object",
19
+ "properties": {
20
+ "url": {"type": "string", "title": _l("Host URL")},
21
+ "user": {"type": "string", "title": _l("Username")},
22
+ "password": {"type": "string", "title": _l("Password")},
23
+ "dav_endpoint_version": {
24
+ "type": "number",
25
+ "title": _l("WebDAV Endpoint Version"),
26
+ "default": 1,
27
+ },
28
+ },
29
+ "order": ["url", "user", "password", "dav_endpoint_version"],
30
+ "required": ["url", "user", "password"],
31
+ "secret": ["password"],
32
+ }
33
+
34
+ def test_connection(self):
35
+ # todo: 暂不校验
36
+ pass