lesscode-flask 0.2.92__tar.gz → 0.2.99__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/PKG-INFO +2 -2
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/lesscode_flask/__init__.py +1 -1
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/lesscode_flask/setup/__init__.py +1 -1
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/lesscode_flask/utils/sign/signature.py +39 -8
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/lesscode_flask.egg-info/PKG-INFO +2 -2
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/lesscode_flask.egg-info/SOURCES.txt +1 -0
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/lesscode_flask.egg-info/requires.txt +1 -1
- lesscode_flask-0.2.99/redash/query_runner/dameng.py +230 -0
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/README.md +0 -0
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/lesscode_flask/app.py +0 -0
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/lesscode_flask/db/__init__.py +0 -0
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/lesscode_flask/db/datasource.py +0 -0
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/lesscode_flask/db/executor.py +0 -0
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/lesscode_flask/export_data/__init__.py +0 -0
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/lesscode_flask/export_data/data_download_handler.py +0 -0
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/lesscode_flask/log/access_log_handler.py +0 -0
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/lesscode_flask/model/access_log.py +0 -0
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/lesscode_flask/model/base_model.py +0 -0
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/lesscode_flask/model/parameterized_query.py +0 -0
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/lesscode_flask/model/resource_param_template.py +0 -0
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/lesscode_flask/model/response_result.py +0 -0
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/lesscode_flask/model/user.py +0 -0
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/lesscode_flask/model/user_limit_policy.py +0 -0
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/lesscode_flask/service/access_log_service.py +0 -0
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/lesscode_flask/service/base_service.py +0 -0
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/lesscode_flask/service/resource_param_template_service.py +0 -0
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/lesscode_flask/setting/__init__.py +0 -0
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/lesscode_flask/signals.py +0 -0
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/lesscode_flask/static/swagger.py +0 -0
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/lesscode_flask/utils/__init__.py +0 -0
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/lesscode_flask/utils/decorator/__init__.py +0 -0
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/lesscode_flask/utils/decorator/cache.py +0 -0
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/lesscode_flask/utils/decorator/sql_injection.py +0 -0
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/lesscode_flask/utils/decorator/swagger.py +0 -0
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/lesscode_flask/utils/dify_utils.py +0 -0
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/lesscode_flask/utils/file/file_exporter.py +0 -0
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/lesscode_flask/utils/file/file_utils.py +0 -0
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/lesscode_flask/utils/fs_util.py +0 -0
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/lesscode_flask/utils/helpers.py +0 -0
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/lesscode_flask/utils/json/NotSortJSONProvider.py +0 -0
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/lesscode_flask/utils/limit/__init__.py +0 -0
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/lesscode_flask/utils/limit/consecutive/consecutive_limiter_handler.py +0 -0
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/lesscode_flask/utils/limit/consecutive/redis_consecutive_limiter.py +0 -0
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/lesscode_flask/utils/limit/limit_util.py +0 -0
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/lesscode_flask/utils/limit/req/rate_limiter_handler.py +0 -0
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/lesscode_flask/utils/limit/req/redis_rate_limiter.py +0 -0
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/lesscode_flask/utils/limit/req_count/count_limiter_handler.py +0 -0
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/lesscode_flask/utils/limit/req_count/redis_count_limiter.py +0 -0
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/lesscode_flask/utils/oss/__init__.py +0 -0
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/lesscode_flask/utils/oss/aliyun_oss.py +0 -0
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/lesscode_flask/utils/oss/ks3_oss.py +0 -0
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/lesscode_flask/utils/oss/minio_oss.py +0 -0
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/lesscode_flask/utils/redis/redis_helper.py +0 -0
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/lesscode_flask/utils/request/request.py +0 -0
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/lesscode_flask/utils/sign/__init__.py +0 -0
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/lesscode_flask/utils/swagger/swagger_template.py +0 -0
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/lesscode_flask/utils/swagger/swagger_util.py +0 -0
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/lesscode_flask/utils/task/__init__.py +0 -0
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/lesscode_flask/utils/task/task_helper.py +0 -0
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/lesscode_flask/utils/thread/thread_utils.py +0 -0
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/lesscode_flask/wsgi.py +0 -0
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/lesscode_flask.egg-info/dependency_links.txt +0 -0
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/lesscode_flask.egg-info/top_level.txt +0 -0
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/redash/query_runner/__init__.py +0 -0
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/redash/query_runner/clickhouse.py +0 -0
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/redash/query_runner/elasticsearch.py +0 -0
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/redash/query_runner/kingbase.py +0 -0
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/redash/query_runner/mysql.py +0 -0
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/redash/query_runner/pg.py +0 -0
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/redash/settings/__init__.py +0 -0
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/redash/settings/helpers.py +0 -0
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/redash/utils/__init__.py +0 -0
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/redash/utils/requests_session.py +0 -0
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/setup.cfg +0 -0
- {lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: lesscode-flask
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.99
|
|
4
4
|
Summary: lesscode-flask 是基于flask的web开发脚手架项目,该项目初衷为简化开发过程,让研发人员更加关注业务。
|
|
5
5
|
Home-page: https://lesscode-flask
|
|
6
6
|
Author: Chao.yy
|
|
@@ -24,7 +24,7 @@ Requires-Dist: requests==2.32.3
|
|
|
24
24
|
Requires-Dist: gunicorn==23.0.0
|
|
25
25
|
Requires-Dist: gevent==25.5.1
|
|
26
26
|
Requires-Dist: openpyxl==3.1.5
|
|
27
|
-
Requires-Dist: iputil==0.3.
|
|
27
|
+
Requires-Dist: iputil==0.3.2
|
|
28
28
|
Requires-Dist: phone==0.4.5
|
|
29
29
|
Requires-Dist: psutil==7.2.2
|
|
30
30
|
Dynamic: author
|
|
@@ -189,7 +189,7 @@ def _build_download_key(target_url, request_method):
|
|
|
189
189
|
key_seed = f"{str(request_method).upper()}::{target_url}"
|
|
190
190
|
# 保持原有 AES 风格,同时拼接首尾片段,避免只截前缀导致不同接口冲突。
|
|
191
191
|
encrypted = AES.encrypt(key_seed)
|
|
192
|
-
return f"{encrypted[:
|
|
192
|
+
return f"{encrypted[:16]}{encrypted[-16:]}"
|
|
193
193
|
|
|
194
194
|
|
|
195
195
|
def _is_auto_export_route(route):
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import hashlib
|
|
2
2
|
import hmac
|
|
3
3
|
import ipaddress
|
|
4
|
+
import logging
|
|
4
5
|
import time
|
|
5
6
|
|
|
6
7
|
from flask import request
|
|
@@ -9,11 +10,38 @@ from lesscode_flask.model.response_result import ResponseResult
|
|
|
9
10
|
from lesscode_flask.utils.redis.redis_helper import RedisHelper
|
|
10
11
|
|
|
11
12
|
SIGN_ERROR_MESSAGE = "未知错误"
|
|
13
|
+
SIGN_ERROR_CODE_MAP = {
|
|
14
|
+
"missing_sign_headers": "01",
|
|
15
|
+
"missing_sign_secret": "02",
|
|
16
|
+
"invalid_timestamp": "03",
|
|
17
|
+
"timestamp_out_of_window": "04",
|
|
18
|
+
"nonce_replayed": "05",
|
|
19
|
+
"nonce_check_exception": "06",
|
|
20
|
+
"signature_mismatch": "07",
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _sign_error_message(error_key: str) -> str:
|
|
25
|
+
error_code = SIGN_ERROR_CODE_MAP.get(error_key)
|
|
26
|
+
if not error_code:
|
|
27
|
+
return SIGN_ERROR_MESSAGE
|
|
28
|
+
return f"{SIGN_ERROR_MESSAGE}{error_code}"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _fail_sign(error_key: str):
|
|
32
|
+
ResponseResult.fail(
|
|
33
|
+
message=_sign_error_message(error_key),
|
|
34
|
+
status_code="401",
|
|
35
|
+
http_code="401",
|
|
36
|
+
)
|
|
12
37
|
|
|
13
38
|
|
|
14
39
|
def _body_sha256() -> str:
|
|
15
40
|
# 对原始请求体做哈希,保证不同 content-type 下签名输入一致。
|
|
16
41
|
body_bytes = request.get_data(cache=True) or b""
|
|
42
|
+
logging.info("body_bytes开始")
|
|
43
|
+
logging.info("body_bytes: %s", body_bytes)
|
|
44
|
+
logging.info("body_bytes结束")
|
|
17
45
|
return hashlib.sha256(body_bytes).hexdigest()
|
|
18
46
|
|
|
19
47
|
|
|
@@ -114,25 +142,25 @@ def verify_request_signature(app):
|
|
|
114
142
|
signature = request.headers.get("Signature", "")
|
|
115
143
|
sign_secret = app.config.get("SIGN_SECRET", "")
|
|
116
144
|
|
|
117
|
-
#
|
|
145
|
+
# 同类错误固定使用同一个编号,方便客户端和服务端对齐排查。
|
|
118
146
|
if not timestamp or not nonce or not signature:
|
|
119
|
-
|
|
147
|
+
_fail_sign("missing_sign_headers")
|
|
120
148
|
# 服务端密钥未配置时也拦截,防止“空密钥”误放行。
|
|
121
149
|
if not sign_secret:
|
|
122
|
-
|
|
150
|
+
_fail_sign("missing_sign_secret")
|
|
123
151
|
|
|
124
152
|
# 时间戳必须是整数秒。
|
|
125
153
|
try:
|
|
126
154
|
ts = int(timestamp)
|
|
127
155
|
except (TypeError, ValueError):
|
|
128
|
-
|
|
156
|
+
_fail_sign("invalid_timestamp")
|
|
129
157
|
return
|
|
130
158
|
|
|
131
159
|
# 时间窗校验,防止旧请求被重放。
|
|
132
160
|
sign_window_sec = int(app.config.get("SIGN_WINDOW_SEC", 300))
|
|
133
161
|
now_ts = int(time.time())
|
|
134
162
|
if abs(now_ts - ts) > sign_window_sec:
|
|
135
|
-
|
|
163
|
+
_fail_sign("timestamp_out_of_window")
|
|
136
164
|
|
|
137
165
|
if app.config.get("SIGN_NONCE_ENABLE", True):
|
|
138
166
|
# 防重放:同一个 nonce 在 TTL 窗口内只能使用一次。
|
|
@@ -143,13 +171,16 @@ def verify_request_signature(app):
|
|
|
143
171
|
nonce_key, "1", ex=nonce_ttl, nx=True
|
|
144
172
|
)
|
|
145
173
|
if not ok:
|
|
146
|
-
|
|
174
|
+
_fail_sign("nonce_replayed")
|
|
147
175
|
except Exception:
|
|
148
176
|
# Redis 异常按失败处理,避免在防重放失效时放过请求。
|
|
149
|
-
|
|
177
|
+
_fail_sign("nonce_check_exception")
|
|
150
178
|
|
|
151
179
|
# 按统一规则组装签名串并计算服务端签名。
|
|
152
180
|
sign_content = _canonical_sign_content(timestamp=timestamp, nonce=nonce)
|
|
181
|
+
logging.info("sign_content开始")
|
|
182
|
+
logging.info("sign_content: %s", sign_content)
|
|
183
|
+
logging.info("sign_content结束")
|
|
153
184
|
expected = hmac.new(
|
|
154
185
|
sign_secret.encode("utf-8"),
|
|
155
186
|
sign_content.encode("utf-8"),
|
|
@@ -157,4 +188,4 @@ def verify_request_signature(app):
|
|
|
157
188
|
).hexdigest()
|
|
158
189
|
# 常量时间比较,降低时序侧信道风险。
|
|
159
190
|
if not hmac.compare_digest(expected, signature):
|
|
160
|
-
|
|
191
|
+
_fail_sign("signature_mismatch")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: lesscode-flask
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.99
|
|
4
4
|
Summary: lesscode-flask 是基于flask的web开发脚手架项目,该项目初衷为简化开发过程,让研发人员更加关注业务。
|
|
5
5
|
Home-page: https://lesscode-flask
|
|
6
6
|
Author: Chao.yy
|
|
@@ -24,7 +24,7 @@ Requires-Dist: requests==2.32.3
|
|
|
24
24
|
Requires-Dist: gunicorn==23.0.0
|
|
25
25
|
Requires-Dist: gevent==25.5.1
|
|
26
26
|
Requires-Dist: openpyxl==3.1.5
|
|
27
|
-
Requires-Dist: iputil==0.3.
|
|
27
|
+
Requires-Dist: iputil==0.3.2
|
|
28
28
|
Requires-Dist: phone==0.4.5
|
|
29
29
|
Requires-Dist: psutil==7.2.2
|
|
30
30
|
Dynamic: author
|
|
@@ -62,6 +62,7 @@ lesscode_flask/utils/task/task_helper.py
|
|
|
62
62
|
lesscode_flask/utils/thread/thread_utils.py
|
|
63
63
|
redash/query_runner/__init__.py
|
|
64
64
|
redash/query_runner/clickhouse.py
|
|
65
|
+
redash/query_runner/dameng.py
|
|
65
66
|
redash/query_runner/elasticsearch.py
|
|
66
67
|
redash/query_runner/kingbase.py
|
|
67
68
|
redash/query_runner/mysql.py
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import threading
|
|
3
|
+
|
|
4
|
+
from redash.query_runner import (BaseSQLQueryRunner, InterruptException, TYPE_DATE, TYPE_DATETIME, TYPE_FLOAT,
|
|
5
|
+
TYPE_INTEGER, TYPE_STRING, register)
|
|
6
|
+
|
|
7
|
+
try:
|
|
8
|
+
import dmPython
|
|
9
|
+
|
|
10
|
+
enabled = True
|
|
11
|
+
except ImportError:
|
|
12
|
+
logging.error("dmPython is not installed, run: pip install dmPython")
|
|
13
|
+
dmPython = None
|
|
14
|
+
enabled = False
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
# dmPython type codes mapping to Redash types
|
|
19
|
+
# Reference: dmPython exposes type objects; cursor.description[i][1] returns type code (int)
|
|
20
|
+
# DM8 SQL types: NUMBER(int/float), CHAR/VARCHAR/TEXT(str), DATE, TIMESTAMP, etc.
|
|
21
|
+
types_map = {
|
|
22
|
+
# Numeric
|
|
23
|
+
-7: TYPE_INTEGER, # BIT
|
|
24
|
+
-6: TYPE_INTEGER, # TINYINT
|
|
25
|
+
5: TYPE_INTEGER, # SMALLINT
|
|
26
|
+
4: TYPE_INTEGER, # INTEGER / INT
|
|
27
|
+
-5: TYPE_INTEGER, # BIGINT
|
|
28
|
+
2: TYPE_FLOAT, # NUMERIC
|
|
29
|
+
3: TYPE_FLOAT, # DECIMAL
|
|
30
|
+
6: TYPE_FLOAT, # FLOAT
|
|
31
|
+
7: TYPE_FLOAT, # REAL
|
|
32
|
+
8: TYPE_FLOAT, # DOUBLE
|
|
33
|
+
# String
|
|
34
|
+
1: TYPE_STRING, # CHAR
|
|
35
|
+
12: TYPE_STRING, # VARCHAR
|
|
36
|
+
-1: TYPE_STRING, # LONGVARCHAR / TEXT
|
|
37
|
+
-9: TYPE_STRING, # NVARCHAR
|
|
38
|
+
-10: TYPE_STRING, # LONGNVARCHAR
|
|
39
|
+
# Date/Time
|
|
40
|
+
91: TYPE_DATE, # DATE
|
|
41
|
+
92: TYPE_DATETIME, # TIME
|
|
42
|
+
93: TYPE_DATETIME, # TIMESTAMP / DATETIME
|
|
43
|
+
# Binary / others fall back to string
|
|
44
|
+
-2: TYPE_STRING,
|
|
45
|
+
-3: TYPE_STRING,
|
|
46
|
+
-4: TYPE_STRING,
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class Result:
|
|
51
|
+
def __init__(self):
|
|
52
|
+
self.data = None
|
|
53
|
+
self.error = None
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class DaMeng(BaseSQLQueryRunner):
|
|
57
|
+
noop_query = "SELECT 1"
|
|
58
|
+
|
|
59
|
+
@classmethod
|
|
60
|
+
def configuration_schema(cls):
|
|
61
|
+
return {
|
|
62
|
+
"type": "object",
|
|
63
|
+
"properties": {
|
|
64
|
+
"host": {"type": "string", "default": "127.0.0.1"},
|
|
65
|
+
"port": {"type": "number", "default": 5236},
|
|
66
|
+
"user": {"type": "string"},
|
|
67
|
+
"password": {"type": "string", "title": "Password"},
|
|
68
|
+
"connect_timeout": {"type": "number", "default": 60, "title": "Connection Timeout"},
|
|
69
|
+
},
|
|
70
|
+
"order": ["host", "port", "user", "password", "connect_timeout"],
|
|
71
|
+
"required": ["host", "user", "password"],
|
|
72
|
+
"secret": ["password"],
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
@classmethod
|
|
76
|
+
def name(cls):
|
|
77
|
+
return "DaMeng"
|
|
78
|
+
|
|
79
|
+
@classmethod
|
|
80
|
+
def type(cls):
|
|
81
|
+
return "dameng"
|
|
82
|
+
|
|
83
|
+
@classmethod
|
|
84
|
+
def enabled(cls):
|
|
85
|
+
return enabled
|
|
86
|
+
|
|
87
|
+
def _connection(self):
|
|
88
|
+
if dmPython is None:
|
|
89
|
+
raise Exception("dmPython is not installed")
|
|
90
|
+
# dmPython.connect accepts: user, password, server (host:port) or host/port separately
|
|
91
|
+
host = self.configuration.get("host", "127.0.0.1")
|
|
92
|
+
port = self.configuration.get("port", 5236)
|
|
93
|
+
user = self.configuration.get("user", "")
|
|
94
|
+
password = self.configuration.get("password", "")
|
|
95
|
+
|
|
96
|
+
connection = dmPython.connect(
|
|
97
|
+
user=user,
|
|
98
|
+
password=password,
|
|
99
|
+
server=host,
|
|
100
|
+
port=int(port)
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
return connection
|
|
104
|
+
|
|
105
|
+
def _get_tables(self, schema):
|
|
106
|
+
database = self.configuration.get("database", "")
|
|
107
|
+
|
|
108
|
+
if database:
|
|
109
|
+
query = """
|
|
110
|
+
SELECT c.OWNER AS table_schema,
|
|
111
|
+
c.TABLE_NAME AS table_name,
|
|
112
|
+
c.COLUMN_NAME AS column_name
|
|
113
|
+
FROM ALL_TAB_COLUMNS c
|
|
114
|
+
WHERE c.OWNER = '{schema}'
|
|
115
|
+
ORDER BY c.OWNER, c.TABLE_NAME, c.COLUMN_ID
|
|
116
|
+
""".format(schema=database.upper())
|
|
117
|
+
else:
|
|
118
|
+
query = """
|
|
119
|
+
SELECT c.OWNER AS table_schema,
|
|
120
|
+
c.TABLE_NAME AS table_name,
|
|
121
|
+
c.COLUMN_NAME AS column_name
|
|
122
|
+
FROM ALL_TAB_COLUMNS c
|
|
123
|
+
ORDER BY c.OWNER, c.TABLE_NAME, c.COLUMN_ID \
|
|
124
|
+
"""
|
|
125
|
+
|
|
126
|
+
results, error = self.run_query(query, None)
|
|
127
|
+
|
|
128
|
+
if error is not None:
|
|
129
|
+
self._handle_run_query_error(error)
|
|
130
|
+
|
|
131
|
+
for row in results["rows"]:
|
|
132
|
+
table_schema = row["table_schema"]
|
|
133
|
+
table_name = row["table_name"]
|
|
134
|
+
|
|
135
|
+
if table_schema.upper() != (database or "").upper():
|
|
136
|
+
full_name = "{}.{}".format(table_schema, table_name)
|
|
137
|
+
else:
|
|
138
|
+
full_name = table_name
|
|
139
|
+
|
|
140
|
+
if full_name not in schema:
|
|
141
|
+
schema[full_name] = {"name": full_name, "columns": []}
|
|
142
|
+
|
|
143
|
+
schema[full_name]["columns"].append(row["column_name"])
|
|
144
|
+
|
|
145
|
+
return list(schema.values())
|
|
146
|
+
|
|
147
|
+
def run_query(self, query, user):
|
|
148
|
+
ev = threading.Event()
|
|
149
|
+
r = Result()
|
|
150
|
+
|
|
151
|
+
try:
|
|
152
|
+
connection = self._connection()
|
|
153
|
+
self._run_query(query, user, connection, r, ev)
|
|
154
|
+
except (KeyboardInterrupt, InterruptException):
|
|
155
|
+
raise
|
|
156
|
+
|
|
157
|
+
return r.data, r.error
|
|
158
|
+
|
|
159
|
+
def _convert_column_name(self, column_name, convert_type):
|
|
160
|
+
if convert_type:
|
|
161
|
+
if convert_type == "lower":
|
|
162
|
+
column_name = column_name.lower()
|
|
163
|
+
elif convert_type == "upper":
|
|
164
|
+
column_name = column_name.upper()
|
|
165
|
+
return column_name
|
|
166
|
+
|
|
167
|
+
def _run_query(self, query, user, connection, r, ev):
|
|
168
|
+
cursor = None
|
|
169
|
+
column_name_convert = self.configuration.get("column_name_convert", "lower")
|
|
170
|
+
try:
|
|
171
|
+
cursor = connection.cursor()
|
|
172
|
+
logger.debug("DaMeng running query: %s", query)
|
|
173
|
+
cursor.execute(query)
|
|
174
|
+
|
|
175
|
+
data = cursor.fetchall()
|
|
176
|
+
desc = cursor.description
|
|
177
|
+
|
|
178
|
+
if desc is not None:
|
|
179
|
+
columns = self.fetch_columns(
|
|
180
|
+
[(self._convert_column_name(i[0], column_name_convert), types_map.get(i[1], TYPE_STRING)) for i in
|
|
181
|
+
desc]
|
|
182
|
+
)
|
|
183
|
+
rows = [
|
|
184
|
+
dict(zip((col["name"] for col in columns), row))
|
|
185
|
+
for row in data
|
|
186
|
+
]
|
|
187
|
+
r.data = {"columns": columns, "rows": rows}
|
|
188
|
+
r.error = None
|
|
189
|
+
else:
|
|
190
|
+
r.data = None
|
|
191
|
+
r.error = "No data was returned."
|
|
192
|
+
|
|
193
|
+
except Exception as e:
|
|
194
|
+
r.data = None
|
|
195
|
+
r.error = str(e)
|
|
196
|
+
finally:
|
|
197
|
+
if cursor:
|
|
198
|
+
cursor.close()
|
|
199
|
+
if connection:
|
|
200
|
+
connection.close()
|
|
201
|
+
ev.set()
|
|
202
|
+
|
|
203
|
+
def exec(self, query, user):
|
|
204
|
+
try:
|
|
205
|
+
connection = self._connection()
|
|
206
|
+
cursor = connection.cursor()
|
|
207
|
+
logger.debug("DaMeng exec query: %s", query)
|
|
208
|
+
cursor.execute(query)
|
|
209
|
+
connection.commit()
|
|
210
|
+
cursor.close()
|
|
211
|
+
connection.close()
|
|
212
|
+
except Exception as e:
|
|
213
|
+
return False, str(e)
|
|
214
|
+
|
|
215
|
+
return True, None
|
|
216
|
+
|
|
217
|
+
def create_options(self, configuration):
|
|
218
|
+
options = {
|
|
219
|
+
"host": configuration.get("host"),
|
|
220
|
+
"port": configuration.get("port"),
|
|
221
|
+
"user": configuration.get("username"),
|
|
222
|
+
"passwd": configuration.get("password"),
|
|
223
|
+
"db": configuration.get("dbname")
|
|
224
|
+
}
|
|
225
|
+
if configuration.get("options"):
|
|
226
|
+
options.update(configuration.get("options"))
|
|
227
|
+
return options
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
register(DaMeng)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/lesscode_flask/export_data/data_download_handler.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/lesscode_flask/model/resource_param_template.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/lesscode_flask/service/access_log_service.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/lesscode_flask/utils/decorator/sql_injection.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/lesscode_flask/utils/json/NotSortJSONProvider.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/lesscode_flask/utils/limit/req/redis_rate_limiter.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/lesscode_flask/utils/swagger/swagger_template.py
RENAMED
|
File without changes
|
{lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/lesscode_flask/utils/swagger/swagger_util.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{lesscode_flask-0.2.92 → lesscode_flask-0.2.99}/lesscode_flask.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|