buildQuery.test.ts 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376
  1. /**
  2. * Licensed to the Apache Software Foundation (ASF) under one
  3. * or more contributor license agreements. See the NOTICE file
  4. * distributed with this work for additional information
  5. * regarding copyright ownership. The ASF licenses this file
  6. * to you under the Apache License, Version 2.0 (the
  7. * "License"); you may not use this file except in compliance
  8. * with the License. You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing,
  13. * software distributed under the License is distributed on an
  14. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  15. * KIND, either express or implied. See the License for the
  16. * specific language governing permissions and limitations
  17. * under the License.
  18. */
  19. import {
  20. ComparisonType,
  21. FreeFormAdhocFilter,
  22. RollingType,
  23. TimeGranularity,
  24. } from '@superset-ui/core';
  25. import buildQuery from '../../src/MixedTimeseries/buildQuery';
  26. const formDataMixedChart = {
  27. datasource: 'dummy',
  28. viz_type: 'my_chart',
  29. // query
  30. // -- common
  31. time_range: '1980 : 2000',
  32. time_grain_sqla: TimeGranularity.WEEK,
  33. granularity_sqla: 'ds',
  34. // -- query a
  35. groupby: ['foo'],
  36. metrics: ['sum(sales)'],
  37. adhoc_filters: [
  38. {
  39. clause: 'WHERE',
  40. expressionType: 'SQL',
  41. sqlExpression: "foo in ('a', 'b')",
  42. } as FreeFormAdhocFilter,
  43. ],
  44. limit: 5,
  45. row_limit: 10,
  46. timeseries_limit_metric: 'count',
  47. order_desc: true,
  48. truncate_metric: true,
  49. show_empty_columns: true,
  50. // -- query b
  51. groupby_b: [],
  52. metrics_b: ['count'],
  53. adhoc_filters_b: [
  54. {
  55. clause: 'WHERE',
  56. expressionType: 'SQL',
  57. sqlExpression: "name in ('c', 'd')",
  58. } as FreeFormAdhocFilter,
  59. ],
  60. limit_b: undefined,
  61. row_limit_b: 100,
  62. timeseries_limit_metric_b: undefined,
  63. order_desc_b: false,
  64. truncate_metric_b: true,
  65. show_empty_columns_b: true,
  66. // chart configs
  67. show_value: false,
  68. show_valueB: undefined,
  69. };
  70. const formDataMixedChartWithAA = {
  71. ...formDataMixedChart,
  72. rolling_type: RollingType.Cumsum,
  73. time_compare: ['1 years ago'],
  74. comparison_type: ComparisonType.Values,
  75. resample_rule: '1AS',
  76. resample_method: 'zerofill',
  77. rolling_type_b: RollingType.Sum,
  78. rolling_periods_b: 1,
  79. min_periods_b: 1,
  80. comparison_type_b: ComparisonType.Difference,
  81. time_compare_b: ['3 years ago'],
  82. resample_rule_b: '1A',
  83. resample_method_b: 'asfreq',
  84. };
  85. test('should compile query object A', () => {
  86. const query = buildQuery(formDataMixedChart).queries[0];
  87. expect(query).toEqual({
  88. time_range: '1980 : 2000',
  89. since: undefined,
  90. until: undefined,
  91. granularity: 'ds',
  92. filters: [],
  93. extras: {
  94. having: '',
  95. time_grain_sqla: 'P1W',
  96. where: "(foo in ('a', 'b'))",
  97. },
  98. applied_time_extras: {},
  99. columns: ['foo'],
  100. metrics: ['sum(sales)'],
  101. annotation_layers: [],
  102. row_limit: 10,
  103. row_offset: undefined,
  104. series_columns: ['foo'],
  105. series_limit: 5,
  106. series_limit_metric: undefined,
  107. group_others_when_limit_reached: false,
  108. url_params: {},
  109. custom_params: {},
  110. custom_form_data: {},
  111. is_timeseries: true,
  112. time_offsets: [],
  113. post_processing: [
  114. {
  115. operation: 'pivot',
  116. options: {
  117. aggregates: {
  118. 'sum(sales)': {
  119. operator: 'mean',
  120. },
  121. },
  122. columns: ['foo'],
  123. drop_missing_columns: false,
  124. index: ['__timestamp'],
  125. },
  126. },
  127. {
  128. operation: 'rename',
  129. options: {
  130. columns: {
  131. 'sum(sales)': null,
  132. },
  133. inplace: true,
  134. level: 0,
  135. },
  136. },
  137. {
  138. operation: 'flatten',
  139. },
  140. ],
  141. orderby: [['count', false]],
  142. });
  143. });
  144. test('should compile query object B', () => {
  145. const query = buildQuery(formDataMixedChart).queries[1];
  146. expect(query).toEqual({
  147. time_range: '1980 : 2000',
  148. since: undefined,
  149. until: undefined,
  150. granularity: 'ds',
  151. filters: [],
  152. extras: {
  153. having: '',
  154. time_grain_sqla: 'P1W',
  155. where: "(name in ('c', 'd'))",
  156. },
  157. applied_time_extras: {},
  158. columns: [],
  159. metrics: ['count'],
  160. annotation_layers: [],
  161. row_limit: 100,
  162. row_offset: undefined,
  163. series_columns: [],
  164. series_limit: 0,
  165. series_limit_metric: undefined,
  166. group_others_when_limit_reached: false,
  167. url_params: {},
  168. custom_params: {},
  169. custom_form_data: {},
  170. is_timeseries: true,
  171. time_offsets: [],
  172. post_processing: [
  173. {
  174. operation: 'pivot',
  175. options: {
  176. aggregates: {
  177. count: {
  178. operator: 'mean',
  179. },
  180. },
  181. columns: [],
  182. drop_missing_columns: false,
  183. index: ['__timestamp'],
  184. },
  185. },
  186. {
  187. operation: 'flatten',
  188. },
  189. ],
  190. orderby: [['count', true]],
  191. });
  192. });
  193. test('should compile AA in query A', () => {
  194. const query = buildQuery(formDataMixedChartWithAA).queries[0];
  195. // time comparison
  196. expect(query.time_offsets).toEqual(['1 years ago']);
  197. // pivot
  198. expect(
  199. query.post_processing?.find(operator => operator?.operation === 'pivot'),
  200. ).toEqual({
  201. operation: 'pivot',
  202. options: {
  203. index: ['__timestamp'],
  204. columns: ['foo'],
  205. drop_missing_columns: false,
  206. aggregates: {
  207. 'sum(sales)': { operator: 'mean' },
  208. 'sum(sales)__1 years ago': { operator: 'mean' },
  209. },
  210. },
  211. });
  212. // cumsum
  213. expect(
  214. // prettier-ignore
  215. query
  216. .post_processing
  217. ?.find(operator => operator?.operation === 'cum')
  218. ?.operation,
  219. ).toEqual('cum');
  220. // resample
  221. expect(
  222. // prettier-ignore
  223. query
  224. .post_processing
  225. ?.find(operator => operator?.operation === 'resample'),
  226. ).toEqual({
  227. operation: 'resample',
  228. options: {
  229. method: 'asfreq',
  230. rule: '1AS',
  231. fill_value: 0,
  232. },
  233. });
  234. });
  235. test('should compile AA in query B', () => {
  236. const query = buildQuery(formDataMixedChartWithAA).queries[1];
  237. // time comparison
  238. expect(query.time_offsets).toEqual(['3 years ago']);
  239. // rolling total
  240. expect(
  241. // prettier-ignore
  242. query
  243. .post_processing
  244. ?.find(operator => operator?.operation === 'rolling'),
  245. ).toEqual({
  246. operation: 'rolling',
  247. options: {
  248. rolling_type: 'sum',
  249. window: 1,
  250. min_periods: 1,
  251. columns: {
  252. count: 'count',
  253. 'count__3 years ago': 'count__3 years ago',
  254. },
  255. },
  256. });
  257. // resample
  258. expect(
  259. // prettier-ignore
  260. query
  261. .post_processing
  262. ?.find(operator => operator?.operation === 'resample'),
  263. ).toEqual({
  264. operation: 'resample',
  265. options: {
  266. method: 'asfreq',
  267. rule: '1A',
  268. fill_value: null,
  269. },
  270. });
  271. });
  272. test("shouldn't convert a queryObject with axis", () => {
  273. const { queries } = buildQuery(formDataMixedChart);
  274. expect(queries[0]).toEqual(
  275. expect.objectContaining({
  276. granularity: 'ds',
  277. columns: ['foo'],
  278. series_columns: ['foo'],
  279. metrics: ['sum(sales)'],
  280. is_timeseries: true,
  281. extras: {
  282. time_grain_sqla: 'P1W',
  283. having: '',
  284. where: "(foo in ('a', 'b'))",
  285. },
  286. post_processing: [
  287. {
  288. operation: 'pivot',
  289. options: {
  290. aggregates: {
  291. 'sum(sales)': {
  292. operator: 'mean',
  293. },
  294. },
  295. columns: ['foo'],
  296. drop_missing_columns: false,
  297. index: ['__timestamp'],
  298. },
  299. },
  300. {
  301. operation: 'rename',
  302. options: { columns: { 'sum(sales)': null }, inplace: true, level: 0 },
  303. },
  304. {
  305. operation: 'flatten',
  306. },
  307. ],
  308. }),
  309. );
  310. expect(queries[1]).toEqual(
  311. expect.objectContaining({
  312. granularity: 'ds',
  313. columns: [],
  314. series_columns: [],
  315. metrics: ['count'],
  316. is_timeseries: true,
  317. extras: {
  318. time_grain_sqla: 'P1W',
  319. having: '',
  320. where: "(name in ('c', 'd'))",
  321. },
  322. post_processing: [
  323. {
  324. operation: 'pivot',
  325. options: {
  326. aggregates: {
  327. count: {
  328. operator: 'mean',
  329. },
  330. },
  331. columns: [],
  332. drop_missing_columns: false,
  333. index: ['__timestamp'],
  334. },
  335. },
  336. {
  337. operation: 'flatten',
  338. },
  339. ],
  340. }),
  341. );
  342. });
  343. test('ensure correct pivot columns', () => {
  344. const query = buildQuery({ ...formDataMixedChartWithAA, x_axis: 'ds' })
  345. .queries[0];
  346. expect(query.time_offsets).toEqual(['1 years ago']);
  347. // pivot
  348. expect(
  349. query.post_processing?.find(operator => operator?.operation === 'pivot'),
  350. ).toEqual({
  351. operation: 'pivot',
  352. options: {
  353. index: ['ds'],
  354. columns: ['foo'],
  355. drop_missing_columns: false,
  356. aggregates: {
  357. 'sum(sales)': { operator: 'mean' },
  358. 'sum(sales)__1 years ago': { operator: 'mean' },
  359. },
  360. },
  361. });
  362. });