utils_tests.py 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953
  1. # Licensed to the Apache Software Foundation (ASF) under one
  2. # or more contributor license agreements. See the NOTICE file
  3. # distributed with this work for additional information
  4. # regarding copyright ownership. The ASF licenses this file
  5. # to you under the Apache License, Version 2.0 (the
  6. # "License"); you may not use this file except in compliance
  7. # with the License. You may obtain a copy of the License at
  8. #
  9. # http://www.apache.org/licenses/LICENSE-2.0
  10. #
  11. # Unless required by applicable law or agreed to in writing,
  12. # software distributed under the License is distributed on an
  13. # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  14. # KIND, either express or implied. See the License for the
  15. # specific language governing permissions and limitations
  16. # under the License.
  17. # isort:skip_file
  18. import unittest
  19. import uuid
  20. from datetime import date, datetime, time, timedelta
  21. from decimal import Decimal
  22. from unittest.mock import Mock, patch
  23. import numpy
  24. from flask import Flask
  25. from flask_caching import Cache
  26. from sqlalchemy.exc import ArgumentError
  27. import tests.test_app
  28. from superset import app, db, security_manager
  29. from superset.exceptions import SupersetException
  30. from superset.models.core import Database
  31. from superset.utils.cache_manager import CacheManager
  32. from superset.utils.core import (
  33. base_json_conv,
  34. convert_legacy_filters_into_adhoc,
  35. datetime_f,
  36. format_timedelta,
  37. get_or_create_db,
  38. get_since_until,
  39. get_stacktrace,
  40. json_int_dttm_ser,
  41. json_iso_dttm_ser,
  42. JSONEncodedDict,
  43. memoized,
  44. merge_extra_filters,
  45. merge_request_params,
  46. parse_human_timedelta,
  47. parse_js_uri_path_item,
  48. parse_past_timedelta,
  49. split,
  50. TimeRangeEndpoint,
  51. validate_json,
  52. zlib_compress,
  53. zlib_decompress,
  54. )
  55. from superset.views.utils import get_time_range_endpoints
  56. from tests.base_tests import SupersetTestCase
  57. def mock_parse_human_datetime(s):
  58. if s == "now":
  59. return datetime(2016, 11, 7, 9, 30, 10)
  60. elif s == "today":
  61. return datetime(2016, 11, 7)
  62. elif s == "yesterday":
  63. return datetime(2016, 11, 6)
  64. elif s == "tomorrow":
  65. return datetime(2016, 11, 8)
  66. elif s == "Last year":
  67. return datetime(2015, 11, 7)
  68. elif s == "Last week":
  69. return datetime(2015, 10, 31)
  70. elif s == "Last 5 months":
  71. return datetime(2016, 6, 7)
  72. elif s == "Next 5 months":
  73. return datetime(2017, 4, 7)
  74. elif s in ["5 days", "5 days ago"]:
  75. return datetime(2016, 11, 2)
  76. elif s == "2018-01-01T00:00:00":
  77. return datetime(2018, 1, 1)
  78. elif s == "2018-12-31T23:59:59":
  79. return datetime(2018, 12, 31, 23, 59, 59)
  80. def mock_to_adhoc(filt, expressionType="SIMPLE", clause="where"):
  81. result = {"clause": clause.upper(), "expressionType": expressionType}
  82. if expressionType == "SIMPLE":
  83. result.update(
  84. {"comparator": filt["val"], "operator": filt["op"], "subject": filt["col"]}
  85. )
  86. elif expressionType == "SQL":
  87. result.update({"sqlExpression": filt[clause]})
  88. return result
  89. class UtilsTestCase(SupersetTestCase):
  90. def test_json_int_dttm_ser(self):
  91. dttm = datetime(2020, 1, 1)
  92. ts = 1577836800000.0
  93. assert json_int_dttm_ser(dttm) == ts
  94. assert json_int_dttm_ser(date(2020, 1, 1)) == ts
  95. assert json_int_dttm_ser(datetime(1970, 1, 1)) == 0
  96. assert json_int_dttm_ser(date(1970, 1, 1)) == 0
  97. assert json_int_dttm_ser(dttm + timedelta(milliseconds=1)) == (ts + 1)
  98. with self.assertRaises(TypeError):
  99. json_int_dttm_ser("this is not a date")
  100. def test_json_iso_dttm_ser(self):
  101. dttm = datetime(2020, 1, 1)
  102. dt = date(2020, 1, 1)
  103. t = time()
  104. assert json_iso_dttm_ser(dttm) == dttm.isoformat()
  105. assert json_iso_dttm_ser(dt) == dt.isoformat()
  106. assert json_iso_dttm_ser(t) == t.isoformat()
  107. with self.assertRaises(TypeError):
  108. json_iso_dttm_ser("this is not a date")
  109. def test_base_json_conv(self):
  110. assert isinstance(base_json_conv(numpy.bool_(1)), bool) is True
  111. assert isinstance(base_json_conv(numpy.int64(1)), int) is True
  112. assert isinstance(base_json_conv(numpy.array([1, 2, 3])), list) is True
  113. assert isinstance(base_json_conv(set([1])), list) is True
  114. assert isinstance(base_json_conv(Decimal("1.0")), float) is True
  115. assert isinstance(base_json_conv(uuid.uuid4()), str) is True
  116. assert isinstance(base_json_conv(timedelta(0)), str) is True
  117. @patch("superset.utils.core.datetime")
  118. def test_parse_human_timedelta(self, mock_datetime):
  119. mock_datetime.now.return_value = datetime(2019, 4, 1)
  120. mock_datetime.side_effect = lambda *args, **kw: datetime(*args, **kw)
  121. self.assertEqual(parse_human_timedelta("now"), timedelta(0))
  122. self.assertEqual(parse_human_timedelta("1 year"), timedelta(366))
  123. self.assertEqual(parse_human_timedelta("-1 year"), timedelta(-365))
  124. self.assertEqual(parse_human_timedelta(None), timedelta(0))
  125. @patch("superset.utils.core.datetime")
  126. def test_parse_past_timedelta(self, mock_datetime):
  127. mock_datetime.now.return_value = datetime(2019, 4, 1)
  128. mock_datetime.side_effect = lambda *args, **kw: datetime(*args, **kw)
  129. self.assertEqual(parse_past_timedelta("1 year"), timedelta(365))
  130. self.assertEqual(parse_past_timedelta("-1 year"), timedelta(365))
  131. self.assertEqual(parse_past_timedelta("52 weeks"), timedelta(364))
  132. self.assertEqual(parse_past_timedelta("1 month"), timedelta(31))
  133. def test_zlib_compression(self):
  134. json_str = '{"test": 1}'
  135. blob = zlib_compress(json_str)
  136. got_str = zlib_decompress(blob)
  137. self.assertEqual(json_str, got_str)
  138. @patch("superset.utils.core.to_adhoc", mock_to_adhoc)
  139. def test_merge_extra_filters(self):
  140. # does nothing if no extra filters
  141. form_data = {"A": 1, "B": 2, "c": "test"}
  142. expected = {"A": 1, "B": 2, "c": "test"}
  143. merge_extra_filters(form_data)
  144. self.assertEqual(form_data, expected)
  145. # empty extra_filters
  146. form_data = {"A": 1, "B": 2, "c": "test", "extra_filters": []}
  147. expected = {"A": 1, "B": 2, "c": "test", "adhoc_filters": []}
  148. merge_extra_filters(form_data)
  149. self.assertEqual(form_data, expected)
  150. # copy over extra filters into empty filters
  151. form_data = {
  152. "extra_filters": [
  153. {"col": "a", "op": "in", "val": "someval"},
  154. {"col": "B", "op": "==", "val": ["c1", "c2"]},
  155. ]
  156. }
  157. expected = {
  158. "adhoc_filters": [
  159. {
  160. "clause": "WHERE",
  161. "comparator": "someval",
  162. "expressionType": "SIMPLE",
  163. "operator": "in",
  164. "subject": "a",
  165. },
  166. {
  167. "clause": "WHERE",
  168. "comparator": ["c1", "c2"],
  169. "expressionType": "SIMPLE",
  170. "operator": "==",
  171. "subject": "B",
  172. },
  173. ]
  174. }
  175. merge_extra_filters(form_data)
  176. self.assertEqual(form_data, expected)
  177. # adds extra filters to existing filters
  178. form_data = {
  179. "extra_filters": [
  180. {"col": "a", "op": "in", "val": "someval"},
  181. {"col": "B", "op": "==", "val": ["c1", "c2"]},
  182. ],
  183. "adhoc_filters": [
  184. {
  185. "clause": "WHERE",
  186. "comparator": ["G1", "g2"],
  187. "expressionType": "SIMPLE",
  188. "operator": "!=",
  189. "subject": "D",
  190. }
  191. ],
  192. }
  193. expected = {
  194. "adhoc_filters": [
  195. {
  196. "clause": "WHERE",
  197. "comparator": ["G1", "g2"],
  198. "expressionType": "SIMPLE",
  199. "operator": "!=",
  200. "subject": "D",
  201. },
  202. {
  203. "clause": "WHERE",
  204. "comparator": "someval",
  205. "expressionType": "SIMPLE",
  206. "operator": "in",
  207. "subject": "a",
  208. },
  209. {
  210. "clause": "WHERE",
  211. "comparator": ["c1", "c2"],
  212. "expressionType": "SIMPLE",
  213. "operator": "==",
  214. "subject": "B",
  215. },
  216. ]
  217. }
  218. merge_extra_filters(form_data)
  219. self.assertEqual(form_data, expected)
  220. # adds extra filters to existing filters and sets time options
  221. form_data = {
  222. "extra_filters": [
  223. {"col": "__time_range", "op": "in", "val": "1 year ago :"},
  224. {"col": "__time_col", "op": "in", "val": "birth_year"},
  225. {"col": "__time_grain", "op": "in", "val": "years"},
  226. {"col": "A", "op": "like", "val": "hello"},
  227. {"col": "__time_origin", "op": "in", "val": "now"},
  228. {"col": "__granularity", "op": "in", "val": "90 seconds"},
  229. ]
  230. }
  231. expected = {
  232. "adhoc_filters": [
  233. {
  234. "clause": "WHERE",
  235. "comparator": "hello",
  236. "expressionType": "SIMPLE",
  237. "operator": "like",
  238. "subject": "A",
  239. }
  240. ],
  241. "time_range": "1 year ago :",
  242. "granularity_sqla": "birth_year",
  243. "time_grain_sqla": "years",
  244. "granularity": "90 seconds",
  245. "druid_time_origin": "now",
  246. }
  247. merge_extra_filters(form_data)
  248. self.assertEqual(form_data, expected)
  249. @patch("superset.utils.core.to_adhoc", mock_to_adhoc)
  250. def test_merge_extra_filters_ignores_empty_filters(self):
  251. form_data = {
  252. "extra_filters": [
  253. {"col": "a", "op": "in", "val": ""},
  254. {"col": "B", "op": "==", "val": []},
  255. ]
  256. }
  257. expected = {"adhoc_filters": []}
  258. merge_extra_filters(form_data)
  259. self.assertEqual(form_data, expected)
  260. @patch("superset.utils.core.to_adhoc", mock_to_adhoc)
  261. def test_merge_extra_filters_ignores_nones(self):
  262. form_data = {
  263. "adhoc_filters": [
  264. {
  265. "clause": "WHERE",
  266. "comparator": "",
  267. "expressionType": "SIMPLE",
  268. "operator": "in",
  269. "subject": None,
  270. }
  271. ],
  272. "extra_filters": [{"col": "B", "op": "==", "val": []}],
  273. }
  274. expected = {
  275. "adhoc_filters": [
  276. {
  277. "clause": "WHERE",
  278. "comparator": "",
  279. "expressionType": "SIMPLE",
  280. "operator": "in",
  281. "subject": None,
  282. }
  283. ]
  284. }
  285. merge_extra_filters(form_data)
  286. self.assertEqual(form_data, expected)
  287. @patch("superset.utils.core.to_adhoc", mock_to_adhoc)
  288. def test_merge_extra_filters_ignores_equal_filters(self):
  289. form_data = {
  290. "extra_filters": [
  291. {"col": "a", "op": "in", "val": "someval"},
  292. {"col": "B", "op": "==", "val": ["c1", "c2"]},
  293. {"col": "c", "op": "in", "val": ["c1", 1, None]},
  294. ],
  295. "adhoc_filters": [
  296. {
  297. "clause": "WHERE",
  298. "comparator": "someval",
  299. "expressionType": "SIMPLE",
  300. "operator": "in",
  301. "subject": "a",
  302. },
  303. {
  304. "clause": "WHERE",
  305. "comparator": ["c1", "c2"],
  306. "expressionType": "SIMPLE",
  307. "operator": "==",
  308. "subject": "B",
  309. },
  310. {
  311. "clause": "WHERE",
  312. "comparator": ["c1", 1, None],
  313. "expressionType": "SIMPLE",
  314. "operator": "in",
  315. "subject": "c",
  316. },
  317. ],
  318. }
  319. expected = {
  320. "adhoc_filters": [
  321. {
  322. "clause": "WHERE",
  323. "comparator": "someval",
  324. "expressionType": "SIMPLE",
  325. "operator": "in",
  326. "subject": "a",
  327. },
  328. {
  329. "clause": "WHERE",
  330. "comparator": ["c1", "c2"],
  331. "expressionType": "SIMPLE",
  332. "operator": "==",
  333. "subject": "B",
  334. },
  335. {
  336. "clause": "WHERE",
  337. "comparator": ["c1", 1, None],
  338. "expressionType": "SIMPLE",
  339. "operator": "in",
  340. "subject": "c",
  341. },
  342. ]
  343. }
  344. merge_extra_filters(form_data)
  345. self.assertEqual(form_data, expected)
  346. @patch("superset.utils.core.to_adhoc", mock_to_adhoc)
  347. def test_merge_extra_filters_merges_different_val_types(self):
  348. form_data = {
  349. "extra_filters": [
  350. {"col": "a", "op": "in", "val": ["g1", "g2"]},
  351. {"col": "B", "op": "==", "val": ["c1", "c2"]},
  352. ],
  353. "adhoc_filters": [
  354. {
  355. "clause": "WHERE",
  356. "comparator": "someval",
  357. "expressionType": "SIMPLE",
  358. "operator": "in",
  359. "subject": "a",
  360. },
  361. {
  362. "clause": "WHERE",
  363. "comparator": ["c1", "c2"],
  364. "expressionType": "SIMPLE",
  365. "operator": "==",
  366. "subject": "B",
  367. },
  368. ],
  369. }
  370. expected = {
  371. "adhoc_filters": [
  372. {
  373. "clause": "WHERE",
  374. "comparator": "someval",
  375. "expressionType": "SIMPLE",
  376. "operator": "in",
  377. "subject": "a",
  378. },
  379. {
  380. "clause": "WHERE",
  381. "comparator": ["c1", "c2"],
  382. "expressionType": "SIMPLE",
  383. "operator": "==",
  384. "subject": "B",
  385. },
  386. {
  387. "clause": "WHERE",
  388. "comparator": ["g1", "g2"],
  389. "expressionType": "SIMPLE",
  390. "operator": "in",
  391. "subject": "a",
  392. },
  393. ]
  394. }
  395. merge_extra_filters(form_data)
  396. self.assertEqual(form_data, expected)
  397. form_data = {
  398. "extra_filters": [
  399. {"col": "a", "op": "in", "val": "someval"},
  400. {"col": "B", "op": "==", "val": ["c1", "c2"]},
  401. ],
  402. "adhoc_filters": [
  403. {
  404. "clause": "WHERE",
  405. "comparator": ["g1", "g2"],
  406. "expressionType": "SIMPLE",
  407. "operator": "in",
  408. "subject": "a",
  409. },
  410. {
  411. "clause": "WHERE",
  412. "comparator": ["c1", "c2"],
  413. "expressionType": "SIMPLE",
  414. "operator": "==",
  415. "subject": "B",
  416. },
  417. ],
  418. }
  419. expected = {
  420. "adhoc_filters": [
  421. {
  422. "clause": "WHERE",
  423. "comparator": ["g1", "g2"],
  424. "expressionType": "SIMPLE",
  425. "operator": "in",
  426. "subject": "a",
  427. },
  428. {
  429. "clause": "WHERE",
  430. "comparator": ["c1", "c2"],
  431. "expressionType": "SIMPLE",
  432. "operator": "==",
  433. "subject": "B",
  434. },
  435. {
  436. "clause": "WHERE",
  437. "comparator": "someval",
  438. "expressionType": "SIMPLE",
  439. "operator": "in",
  440. "subject": "a",
  441. },
  442. ]
  443. }
  444. merge_extra_filters(form_data)
  445. self.assertEqual(form_data, expected)
  446. @patch("superset.utils.core.to_adhoc", mock_to_adhoc)
  447. def test_merge_extra_filters_adds_unequal_lists(self):
  448. form_data = {
  449. "extra_filters": [
  450. {"col": "a", "op": "in", "val": ["g1", "g2", "g3"]},
  451. {"col": "B", "op": "==", "val": ["c1", "c2", "c3"]},
  452. ],
  453. "adhoc_filters": [
  454. {
  455. "clause": "WHERE",
  456. "comparator": ["g1", "g2"],
  457. "expressionType": "SIMPLE",
  458. "operator": "in",
  459. "subject": "a",
  460. },
  461. {
  462. "clause": "WHERE",
  463. "comparator": ["c1", "c2"],
  464. "expressionType": "SIMPLE",
  465. "operator": "==",
  466. "subject": "B",
  467. },
  468. ],
  469. }
  470. expected = {
  471. "adhoc_filters": [
  472. {
  473. "clause": "WHERE",
  474. "comparator": ["g1", "g2"],
  475. "expressionType": "SIMPLE",
  476. "operator": "in",
  477. "subject": "a",
  478. },
  479. {
  480. "clause": "WHERE",
  481. "comparator": ["c1", "c2"],
  482. "expressionType": "SIMPLE",
  483. "operator": "==",
  484. "subject": "B",
  485. },
  486. {
  487. "clause": "WHERE",
  488. "comparator": ["g1", "g2", "g3"],
  489. "expressionType": "SIMPLE",
  490. "operator": "in",
  491. "subject": "a",
  492. },
  493. {
  494. "clause": "WHERE",
  495. "comparator": ["c1", "c2", "c3"],
  496. "expressionType": "SIMPLE",
  497. "operator": "==",
  498. "subject": "B",
  499. },
  500. ]
  501. }
  502. merge_extra_filters(form_data)
  503. self.assertEqual(form_data, expected)
  504. def test_merge_request_params_when_url_params_undefined(self):
  505. form_data = {"since": "2000", "until": "now"}
  506. url_params = {"form_data": form_data, "dashboard_ids": "(1,2,3,4,5)"}
  507. merge_request_params(form_data, url_params)
  508. self.assertIn("url_params", form_data.keys())
  509. self.assertIn("dashboard_ids", form_data["url_params"])
  510. self.assertNotIn("form_data", form_data.keys())
  511. def test_merge_request_params_when_url_params_predefined(self):
  512. form_data = {
  513. "since": "2000",
  514. "until": "now",
  515. "url_params": {"abc": "123", "dashboard_ids": "(1,2,3)"},
  516. }
  517. url_params = {"form_data": form_data, "dashboard_ids": "(1,2,3,4,5)"}
  518. merge_request_params(form_data, url_params)
  519. self.assertIn("url_params", form_data.keys())
  520. self.assertIn("abc", form_data["url_params"])
  521. self.assertEquals(
  522. url_params["dashboard_ids"], form_data["url_params"]["dashboard_ids"]
  523. )
  524. def test_datetime_f(self):
  525. self.assertEqual(
  526. datetime_f(datetime(1990, 9, 21, 19, 11, 19, 626096)),
  527. "<nobr>1990-09-21T19:11:19.626096</nobr>",
  528. )
  529. self.assertEqual(len(datetime_f(datetime.now())), 28)
  530. self.assertEqual(datetime_f(None), "<nobr>None</nobr>")
  531. iso = datetime.now().isoformat()[:10].split("-")
  532. [a, b, c] = [int(v) for v in iso]
  533. self.assertEqual(datetime_f(datetime(a, b, c)), "<nobr>00:00:00</nobr>")
  534. def test_format_timedelta(self):
  535. self.assertEqual(format_timedelta(timedelta(0)), "0:00:00")
  536. self.assertEqual(format_timedelta(timedelta(days=1)), "1 day, 0:00:00")
  537. self.assertEqual(format_timedelta(timedelta(minutes=-6)), "-0:06:00")
  538. self.assertEqual(
  539. format_timedelta(timedelta(0) - timedelta(days=1, hours=5, minutes=6)),
  540. "-1 day, 5:06:00",
  541. )
  542. self.assertEqual(
  543. format_timedelta(timedelta(0) - timedelta(days=16, hours=4, minutes=3)),
  544. "-16 days, 4:03:00",
  545. )
  546. def test_json_encoded_obj(self):
  547. obj = {"a": 5, "b": ["a", "g", 5]}
  548. val = '{"a": 5, "b": ["a", "g", 5]}'
  549. jsonObj = JSONEncodedDict()
  550. resp = jsonObj.process_bind_param(obj, "dialect")
  551. self.assertIn('"a": 5', resp)
  552. self.assertIn('"b": ["a", "g", 5]', resp)
  553. self.assertEqual(jsonObj.process_result_value(val, "dialect"), obj)
  554. def test_validate_json(self):
  555. invalid = '{"a": 5, "b": [1, 5, ["g", "h]]}'
  556. with self.assertRaises(SupersetException):
  557. validate_json(invalid)
  558. def test_memoized_on_functions(self):
  559. watcher = {"val": 0}
  560. @memoized
  561. def test_function(a, b, c):
  562. watcher["val"] += 1
  563. return a * b * c
  564. result1 = test_function(1, 2, 3)
  565. result2 = test_function(1, 2, 3)
  566. self.assertEqual(result1, result2)
  567. self.assertEqual(watcher["val"], 1)
  568. def test_memoized_on_methods(self):
  569. class test_class:
  570. def __init__(self, num):
  571. self.num = num
  572. self.watcher = 0
  573. @memoized
  574. def test_method(self, a, b, c):
  575. self.watcher += 1
  576. return a * b * c * self.num
  577. instance = test_class(5)
  578. result1 = instance.test_method(1, 2, 3)
  579. result2 = instance.test_method(1, 2, 3)
  580. self.assertEqual(result1, result2)
  581. self.assertEqual(instance.watcher, 1)
  582. instance.num = 10
  583. self.assertEqual(result2, instance.test_method(1, 2, 3))
  584. def test_memoized_on_methods_with_watches(self):
  585. class test_class:
  586. def __init__(self, x, y):
  587. self.x = x
  588. self.y = y
  589. self.watcher = 0
  590. @memoized(watch=("x", "y"))
  591. def test_method(self, a, b, c):
  592. self.watcher += 1
  593. return a * b * c * self.x * self.y
  594. instance = test_class(3, 12)
  595. result1 = instance.test_method(1, 2, 3)
  596. result2 = instance.test_method(1, 2, 3)
  597. self.assertEqual(result1, result2)
  598. self.assertEqual(instance.watcher, 1)
  599. result3 = instance.test_method(2, 3, 4)
  600. self.assertEqual(instance.watcher, 2)
  601. result4 = instance.test_method(2, 3, 4)
  602. self.assertEqual(instance.watcher, 2)
  603. self.assertEqual(result3, result4)
  604. self.assertNotEqual(result3, result1)
  605. instance.x = 1
  606. result5 = instance.test_method(2, 3, 4)
  607. self.assertEqual(instance.watcher, 3)
  608. self.assertNotEqual(result5, result4)
  609. result6 = instance.test_method(2, 3, 4)
  610. self.assertEqual(instance.watcher, 3)
  611. self.assertEqual(result6, result5)
  612. instance.x = 10
  613. instance.y = 10
  614. result7 = instance.test_method(2, 3, 4)
  615. self.assertEqual(instance.watcher, 4)
  616. self.assertNotEqual(result7, result6)
  617. instance.x = 3
  618. instance.y = 12
  619. result8 = instance.test_method(1, 2, 3)
  620. self.assertEqual(instance.watcher, 4)
  621. self.assertEqual(result1, result8)
  622. @patch("superset.utils.core.parse_human_datetime", mock_parse_human_datetime)
  623. def test_get_since_until(self):
  624. result = get_since_until()
  625. expected = None, datetime(2016, 11, 7)
  626. self.assertEqual(result, expected)
  627. result = get_since_until(" : now")
  628. expected = None, datetime(2016, 11, 7, 9, 30, 10)
  629. self.assertEqual(result, expected)
  630. result = get_since_until("yesterday : tomorrow")
  631. expected = datetime(2016, 11, 6), datetime(2016, 11, 8)
  632. self.assertEqual(result, expected)
  633. result = get_since_until("2018-01-01T00:00:00 : 2018-12-31T23:59:59")
  634. expected = datetime(2018, 1, 1), datetime(2018, 12, 31, 23, 59, 59)
  635. self.assertEqual(result, expected)
  636. result = get_since_until("Last year")
  637. expected = datetime(2015, 11, 7), datetime(2016, 11, 7)
  638. self.assertEqual(result, expected)
  639. result = get_since_until("Last 5 months")
  640. expected = datetime(2016, 6, 7), datetime(2016, 11, 7)
  641. self.assertEqual(result, expected)
  642. result = get_since_until("Next 5 months")
  643. expected = datetime(2016, 11, 7), datetime(2017, 4, 7)
  644. self.assertEqual(result, expected)
  645. result = get_since_until(since="5 days")
  646. expected = datetime(2016, 11, 2), datetime(2016, 11, 7)
  647. self.assertEqual(result, expected)
  648. result = get_since_until(since="5 days ago", until="tomorrow")
  649. expected = datetime(2016, 11, 2), datetime(2016, 11, 8)
  650. self.assertEqual(result, expected)
  651. result = get_since_until(time_range="yesterday : tomorrow", time_shift="1 day")
  652. expected = datetime(2016, 11, 5), datetime(2016, 11, 7)
  653. self.assertEqual(result, expected)
  654. result = get_since_until(time_range="5 days : now")
  655. expected = datetime(2016, 11, 2), datetime(2016, 11, 7, 9, 30, 10)
  656. self.assertEqual(result, expected)
  657. result = get_since_until("Last week", relative_end="now")
  658. expected = datetime(2016, 10, 31), datetime(2016, 11, 7, 9, 30, 10)
  659. self.assertEqual(result, expected)
  660. result = get_since_until("Last week", relative_start="now")
  661. expected = datetime(2016, 10, 31, 9, 30, 10), datetime(2016, 11, 7)
  662. self.assertEqual(result, expected)
  663. result = get_since_until("Last week", relative_start="now", relative_end="now")
  664. expected = datetime(2016, 10, 31, 9, 30, 10), datetime(2016, 11, 7, 9, 30, 10)
  665. self.assertEqual(result, expected)
  666. with self.assertRaises(ValueError):
  667. get_since_until(time_range="tomorrow : yesterday")
  668. @patch("superset.utils.core.to_adhoc", mock_to_adhoc)
  669. def test_convert_legacy_filters_into_adhoc_where(self):
  670. form_data = {"where": "a = 1"}
  671. expected = {
  672. "adhoc_filters": [
  673. {"clause": "WHERE", "expressionType": "SQL", "sqlExpression": "a = 1"}
  674. ]
  675. }
  676. convert_legacy_filters_into_adhoc(form_data)
  677. self.assertEqual(form_data, expected)
  678. @patch("superset.utils.core.to_adhoc", mock_to_adhoc)
  679. def test_convert_legacy_filters_into_adhoc_filters(self):
  680. form_data = {"filters": [{"col": "a", "op": "in", "val": "someval"}]}
  681. expected = {
  682. "adhoc_filters": [
  683. {
  684. "clause": "WHERE",
  685. "comparator": "someval",
  686. "expressionType": "SIMPLE",
  687. "operator": "in",
  688. "subject": "a",
  689. }
  690. ]
  691. }
  692. convert_legacy_filters_into_adhoc(form_data)
  693. self.assertEqual(form_data, expected)
  694. @patch("superset.utils.core.to_adhoc", mock_to_adhoc)
  695. def test_convert_legacy_filters_into_adhoc_having(self):
  696. form_data = {"having": "COUNT(1) = 1"}
  697. expected = {
  698. "adhoc_filters": [
  699. {
  700. "clause": "HAVING",
  701. "expressionType": "SQL",
  702. "sqlExpression": "COUNT(1) = 1",
  703. }
  704. ]
  705. }
  706. convert_legacy_filters_into_adhoc(form_data)
  707. self.assertEqual(form_data, expected)
  708. @patch("superset.utils.core.to_adhoc", mock_to_adhoc)
  709. def test_convert_legacy_filters_into_adhoc_having_filters(self):
  710. form_data = {"having_filters": [{"col": "COUNT(1)", "op": "==", "val": 1}]}
  711. expected = {
  712. "adhoc_filters": [
  713. {
  714. "clause": "HAVING",
  715. "comparator": 1,
  716. "expressionType": "SIMPLE",
  717. "operator": "==",
  718. "subject": "COUNT(1)",
  719. }
  720. ]
  721. }
  722. convert_legacy_filters_into_adhoc(form_data)
  723. self.assertEqual(form_data, expected)
  724. @patch("superset.utils.core.to_adhoc", mock_to_adhoc)
  725. def test_convert_legacy_filters_into_adhoc_present_and_empty(self):
  726. form_data = {"adhoc_filters": [], "where": "a = 1"}
  727. expected = {
  728. "adhoc_filters": [
  729. {"clause": "WHERE", "expressionType": "SQL", "sqlExpression": "a = 1"}
  730. ]
  731. }
  732. convert_legacy_filters_into_adhoc(form_data)
  733. self.assertEqual(form_data, expected)
  734. @patch("superset.utils.core.to_adhoc", mock_to_adhoc)
  735. def test_convert_legacy_filters_into_adhoc_present_and_nonempty(self):
  736. form_data = {
  737. "adhoc_filters": [
  738. {"clause": "WHERE", "expressionType": "SQL", "sqlExpression": "a = 1"}
  739. ],
  740. "filters": [{"col": "a", "op": "in", "val": "someval"}],
  741. "having": "COUNT(1) = 1",
  742. "having_filters": [{"col": "COUNT(1)", "op": "==", "val": 1}],
  743. }
  744. expected = {
  745. "adhoc_filters": [
  746. {"clause": "WHERE", "expressionType": "SQL", "sqlExpression": "a = 1"}
  747. ]
  748. }
  749. convert_legacy_filters_into_adhoc(form_data)
  750. self.assertEqual(form_data, expected)
  751. def test_parse_js_uri_path_items_eval_undefined(self):
  752. self.assertIsNone(parse_js_uri_path_item("undefined", eval_undefined=True))
  753. self.assertIsNone(parse_js_uri_path_item("null", eval_undefined=True))
  754. self.assertEqual("undefined", parse_js_uri_path_item("undefined"))
  755. self.assertEqual("null", parse_js_uri_path_item("null"))
  756. def test_parse_js_uri_path_items_unquote(self):
  757. self.assertEqual("slashed/name", parse_js_uri_path_item("slashed%2fname"))
  758. self.assertEqual(
  759. "slashed%2fname", parse_js_uri_path_item("slashed%2fname", unquote=False)
  760. )
  761. def test_parse_js_uri_path_items_item_optional(self):
  762. self.assertIsNone(parse_js_uri_path_item(None))
  763. self.assertIsNotNone(parse_js_uri_path_item("item"))
  764. def test_setup_cache_null_config(self):
  765. app = Flask(__name__)
  766. cache_config = {"CACHE_TYPE": "null"}
  767. assert isinstance(CacheManager._setup_cache(app, cache_config), Cache)
  768. def test_setup_cache_standard_config(self):
  769. app = Flask(__name__)
  770. cache_config = {
  771. "CACHE_TYPE": "redis",
  772. "CACHE_DEFAULT_TIMEOUT": 60,
  773. "CACHE_KEY_PREFIX": "superset_results",
  774. "CACHE_REDIS_URL": "redis://localhost:6379/0",
  775. }
  776. assert isinstance(CacheManager._setup_cache(app, cache_config), Cache) is True
  777. def test_setup_cache_custom_function(self):
  778. app = Flask(__name__)
  779. CustomCache = type("CustomCache", (object,), {"__init__": lambda *args: None})
  780. def init_cache(app):
  781. return CustomCache(app, {})
  782. assert (
  783. isinstance(CacheManager._setup_cache(app, init_cache), CustomCache) is True
  784. )
  785. def test_get_stacktrace(self):
  786. with app.app_context():
  787. app.config["SHOW_STACKTRACE"] = True
  788. try:
  789. raise Exception("NONONO!")
  790. except Exception:
  791. stacktrace = get_stacktrace()
  792. self.assertIn("NONONO", stacktrace)
  793. app.config["SHOW_STACKTRACE"] = False
  794. try:
  795. raise Exception("NONONO!")
  796. except Exception:
  797. stacktrace = get_stacktrace()
  798. assert stacktrace is None
  799. def test_split(self):
  800. self.assertEqual(list(split("a b")), ["a", "b"])
  801. self.assertEqual(list(split("a,b", delimiter=",")), ["a", "b"])
  802. self.assertEqual(list(split("a,(b,a)", delimiter=",")), ["a", "(b,a)"])
  803. self.assertEqual(
  804. list(split('a,(b,a),"foo , bar"', delimiter=",")),
  805. ["a", "(b,a)", '"foo , bar"'],
  806. )
  807. self.assertEqual(
  808. list(split("a,'b,c'", delimiter=",", quote="'")), ["a", "'b,c'"]
  809. )
  810. self.assertEqual(list(split('a "b c"')), ["a", '"b c"'])
  811. self.assertEqual(list(split(r'a "b \" c"')), ["a", r'"b \" c"'])
  812. def test_get_or_create_db(self):
  813. get_or_create_db("test_db", "sqlite:///superset.db")
  814. database = db.session.query(Database).filter_by(database_name="test_db").one()
  815. self.assertIsNotNone(database)
  816. self.assertEqual(database.sqlalchemy_uri, "sqlite:///superset.db")
  817. self.assertIsNotNone(
  818. security_manager.find_permission_view_menu("database_access", database.perm)
  819. )
  820. # Test change URI
  821. get_or_create_db("test_db", "sqlite:///changed.db")
  822. database = db.session.query(Database).filter_by(database_name="test_db").one()
  823. self.assertEqual(database.sqlalchemy_uri, "sqlite:///changed.db")
  824. db.session.delete(database)
  825. db.session.commit()
  826. def test_get_or_create_db_invalid_uri(self):
  827. with self.assertRaises(ArgumentError):
  828. get_or_create_db("test_db", "yoursql:superset.db/()")
  829. def test_get_time_range_endpoints(self):
  830. self.assertEqual(
  831. get_time_range_endpoints(form_data={}),
  832. (TimeRangeEndpoint.INCLUSIVE, TimeRangeEndpoint.EXCLUSIVE),
  833. )
  834. self.assertEqual(
  835. get_time_range_endpoints(
  836. form_data={"time_range_endpoints": ["inclusive", "inclusive"]}
  837. ),
  838. (TimeRangeEndpoint.INCLUSIVE, TimeRangeEndpoint.INCLUSIVE),
  839. )
  840. self.assertEqual(
  841. get_time_range_endpoints(form_data={"datasource": "1_druid"}),
  842. (TimeRangeEndpoint.INCLUSIVE, TimeRangeEndpoint.EXCLUSIVE),
  843. )
  844. slc = Mock()
  845. slc.datasource.database.get_extra.return_value = {}
  846. self.assertEqual(
  847. get_time_range_endpoints(form_data={"datasource": "1__table"}, slc=slc),
  848. (TimeRangeEndpoint.UNKNOWN, TimeRangeEndpoint.INCLUSIVE),
  849. )
  850. slc.datasource.database.get_extra.return_value = {
  851. "time_range_endpoints": ["inclusive", "inclusive"]
  852. }
  853. self.assertEqual(
  854. get_time_range_endpoints(form_data={"datasource": "1__table"}, slc=slc),
  855. (TimeRangeEndpoint.INCLUSIVE, TimeRangeEndpoint.INCLUSIVE),
  856. )
  857. self.assertIsNone(get_time_range_endpoints(form_data={}, slc=slc))
  858. with app.app_context():
  859. app.config["SIP_15_GRACE_PERIOD_END"] = date.today() + timedelta(days=1)
  860. self.assertEqual(
  861. get_time_range_endpoints(form_data={"datasource": "1__table"}, slc=slc),
  862. (TimeRangeEndpoint.INCLUSIVE, TimeRangeEndpoint.INCLUSIVE),
  863. )
  864. app.config["SIP_15_GRACE_PERIOD_END"] = date.today()
  865. self.assertEqual(
  866. get_time_range_endpoints(form_data={"datasource": "1__table"}, slc=slc),
  867. (TimeRangeEndpoint.INCLUSIVE, TimeRangeEndpoint.EXCLUSIVE),
  868. )