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.
- recurvedata/__init__.py +0 -0
- recurvedata/__version__.py +1 -0
- recurvedata/client/__init__.py +3 -0
- recurvedata/client/client.py +150 -0
- recurvedata/client/server_client.py +91 -0
- recurvedata/config.py +99 -0
- recurvedata/connectors/__init__.py +20 -0
- recurvedata/connectors/_register.py +46 -0
- recurvedata/connectors/base.py +111 -0
- recurvedata/connectors/config_schema.py +1575 -0
- recurvedata/connectors/connectors/__init__.py +0 -0
- recurvedata/connectors/connectors/aliyun_access_key.py +30 -0
- recurvedata/connectors/connectors/auth.py +44 -0
- recurvedata/connectors/connectors/azure_blob.py +89 -0
- recurvedata/connectors/connectors/azure_synapse.py +79 -0
- recurvedata/connectors/connectors/bigquery.py +359 -0
- recurvedata/connectors/connectors/clickhouse.py +219 -0
- recurvedata/connectors/connectors/dingtalk.py +61 -0
- recurvedata/connectors/connectors/doris.py +215 -0
- recurvedata/connectors/connectors/es.py +62 -0
- recurvedata/connectors/connectors/feishu.py +65 -0
- recurvedata/connectors/connectors/ftp.py +50 -0
- recurvedata/connectors/connectors/generic.py +49 -0
- recurvedata/connectors/connectors/google_cloud_storage.py +115 -0
- recurvedata/connectors/connectors/google_service_account.py +225 -0
- recurvedata/connectors/connectors/hive.py +207 -0
- recurvedata/connectors/connectors/impala.py +210 -0
- recurvedata/connectors/connectors/jenkins.py +51 -0
- recurvedata/connectors/connectors/mail.py +89 -0
- recurvedata/connectors/connectors/microsoft_fabric.py +284 -0
- recurvedata/connectors/connectors/mongo.py +79 -0
- recurvedata/connectors/connectors/mssql.py +131 -0
- recurvedata/connectors/connectors/mysql.py +191 -0
- recurvedata/connectors/connectors/n8n.py +141 -0
- recurvedata/connectors/connectors/oss.py +74 -0
- recurvedata/connectors/connectors/owncloud.py +36 -0
- recurvedata/connectors/connectors/phoenix.py +36 -0
- recurvedata/connectors/connectors/postgres.py +230 -0
- recurvedata/connectors/connectors/python.py +50 -0
- recurvedata/connectors/connectors/redshift.py +187 -0
- recurvedata/connectors/connectors/s3.py +93 -0
- recurvedata/connectors/connectors/sftp.py +87 -0
- recurvedata/connectors/connectors/slack.py +35 -0
- recurvedata/connectors/connectors/spark.py +99 -0
- recurvedata/connectors/connectors/starrocks.py +175 -0
- recurvedata/connectors/connectors/tencent_cos.py +40 -0
- recurvedata/connectors/connectors/tidb.py +49 -0
- recurvedata/connectors/const.py +315 -0
- recurvedata/connectors/datasource.py +189 -0
- recurvedata/connectors/dbapi.py +469 -0
- recurvedata/connectors/fs.py +66 -0
- recurvedata/connectors/ftp.py +40 -0
- recurvedata/connectors/object_store.py +60 -0
- recurvedata/connectors/pigeon.py +172 -0
- recurvedata/connectors/proxy.py +104 -0
- recurvedata/connectors/service.py +223 -0
- recurvedata/connectors/utils.py +47 -0
- recurvedata/consts.py +49 -0
- recurvedata/core/__init__.py +0 -0
- recurvedata/core/config.py +46 -0
- recurvedata/core/configurable.py +27 -0
- recurvedata/core/consts.py +2 -0
- recurvedata/core/templating.py +206 -0
- recurvedata/core/tracing.py +223 -0
- recurvedata/core/transformer.py +186 -0
- recurvedata/core/translation.py +91 -0
- recurvedata/dbt/client.py +97 -0
- recurvedata/dbt/consts.py +99 -0
- recurvedata/dbt/cosmos_utils.py +275 -0
- recurvedata/dbt/error_codes.py +18 -0
- recurvedata/dbt/schemas.py +98 -0
- recurvedata/dbt/service.py +451 -0
- recurvedata/dbt/utils.py +246 -0
- recurvedata/error_codes.py +71 -0
- recurvedata/exceptions.py +72 -0
- recurvedata/executors/__init__.py +4 -0
- recurvedata/executors/cli/__init__.py +7 -0
- recurvedata/executors/cli/connector.py +117 -0
- recurvedata/executors/cli/dbt.py +118 -0
- recurvedata/executors/cli/main.py +82 -0
- recurvedata/executors/cli/parameters.py +18 -0
- recurvedata/executors/client.py +190 -0
- recurvedata/executors/consts.py +50 -0
- recurvedata/executors/debug_executor.py +100 -0
- recurvedata/executors/executor.py +300 -0
- recurvedata/executors/link_executor.py +189 -0
- recurvedata/executors/models.py +34 -0
- recurvedata/executors/schemas.py +222 -0
- recurvedata/executors/service/__init__.py +0 -0
- recurvedata/executors/service/connector.py +380 -0
- recurvedata/executors/utils.py +172 -0
- recurvedata/filestorage/__init__.py +11 -0
- recurvedata/filestorage/_factory.py +33 -0
- recurvedata/filestorage/backends/__init__.py +0 -0
- recurvedata/filestorage/backends/fsspec.py +45 -0
- recurvedata/filestorage/backends/local.py +67 -0
- recurvedata/filestorage/backends/oss.py +56 -0
- recurvedata/filestorage/interface.py +84 -0
- recurvedata/operators/__init__.py +10 -0
- recurvedata/operators/base.py +28 -0
- recurvedata/operators/config.py +21 -0
- recurvedata/operators/context.py +255 -0
- recurvedata/operators/dbt_operator/__init__.py +2 -0
- recurvedata/operators/dbt_operator/model_pipeline_link_operator.py +55 -0
- recurvedata/operators/dbt_operator/operator.py +353 -0
- recurvedata/operators/link_operator/__init__.py +1 -0
- recurvedata/operators/link_operator/operator.py +120 -0
- recurvedata/operators/models.py +55 -0
- recurvedata/operators/notify_operator/__init__.py +1 -0
- recurvedata/operators/notify_operator/operator.py +180 -0
- recurvedata/operators/operator.py +119 -0
- recurvedata/operators/python_operator/__init__.py +1 -0
- recurvedata/operators/python_operator/operator.py +132 -0
- recurvedata/operators/sensor_operator/__init__.py +1 -0
- recurvedata/operators/sensor_operator/airflow_utils.py +63 -0
- recurvedata/operators/sensor_operator/operator.py +172 -0
- recurvedata/operators/spark_operator/__init__.py +1 -0
- recurvedata/operators/spark_operator/operator.py +200 -0
- recurvedata/operators/spark_operator/spark_sample.py +47 -0
- recurvedata/operators/sql_operator/__init__.py +1 -0
- recurvedata/operators/sql_operator/operator.py +90 -0
- recurvedata/operators/task.py +211 -0
- recurvedata/operators/transfer_operator/__init__.py +40 -0
- recurvedata/operators/transfer_operator/const.py +10 -0
- recurvedata/operators/transfer_operator/dump_aliyun_sls.py +82 -0
- recurvedata/operators/transfer_operator/dump_sheet_task_base.py +292 -0
- recurvedata/operators/transfer_operator/dump_task_cass.py +155 -0
- recurvedata/operators/transfer_operator/dump_task_dbapi.py +209 -0
- recurvedata/operators/transfer_operator/dump_task_es.py +113 -0
- recurvedata/operators/transfer_operator/dump_task_feishu_sheet.py +114 -0
- recurvedata/operators/transfer_operator/dump_task_ftp.py +234 -0
- recurvedata/operators/transfer_operator/dump_task_google_sheet.py +66 -0
- recurvedata/operators/transfer_operator/dump_task_mongodb.py +168 -0
- recurvedata/operators/transfer_operator/dump_task_oss.py +285 -0
- recurvedata/operators/transfer_operator/dump_task_python.py +212 -0
- recurvedata/operators/transfer_operator/dump_task_s3.py +270 -0
- recurvedata/operators/transfer_operator/dump_task_sftp.py +229 -0
- recurvedata/operators/transfer_operator/load_task_aliyun_oss.py +107 -0
- recurvedata/operators/transfer_operator/load_task_azure_blob.py +115 -0
- recurvedata/operators/transfer_operator/load_task_azure_synapse.py +90 -0
- recurvedata/operators/transfer_operator/load_task_clickhouse.py +167 -0
- recurvedata/operators/transfer_operator/load_task_doris.py +164 -0
- recurvedata/operators/transfer_operator/load_task_email.py +188 -0
- recurvedata/operators/transfer_operator/load_task_es.py +86 -0
- recurvedata/operators/transfer_operator/load_task_filebrowser.py +151 -0
- recurvedata/operators/transfer_operator/load_task_ftp.py +19 -0
- recurvedata/operators/transfer_operator/load_task_google_bigquery.py +90 -0
- recurvedata/operators/transfer_operator/load_task_google_cloud_storage.py +127 -0
- recurvedata/operators/transfer_operator/load_task_google_sheet.py +130 -0
- recurvedata/operators/transfer_operator/load_task_hive.py +158 -0
- recurvedata/operators/transfer_operator/load_task_microsoft_fabric.py +105 -0
- recurvedata/operators/transfer_operator/load_task_mssql.py +153 -0
- recurvedata/operators/transfer_operator/load_task_mysql.py +157 -0
- recurvedata/operators/transfer_operator/load_task_owncloud.py +135 -0
- recurvedata/operators/transfer_operator/load_task_postgresql.py +109 -0
- recurvedata/operators/transfer_operator/load_task_qcloud_cos.py +119 -0
- recurvedata/operators/transfer_operator/load_task_recurve_data_prep.py +75 -0
- recurvedata/operators/transfer_operator/load_task_redshift.py +95 -0
- recurvedata/operators/transfer_operator/load_task_s3.py +150 -0
- recurvedata/operators/transfer_operator/load_task_sftp.py +90 -0
- recurvedata/operators/transfer_operator/load_task_starrocks.py +169 -0
- recurvedata/operators/transfer_operator/load_task_yicrowds.py +97 -0
- recurvedata/operators/transfer_operator/mixin.py +31 -0
- recurvedata/operators/transfer_operator/operator.py +231 -0
- recurvedata/operators/transfer_operator/task.py +223 -0
- recurvedata/operators/transfer_operator/utils.py +134 -0
- recurvedata/operators/ui.py +80 -0
- recurvedata/operators/utils/__init__.py +51 -0
- recurvedata/operators/utils/file_factory.py +150 -0
- recurvedata/operators/utils/fs.py +10 -0
- recurvedata/operators/utils/lineage.py +265 -0
- recurvedata/operators/web_init.py +15 -0
- recurvedata/pigeon/connector/__init__.py +294 -0
- recurvedata/pigeon/connector/_registry.py +17 -0
- recurvedata/pigeon/connector/aliyun_oss.py +80 -0
- recurvedata/pigeon/connector/awss3.py +123 -0
- recurvedata/pigeon/connector/azure_blob.py +176 -0
- recurvedata/pigeon/connector/azure_synapse.py +51 -0
- recurvedata/pigeon/connector/cass.py +151 -0
- recurvedata/pigeon/connector/clickhouse.py +403 -0
- recurvedata/pigeon/connector/clickhouse_native.py +351 -0
- recurvedata/pigeon/connector/dbapi.py +571 -0
- recurvedata/pigeon/connector/doris.py +166 -0
- recurvedata/pigeon/connector/es.py +176 -0
- recurvedata/pigeon/connector/feishu.py +1135 -0
- recurvedata/pigeon/connector/ftp.py +163 -0
- recurvedata/pigeon/connector/google_bigquery.py +283 -0
- recurvedata/pigeon/connector/google_cloud_storage.py +130 -0
- recurvedata/pigeon/connector/hbase_phoenix.py +108 -0
- recurvedata/pigeon/connector/hdfs.py +204 -0
- recurvedata/pigeon/connector/hive_impala.py +383 -0
- recurvedata/pigeon/connector/microsoft_fabric.py +95 -0
- recurvedata/pigeon/connector/mongodb.py +56 -0
- recurvedata/pigeon/connector/mssql.py +467 -0
- recurvedata/pigeon/connector/mysql.py +175 -0
- recurvedata/pigeon/connector/owncloud.py +92 -0
- recurvedata/pigeon/connector/postgresql.py +267 -0
- recurvedata/pigeon/connector/power_bi.py +179 -0
- recurvedata/pigeon/connector/qcloud_cos.py +79 -0
- recurvedata/pigeon/connector/redshift.py +123 -0
- recurvedata/pigeon/connector/sftp.py +73 -0
- recurvedata/pigeon/connector/sqlite.py +42 -0
- recurvedata/pigeon/connector/starrocks.py +144 -0
- recurvedata/pigeon/connector/tableau.py +162 -0
- recurvedata/pigeon/const.py +21 -0
- recurvedata/pigeon/csv.py +172 -0
- recurvedata/pigeon/docs/datasources-example.json +82 -0
- recurvedata/pigeon/docs/images/pigeon_design.png +0 -0
- recurvedata/pigeon/docs/lightweight-data-sync-solution.md +111 -0
- recurvedata/pigeon/dumper/__init__.py +171 -0
- recurvedata/pigeon/dumper/aliyun_sls.py +415 -0
- recurvedata/pigeon/dumper/base.py +141 -0
- recurvedata/pigeon/dumper/cass.py +213 -0
- recurvedata/pigeon/dumper/dbapi.py +346 -0
- recurvedata/pigeon/dumper/es.py +112 -0
- recurvedata/pigeon/dumper/ftp.py +64 -0
- recurvedata/pigeon/dumper/mongodb.py +103 -0
- recurvedata/pigeon/handler/__init__.py +4 -0
- recurvedata/pigeon/handler/base.py +153 -0
- recurvedata/pigeon/handler/csv_handler.py +290 -0
- recurvedata/pigeon/loader/__init__.py +87 -0
- recurvedata/pigeon/loader/base.py +83 -0
- recurvedata/pigeon/loader/csv_to_azure_synapse.py +214 -0
- recurvedata/pigeon/loader/csv_to_clickhouse.py +152 -0
- recurvedata/pigeon/loader/csv_to_doris.py +215 -0
- recurvedata/pigeon/loader/csv_to_es.py +51 -0
- recurvedata/pigeon/loader/csv_to_google_bigquery.py +169 -0
- recurvedata/pigeon/loader/csv_to_hive.py +468 -0
- recurvedata/pigeon/loader/csv_to_microsoft_fabric.py +242 -0
- recurvedata/pigeon/loader/csv_to_mssql.py +174 -0
- recurvedata/pigeon/loader/csv_to_mysql.py +180 -0
- recurvedata/pigeon/loader/csv_to_postgresql.py +248 -0
- recurvedata/pigeon/loader/csv_to_redshift.py +240 -0
- recurvedata/pigeon/loader/csv_to_starrocks.py +233 -0
- recurvedata/pigeon/meta.py +116 -0
- recurvedata/pigeon/row_factory.py +42 -0
- recurvedata/pigeon/schema/__init__.py +124 -0
- recurvedata/pigeon/schema/types.py +13 -0
- recurvedata/pigeon/sync.py +283 -0
- recurvedata/pigeon/transformer.py +146 -0
- recurvedata/pigeon/utils/__init__.py +134 -0
- recurvedata/pigeon/utils/bloomfilter.py +181 -0
- recurvedata/pigeon/utils/date_time.py +323 -0
- recurvedata/pigeon/utils/escape.py +15 -0
- recurvedata/pigeon/utils/fs.py +266 -0
- recurvedata/pigeon/utils/json.py +44 -0
- recurvedata/pigeon/utils/keyed_tuple.py +85 -0
- recurvedata/pigeon/utils/mp.py +156 -0
- recurvedata/pigeon/utils/sql.py +328 -0
- recurvedata/pigeon/utils/timing.py +155 -0
- recurvedata/provider_manager.py +0 -0
- recurvedata/providers/__init__.py +0 -0
- recurvedata/providers/dbapi/__init__.py +0 -0
- recurvedata/providers/flywheel/__init__.py +0 -0
- recurvedata/providers/mysql/__init__.py +0 -0
- recurvedata/schedulers/__init__.py +1 -0
- recurvedata/schedulers/airflow.py +974 -0
- recurvedata/schedulers/airflow_db_process.py +331 -0
- recurvedata/schedulers/airflow_operators.py +61 -0
- recurvedata/schedulers/airflow_plugin.py +9 -0
- recurvedata/schedulers/airflow_trigger_dag_patch.py +117 -0
- recurvedata/schedulers/base.py +99 -0
- recurvedata/schedulers/cli.py +228 -0
- recurvedata/schedulers/client.py +56 -0
- recurvedata/schedulers/consts.py +52 -0
- recurvedata/schedulers/debug_celery.py +62 -0
- recurvedata/schedulers/model.py +63 -0
- recurvedata/schedulers/schemas.py +97 -0
- recurvedata/schedulers/service.py +20 -0
- recurvedata/schedulers/system_dags.py +59 -0
- recurvedata/schedulers/task_status.py +279 -0
- recurvedata/schedulers/utils.py +73 -0
- recurvedata/schema/__init__.py +0 -0
- recurvedata/schema/field.py +88 -0
- recurvedata/schema/schema.py +55 -0
- recurvedata/schema/types.py +17 -0
- recurvedata/schema.py +0 -0
- recurvedata/server/__init__.py +0 -0
- recurvedata/server/app.py +7 -0
- recurvedata/server/connector/__init__.py +0 -0
- recurvedata/server/connector/api.py +79 -0
- recurvedata/server/connector/schemas.py +28 -0
- recurvedata/server/data_service/__init__.py +0 -0
- recurvedata/server/data_service/api.py +126 -0
- recurvedata/server/data_service/client.py +18 -0
- recurvedata/server/data_service/consts.py +1 -0
- recurvedata/server/data_service/schemas.py +68 -0
- recurvedata/server/data_service/service.py +218 -0
- recurvedata/server/dbt/__init__.py +0 -0
- recurvedata/server/dbt/api.py +116 -0
- recurvedata/server/error_code.py +49 -0
- recurvedata/server/exceptions.py +19 -0
- recurvedata/server/executor/__init__.py +0 -0
- recurvedata/server/executor/api.py +37 -0
- recurvedata/server/executor/schemas.py +30 -0
- recurvedata/server/executor/service.py +220 -0
- recurvedata/server/main.py +32 -0
- recurvedata/server/schedulers/__init__.py +0 -0
- recurvedata/server/schedulers/api.py +252 -0
- recurvedata/server/schedulers/schemas.py +50 -0
- recurvedata/server/schemas.py +50 -0
- recurvedata/utils/__init__.py +15 -0
- recurvedata/utils/_typer.py +61 -0
- recurvedata/utils/attrdict.py +19 -0
- recurvedata/utils/command_helper.py +20 -0
- recurvedata/utils/compat.py +12 -0
- recurvedata/utils/compression.py +203 -0
- recurvedata/utils/crontab.py +42 -0
- recurvedata/utils/crypto_util.py +305 -0
- recurvedata/utils/dataclass.py +11 -0
- recurvedata/utils/date_time.py +464 -0
- recurvedata/utils/dispatch.py +114 -0
- recurvedata/utils/email_util.py +104 -0
- recurvedata/utils/files.py +386 -0
- recurvedata/utils/helpers.py +170 -0
- recurvedata/utils/httputil.py +117 -0
- recurvedata/utils/imports.py +132 -0
- recurvedata/utils/json.py +80 -0
- recurvedata/utils/log.py +117 -0
- recurvedata/utils/log_capture.py +153 -0
- recurvedata/utils/mp.py +178 -0
- recurvedata/utils/normalizer.py +102 -0
- recurvedata/utils/redis_lock.py +474 -0
- recurvedata/utils/registry.py +54 -0
- recurvedata/utils/shell.py +15 -0
- recurvedata/utils/singleton.py +33 -0
- recurvedata/utils/sql.py +6 -0
- recurvedata/utils/timeout.py +28 -0
- recurvedata/utils/tracing.py +14 -0
- recurvedata_lib-0.1.487.dist-info/METADATA +605 -0
- recurvedata_lib-0.1.487.dist-info/RECORD +333 -0
- recurvedata_lib-0.1.487.dist-info/WHEEL +5 -0
- 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
|