From 99e2051d543d0164cd010a01f135beb8d0c8f5d9 Mon Sep 17 00:00:00 2001 From: Felipe Cadavid Date: Tue, 9 Apr 2024 16:05:05 -0500 Subject: [PATCH 1/7] feat: experimental Report component --- .../src/components/Report/Report.module.scss | 25 +++ .../src/components/Report/Report.stories.tsx | 212 ++++++++++++++++++ .../ui-kit/src/components/Report/Report.tsx | 74 ++++++ .../src/components/Report/Report.types.ts | 6 + .../ui-kit/src/components/Report/index.ts | 2 + .../ui-kit/src/components/Report/utils.ts | 3 + packages/ui-kit/src/components/index.ts | 1 + packages/ui-kit/src/hooks/index.ts | 1 + .../src/hooks/useReportComponents/index.ts | 1 + .../useReportComponents.ts | 69 ++++++ 10 files changed, 394 insertions(+) create mode 100644 packages/ui-kit/src/components/Report/Report.module.scss create mode 100644 packages/ui-kit/src/components/Report/Report.stories.tsx create mode 100644 packages/ui-kit/src/components/Report/Report.tsx create mode 100644 packages/ui-kit/src/components/Report/Report.types.ts create mode 100644 packages/ui-kit/src/components/Report/index.ts create mode 100644 packages/ui-kit/src/components/Report/utils.ts create mode 100644 packages/ui-kit/src/hooks/useReportComponents/index.ts create mode 100644 packages/ui-kit/src/hooks/useReportComponents/useReportComponents.ts diff --git a/packages/ui-kit/src/components/Report/Report.module.scss b/packages/ui-kit/src/components/Report/Report.module.scss new file mode 100644 index 00000000..bdeb8e94 --- /dev/null +++ b/packages/ui-kit/src/components/Report/Report.module.scss @@ -0,0 +1,25 @@ +.container { + display: grid; + + height: 100%; + + gap: 1rem; +} + +.loader { + height: 100% !important; +} + +.loader > div[data-role="loader-animation"] { + height: 100% !important; +} + +.card { + display: flex; + justify-content: center; + align-items: center; +} + +.clickable { + cursor: pointer; +} diff --git a/packages/ui-kit/src/components/Report/Report.stories.tsx b/packages/ui-kit/src/components/Report/Report.stories.tsx new file mode 100644 index 00000000..88c9a520 --- /dev/null +++ b/packages/ui-kit/src/components/Report/Report.stories.tsx @@ -0,0 +1,212 @@ +import React from 'react' + +import { Meta, StoryObj } from '@storybook/react' +import { quotedStringRegex, storybookCodeTemplate } from '../../helpers' +import { Report } from './Report' +import { AccessTokenProvider } from '../AccessTokenProvider' + +type Story = StoryObj + +const meta: Meta = { + title: 'Components/Report', + component: Report, + parameters: { + controls: { sort: 'alpha' }, + imports: 'PieChart, RelativeTimeRange', + transformBody: (body: string) => body.replace(quotedStringRegex('LAST_N_DAYS'), 'RelativeTimeRange.LastNDays'), + codeTemplate: storybookCodeTemplate + } +} + +export default meta + +export const Basic: Story = { + args: { + layout: [ + ['timeSeries', 'timeSeries', 'leaderboard'], + ['counter1', 'counter2', 'leaderboard'], + ['timeSeries2', 'timeSeries2', 'timeSeries2'] + ], + charts: [ + { + id: 'timeSeries', + query: `query TimeSeriesQuery($input: TimeSeriesInput!) { + timeSeries(input: $input) { + labels + values + } + }`, + type: 'timeSeries', + variables: `{ + "input": { + "metric": { + "count": { + "dataPool": { + "id": "DPO01HB9W3DCA1756WT6CJ6BV9H48" + } + } + }, + "timeZone": "UTC", + "granularity": "DAY", + "timeRange": { + "relative": "LAST_N_DAYS", + "n": 30 + }, + "filters": [] + } + }` + }, + { + id: `leaderboard`, + query: ``, + type: '', + variables: `{}` + }, + { + id: 'counter1', + query: `query CounterQuery($input: CounterInput!) { + counter(input: $input) { + value + } + }`, + type: 'counter', + variables: `{ + "input": { + "metric": { + "count": { + "dataPool": { + "id": "DPO01HB9W3DCA1756WT6CJ6BV9H48" + } + } + }, + "timeZone": "UTC", + "timeRange": { + "relative": "LAST_N_DAYS", + "n": 30 + }, + "filters": [] + } + }` + }, + { + id: 'counter2', + query: `query CounterQuery($input: CounterInput!) { + counter(input: $input) { + value + } + }`, + type: 'counter', + variables: `{ + "input": { + "metric": { + "count": { + "dataPool": { + "id": "DPO01HB9W3DCA1756WT6CJ6BV9H48" + } + } + }, + "timeZone": "UTC", + "timeRange": { + "relative": "LAST_N_DAYS", + "n": 30 + }, + "filters": [ + { + "column": "quantity", + "operator": "EQUALS", + "value": "2" + } + ] + } + }` + }, + { + id: 'timeSeries2', + query: `query TimeSeriesQuery($input: TimeSeriesInput!) { + timeSeries(input: $input) { + labels + values + } + }`, + type: 'timeSeries', + variables: `{ + "input": { + "metric": { + "count": { + "dataPool": { + "id": "DPO01HB9W3DCA1756WT6CJ6BV9H48" + } + } + }, + "timeZone": "UTC", + "granularity": "DAY", + "timeRange": { + "relative": "LAST_N_DAYS", + "n": 30 + }, + "filters": [ + { + "column": "quantity", + "operator": "EQUALS", + "value": "2" + } + ] + } + }` + } + ] + }, + render: (args) => ( + + + + ) +} + +export const Test: Story = { + args: { + layout: [ + ['a60675eefa49741608c1610f8f8b25116', 'a60675eefa49741608c1610f8f8b25116', 'a76d31602c6224c2ca3e2e36d2afcc30e'], + ['a19c883f0281c4fa8852d453faba28b8d', 'a877df96b498b45bf84b1a21d022a4fb5', 'a76d31602c6224c2ca3e2e36d2afcc30e'], + ['a7315e8ad8d004c9597e211d75f9117ba', 'a7315e8ad8d004c9597e211d75f9117ba', 'a7315e8ad8d004c9597e211d75f9117ba'] + ], + charts: [ + { + id: 'a60675eefa49741608c1610f8f8b25116', + type: 'counter', + query: '{}', + variables: '{}' + }, + { + id: 'a76d31602c6224c2ca3e2e36d2afcc30e', + type: 'counter', + query: '{}', + variables: '{}' + }, + { + id: 'a19c883f0281c4fa8852d453faba28b8d', + type: 'counter', + query: '{}', + variables: '{}' + }, + { + id: 'a877df96b498b45bf84b1a21d022a4fb5', + type: 'counter', + query: '{}', + variables: '{}' + }, + { + id: 'a7315e8ad8d004c9597e211d75f9117ba', + type: 'counter', + query: '{}', + variables: '{}' + } + ], + reportCardProps: { className: 'shadow-sm' } + }, + render: (args) => ( + + + + ) +} diff --git a/packages/ui-kit/src/components/Report/Report.tsx b/packages/ui-kit/src/components/Report/Report.tsx new file mode 100644 index 00000000..72bf7dd6 --- /dev/null +++ b/packages/ui-kit/src/components/Report/Report.tsx @@ -0,0 +1,74 @@ +import React from 'react' +import classNames from 'classnames' + +import { ErrorFallback } from '../ErrorFallback' +import { withContainer } from '../withContainer' +import { Card } from '../Card' +import { useReportComponents } from '../../hooks' + +import componentStyles from './Report.module.scss' +import { ChartProp } from './Report.types' +import { buildGridTemplateAreas } from './utils' +import { TimeSeries, TimeSeriesProps } from '../TimeSeries' +import { Leaderboard, LeaderboardProps } from '../Leaderboard' +import { Counter, CounterProps } from '../Counter' +import { Loader } from '../Loader' + +interface Props { + layout: string[][] + charts: ChartProp[] + clickable?: boolean + onCardClick?: (chart: ChartProp) => void + reportCardProps?: React.HTMLAttributes + propelApiUrl?: string +} + +const componentMap = { + timeSeries: (args: TimeSeriesProps) => , + leaderboard: (args: LeaderboardProps) => , + counter: (args: CounterProps) => , + '': () => null +} + +export const ReportComponent = React.forwardRef((props: Props) => { + const { layout, charts, clickable = false, onCardClick, reportCardProps, propelApiUrl } = props + + const { charts: reportCharts, isLoading } = useReportComponents(charts, { propelApiUrl }) + + const gridTemplateAreas = buildGridTemplateAreas(layout) + + return ( +
'1fr').join(' '), + gridTemplateColumns: layout[0].map(() => '1fr').join(' ') + }} + > + {reportCharts.map((chart, chartIdx) => ( + clickable && (onCardClick?.(chart) ?? reportCardProps?.onClick?.(e))} + style={{ ...reportCardProps?.style, gridArea: `report-area-${chart.id}` }} + > + {!isLoading ? ( + chart.result != null && componentMap[chart.type]({ ...chart.result[chart.type] }) + ) : ( + + )} + + ))} +
+ ) +}) + +ReportComponent.displayName = 'ReportComponent' + +export const Report = withContainer(ReportComponent, ErrorFallback) as typeof ReportComponent diff --git a/packages/ui-kit/src/components/Report/Report.types.ts b/packages/ui-kit/src/components/Report/Report.types.ts new file mode 100644 index 00000000..c6786c33 --- /dev/null +++ b/packages/ui-kit/src/components/Report/Report.types.ts @@ -0,0 +1,6 @@ +export interface ChartProp { + id: string + query?: string + type: 'timeSeries' | 'leaderboard' | 'counter' | '' + variables?: string +} diff --git a/packages/ui-kit/src/components/Report/index.ts b/packages/ui-kit/src/components/Report/index.ts new file mode 100644 index 00000000..e3818666 --- /dev/null +++ b/packages/ui-kit/src/components/Report/index.ts @@ -0,0 +1,2 @@ +export { Report } from './Report' +export * from './Report.types' diff --git a/packages/ui-kit/src/components/Report/utils.ts b/packages/ui-kit/src/components/Report/utils.ts new file mode 100644 index 00000000..93bb9fd6 --- /dev/null +++ b/packages/ui-kit/src/components/Report/utils.ts @@ -0,0 +1,3 @@ +export function buildGridTemplateAreas(layout: string[][]) { + return layout.map((row) => `"${row.map((cell) => `report-area-${cell}`).join(' ')}"`).join(' ') +} diff --git a/packages/ui-kit/src/components/index.ts b/packages/ui-kit/src/components/index.ts index 95c0709b..916fba7f 100644 --- a/packages/ui-kit/src/components/index.ts +++ b/packages/ui-kit/src/components/index.ts @@ -14,3 +14,4 @@ export * from './TimeSeries' export * from './withContainer' export * from './FilterProvider' export * from './SimpleFilter' +export * from './Report' diff --git a/packages/ui-kit/src/hooks/index.ts b/packages/ui-kit/src/hooks/index.ts index de80fb34..05d19d95 100644 --- a/packages/ui-kit/src/hooks/index.ts +++ b/packages/ui-kit/src/hooks/index.ts @@ -6,3 +6,4 @@ export * from './useRecordsById' export * from './useSql' export * from './useTimeSeries' export * from './useTopValues' +export * from './useReportComponents' diff --git a/packages/ui-kit/src/hooks/useReportComponents/index.ts b/packages/ui-kit/src/hooks/useReportComponents/index.ts new file mode 100644 index 00000000..f9f2d43a --- /dev/null +++ b/packages/ui-kit/src/hooks/useReportComponents/index.ts @@ -0,0 +1 @@ +export * from './useReportComponents' diff --git a/packages/ui-kit/src/hooks/useReportComponents/useReportComponents.ts b/packages/ui-kit/src/hooks/useReportComponents/useReportComponents.ts new file mode 100644 index 00000000..86a55b0a --- /dev/null +++ b/packages/ui-kit/src/hooks/useReportComponents/useReportComponents.ts @@ -0,0 +1,69 @@ +import { PROPEL_GRAPHQL_API_ENDPOINT } from '../../helpers' +import { ChartProp } from '../../components/Report' +import { useAccessToken } from '../../components' +import { useEffect, useMemo, useRef, useState } from 'react' + +function fetcher(endpoint: string, requestInit: RequestInit, query: string, variables?: string) { + return async (): Promise => { + const res = await fetch(endpoint, { + method: 'POST', + ...requestInit, + body: JSON.stringify({ query, variables }) + }) + + const json = await res.json() + + if (json.errors) { + const { message } = json.errors[0] + + throw new Error(message) + } + + return json.data + } +} + +interface Options { + propelApiUrl?: string +} + +export function useReportComponents(charts: ChartProp[], options?: Options) { + const [result, setResult] = useState(null) + const [isLoading, setIsLoading] = useState(true) + + const { accessToken } = useAccessToken() + + const memoizedCharts = useRef(charts) + + const fetchers = useMemo( + () => + memoizedCharts.current.map((chart) => + fetcher( + options?.propelApiUrl ?? PROPEL_GRAPHQL_API_ENDPOINT, + { + headers: { + 'content-type': 'application/graphql-response+json', + authorization: `Bearer ${accessToken}` + } + }, + chart.query ?? '', + JSON.parse(chart.variables ?? '{}') + ) + ), + [accessToken, options?.propelApiUrl] + ) + + useEffect(() => { + async function getData() { + const result = await Promise.allSettled(fetchers.map((fetcher) => fetcher())) + setResult(result.map((res) => (res.status === 'fulfilled' ? res.value : null))) + setIsLoading(false) + } + + if (accessToken) { + getData() + } + }, [accessToken, fetchers]) + + return { charts: charts.map((chart, idx) => ({ ...chart, result: result != null ? result[idx] : null })), isLoading } +} From 407f23dcf68fbcb7181353f9981c1d0c0c42cb89 Mon Sep 17 00:00:00 2001 From: Felipe Cadavid Date: Tue, 9 Apr 2024 16:12:19 -0500 Subject: [PATCH 2/7] chore: add changeset and experimental warning --- .changeset/rotten-guests-decide.md | 5 +++++ packages/ui-kit/src/components/Report/Report.tsx | 4 ++++ 2 files changed, 9 insertions(+) create mode 100644 .changeset/rotten-guests-decide.md diff --git a/.changeset/rotten-guests-decide.md b/.changeset/rotten-guests-decide.md new file mode 100644 index 00000000..511318cf --- /dev/null +++ b/.changeset/rotten-guests-decide.md @@ -0,0 +1,5 @@ +--- +'@propeldata/ui-kit': minor +--- + +Added experimental Report component diff --git a/packages/ui-kit/src/components/Report/Report.tsx b/packages/ui-kit/src/components/Report/Report.tsx index 72bf7dd6..c19c7834 100644 --- a/packages/ui-kit/src/components/Report/Report.tsx +++ b/packages/ui-kit/src/components/Report/Report.tsx @@ -71,4 +71,8 @@ export const ReportComponent = React.forwardRef((props: Props) => { ReportComponent.displayName = 'ReportComponent' +/** + * @deprecated + * This component is EXPERIMENTAL, we do not recommend using it in production. + */ export const Report = withContainer(ReportComponent, ErrorFallback) as typeof ReportComponent From 579c4e34b60fca405a27ef2ad900f295cdc55f6a Mon Sep 17 00:00:00 2001 From: Felipe Cadavid Date: Wed, 10 Apr 2024 13:02:36 -0500 Subject: [PATCH 3/7] feat: add connected mode --- .../src/components/Report/Report.stories.tsx | 14 +- .../ui-kit/src/components/Report/Report.tsx | 48 +- .../src/components/Report/Report.types.ts | 32 +- .../ui-kit/src/components/Report/utils.ts | 4 +- .../ui-kit/src/helpers/graphql/public.graphql | 605 ++++++++++++++++-- .../helpers/graphql/queries/Report.graphql | 12 + packages/ui-kit/src/hooks/useReport/index.ts | 1 + .../ui-kit/src/hooks/useReport/useReport.ts | 31 + .../useReportComponents.ts | 29 +- 9 files changed, 702 insertions(+), 74 deletions(-) create mode 100644 packages/ui-kit/src/helpers/graphql/queries/Report.graphql create mode 100644 packages/ui-kit/src/hooks/useReport/index.ts create mode 100644 packages/ui-kit/src/hooks/useReport/useReport.ts diff --git a/packages/ui-kit/src/components/Report/Report.stories.tsx b/packages/ui-kit/src/components/Report/Report.stories.tsx index 88c9a520..a24025a9 100644 --- a/packages/ui-kit/src/components/Report/Report.stories.tsx +++ b/packages/ui-kit/src/components/Report/Report.stories.tsx @@ -157,7 +157,7 @@ export const Basic: Story = { ] }, render: (args) => ( - + ) @@ -210,3 +210,15 @@ export const Test: Story = { ) } + +export const Connected: Story = { + args: { + query: { + accessToken: + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdXRoX3RpbWUiOjE3MTI3NzEzODgsImNsaWVudF9pZCI6IkFQUDAxSEZQVFc4NjM1WVQ2Q1E5MjVFNUtZM1JOIiwiZXhwIjoxNzEyNzc0OTg4LCJpYXQiOjE3MTI3NzEzODgsImlzcyI6Imh0dHBzOi8vYXV0aC51cy1lYXN0LTIuZGV2LnByb3BlbGRhdGEuY29tIiwianRpIjoiMjA3OWM0NTUtYzg2Mi00ZWM0LTgyYmQtODRmOGEwNzY3NDE5Iiwic2NvcGUiOiJtZXRyaWM6cXVlcnkgbWV0cmljOnN0YXRzIGRhdGFfcG9vbDpxdWVyeSBkYXRhX3Bvb2w6cmVhZCBkYXRhX3Bvb2w6c3RhdHMgYWRtaW4gYXBwbGljYXRpb246YWRtaW4gbWV0cmljOnJlYWQiLCJzdWIiOiJBUFAwMUhGUFRXODYzNVlUNkNROTI1RTVLWTNSTiIsInRva2VuX3VzZSI6ImFjY2VzcyIsInZlcnNpb24iOjF9.DImh1Mu2OMrxji5G6tWEQ2m7bnQ_YvuDdC2j84pdCOU', + propelApiUrl: 'https://api.us-east-2.dev.propeldata.com/graphql', + reportId: 'RPT01HV4GY04A4B1054KCK3EV08AR' + } + }, + render: (args) => +} diff --git a/packages/ui-kit/src/components/Report/Report.tsx b/packages/ui-kit/src/components/Report/Report.tsx index c19c7834..b1d9abf1 100644 --- a/packages/ui-kit/src/components/Report/Report.tsx +++ b/packages/ui-kit/src/components/Report/Report.tsx @@ -7,21 +7,13 @@ import { Card } from '../Card' import { useReportComponents } from '../../hooks' import componentStyles from './Report.module.scss' -import { ChartProp } from './Report.types' +import { ReportProps } from './Report.types' import { buildGridTemplateAreas } from './utils' import { TimeSeries, TimeSeriesProps } from '../TimeSeries' import { Leaderboard, LeaderboardProps } from '../Leaderboard' import { Counter, CounterProps } from '../Counter' import { Loader } from '../Loader' - -interface Props { - layout: string[][] - charts: ChartProp[] - clickable?: boolean - onCardClick?: (chart: ChartProp) => void - reportCardProps?: React.HTMLAttributes - propelApiUrl?: string -} +import { useReport } from '../../hooks/useReport' const componentMap = { timeSeries: (args: TimeSeriesProps) => , @@ -30,10 +22,30 @@ const componentMap = { '': () => null } -export const ReportComponent = React.forwardRef((props: Props) => { - const { layout, charts, clickable = false, onCardClick, reportCardProps, propelApiUrl } = props +export const ReportComponent = React.forwardRef((props: ReportProps) => { + const { + layout: layoutProp, + charts: chartsProp, + clickable = false, + onCardClick, + reportCardProps, + propelApiUrl, + query + } = props + + const isStatic = query?.accessToken == null + + const { data, isLoading: isLoadingReport } = useReport({ ...query, enabled: !isStatic }) + + const charts = isStatic ? chartsProp : data?.report?.charts + const layout = isStatic ? layoutProp : data?.report?.layout + + const { charts: reportCharts, isLoading: isLoadingComponents } = useReportComponents(charts, { + propelApiUrl: propelApiUrl ?? query?.propelApiUrl, + accessToken: query?.accessToken + }) - const { charts: reportCharts, isLoading } = useReportComponents(charts, { propelApiUrl }) + const isLoading = (!isStatic && isLoadingReport) || isLoadingComponents const gridTemplateAreas = buildGridTemplateAreas(layout) @@ -42,11 +54,11 @@ export const ReportComponent = React.forwardRef((props: Props) => { className={componentStyles.container} style={{ gridTemplateAreas, - gridTemplateRows: layout.map(() => '1fr').join(' '), - gridTemplateColumns: layout[0].map(() => '1fr').join(' ') + gridTemplateRows: layout?.map(() => '1fr').join(' '), + gridTemplateColumns: layout?.[0]?.map(() => '1fr').join(' ') }} > - {reportCharts.map((chart, chartIdx) => ( + {reportCharts?.map((chart, chartIdx) => ( { style={{ ...reportCardProps?.style, gridArea: `report-area-${chart.id}` }} > {!isLoading ? ( - chart.result != null && componentMap[chart.type]({ ...chart.result[chart.type] }) + chart.result != null && + chart.type != null && + componentMap[chart.type as 'timeSeries' | 'leaderboard' | 'counter']({ ...chart.result[chart.type] }) ) : ( )} diff --git a/packages/ui-kit/src/components/Report/Report.types.ts b/packages/ui-kit/src/components/Report/Report.types.ts index c6786c33..3a8252f7 100644 --- a/packages/ui-kit/src/components/Report/Report.types.ts +++ b/packages/ui-kit/src/components/Report/Report.types.ts @@ -1,6 +1,34 @@ export interface ChartProp { - id: string + id?: string query?: string - type: 'timeSeries' | 'leaderboard' | 'counter' | '' + type?: 'timeSeries' | 'leaderboard' | 'counter' | '' | string variables?: string } + +export interface ReportQueryProp { + reportId: string + /** + * Access token used for the query. While you can pass this one to each component, we recommend wrapping components in the `AccessTokenProvider` instead: + * @example + * ```jsx + * + * + * + * + * + * ``` + * */ + accessToken?: string + propelApiUrl?: string + enabled?: boolean +} + +export interface ReportProps { + layout: string[][] + charts: ChartProp[] + clickable?: boolean + onCardClick?: (chart: ChartProp | null | undefined) => void + reportCardProps?: React.HTMLAttributes + propelApiUrl?: string + query?: ReportQueryProp +} diff --git a/packages/ui-kit/src/components/Report/utils.ts b/packages/ui-kit/src/components/Report/utils.ts index 93bb9fd6..53cb759b 100644 --- a/packages/ui-kit/src/components/Report/utils.ts +++ b/packages/ui-kit/src/components/Report/utils.ts @@ -1,3 +1,3 @@ -export function buildGridTemplateAreas(layout: string[][]) { - return layout.map((row) => `"${row.map((cell) => `report-area-${cell}`).join(' ')}"`).join(' ') +export function buildGridTemplateAreas(layout?: Array | null | undefined>) { + return layout?.map((row) => `"${row?.map((cell) => `report-area-${cell}`).join(' ')}"`).join(' ') } diff --git a/packages/ui-kit/src/helpers/graphql/public.graphql b/packages/ui-kit/src/helpers/graphql/public.graphql index 23432505..87ddab18 100644 --- a/packages/ui-kit/src/helpers/graphql/public.graphql +++ b/packages/ui-kit/src/helpers/graphql/public.graphql @@ -729,9 +729,17 @@ enum DataSourceType { """ CLICKHOUSE """ + Indicates a BigQuery Data Source. + """ + BIGQUERY + """ Indicates a Snowflake Data Source. """ Snowflake + """ + Indicates an internal Data Source. + """ + INTERNAL } """ @@ -760,9 +768,15 @@ enum DataSourceStatus { DELETING } +type InternalConnectionSettings { + _: Boolean +} + union ConnectionSettings = SnowflakeConnectionSettings + | InternalConnectionSettings | HttpConnectionSettings + | KafkaConnectionSettings | S3ConnectionSettings | WebhookConnectionSettings @@ -1221,8 +1235,8 @@ type DataPool implements Node & Common { dataRetentionInDays: Int! "The name of the Data Pool's table." table: String! - "The Data Pool's primary timestamp column." - timestamp: Timestamp! + "The Data Pool's primary timestamp column, if any." + timestamp: Timestamp "The Data Pool's tenant ID, if configured." tenant: Tenant @deprecated(reason: "Will be removed; use Data Pool Access Policies instead") "The Data Pool's unique ID column. Propel uses the primary timestamp and a unique ID to compose a primary key for determining whether records should be inserted, deleted, or updated within the Data Pool." @@ -1240,7 +1254,13 @@ type DataPool implements Node & Common { "Settings related to Data Pool syncing." syncing: DataPoolSyncing! "The list of Syncs of the Data Pool." - syncs(filter: SyncsFilter, first: Int, after: String, last: Int, before: String): SyncConnection + syncs( + filter: SyncsFilter @deprecated(reason: "No longer supported") + first: Int + after: String + last: Int + before: String + ): SyncConnection "The list of Metrics powered by the Data Pool." metrics(first: Int, after: String, last: Int, before: String): MetricConnection "The Deletion Jobs that were historically issued to this Data Pool, sorted by creation time, in descending order." @@ -1260,6 +1280,161 @@ type DataPool implements Node & Common { dataPoolAccessPolicies(first: Int, after: String, last: Int, before: String): DataPoolAccessPolicyConnection! "Validates a custom expression against the Data Pool's available columns. If the provided expression is invalid, the ValidateExpressionResult response will contain a reason explaining why." validateExpression(expression: String!): ValidateExpressionResult! + "The Data Pool's table settings." + tableSettings: TableSettings + "The Data Pool's columns that participate in its PARTITION BY clause." + partitionByColumns: [DataPoolColumn!] + "The Data Pool's columns that participate in its PRIMARY KEY clause." + primaryKeyColumns: [DataPoolColumn!] + "The Data Pool's columns that participate in its ORDER BY clause." + orderByColumns: [DataPoolColumn!] +} + +""" +A Data Pool's table settings. + +These describe how the Data Pool's table is created in ClickHouse. +""" +type TableSettings { + "The ClickHouse table engine for the Data Pool's table." + engine: TableEngine + "The PARTITION BY clause for the Data Pool's table." + partitionBy: [String!] + "The PRIMARY KEY clause for the Data Pool's table." + primaryKey: [String!] + "The ORDER BY clause for the Data Pool's table." + orderBy: [String!] +} + +""" +A Data Pool's table settings. + +These describe how the Data Pool's table is created in ClickHouse. +""" +input TableSettingsInput { + """ + The ClickHouse table engine for the Data Pool's table. + + This field is optional. A default will be chosen based on the Data Pool's `timestamp` and `uniqueId` values, if specified. + """ + engine: TableEngineInput + """ + The PARTITION BY clause for the Data Pool's table. + + This field is optional. A default will be chosen based on the Data Pool's `timestamp` and `uniqueId` values, if specified. + """ + partitionBy: [String!] + """ + The PRIMARY KEY clause for the Data Pool's table. + + This field is optional. A default will be chosen based on the Data Pool's `timestamp` and `uniqueId` values, if specified. + """ + primaryKey: [String!] + """ + The ORDER BY clause for the Data Pool's table. + + This field is optional. A default will be chosen based on the Data Pool's `timestamp` and `uniqueId` values, if specified. + """ + orderBy: [String!] +} + +""" +ClickHouse table engine types. +""" +enum TableEngineType { + "The MergeTree table engine." + MERGE_TREE + "The ReplacingMergeTree table engine." + REPLACING_MERGE_TREE + "The SummingMergeTree table engine." + SUMMING_MERGE_TREE +} + +""" +An interface for accessing a table engine's type. +""" +interface HasTableEngineType { + "The type of the table engine." + type: TableEngineType! +} + +""" +A Data Pool's table engine. +""" +union TableEngine = MergeTreeTableEngine | ReplacingMergeTreeTableEngine | SummingMergeTreeTableEngine + +""" +A Data Pool's table engine. +""" +input TableEngineInput @oneOf { + """ + Field for specifying the MergeTree table engine. + """ + mergeTree: MergeTreeTableEngineInput + """ + Field for specifying the ReplacingMergeTree table engine. + """ + replacingMergeTree: ReplacingMergeTreeTableEngineInput + """ + Field for specifying the SummingMergeTree table engine. + """ + summingMergeTree: SummingMergeTreeTableEngineInput +} + +""" +Parameters for the MergeTree table engine. +""" +type MergeTreeTableEngine implements HasTableEngineType { + "The type is always `MERGE_TREE`." + type: TableEngineType! +} + +""" +Parameters for the MergeTree table engine. +""" +input MergeTreeTableEngineInput { + "The type is always `MERGE_TREE`." + type: TableEngineType +} + +""" +Parameters for the ReplacingMergeTree table engine. +""" +type ReplacingMergeTreeTableEngine implements HasTableEngineType { + "The type is always `REPLACING_MERGE_TREE`." + type: TableEngineType! + "The `ver` parameter to the ReplacingMergeTree engine." + ver: String +} + +""" +Parameters for the ReplacingMergeTree table engine. +""" +input ReplacingMergeTreeTableEngineInput { + "The type is always `REPLACING_MERGE_TREE`." + type: TableEngineType + "The `ver` parameter to the ReplacingMergeTree engine." + ver: String +} + +""" +Parameters for the SummingMergeTree table engine. +""" +type SummingMergeTreeTableEngine implements HasTableEngineType { + "The type is always `SUMMING_MERGE_TREE`." + type: TableEngineType! + "The columns argument for the SummingMergeTree table engine" + columns: [String!] +} + +""" +Parameters for the SummingMergeTree table engine. +""" +input SummingMergeTreeTableEngineInput { + "The type is always `SUMMING_MERGE_TREE`." + type: TableEngineType + "The columns argument for the SummingMergeTree table engine" + columns: [String!] } """ @@ -1333,7 +1508,7 @@ type Dimension { """ The statistics for the dimension values. Fetching statistics incurs query costs. """ - stats: DimensionStatistics + stats: DimensionStatistics @deprecated(reason: "Issue normal queries for calculating stats") } """ @@ -1362,6 +1537,41 @@ type DimensionStatistics { query: QueryInfo! } +type MaterializedView implements Node & Common { + "The Materialized View's unique identifier." + id: ID! + "The Materialized View's unique name." + uniqueName: String! + "The Materialized View's description." + description: String! + "The Materialized View's Account." + account: Account! + "The Materialized View's Environment." + environment: Environment! + "The Materialized View's creation date and time in UTC." + createdAt: DateTime! + "The Materialized View's last modification date and time in UTC." + modifiedAt: DateTime! + """ + The Materialized View's creator. It can be either a User ID, an Application ID, or "system" if it was created by Propel. + """ + createdBy: String! + """ + The Materialized View's last modifier. It can be either a User ID, an Application ID, or "system" if it was modified by Propel. + """ + modifiedBy: String! + "The SQL that the Materialized View executes." + sql: String! + """ + The Materialized View's destination (AKA "target") Data Pool. + """ + destination: DataPool! + "The Materialized View's source Data Pool." + source: DataPool! + "Other Data Pools queried by the Materialized View." + others: [DataPool!]! +} + """ The fields of a filter. @@ -1595,11 +1805,19 @@ The fields for creating a Data Pool. """ input CreateDataPoolInputV2 { "The Data Source that will be used to create the Data Pool." - dataSource: ID! + dataSource: ID "The table that the Data Pool will sync from." - table: String! - "The table's primary timestamp column. Propel uses the primary timestamp to order and partition your data in Data Pools. It will serve as the time dimension for your Metrics." - timestamp: TimestampInput! + table: String + """ + The table's primary timestamp column. + + Propel uses the primary timestamp to order and partition your data in Data Pools. It's part of what makes Propel + fast for larger data sets. It will also serve as the time dimension for your Metrics. + + If you do not provide a primary timestamp column, you will need to supply an alternate timestamp when querying your + Data Pool or its Metrics using the TimeRangeInput. + """ + timestamp: TimestampInput "The Data Pool's unique name. If not specified, Propel will set the ID as the unique name." uniqueName: String "The Data Pool's description." @@ -1623,6 +1841,12 @@ input CreateDataPoolInputV2 { Policies in order to query the Data Pool and its Metrics. """ accessControlEnabled: Boolean + """ + Override the Data Pool's table settings. These describe how the Data Pool's table is created in ClickHouse, and a + default will be chosen based on the Data Pool's `timestamp` and `uniqueId` values, if any. You can override these + defaults in order to specify a custom table engine, custom ORDER BY, etc. + """ + tableSettings: TableSettingsInput } """ @@ -1773,19 +1997,22 @@ type Sync implements Node { """ The number of new records contained within the Sync, if known. This excludes filtered records. """ - newRecords: String + newRecords: String @deprecated(reason: "All records are considered to be processed; see `processedRecords` instead") """ The number of updated records contained within the Sync, if known. This excludes filtered records. """ updatedRecords: String + @deprecated(reason: "All records are considered to be processed; see `processedRecords` instead") """ The number of deleted records contained within the Sync, if known. This excludes filtered records. """ deletedRecords: String + @deprecated(reason: "All records are considered to be processed; see `processedRecords` instead") """ The number of filtered records contained within the Sync, due to issues such as a missing timestamp Dimension, if any are known to be invalid. """ invalidRecords: String + @deprecated(reason: "All records are considered to be processed; see `processedRecords` instead") """ The (compressed) size of the Sync, in bytes, if known. """ @@ -2124,9 +2351,9 @@ type Metric implements Node & Common { """ dimensions: [Dimension!]! """ - The Metric's timestamp. This is the same as its Data Pool's timestamp. + The Metric's timestamp, if any. This is the same as its Data Pool's timestamp, if any. """ - timestamp: Dimension! + timestamp: Dimension """ The Metric's measure. Access this from the Metric's `settings` object instead. """ @@ -2144,17 +2371,19 @@ type Metric implements Node & Common { """ settings: MetricSettings! """ - The Metric data in counter format. A single metric value for the given time range and filters. + Query the Metric in counter format. Returns the Metric's value for the given time range and filters. """ - counter(input: CounterInput!): CounterResponse + counter(input: CounterInput!): CounterResponse @deprecated(reason: "Use the top-level `counter` query instead") """ - The Metric data in time series format. Arrays of timestamps and Metric values for the given time range and filters. + Query the Metric in time series format. Returns arrays of timestamps and the Metric's values for the given time range and filters. """ timeSeries(input: TimeSeriesInput!): TimeSeriesResponse + @deprecated(reason: "Use the top-level `timeSeries` query instead") """ - The Metric data in leaderboard format. A table (array of rows) with the selected dimensions and corresponding Metric values for the given time range and filters. + Query the Metric in leaderboard format. Returns a table (array of rows) with the selected dimensions and the Metric's corresponding values for the given time range and filters. """ leaderboard(input: LeaderboardInput!): LeaderboardResponse + @deprecated(reason: "Use the top-level `leaderboard` query instead") """ List the Policies associated to the Metric. """ @@ -2217,7 +2446,13 @@ type CountMetricSettings { """ Metric Filters allow defining a Metric with a subset of records from the given Data Pool. If no Metric Filters are present, all records will be included. To filter at query time, add Dimensions and use the `filters` property on the `timeSeriesInput`, `counterInput`, or `leaderboardInput` objects. There is no need to add `filters` to be able to filter at query time. """ - filters: [Filter!] + filters: [Filter!] @deprecated(reason: "Use `filterSql` instead") + """ + Metric Filters allow defining a Metric with a subset of records from the given Data Pool. If no Metric Filters are present, all records will be included. To filter at query time, add Dimensions and use the `filters` property on the `timeSeriesInput`, `counterInput`, or `leaderboardInput` objects. There is no need to add `filters` to be able to filter at query time. + + You can provide the filters in the form of SQL. + """ + filterSql: String } """ @@ -2227,7 +2462,13 @@ type SumMetricSettings { """ Metric Filters allow defining a Metric with a subset of records from the given Data Pool. If no Metric Filters are present, all records will be included. To filter at query time, add Dimensions and use the `filters` property on the `timeSeriesInput`, `counterInput`, or `leaderboardInput` objects. There is no need to add `filters` to be able to filter at query time. """ - filters: [Filter!] + filters: [Filter!] @deprecated(reason: "Use `filterSql` instead") + """ + Metric Filters allow defining a Metric with a subset of records from the given Data Pool. If no Metric Filters are present, all records will be included. To filter at query time, add Dimensions and use the `filters` property on the `timeSeriesInput`, `counterInput`, or `leaderboardInput` objects. There is no need to add `filters` to be able to filter at query time. + + You can provide the filters in the form of SQL. + """ + filterSql: String """ The Dimension to be summed. """ @@ -2241,7 +2482,13 @@ type CountDistinctMetricSettings { """ Metric Filters allow defining a Metric with a subset of records from the given Data Pool. If no Metric Filters are present, all records will be included. To filter at query time, add Dimensions and use the `filters` property on the `timeSeriesInput`, `counterInput`, or `leaderboardInput` objects. There is no need to add `filters` to be able to filter at query time. """ - filters: [Filter!] + filters: [Filter!] @deprecated(reason: "Use `filterSql` instead") + """ + Metric Filters allow defining a Metric with a subset of records from the given Data Pool. If no Metric Filters are present, all records will be included. To filter at query time, add Dimensions and use the `filters` property on the `timeSeriesInput`, `counterInput`, or `leaderboardInput` objects. There is no need to add `filters` to be able to filter at query time. + + You can provide the filters in the form of SQL. + """ + filterSql: String """ The Dimension where the count distinct operation is going to be performed. """ @@ -2255,7 +2502,13 @@ type AverageMetricSettings { """ Metric Filters allow defining a Metric with a subset of records from the given Data Pool. If no Metric Filters are present, all records will be included. To filter at query time, add Dimensions and use the `filters` property on the `timeSeriesInput`, `counterInput`, or `leaderboardInput` objects. There is no need to add `filters` to be able to filter at query time. """ - filters: [Filter!] + filters: [Filter!] @deprecated(reason: "Use `filterSql` instead") + """ + Metric Filters allow defining a Metric with a subset of records from the given Data Pool. If no Metric Filters are present, all records will be included. To filter at query time, add Dimensions and use the `filters` property on the `timeSeriesInput`, `counterInput`, or `leaderboardInput` objects. There is no need to add `filters` to be able to filter at query time. + + You can provide the filters in the form of SQL. + """ + filterSql: String """ The Dimension to be averaged. """ @@ -2269,7 +2522,13 @@ type MinMetricSettings { """ Metric Filters allow defining a Metric with a subset of records from the given Data Pool. If no Metric Filters are present, all records will be included. To filter at query time, add Dimensions and use the `filters` property on the `timeSeriesInput`, `counterInput`, or `leaderboardInput` objects. There is no need to add `filters` to be able to filter at query time. """ - filters: [Filter!] + filters: [Filter!] @deprecated(reason: "Use `filterSql` instead") + """ + Metric Filters allow defining a Metric with a subset of records from the given Data Pool. If no Metric Filters are present, all records will be included. To filter at query time, add Dimensions and use the `filters` property on the `timeSeriesInput`, `counterInput`, or `leaderboardInput` objects. There is no need to add `filters` to be able to filter at query time. + + You can provide the filters in the form of SQL. + """ + filterSql: String """ The Dimension to select the minimum from. """ @@ -2283,7 +2542,13 @@ type MaxMetricSettings { """ Metric Filters allow defining a Metric with a subset of records from the given Data Pool. If no Metric Filters are present, all records will be included. To filter at query time, add Dimensions and use the `filters` property on the `timeSeriesInput`, `counterInput`, or `leaderboardInput` objects. There is no need to add `filters` to be able to filter at query time. """ - filters: [Filter!] + filters: [Filter!] @deprecated(reason: "Use `filterSql` instead") + """ + Metric Filters allow defining a Metric with a subset of records from the given Data Pool. If no Metric Filters are present, all records will be included. To filter at query time, add Dimensions and use the `filters` property on the `timeSeriesInput`, `counterInput`, or `leaderboardInput` objects. There is no need to add `filters` to be able to filter at query time. + + You can provide the filters in the form of SQL. + """ + filterSql: String """ The Dimension to select the maximum from. """ @@ -2297,7 +2562,13 @@ type CustomMetricSettings { """ Metric Filters allow defining a Metric with a subset of records from the given Data Pool. If no Metric Filters are present, all records will be included. To filter at query time, add Dimensions and use the `filters` property on the `timeSeriesInput`, `counterInput`, or `leaderboardInput` objects. There is no need to add `filters` to be able to filter at query time. """ - filters: [Filter!] + filters: [Filter!] @deprecated(reason: "Use `filterSql` instead") + """ + Metric Filters allow defining a Metric with a subset of records from the given Data Pool. If no Metric Filters are present, all records will be included. To filter at query time, add Dimensions and use the `filters` property on the `timeSeriesInput`, `counterInput`, or `leaderboardInput` objects. There is no need to add `filters` to be able to filter at query time. + + You can provide the filters in the form of SQL. + """ + filterSql: String """ The expression that defines the aggregation function for this Metric. """ @@ -2798,6 +3069,11 @@ If both relative and absolute time ranges are provided, the relative time range If a `LAST_N` relative time period is selected, an `n` ≥ 1 must be provided. If no `n` is provided or `n` < 1, a `BAD_REQUEST` error will be returned. """ input TimeRangeInput { + """ + The timestamp field to use when querying. Defaults to the timestamp configured on the Data Pool or Metric, if any. + Set this to filter on an alternative timestamp field. + """ + timestamp: String """ The relative time period. """ @@ -3637,7 +3913,9 @@ type DeletionJob implements Node & Job { "The current Deletion Job's status." status: JobStatus! "The list of filters that will be used for deleting data. Data matching the filters will be deleted." - filters: [Filter!]! + filters: [Filter!]! @deprecated(reason: "Use `filterSql` instead") + "The filters that will be used for deleting data, in the form of SQL. Data matching the filters will be deleted." + filterSql: String "The error that occurred while deleting data, if any." error: Error "The current progress of the Deletion Job, from 0.0 to 1.0." @@ -3673,7 +3951,7 @@ input DeletionRequestInput { "The Data Pool that is going to get the data deleted" dataPool: ID! "The list of filters that will be used for deleting data. Data matching these filters will be deleted." - filters: [FilterInput!]! + filters: [FilterInput!] } """ @@ -3683,7 +3961,7 @@ input CreateDeletionJobInput { "The Data Pool that is going to get the data deleted" dataPool: ID! "The list of filters that will be used for deleting data. Data matching these filters will be deleted." - filters: [FilterInput!]! + filters: [FilterInput!] } """ @@ -3778,7 +4056,9 @@ type UpdateDataPoolRecordsJob implements Node & Job { "The current UpdateDataPoolRecords Job's status" status: JobStatus! "The list of filters that will be used for updating data. Data matching the filters will be updated." - filters: [Filter!]! + filters: [Filter!] @deprecated(reason: "Use `filterSql` instead") + "The filters that will be used for updating data, in the form of SQL. Data matching the filters will be updated." + filterSql: String "Describes how the job will update the records." set: [UpdateDataPoolRecordsJobSetColumn!]! "The error that occurred while updating data, if any." @@ -3800,7 +4080,7 @@ input CreateUpdateDataPoolRecordsJobInput { "The Data Pool that is going to get its records updated." dataPool: ID! "The list of filters that will be used for updating records. Records matching these filters will be updated." - filters: [FilterInput!]! + filters: [FilterInput!] "Describes how the job will update the records." set: [UpdateDataPoolRecordsJobSetColumnInput!]! } @@ -3861,7 +4141,11 @@ type DataPoolAccessPolicy implements Node & Common { """ Row-level filters that the Access Policy applies before executing queries. """ - rows: [Filter!]! + rows: [Filter!]! @deprecated(reason: "Use `filtersSql` instead") + """ + Row-level filters that the Access Policy applies before executing queries, in the form of SQL. + """ + filterSql: String """ Applications that are assigned to this Data Pool Access Policy. """ @@ -4186,6 +4470,18 @@ type Mutation { """ modifyHttpDataSource(input: ModifyHttpDataSourceInput!): DataSourceResponse! """ + This mutation creates a new Kafka Data Source. + + The mutation returns the newly created Data Source (or an error message if creating the Data Source fails). + """ + createKafkaDataSource(input: CreateKafkaDataSourceInput!): DataSourceResponse! + """ + This mutation selects a Data Source by its ID or unique name and modifies it to have the given unique name, description, and connection settings. + + If any of the optional arguments are omitted, those properties will be unchanged on the Data Source. + """ + modifyKafkaDataSource(input: ModifyKafkaDataSourceInput!): DataSourceResponse! + """ Creates a new Amazon S3 Data Source pointed at the specified S3 bucket. Returns the newly created Data Source (or an error message if creating the Data Source fails). @@ -4221,6 +4517,16 @@ input SqlV1Input { query: String! } +""" +Input for describing SqlV1 inputs. +""" +input DescribeSqlV1Input { + """ + The SQL query. + """ + query: String! +} + type SqlColumnResponse { """ The name of the returned column. @@ -4256,6 +4562,16 @@ type SqlResponse { info: QueryInfo! } +""" +Response from the describe SQL API. +""" +type DescribeSqlResponse { + """ + The columns that the query would return. + """ + columns: [SqlColumnResponse!]! +} + """ The Data Grid connection. @@ -4393,7 +4709,24 @@ type TopValuesResponse { values: [String!]! } +type ReportType implements Node & Common { + id: ID! + uniqueName: String! + description: String! + application: Application! + layout: [[String]]! + charts: [ChartType]! + createdAt: DateTime! + modifiedAt: DateTime! + createdBy: String! + modifiedBy: String! + account: Account! + environment: Environment! +} + type Query { + report(id: ID!): ReportType @deprecated(reason: "EXPERIMENTAL") + """ This query returns the Application specified by the given ID. @@ -4467,6 +4800,16 @@ type Query { """ dataPools(first: Int, after: String, last: Int, before: String): DataPoolConnection """ + This query returns the Materialized Views within the Environment. + + The `materializedViews` query uses [cursor-based pagination](/docs/api/pagination) typical of GraphQL APIs. You can use the pairs of parameters `first` and `after` or `last` and `before` to page forward or backward through the results, respectively. + + For forward pagination, the `first` parameter defines the number of results to return, and the `after` parameter defines the cursor to continue from. You should pass the cursor for the _last_ result of the current page to `after`. + + For backward pagination, the `last` parameter defines the number of results to return, and the `before` parameter defines the cursor to continue from. You should pass the cursor for the _first_ result of the current page to `before`. + """ + materializedViews(first: Int, after: String, last: Int, before: String): MaterializedViewConnection + """ This query returns the Data Pool Access Policy specified by the given ID. A Data Pool Access Policy limits the data that Applications can access within a Data Pool. @@ -4530,6 +4873,10 @@ type Query { """ sqlV1(input: SqlV1Input!): SqlResponse! """ + Describe SQL statements Data Pools. + """ + describeSqlV1(input: DescribeSqlV1Input!): DescribeSqlResponse! + """ This query returns the individual records of a Data Pool with the convenience of built-in pagination, filtering, and sorting. """ dataGrid(input: DataGridInput!): DataGridConnection! @@ -4542,15 +4889,19 @@ type Query { """ topValues(input: TopValuesInput!): TopValuesResponse! """ - Query a Metric in counter format. A single Metric value for the given time range and filters. + Query a metric in counter format. Returns a single metric value for the given time range and filters. """ counter(input: CounterInput!): CounterResponse """ - Query a Metric in time series format. Arrays of timestamps and Metric values for the given time range and filters. + Query metrics in counter format. Returns a metric value for each input in the array of inputs. + """ + counters(input: [CounterInput!]!): [CounterResponse]! + """ + Query a metric in time series format. Returns arrays of timestamps and metric values for the given time range and filters. """ timeSeries(input: TimeSeriesInput!): TimeSeriesResponse """ - Query a Metric in leaderboard format. A table (array of rows) with the selected dimensions and corresponding Metric values for the given time range and filters. + Query a metric in leaderboard format. Returns a table (array of rows) with the selected dimensions and the metric's corresponding values for the given time range and filters. """ leaderboard(input: LeaderboardInput!): LeaderboardResponse """ @@ -4594,6 +4945,14 @@ type Query { last: Int before: String ): UpdateDataPoolRecordsJobConnection! + """ + This query returns the Materialized View specified by the given ID. + """ + materializedView(id: ID!): MaterializedView + """ + This query returns the Materialized View specified by its unique name. + """ + materializedViewByName(uniqueName: String!): MaterializedView } """ @@ -4924,6 +5283,42 @@ type DeletionJobEdge { node: DeletionJob! } +""" +The Materialized View connection object. + +Learn more about [pagination in GraphQL](https://www.propeldata.com/docs/api/pagination). +""" +type MaterializedViewConnection { + """ + The Materialized View connection's edges. + """ + edges: [MaterializedViewEdge!]! + """ + The Materialized View connection's nodes. + """ + nodes: [MaterializedView!]! + """ + The Materialized View connection's page info. + """ + pageInfo: PageInfo! +} + +""" +The Materialized View edge object. + +Learn more about [pagination in GraphQL](https://www.propeldata.com/docs/api/pagination). +""" +type MaterializedViewEdge { + """ + The edge's cursor. + """ + cursor: String! + """ + The edge's node. + """ + node: MaterializedView! +} + """ The Metric connection object. @@ -5230,6 +5625,118 @@ input PartialHttpConnectionSettingsInput { tables: [HttpDataSourceTableInput!] } +input CreateKafkaDataSourceInput { + """ + The Kafka Data Source's connection settings + """ + connectionSettings: KafkaConnectionSettingsInput! + """ + The Kafka Data Source's description. + """ + description: String + """ + The Kafka Data Source's unique name. If not specified, Propel will set the ID as unique name. + """ + uniqueName: String +} + +""" +The Kafka Data Source connection settings. +""" +type KafkaConnectionSettings { + """ + The type of authentication to use. Can be SCRAM-SHA-256, SCRAM-SHA-512, PLAIN or NONE + """ + auth: String! + """ + The bootstrap server(s) to connect to + """ + bootstrapServers: [String!]! + """ + The password for the provided user + """ + password: String! + """ + Whether the the connection to the Kafka servers is encrypted or not + """ + tls: Boolean + """ + The user for authenticating against the Kafka servers + """ + user: String! +} + +""" +The Kafka Data Source connection settings. +""" +input KafkaConnectionSettingsInput { + """ + The type of authentication to use. Can be SCRAM-SHA-256, SCRAM-SHA-512, PLAIN or NONE + """ + auth: String! + """ + The bootstrap server(s) to connect to + """ + bootstrapServers: [String!]! + """ + The password for the provided user + """ + password: String! + """ + Whether the the connection to the Kafka servers is encrypted or not + """ + tls: Boolean + """ + The user for authenticating against the Kafka servers + """ + user: String! +} + +input ModifyKafkaDataSourceInput { + """ + The Kafka Data Source's new connection settings. If not provided this property will not be modified. + """ + connectionSettings: PartialKafkaConnectionSettingsInput + """ + The Kafka Data Source's new description. If not provided this property will not be modified. + """ + description: String + """ + The ID or unique name of the Kafka Data Source to modify. + """ + idOrUniqueName: idOrUniqueName! + """ + The Kafka Data Source's new unique name. If not provided this property will not be modified. + """ + uniqueName: String +} + +""" +The Kafka Data Source connection settings. +""" +input PartialKafkaConnectionSettingsInput { + """ + The type of authentication to use. Can be SCRAM-SHA-256, SCRAM-SHA-512, PLAIN or NONE If not provided this property will not be modified. + """ + auth: String + """ + The bootstrap server(s) to connect to If not provided this property will not be modified. + """ + bootstrapServers: [String!]! + """ + The password for the provided user If not provided this property will not be modified. + """ + password: String + """ + Whether the the connection to the Kafka servers is encrypted or not If not provided this property will not be modified. + """ + tls: Boolean + """ + The user for authenticating against the Kafka servers If not provided this property will not be modified. + """ + user: String +} + input CreateS3DataSourceInput { """ The S3 Data Source's connection settings @@ -5387,15 +5894,21 @@ type WebhookConnectionSettings { """ columns: [WebhookDataSourceColumn!] """ - The tenant ID column, if configured. + Override the Data Pool's table settings. These describe how the Data Pool's table is created in ClickHouse, and a + default will be chosen based on the Data Pool's `timestamp` and `uniqueId` values, if any. You can override these + defaults in order to specify a custom table engine, custom ORDER BY, etc. + """ + tableSettings: TableSettings + """ + The tenant ID column, if any. """ tenant: String """ - The primary timestamp column. + The primary timestamp column, if any. """ - timestamp: String! + timestamp: String """ - The unique ID column. Propel uses the primary timestamp and a unique ID to compose a primary key for determining whether records should be inserted, deleted, or updated. + The unique ID column, if any. Propel uses the primary timestamp and a unique ID to compose a primary key for determining whether records should be inserted, deleted, or updated. """ uniqueId: String """ @@ -5404,6 +5917,14 @@ type WebhookConnectionSettings { webhookUrl: String! } +type ChartType { + id: String! + type: String! + title: String! + query: String! + variables: String! +} + """ The Webhook Data Source connection settings. """ @@ -5417,15 +5938,21 @@ input WebhookConnectionSettingsInput { """ columns: [WebhookDataSourceColumnInput!] """ - The tenant ID column, if configured. + Override the Data Pool's table settings. These describe how the Data Pool's table is created in ClickHouse, and a + default will be chosen based on the Data Pool's `timestamp` and `uniqueId` values, if any. You can override these + defaults in order to specify a custom table engine, custom ORDER BY, etc. + """ + tableSettings: TableSettingsInput + """ + The tenant ID column, if any. """ tenant: String """ - The primary timestamp column. + The primary timestamp column, if any. """ - timestamp: String! + timestamp: String """ - The unique ID column. Propel uses the primary timestamp and a unique ID to compose a primary key for determining whether records should be inserted, deleted, or updated. + The unique ID column, if any. Propel uses the primary timestamp and a unique ID to compose a primary key for determining whether records should be inserted, deleted, or updated. """ uniqueId: String } diff --git a/packages/ui-kit/src/helpers/graphql/queries/Report.graphql b/packages/ui-kit/src/helpers/graphql/queries/Report.graphql new file mode 100644 index 00000000..41d3f7ab --- /dev/null +++ b/packages/ui-kit/src/helpers/graphql/queries/Report.graphql @@ -0,0 +1,12 @@ +query Report($id: ID!) { + report(id: $id) { + layout + charts { + id + type + title + query + variables + } + } +} diff --git a/packages/ui-kit/src/hooks/useReport/index.ts b/packages/ui-kit/src/hooks/useReport/index.ts new file mode 100644 index 00000000..4096c83f --- /dev/null +++ b/packages/ui-kit/src/hooks/useReport/index.ts @@ -0,0 +1 @@ +export * from './useReport' diff --git a/packages/ui-kit/src/hooks/useReport/useReport.ts b/packages/ui-kit/src/hooks/useReport/useReport.ts new file mode 100644 index 00000000..e4d332d2 --- /dev/null +++ b/packages/ui-kit/src/hooks/useReport/useReport.ts @@ -0,0 +1,31 @@ +import { useAccessToken } from '../../components' +import { PROPEL_GRAPHQL_API_ENDPOINT, ReportQuery, useReportQuery } from '../../helpers' +import { ReportQueryProp } from '../../components/Report/Report.types' + +export function useReport(props: Partial) { + const { accessToken: accessTokenFromProp, propelApiUrl, reportId, enabled = true } = props + + const { accessToken: accessTokenFromProvider, isLoading: isLoadingAccessToken } = useAccessToken() + + const accessToken = accessTokenFromProp ?? accessTokenFromProvider + + const { data, isLoading } = useReportQuery( + { + endpoint: propelApiUrl ?? PROPEL_GRAPHQL_API_ENDPOINT, + fetchParams: { + headers: { + 'content-type': 'application/graphql-response+json', + authorization: `Bearer ${accessToken}` + } + } + }, + { + id: reportId ?? '' + }, + { + enabled: accessToken != null && enabled + } + ) + + return { data, isLoading: isLoading || isLoadingAccessToken } +} diff --git a/packages/ui-kit/src/hooks/useReportComponents/useReportComponents.ts b/packages/ui-kit/src/hooks/useReportComponents/useReportComponents.ts index 86a55b0a..dffdef3e 100644 --- a/packages/ui-kit/src/hooks/useReportComponents/useReportComponents.ts +++ b/packages/ui-kit/src/hooks/useReportComponents/useReportComponents.ts @@ -1,7 +1,7 @@ import { PROPEL_GRAPHQL_API_ENDPOINT } from '../../helpers' import { ChartProp } from '../../components/Report' import { useAccessToken } from '../../components' -import { useEffect, useMemo, useRef, useState } from 'react' +import { useEffect, useMemo, useState } from 'react' function fetcher(endpoint: string, requestInit: RequestInit, query: string, variables?: string) { return async (): Promise => { @@ -25,19 +25,20 @@ function fetcher(endpoint: string, requestInit: RequestInit, query: strin interface Options { propelApiUrl?: string + accessToken?: string } -export function useReportComponents(charts: ChartProp[], options?: Options) { +export function useReportComponents(charts: Array | null | undefined, options?: Options) { const [result, setResult] = useState(null) const [isLoading, setIsLoading] = useState(true) - const { accessToken } = useAccessToken() + const { accessToken: accessTokenFromProvider } = useAccessToken() - const memoizedCharts = useRef(charts) + const accessToken = options?.accessToken ?? accessTokenFromProvider const fetchers = useMemo( () => - memoizedCharts.current.map((chart) => + charts?.map((chart) => fetcher( options?.propelApiUrl ?? PROPEL_GRAPHQL_API_ENDPOINT, { @@ -46,24 +47,26 @@ export function useReportComponents(charts: ChartProp[], options?: Options) { authorization: `Bearer ${accessToken}` } }, - chart.query ?? '', - JSON.parse(chart.variables ?? '{}') + chart?.query ?? '', + JSON.parse(chart?.variables ?? '{}') ) ), - [accessToken, options?.propelApiUrl] + [accessToken, options?.propelApiUrl, charts] ) useEffect(() => { async function getData() { - const result = await Promise.allSettled(fetchers.map((fetcher) => fetcher())) - setResult(result.map((res) => (res.status === 'fulfilled' ? res.value : null))) - setIsLoading(false) + if (fetchers != null) { + const result = await Promise.allSettled(fetchers.map((fetcher) => fetcher())) + setResult(result.map((res) => (res.status === 'fulfilled' ? res.value : null))) + setIsLoading(false) + } } - if (accessToken) { + if (accessToken != null) { getData() } }, [accessToken, fetchers]) - return { charts: charts.map((chart, idx) => ({ ...chart, result: result != null ? result[idx] : null })), isLoading } + return { charts: charts?.map((chart, idx) => ({ ...chart, result: result != null ? result[idx] : null })), isLoading } } From c8f8f26d6d8326ed6d76bc9f14bad75b4b95a9d5 Mon Sep 17 00:00:00 2001 From: Felipe Cadavid Date: Wed, 10 Apr 2024 13:09:11 -0500 Subject: [PATCH 4/7] fix: set chart and layout props to optional --- packages/ui-kit/src/components/Report/Report.types.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/ui-kit/src/components/Report/Report.types.ts b/packages/ui-kit/src/components/Report/Report.types.ts index 3a8252f7..8ca731f3 100644 --- a/packages/ui-kit/src/components/Report/Report.types.ts +++ b/packages/ui-kit/src/components/Report/Report.types.ts @@ -24,8 +24,8 @@ export interface ReportQueryProp { } export interface ReportProps { - layout: string[][] - charts: ChartProp[] + layout?: string[][] + charts?: ChartProp[] clickable?: boolean onCardClick?: (chart: ChartProp | null | undefined) => void reportCardProps?: React.HTMLAttributes From e8b01918a915638d6918d8b9439a3b0640281699 Mon Sep 17 00:00:00 2001 From: Felipe Cadavid Date: Wed, 10 Apr 2024 15:11:29 -0500 Subject: [PATCH 5/7] feat: support titles --- .../src/components/Report/Report.module.scss | 33 +++++++++ .../src/components/Report/Report.stories.tsx | 8 +- .../ui-kit/src/components/Report/Report.tsx | 74 +++++++++++-------- .../src/components/Report/Report.types.ts | 2 + .../helpers/graphql/queries/Report.graphql | 1 + .../useReportComponents.ts | 2 + 6 files changed, 88 insertions(+), 32 deletions(-) diff --git a/packages/ui-kit/src/components/Report/Report.module.scss b/packages/ui-kit/src/components/Report/Report.module.scss index bdeb8e94..9ac4e596 100644 --- a/packages/ui-kit/src/components/Report/Report.module.scss +++ b/packages/ui-kit/src/components/Report/Report.module.scss @@ -1,4 +1,27 @@ .container { + height: 100%; + align-items: center; +} + +.container > header { + display: flex; + justify-content: space-between; +} + +.container > header > p { + display: flex; + align-items: center; + gap: 4px; +} + +.container > header > p > a { + color: #7f56d9; + display: flex; + align-items: center; + text-decoration: none; +} + +.reportGrid { display: grid; height: 100%; @@ -16,6 +39,7 @@ .card { display: flex; + flex-direction: column; justify-content: center; align-items: center; } @@ -23,3 +47,12 @@ .clickable { cursor: pointer; } + +.cardTitle { + align-self: flex-start; +} + +.cardTitle > h2 { + margin: 0; + font-size: 18px; +} diff --git a/packages/ui-kit/src/components/Report/Report.stories.tsx b/packages/ui-kit/src/components/Report/Report.stories.tsx index a24025a9..da69fdcb 100644 --- a/packages/ui-kit/src/components/Report/Report.stories.tsx +++ b/packages/ui-kit/src/components/Report/Report.stories.tsx @@ -22,6 +22,7 @@ export default meta export const Basic: Story = { args: { + title: 'My Report', layout: [ ['timeSeries', 'timeSeries', 'leaderboard'], ['counter1', 'counter2', 'leaderboard'], @@ -36,6 +37,7 @@ export const Basic: Story = { values } }`, + title: 'Time Series Title', type: 'timeSeries', variables: `{ "input": { @@ -157,7 +159,7 @@ export const Basic: Story = { ] }, render: (args) => ( - + ) @@ -215,9 +217,9 @@ export const Connected: Story = { args: { query: { accessToken: - 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdXRoX3RpbWUiOjE3MTI3NzEzODgsImNsaWVudF9pZCI6IkFQUDAxSEZQVFc4NjM1WVQ2Q1E5MjVFNUtZM1JOIiwiZXhwIjoxNzEyNzc0OTg4LCJpYXQiOjE3MTI3NzEzODgsImlzcyI6Imh0dHBzOi8vYXV0aC51cy1lYXN0LTIuZGV2LnByb3BlbGRhdGEuY29tIiwianRpIjoiMjA3OWM0NTUtYzg2Mi00ZWM0LTgyYmQtODRmOGEwNzY3NDE5Iiwic2NvcGUiOiJtZXRyaWM6cXVlcnkgbWV0cmljOnN0YXRzIGRhdGFfcG9vbDpxdWVyeSBkYXRhX3Bvb2w6cmVhZCBkYXRhX3Bvb2w6c3RhdHMgYWRtaW4gYXBwbGljYXRpb246YWRtaW4gbWV0cmljOnJlYWQiLCJzdWIiOiJBUFAwMUhGUFRXODYzNVlUNkNROTI1RTVLWTNSTiIsInRva2VuX3VzZSI6ImFjY2VzcyIsInZlcnNpb24iOjF9.DImh1Mu2OMrxji5G6tWEQ2m7bnQ_YvuDdC2j84pdCOU', + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdXRoX3RpbWUiOjE3MTI3NzkzNzEsImNsaWVudF9pZCI6IkFQUDAxSEZQVFc4NjM1WVQ2Q1E5MjVFNUtZM1JOIiwiZXhwIjoxNzEyNzgyOTcxLCJpYXQiOjE3MTI3NzkzNzEsImlzcyI6Imh0dHBzOi8vYXV0aC51cy1lYXN0LTIuZGV2LnByb3BlbGRhdGEuY29tIiwianRpIjoiOTc5OThhYzgtOWMzMC00OTgxLWI2NDMtMzZiNTU0ODk4YzAxIiwic2NvcGUiOiJtZXRyaWM6cXVlcnkgbWV0cmljOnN0YXRzIGRhdGFfcG9vbDpxdWVyeSBkYXRhX3Bvb2w6cmVhZCBkYXRhX3Bvb2w6c3RhdHMgYWRtaW4gYXBwbGljYXRpb246YWRtaW4gbWV0cmljOnJlYWQiLCJzdWIiOiJBUFAwMUhGUFRXODYzNVlUNkNROTI1RTVLWTNSTiIsInRva2VuX3VzZSI6ImFjY2VzcyIsInZlcnNpb24iOjF9.YaZO2h-rt3puhecWBkWYqMI55VBRIOp5CqQx1WOG_JM', propelApiUrl: 'https://api.us-east-2.dev.propeldata.com/graphql', - reportId: 'RPT01HV4GY04A4B1054KCK3EV08AR' + reportId: 'RPT01HV4RBM3WETGC1F9ERQZGBENG' } }, render: (args) => diff --git a/packages/ui-kit/src/components/Report/Report.tsx b/packages/ui-kit/src/components/Report/Report.tsx index b1d9abf1..73eb9365 100644 --- a/packages/ui-kit/src/components/Report/Report.tsx +++ b/packages/ui-kit/src/components/Report/Report.tsx @@ -24,6 +24,7 @@ const componentMap = { export const ReportComponent = React.forwardRef((props: ReportProps) => { const { + title: titleProp, layout: layoutProp, charts: chartsProp, clickable = false, @@ -39,6 +40,7 @@ export const ReportComponent = React.forwardRef((props: ReportProps) => { const charts = isStatic ? chartsProp : data?.report?.charts const layout = isStatic ? layoutProp : data?.report?.layout + const title = isStatic ? titleProp : data?.report?.uniqueName const { charts: reportCharts, isLoading: isLoadingComponents } = useReportComponents(charts, { propelApiUrl: propelApiUrl ?? query?.propelApiUrl, @@ -50,35 +52,49 @@ export const ReportComponent = React.forwardRef((props: ReportProps) => { const gridTemplateAreas = buildGridTemplateAreas(layout) return ( -
'1fr').join(' '), - gridTemplateColumns: layout?.[0]?.map(() => '1fr').join(' ') - }} - > - {reportCharts?.map((chart, chartIdx) => ( - clickable && (onCardClick?.(chart) ?? reportCardProps?.onClick?.(e))} - style={{ ...reportCardProps?.style, gridArea: `report-area-${chart.id}` }} - > - {!isLoading ? ( - chart.result != null && - chart.type != null && - componentMap[chart.type as 'timeSeries' | 'leaderboard' | 'counter']({ ...chart.result[chart.type] }) - ) : ( - - )} - - ))} +
+ {title != null && ( +
+

{title}

+

+ Powered by{' '} + + Propel + +

+
+ )} +
'1fr').join(' '), + gridTemplateColumns: layout?.[0]?.map(() => '1fr').join(' ') + }} + > + {reportCharts?.map((chart, chartIdx) => ( + clickable && (onCardClick?.(chart) ?? reportCardProps?.onClick?.(e))} + style={{ ...reportCardProps?.style, gridArea: `report-area-${chart.id}` }} + > +
{chart.title != null &&

{chart.title}

}
+ {!isLoading ? ( + chart.result != null && + chart.type != null && + componentMap[chart.type as 'timeSeries' | 'leaderboard' | 'counter']({ ...chart.result[chart.type] }) + ) : ( + + )} +
+ ))} +
) }) diff --git a/packages/ui-kit/src/components/Report/Report.types.ts b/packages/ui-kit/src/components/Report/Report.types.ts index 8ca731f3..186e5b43 100644 --- a/packages/ui-kit/src/components/Report/Report.types.ts +++ b/packages/ui-kit/src/components/Report/Report.types.ts @@ -1,5 +1,6 @@ export interface ChartProp { id?: string + title?: string query?: string type?: 'timeSeries' | 'leaderboard' | 'counter' | '' | string variables?: string @@ -24,6 +25,7 @@ export interface ReportQueryProp { } export interface ReportProps { + title?: string layout?: string[][] charts?: ChartProp[] clickable?: boolean diff --git a/packages/ui-kit/src/helpers/graphql/queries/Report.graphql b/packages/ui-kit/src/helpers/graphql/queries/Report.graphql index 41d3f7ab..74782780 100644 --- a/packages/ui-kit/src/helpers/graphql/queries/Report.graphql +++ b/packages/ui-kit/src/helpers/graphql/queries/Report.graphql @@ -1,5 +1,6 @@ query Report($id: ID!) { report(id: $id) { + uniqueName layout charts { id diff --git a/packages/ui-kit/src/hooks/useReportComponents/useReportComponents.ts b/packages/ui-kit/src/hooks/useReportComponents/useReportComponents.ts index dffdef3e..3b3a36d9 100644 --- a/packages/ui-kit/src/hooks/useReportComponents/useReportComponents.ts +++ b/packages/ui-kit/src/hooks/useReportComponents/useReportComponents.ts @@ -68,5 +68,7 @@ export function useReportComponents(charts: Array } }, [accessToken, fetchers]) + console.log(result) + return { charts: charts?.map((chart, idx) => ({ ...chart, result: result != null ? result[idx] : null })), isLoading } } From cb80e299441579b8cd09006c49f4b27d58a30c17 Mon Sep 17 00:00:00 2001 From: Felipe Cadavid Date: Wed, 10 Apr 2024 16:13:45 -0500 Subject: [PATCH 6/7] feat: improve customization --- packages/ui-kit/src/components/Report/Report.tsx | 9 ++++++--- packages/ui-kit/src/components/Report/Report.types.ts | 2 ++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/ui-kit/src/components/Report/Report.tsx b/packages/ui-kit/src/components/Report/Report.tsx index 73eb9365..e715a276 100644 --- a/packages/ui-kit/src/components/Report/Report.tsx +++ b/packages/ui-kit/src/components/Report/Report.tsx @@ -22,7 +22,7 @@ const componentMap = { '': () => null } -export const ReportComponent = React.forwardRef((props: ReportProps) => { +export const ReportComponent = React.forwardRef((props, ref) => { const { title: titleProp, layout: layoutProp, @@ -31,7 +31,10 @@ export const ReportComponent = React.forwardRef((props: ReportProps) => { onCardClick, reportCardProps, propelApiUrl, - query + query, + className, + style, + ...rest } = props const isStatic = query?.accessToken == null @@ -52,7 +55,7 @@ export const ReportComponent = React.forwardRef((props: ReportProps) => { const gridTemplateAreas = buildGridTemplateAreas(layout) return ( -
+
{title != null && (

{title}

diff --git a/packages/ui-kit/src/components/Report/Report.types.ts b/packages/ui-kit/src/components/Report/Report.types.ts index 186e5b43..51f1aaf5 100644 --- a/packages/ui-kit/src/components/Report/Report.types.ts +++ b/packages/ui-kit/src/components/Report/Report.types.ts @@ -33,4 +33,6 @@ export interface ReportProps { reportCardProps?: React.HTMLAttributes propelApiUrl?: string query?: ReportQueryProp + className?: string + style?: React.CSSProperties } From 834f3dce477699a3ce345fb1f806a3bc5bd7642a Mon Sep 17 00:00:00 2001 From: Felipe Cadavid Date: Wed, 10 Apr 2024 22:14:43 -0500 Subject: [PATCH 7/7] fix: styles --- .../src/components/Report/Report.module.scss | 10 +++++- .../src/components/Report/Report.stories.tsx | 35 ++++++++++++++++--- .../ui-kit/src/components/Report/Report.tsx | 31 +++++++++++----- 3 files changed, 62 insertions(+), 14 deletions(-) diff --git a/packages/ui-kit/src/components/Report/Report.module.scss b/packages/ui-kit/src/components/Report/Report.module.scss index 9ac4e596..b5c24b07 100644 --- a/packages/ui-kit/src/components/Report/Report.module.scss +++ b/packages/ui-kit/src/components/Report/Report.module.scss @@ -38,10 +38,14 @@ } .card { + display: block; +} + +.card > div { display: flex; - flex-direction: column; justify-content: center; align-items: center; + height: 100%; } .clickable { @@ -56,3 +60,7 @@ margin: 0; font-size: 18px; } + +.pie > div > canvas { + max-height: 240px !important; +} diff --git a/packages/ui-kit/src/components/Report/Report.stories.tsx b/packages/ui-kit/src/components/Report/Report.stories.tsx index da69fdcb..0007308f 100644 --- a/packages/ui-kit/src/components/Report/Report.stories.tsx +++ b/packages/ui-kit/src/components/Report/Report.stories.tsx @@ -4,6 +4,7 @@ import { Meta, StoryObj } from '@storybook/react' import { quotedStringRegex, storybookCodeTemplate } from '../../helpers' import { Report } from './Report' import { AccessTokenProvider } from '../AccessTokenProvider' +import { ThemeProvider } from '../ThemeProvider' type Story = StoryObj @@ -60,9 +61,35 @@ export const Basic: Story = { }, { id: `leaderboard`, - query: ``, - type: '', - variables: `{}` + query: `query LeaderboardQuery($input: LeaderboardInput!) { + leaderboard(input: $input) { + headers + rows + } + }`, + type: 'pie', + variables: `{ + "input": { + "metric": { + "count": { + "dataPool": { + "name": "TacoSoft Demo Data" + } + } + }, + "sort": "DESC", + "timeZone": "UTC", + "timeRange": { + "relative": "LAST_N_DAYS", + "n": 30 + }, + "rowLimit": 10, + "dimensions": [{ + "columnName": "taco_name" + }], + "filters": [] + } + }` }, { id: 'counter1', @@ -159,7 +186,7 @@ export const Basic: Story = { ] }, render: (args) => ( - + ) diff --git a/packages/ui-kit/src/components/Report/Report.tsx b/packages/ui-kit/src/components/Report/Report.tsx index e715a276..452563cd 100644 --- a/packages/ui-kit/src/components/Report/Report.tsx +++ b/packages/ui-kit/src/components/Report/Report.tsx @@ -5,6 +5,7 @@ import { ErrorFallback } from '../ErrorFallback' import { withContainer } from '../withContainer' import { Card } from '../Card' import { useReportComponents } from '../../hooks' +import { PieChartProps, PieChart } from '../PieChart' import componentStyles from './Report.module.scss' import { ReportProps } from './Report.types' @@ -16,9 +17,17 @@ import { Loader } from '../Loader' import { useReport } from '../../hooks/useReport' const componentMap = { - timeSeries: (args: TimeSeriesProps) => , + timeSeries: (args: TimeSeriesProps) => , leaderboard: (args: LeaderboardProps) => , counter: (args: CounterProps) => , + pie: (args: PieChartProps) => ( + ({ ...config, options: { ...config.options, maintainAspectRatio: true } })} + {...args} + /> + ), '': () => null } @@ -87,14 +96,18 @@ export const ReportComponent = React.forwardRef((pr onClick={(e) => clickable && (onCardClick?.(chart) ?? reportCardProps?.onClick?.(e))} style={{ ...reportCardProps?.style, gridArea: `report-area-${chart.id}` }} > -
{chart.title != null &&

{chart.title}

}
- {!isLoading ? ( - chart.result != null && - chart.type != null && - componentMap[chart.type as 'timeSeries' | 'leaderboard' | 'counter']({ ...chart.result[chart.type] }) - ) : ( - - )} +
{chart.title != null &&

{chart.title}

}
+
+ {!isLoading ? ( + chart.result != null && + chart.type != null && + componentMap[chart.type as 'timeSeries' | 'leaderboard' | 'counter' | 'pie']({ + ...chart.result[chart.type === 'pie' ? 'leaderboard' : chart.type] + }) + ) : ( + + )} +
))}