From commits-return-1853-archive-asf-public=cust-asf.ponee.io@superset.incubator.apache.org Sat Nov 17 00:51:58 2018 Return-Path: X-Original-To: archive-asf-public@cust-asf.ponee.io Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by mx-eu-01.ponee.io (Postfix) with SMTP id BD222180670 for ; Sat, 17 Nov 2018 00:51:57 +0100 (CET) Received: (qmail 70291 invoked by uid 500); 16 Nov 2018 23:51:56 -0000 Mailing-List: contact commits-help@superset.incubator.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@superset.incubator.apache.org Delivered-To: mailing list commits@superset.incubator.apache.org Received: (qmail 70282 invoked by uid 99); 16 Nov 2018 23:51:56 -0000 Received: from ec2-52-202-80-70.compute-1.amazonaws.com (HELO gitbox.apache.org) (52.202.80.70) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 16 Nov 2018 23:51:56 +0000 Received: by gitbox.apache.org (ASF Mail Server at gitbox.apache.org, from userid 33) id 4B5CA85277; Fri, 16 Nov 2018 23:51:56 +0000 (UTC) Date: Fri, 16 Nov 2018 23:51:56 +0000 To: "commits@superset.apache.org" Subject: [incubator-superset] branch master updated: [SIP-5] QueryBuilder in the client for granularity and groupby in word cloud (#6377) MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit Message-ID: <154241231598.28611.12330197406562519337@gitbox.apache.org> From: ccwilliams@apache.org X-Git-Host: gitbox.apache.org X-Git-Repo: incubator-superset X-Git-Refname: refs/heads/master X-Git-Reftype: branch X-Git-Oldrev: c42bcf81bc51bd5ef8d214a1a2036ab781ddb5c9 X-Git-Newrev: 8b2cae007d0b38217bd9f227c7079cca690fafa8 X-Git-Rev: 8b2cae007d0b38217bd9f227c7079cca690fafa8 X-Git-NotificationType: ref_changed_plus_diff X-Git-Multimail-Version: 1.5.dev Auto-Submitted: auto-generated This is an automated email from the ASF dual-hosted git repository. ccwilliams pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/incubator-superset.git The following commit(s) were added to refs/heads/master by this push: new 8b2cae0 [SIP-5] QueryBuilder in the client for granularity and groupby in word cloud (#6377) 8b2cae0 is described below commit 8b2cae007d0b38217bd9f227c7079cca690fafa8 Author: Christine Chambers AuthorDate: Fri Nov 16 15:51:50 2018 -0800 [SIP-5] QueryBuilder in the client for granularity and groupby in word cloud (#6377) * [SIP-5] QueryBuilder in the client for groupby field - Lay the structure of the QueryContext builder in the client - QueryContext builder composes different portions of the queryContext object (to be sent to server from the client) from the datasourceBuilder and the queryObjectBuilder. - The datasourceBuilder builds the datasource id and type by parsing them from the datasource field in formdata - The queryObjectBuilder currently only builds the groupby field. It will further compose the queryObject from sub query builders in future PRs. - Create a buildQuery method for WordCloud and override the groupby value with the value of series from formdata. * Addressing PR comments - Rename query builder files and their default exports - Move tests into spec/javascripts/superset-ui for easy mass migration to superset-uiin the future - Define viz specific formData and export formData for each viz type as intersection type of the generic formData and the viz specific one - Specify the type signature for sqla and druid based data sources * Addressing additional PR comments. - Introduce a Datasource class and leverage Typescript's declaration merging of the interface by the same name. - Let Typescript infer the return type of various builders instead of specifying them explicitly * Further tweaking the generic buildQueryContext method and viz speicific buildQuery methodes per PR comments. * git mv a renamed file. * addressing additional pr comments --- superset/assets/jest.config.js | 2 +- .../superset-ui/WordCloudBuildQuery.test.ts | 15 +++++++++++ .../superset-ui/buildQueryContext.test.ts | 22 ++++++++++++++++ .../superset-ui/buildQueryObject.test.ts | 13 ++++++++++ superset/assets/src/query/DatasourceKey.ts | 29 ++++++++++++++++++++++ superset/assets/src/query/FormData.ts | 21 ++++++++++++++++ superset/assets/src/query/buildQueryContext.ts | 15 +++++++++++ superset/assets/src/query/buildQueryObject.ts | 18 ++++++++++++++ superset/assets/src/query/index.ts | 3 +++ .../src/visualizations/wordcloud/FormData.ts | 11 ++++++++ .../wordcloud/WordCloudChartPlugin.js | 2 ++ .../src/visualizations/wordcloud/buildQuery.ts | 10 ++++++++ 12 files changed, 160 insertions(+), 1 deletion(-) diff --git a/superset/assets/jest.config.js b/superset/assets/jest.config.js index 64dcb6f..858f6f9 100644 --- a/superset/assets/jest.config.js +++ b/superset/assets/jest.config.js @@ -1,5 +1,5 @@ module.exports = { - testRegex: '\\/spec\\/.*_spec\\.(j|t)sx?$', + testRegex: '\\/spec\\/.*(_spec|\\.test)\\.(j|t)sx?$', moduleNameMapper: { '\\.(css|less)$': '/spec/__mocks__/styleMock.js', '\\.(gif|ttf|eot|svg)$': '/spec/__mocks__/fileMock.js', diff --git a/superset/assets/spec/javascripts/superset-ui/WordCloudBuildQuery.test.ts b/superset/assets/spec/javascripts/superset-ui/WordCloudBuildQuery.test.ts new file mode 100644 index 0000000..a6edfbc --- /dev/null +++ b/superset/assets/spec/javascripts/superset-ui/WordCloudBuildQuery.test.ts @@ -0,0 +1,15 @@ +import buildQuery from 'src/visualizations/wordcloud/buildQuery'; + +describe('WordCloud buildQuery', () => { + const formData = { + datasource: '5__table', + granularity_sqla: 'ds', + series: 'foo', + }; + + it('should build groupby with series in form data', () => { + const queryContext = buildQuery(formData); + const [ query ] = queryContext.queries; + expect(query.groupby).toEqual(['foo']); + }); +}); diff --git a/superset/assets/spec/javascripts/superset-ui/buildQueryContext.test.ts b/superset/assets/spec/javascripts/superset-ui/buildQueryContext.test.ts new file mode 100644 index 0000000..b6fd995 --- /dev/null +++ b/superset/assets/spec/javascripts/superset-ui/buildQueryContext.test.ts @@ -0,0 +1,22 @@ +import build from 'src/query/buildQueryContext'; +import * as queryObjectBuilder from 'src/query/buildQueryObject'; + +describe('queryContextBuilder', () => { + it('should build datasource for table sources', () => { + const queryContext = build({ datasource: '5__table', granularity_sqla: 'ds'}); + expect(queryContext.datasource.id).toBe(5); + expect(queryContext.datasource.type).toBe('table'); + }); + + it('should build datasource for druid sources', () => { + const queryContext = build({ datasource: '5__druid', granularity: 'ds'}); + expect(queryContext.datasource.id).toBe(5); + expect(queryContext.datasource.type).toBe('druid'); + }); + + it('should call queryObjectBuilder to build queries', () => { + const buildQueryObjectSpy = jest.spyOn(queryObjectBuilder, 'default'); + build({ datasource: '5__table', granularity_sqla: 'ds'}); + expect(buildQueryObjectSpy).toHaveBeenCalledTimes(1); + }); +}); diff --git a/superset/assets/spec/javascripts/superset-ui/buildQueryObject.test.ts b/superset/assets/spec/javascripts/superset-ui/buildQueryObject.test.ts new file mode 100644 index 0000000..ec111df --- /dev/null +++ b/superset/assets/spec/javascripts/superset-ui/buildQueryObject.test.ts @@ -0,0 +1,13 @@ +import build from 'src/query/buildQueryObject'; + +describe('queryObjectBuilder', () => { + it('should build granularity for sql alchemy datasources', () => { + const query = build({datasource: '5__table', granularity_sqla: 'ds'}); + expect(query.granularity).toEqual('ds'); + }); + + it('should build granularity for sql alchemy datasources', () => { + const query = build({datasource: '5__druid', granularity: 'ds'}); + expect(query.granularity).toEqual('ds'); + }); +}); diff --git a/superset/assets/src/query/DatasourceKey.ts b/superset/assets/src/query/DatasourceKey.ts new file mode 100644 index 0000000..a13c121 --- /dev/null +++ b/superset/assets/src/query/DatasourceKey.ts @@ -0,0 +1,29 @@ +enum DatasourceType { + Table = 'table', + Druid = 'druid', +} + +export default interface DatasourceKey { + id: number; + type: DatasourceType; +} + +// Declaration merging with the interface above. No need to redeclare id and type. +export default class DatasourceKey { + constructor(key: string) { + const [ idStr, typeStr ] = key.split('__'); + this.id = parseInt(idStr, 10); + this.type = typeStr === 'table' ? DatasourceType.Table : DatasourceType.Druid; + } + + public toString() { + return `${this.id}__${this.type}`; + } + + public toObject() { + return { + id: this.id, + type: this.type, + }; + } +} diff --git a/superset/assets/src/query/FormData.ts b/superset/assets/src/query/FormData.ts new file mode 100644 index 0000000..daa7abf --- /dev/null +++ b/superset/assets/src/query/FormData.ts @@ -0,0 +1,21 @@ +// Type signature and utility functions for formData shared by all viz types +// It will be gradually filled out as we build out the query object +interface BaseFormData { + datasource: string; +} + +// FormData is either sqla-based or druid-based +interface SqlaFormData extends BaseFormData { + granularity_sqla: string; +} + +interface DruidFormData extends BaseFormData { + granularity: string; +} + +type FormData = SqlaFormData | DruidFormData; +export default FormData; + +export function getGranularity(formData: FormData): string { + return 'granularity_sqla' in formData ? formData.granularity_sqla : formData.granularity; +} diff --git a/superset/assets/src/query/buildQueryContext.ts b/superset/assets/src/query/buildQueryContext.ts new file mode 100644 index 0000000..8ce0464 --- /dev/null +++ b/superset/assets/src/query/buildQueryContext.ts @@ -0,0 +1,15 @@ +import buildQueryObject, { QueryObject } from './buildQueryObject'; +import DatasourceKey from './DatasourceKey'; +import FormData from './FormData'; + +const WRAP_IN_ARRAY = (baseQueryObject: QueryObject) => [baseQueryObject]; + +// Note: let TypeScript infer the return type +export default function buildQueryContext( + formData: FormData, + buildQuery: (baseQueryObject: QueryObject) => QueryObject[] = WRAP_IN_ARRAY) { + return { + datasource: new DatasourceKey(formData.datasource).toObject(), + queries: buildQuery(buildQueryObject(formData)), + }; +} diff --git a/superset/assets/src/query/buildQueryObject.ts b/superset/assets/src/query/buildQueryObject.ts new file mode 100644 index 0000000..578d9ae --- /dev/null +++ b/superset/assets/src/query/buildQueryObject.ts @@ -0,0 +1,18 @@ +import FormData, { getGranularity } from './FormData'; + +// TODO: fill out the rest of the query object +export interface QueryObject { + granularity: string; + groupby?: string[]; +} + +// Build the common segments of all query objects (e.g. the granularity field derived from +// either sql alchemy or druid). The segments specific to each viz type is constructed in the +// buildQuery method for each viz type (see `wordcloud/buildQuery.ts` for an example). +// Note the type of the formData argument passed in here is the type of the formData for a +// specific viz, which is a subtype of the generic formData shared among all viz types. +export default function buildQueryObject(formData: T): QueryObject { + return { + granularity: getGranularity(formData), + }; +} diff --git a/superset/assets/src/query/index.ts b/superset/assets/src/query/index.ts new file mode 100644 index 0000000..cda7126 --- /dev/null +++ b/superset/assets/src/query/index.ts @@ -0,0 +1,3 @@ +// Public API of the query module +export { default } from './buildQueryContext'; +export { default as FormData } from './FormData'; diff --git a/superset/assets/src/visualizations/wordcloud/FormData.ts b/superset/assets/src/visualizations/wordcloud/FormData.ts new file mode 100644 index 0000000..55f8131 --- /dev/null +++ b/superset/assets/src/visualizations/wordcloud/FormData.ts @@ -0,0 +1,11 @@ +import { FormData as GenericFormData } from 'src/query'; + +// FormData specific to the wordcloud viz +interface WordCloudFormData { + series: string; +} + +// FormData for wordcloud contains both common properties of all form data +// and properties specific to wordcloud vizzes +type FormData = GenericFormData & WordCloudFormData; +export default FormData; diff --git a/superset/assets/src/visualizations/wordcloud/WordCloudChartPlugin.js b/superset/assets/src/visualizations/wordcloud/WordCloudChartPlugin.js index b828820..19b43c5 100644 --- a/superset/assets/src/visualizations/wordcloud/WordCloudChartPlugin.js +++ b/superset/assets/src/visualizations/wordcloud/WordCloudChartPlugin.js @@ -1,5 +1,6 @@ import { t } from '@superset-ui/translation'; import { ChartMetadata, ChartPlugin } from '@superset-ui/chart'; +import buildQuery from './buildQuery'; import transformProps from './transformProps'; import thumbnail from './images/thumbnail.png'; @@ -14,6 +15,7 @@ export default class WordCloudChartPlugin extends ChartPlugin { constructor() { super({ metadata, + buildQuery, transformProps, loadChart: () => import('./ReactWordCloud.js'), }); diff --git a/superset/assets/src/visualizations/wordcloud/buildQuery.ts b/superset/assets/src/visualizations/wordcloud/buildQuery.ts new file mode 100644 index 0000000..2aa0f2c --- /dev/null +++ b/superset/assets/src/visualizations/wordcloud/buildQuery.ts @@ -0,0 +1,10 @@ +import buildQueryContext from 'src/query'; +import FormData from './FormData'; + +export default function buildQuery(formData: FormData) { + // Set the single QueryObject's groupby field with series in formData + return buildQueryContext(formData, (baseQueryObject) => [{ + ...baseQueryObject, + groupby: [formData.series], + }]); +}