From 7b979a8c2a0918f9ea0a8b32b73d17f3cb30f4d0 Mon Sep 17 00:00:00 2001 From: Alexander Polyankin <alexander.polyankin@metabase.com> Date: Fri, 8 Jul 2022 16:41:20 +0300 Subject: [PATCH] Add question last cached time info (#23787) --- .../components/CacheSection/CacheSection.tsx | 7 ++-- .../caching/components/CacheSection/index.js | 1 - .../caching/components/CacheSection/index.ts | 1 + .../CacheTTLField/CacheTTLField.jsx | 4 +- .../CacheTTLField/CacheTTLField.unit.spec.js | 2 +- .../caching/components/CacheTTLField/index.js | 2 +- .../DashboardCacheSection.tsx | 6 ++- .../components/DashboardCacheSection/index.ts | 2 +- .../DatabaseCacheTTLField.jsx | 6 ++- .../DatabaseCacheTTLField.unit.spec.js | 2 +- .../components/DatabaseCacheTTLField/index.js | 2 +- .../QuestionCacheSection.styled.tsx | 8 ++++ .../QuestionCacheSection.tsx | 25 +++++++++-- .../QuestionCacheSection.unit.spec.tsx | 41 +++++++++++++++++++ .../components/QuestionCacheSection/index.js | 1 - .../components/QuestionCacheSection/index.ts | 1 + .../QuestionCacheTTLField.jsx | 4 +- .../QuestionCacheTTLField.styled.jsx | 2 +- .../QuestionCacheTTLField.unit.spec.js | 2 +- .../components/QuestionCacheTTLField/index.js | 2 +- .../src/metabase-enterprise/caching/index.js | 10 ++--- frontend/src/metabase-lib/lib/Question.ts | 4 ++ frontend/src/metabase-types/api/card.ts | 19 +++++++++ .../api/{foreignKey.ts => foreign-key.ts} | 0 frontend/src/metabase-types/api/index.ts | 17 ++++---- frontend/src/metabase-types/api/mocks/card.ts | 37 ++++++++++++++++- .../src/metabase-types/api/mocks/index.ts | 1 + .../src/metabase-types/api/mocks/query.ts | 37 +++++++++++++++++ frontend/src/metabase-types/api/query.ts | 24 +++++++++++ frontend/src/metabase-types/api/question.ts | 1 - frontend/src/metabase-types/api/table.ts | 6 ++- frontend/src/metabase-types/types/Table.ts | 2 +- .../components/ObjectDetail/ObjectDetail.tsx | 2 +- .../ObjectDetail/ObjectRelationships.tsx | 2 +- 34 files changed, 239 insertions(+), 44 deletions(-) delete mode 100644 enterprise/frontend/src/metabase-enterprise/caching/components/CacheSection/index.js create mode 100644 enterprise/frontend/src/metabase-enterprise/caching/components/CacheSection/index.ts create mode 100644 enterprise/frontend/src/metabase-enterprise/caching/components/QuestionCacheSection/QuestionCacheSection.styled.tsx create mode 100644 enterprise/frontend/src/metabase-enterprise/caching/components/QuestionCacheSection/QuestionCacheSection.unit.spec.tsx delete mode 100644 enterprise/frontend/src/metabase-enterprise/caching/components/QuestionCacheSection/index.js create mode 100644 enterprise/frontend/src/metabase-enterprise/caching/components/QuestionCacheSection/index.ts rename frontend/src/metabase-types/api/{foreignKey.ts => foreign-key.ts} (100%) create mode 100644 frontend/src/metabase-types/api/mocks/query.ts create mode 100644 frontend/src/metabase-types/api/query.ts delete mode 100644 frontend/src/metabase-types/api/question.ts diff --git a/enterprise/frontend/src/metabase-enterprise/caching/components/CacheSection/CacheSection.tsx b/enterprise/frontend/src/metabase-enterprise/caching/components/CacheSection/CacheSection.tsx index 2645a3d610c..be7ff82ee18 100644 --- a/enterprise/frontend/src/metabase-enterprise/caching/components/CacheSection/CacheSection.tsx +++ b/enterprise/frontend/src/metabase-enterprise/caching/components/CacheSection/CacheSection.tsx @@ -16,10 +16,7 @@ interface CacheSectionProps { onSave: (cache_ttl: number | null) => Promise<any>; } -export const CacheSection = ({ - initialCacheTTL, - onSave, -}: CacheSectionProps) => { +const CacheSection = ({ initialCacheTTL, onSave }: CacheSectionProps) => { const [cacheTTL, setCacheTTL] = useState(initialCacheTTL); const handleChange = useCallback( @@ -70,3 +67,5 @@ export const CacheSection = ({ </CacheSectionRoot> ); }; + +export default CacheSection; diff --git a/enterprise/frontend/src/metabase-enterprise/caching/components/CacheSection/index.js b/enterprise/frontend/src/metabase-enterprise/caching/components/CacheSection/index.js deleted file mode 100644 index 945d4ff0f4a..00000000000 --- a/enterprise/frontend/src/metabase-enterprise/caching/components/CacheSection/index.js +++ /dev/null @@ -1 +0,0 @@ -export * from "./CacheSection"; diff --git a/enterprise/frontend/src/metabase-enterprise/caching/components/CacheSection/index.ts b/enterprise/frontend/src/metabase-enterprise/caching/components/CacheSection/index.ts new file mode 100644 index 00000000000..e2d399aae7c --- /dev/null +++ b/enterprise/frontend/src/metabase-enterprise/caching/components/CacheSection/index.ts @@ -0,0 +1 @@ +export { default } from "./CacheSection"; diff --git a/enterprise/frontend/src/metabase-enterprise/caching/components/CacheTTLField/CacheTTLField.jsx b/enterprise/frontend/src/metabase-enterprise/caching/components/CacheTTLField/CacheTTLField.jsx index 1c00d2a11c9..22ea7f861b0 100644 --- a/enterprise/frontend/src/metabase-enterprise/caching/components/CacheTTLField/CacheTTLField.jsx +++ b/enterprise/frontend/src/metabase-enterprise/caching/components/CacheTTLField/CacheTTLField.jsx @@ -17,7 +17,7 @@ const propTypes = { message: PropTypes.string, }; -export function CacheTTLField({ field, message, ...props }) { +function CacheTTLField({ field, message, ...props }) { const hasError = !!field.error; return ( <CacheTTLFieldContainer {...props} data-testid="cache-ttl-field"> @@ -40,3 +40,5 @@ export function CacheTTLField({ field, message, ...props }) { } CacheTTLField.propTypes = propTypes; + +export default CacheTTLField; diff --git a/enterprise/frontend/src/metabase-enterprise/caching/components/CacheTTLField/CacheTTLField.unit.spec.js b/enterprise/frontend/src/metabase-enterprise/caching/components/CacheTTLField/CacheTTLField.unit.spec.js index 1c2bc1a09ac..206f0b1eb54 100644 --- a/enterprise/frontend/src/metabase-enterprise/caching/components/CacheTTLField/CacheTTLField.unit.spec.js +++ b/enterprise/frontend/src/metabase-enterprise/caching/components/CacheTTLField/CacheTTLField.unit.spec.js @@ -1,7 +1,7 @@ import React from "react"; import { render, screen } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; -import { CacheTTLField } from "./CacheTTLField"; +import CacheTTLField from "./CacheTTLField"; function setup({ name = "cache_ttl", message, value }) { const onChange = jest.fn(); diff --git a/enterprise/frontend/src/metabase-enterprise/caching/components/CacheTTLField/index.js b/enterprise/frontend/src/metabase-enterprise/caching/components/CacheTTLField/index.js index 35634c0a52f..fdb4536d31c 100644 --- a/enterprise/frontend/src/metabase-enterprise/caching/components/CacheTTLField/index.js +++ b/enterprise/frontend/src/metabase-enterprise/caching/components/CacheTTLField/index.js @@ -1 +1 @@ -export * from "./CacheTTLField"; +export { default } from "./CacheTTLField"; diff --git a/enterprise/frontend/src/metabase-enterprise/caching/components/DashboardCacheSection/DashboardCacheSection.tsx b/enterprise/frontend/src/metabase-enterprise/caching/components/DashboardCacheSection/DashboardCacheSection.tsx index 70c1606ae5b..07db2a6f5b8 100644 --- a/enterprise/frontend/src/metabase-enterprise/caching/components/DashboardCacheSection/DashboardCacheSection.tsx +++ b/enterprise/frontend/src/metabase-enterprise/caching/components/DashboardCacheSection/DashboardCacheSection.tsx @@ -1,15 +1,17 @@ import React from "react"; import { Dashboard } from "metabase-types/api"; -import { CacheSection } from "../CacheSection"; +import CacheSection from "../CacheSection"; interface DashboardCacheSectionProps { dashboard: Dashboard; onSave: (cache_ttl: number | null) => Promise<Dashboard>; } -export const DashboardCacheSection = ({ +const DashboardCacheSection = ({ dashboard, onSave, }: DashboardCacheSectionProps) => { return <CacheSection initialCacheTTL={dashboard.cache_ttl} onSave={onSave} />; }; + +export default DashboardCacheSection; diff --git a/enterprise/frontend/src/metabase-enterprise/caching/components/DashboardCacheSection/index.ts b/enterprise/frontend/src/metabase-enterprise/caching/components/DashboardCacheSection/index.ts index 19cf2e99322..856e7a25f65 100644 --- a/enterprise/frontend/src/metabase-enterprise/caching/components/DashboardCacheSection/index.ts +++ b/enterprise/frontend/src/metabase-enterprise/caching/components/DashboardCacheSection/index.ts @@ -1 +1 @@ -export * from "./DashboardCacheSection"; +export { default } from "./DashboardCacheSection"; diff --git a/enterprise/frontend/src/metabase-enterprise/caching/components/DatabaseCacheTTLField/DatabaseCacheTTLField.jsx b/enterprise/frontend/src/metabase-enterprise/caching/components/DatabaseCacheTTLField/DatabaseCacheTTLField.jsx index ae1574f47f4..883f1435f73 100644 --- a/enterprise/frontend/src/metabase-enterprise/caching/components/DatabaseCacheTTLField/DatabaseCacheTTLField.jsx +++ b/enterprise/frontend/src/metabase-enterprise/caching/components/DatabaseCacheTTLField/DatabaseCacheTTLField.jsx @@ -2,7 +2,7 @@ import React, { useCallback, useEffect, useState } from "react"; import PropTypes from "prop-types"; import { t } from "ttag"; import Select, { Option } from "metabase/core/components/Select"; -import { CacheTTLField } from "../CacheTTLField"; +import CacheTTLField from "../CacheTTLField"; import { CacheFieldContainer, FieldContainer, @@ -20,7 +20,7 @@ const propTypes = { field: PropTypes.object.isRequired, }; -export function DatabaseCacheTTLField({ field }) { +function DatabaseCacheTTLField({ field }) { const [mode, setMode] = useState( field.value > 0 ? MODE.CUSTOM : MODE.INSTANCE_DEFAULT, ); @@ -55,3 +55,5 @@ export function DatabaseCacheTTLField({ field }) { } DatabaseCacheTTLField.propTypes = propTypes; + +export default DatabaseCacheTTLField; diff --git a/enterprise/frontend/src/metabase-enterprise/caching/components/DatabaseCacheTTLField/DatabaseCacheTTLField.unit.spec.js b/enterprise/frontend/src/metabase-enterprise/caching/components/DatabaseCacheTTLField/DatabaseCacheTTLField.unit.spec.js index e4670251a82..11a4debeb19 100644 --- a/enterprise/frontend/src/metabase-enterprise/caching/components/DatabaseCacheTTLField/DatabaseCacheTTLField.unit.spec.js +++ b/enterprise/frontend/src/metabase-enterprise/caching/components/DatabaseCacheTTLField/DatabaseCacheTTLField.unit.spec.js @@ -1,7 +1,7 @@ import React from "react"; import { render, screen } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; -import { DatabaseCacheTTLField } from "./DatabaseCacheTTLField"; +import DatabaseCacheTTLField from "./DatabaseCacheTTLField"; function setup({ value = null } = {}) { const onChange = jest.fn(); diff --git a/enterprise/frontend/src/metabase-enterprise/caching/components/DatabaseCacheTTLField/index.js b/enterprise/frontend/src/metabase-enterprise/caching/components/DatabaseCacheTTLField/index.js index 0e1d95230ac..eedffdc43ac 100644 --- a/enterprise/frontend/src/metabase-enterprise/caching/components/DatabaseCacheTTLField/index.js +++ b/enterprise/frontend/src/metabase-enterprise/caching/components/DatabaseCacheTTLField/index.js @@ -1 +1 @@ -export * from "./DatabaseCacheTTLField"; +export { default } from "./DatabaseCacheTTLField"; diff --git a/enterprise/frontend/src/metabase-enterprise/caching/components/QuestionCacheSection/QuestionCacheSection.styled.tsx b/enterprise/frontend/src/metabase-enterprise/caching/components/QuestionCacheSection/QuestionCacheSection.styled.tsx new file mode 100644 index 00000000000..33380eb4733 --- /dev/null +++ b/enterprise/frontend/src/metabase-enterprise/caching/components/QuestionCacheSection/QuestionCacheSection.styled.tsx @@ -0,0 +1,8 @@ +import styled from "@emotion/styled"; +import { color } from "metabase/lib/colors"; + +export const QueryStartLabel = styled.div` + color: ${color("text-dark")}; + font-weight: bold; + margin-bottom: 0.5rem; +`; diff --git a/enterprise/frontend/src/metabase-enterprise/caching/components/QuestionCacheSection/QuestionCacheSection.tsx b/enterprise/frontend/src/metabase-enterprise/caching/components/QuestionCacheSection/QuestionCacheSection.tsx index 14651709222..e76a23dde78 100644 --- a/enterprise/frontend/src/metabase-enterprise/caching/components/QuestionCacheSection/QuestionCacheSection.tsx +++ b/enterprise/frontend/src/metabase-enterprise/caching/components/QuestionCacheSection/QuestionCacheSection.tsx @@ -1,15 +1,32 @@ import React from "react"; +import { t } from "ttag"; +import { getRelativeTime } from "metabase/lib/time"; import Question from "metabase-lib/lib/Question"; -import { CacheSection } from "../CacheSection"; +import CacheSection from "../CacheSection"; +import { QueryStartLabel } from "./QuestionCacheSection.styled"; -interface QuestionCacheSectionProps { +export interface QuestionCacheSectionProps { question: Question; onSave: (cache_ttl: number | null) => Promise<Question>; } -export const QuestionCacheSection = ({ +const QuestionCacheSection = ({ question, onSave, }: QuestionCacheSectionProps) => { - return <CacheSection initialCacheTTL={question.cacheTTL()} onSave={onSave} />; + const cacheTimestamp = question.lastQueryStart(); + const cacheRelativeTime = cacheTimestamp && getRelativeTime(cacheTimestamp); + + return ( + <div> + {cacheTimestamp && ( + <QueryStartLabel> + {t`Question last cached ${cacheRelativeTime}`} + </QueryStartLabel> + )} + <CacheSection initialCacheTTL={question.cacheTTL()} onSave={onSave} /> + </div> + ); }; + +export default QuestionCacheSection; diff --git a/enterprise/frontend/src/metabase-enterprise/caching/components/QuestionCacheSection/QuestionCacheSection.unit.spec.tsx b/enterprise/frontend/src/metabase-enterprise/caching/components/QuestionCacheSection/QuestionCacheSection.unit.spec.tsx new file mode 100644 index 00000000000..e49bc029b83 --- /dev/null +++ b/enterprise/frontend/src/metabase-enterprise/caching/components/QuestionCacheSection/QuestionCacheSection.unit.spec.tsx @@ -0,0 +1,41 @@ +import React from "react"; +import { render, screen } from "@testing-library/react"; +import Question from "metabase-lib/lib/Question"; +import { createMockCard } from "metabase-types/api/mocks"; +import QuestionCacheSection, { + QuestionCacheSectionProps, +} from "./QuestionCacheSection"; + +describe("QuestionCacheSection", () => { + beforeEach(() => { + jest.useFakeTimers(); + jest.setSystemTime(new Date(2020, 0, 10)); + }); + + afterEach(() => { + jest.useRealTimers(); + }); + + it("should show the time of the last cached query", () => { + const props = getProps({ + question: new Question( + createMockCard({ + last_query_start: "2020-01-05T00:00:00Z", + }), + ), + }); + + render(<QuestionCacheSection {...props} />); + + const cacheLabel = screen.getByText("Question last cached 5 days ago"); + expect(cacheLabel).toBeInTheDocument(); + }); +}); + +const getProps = ( + opts?: Partial<QuestionCacheSectionProps>, +): QuestionCacheSectionProps => ({ + question: new Question(createMockCard()), + onSave: jest.fn(), + ...opts, +}); diff --git a/enterprise/frontend/src/metabase-enterprise/caching/components/QuestionCacheSection/index.js b/enterprise/frontend/src/metabase-enterprise/caching/components/QuestionCacheSection/index.js deleted file mode 100644 index 4a9bc9fc8c5..00000000000 --- a/enterprise/frontend/src/metabase-enterprise/caching/components/QuestionCacheSection/index.js +++ /dev/null @@ -1 +0,0 @@ -export * from "./QuestionCacheSection"; diff --git a/enterprise/frontend/src/metabase-enterprise/caching/components/QuestionCacheSection/index.ts b/enterprise/frontend/src/metabase-enterprise/caching/components/QuestionCacheSection/index.ts new file mode 100644 index 00000000000..15d8fcdef47 --- /dev/null +++ b/enterprise/frontend/src/metabase-enterprise/caching/components/QuestionCacheSection/index.ts @@ -0,0 +1 @@ +export { default } from "./QuestionCacheSection"; diff --git a/enterprise/frontend/src/metabase-enterprise/caching/components/QuestionCacheTTLField/QuestionCacheTTLField.jsx b/enterprise/frontend/src/metabase-enterprise/caching/components/QuestionCacheTTLField/QuestionCacheTTLField.jsx index 51ab91308c9..65843b980eb 100644 --- a/enterprise/frontend/src/metabase-enterprise/caching/components/QuestionCacheTTLField/QuestionCacheTTLField.jsx +++ b/enterprise/frontend/src/metabase-enterprise/caching/components/QuestionCacheTTLField/QuestionCacheTTLField.jsx @@ -31,7 +31,7 @@ function getInitialMode(question, implicitCacheTTL) { return MODE.DEFAULT; } -export function QuestionCacheTTLField({ field, question, ...props }) { +function QuestionCacheTTLField({ field, question, ...props }) { const implicitCacheTTL = useMemo( () => getQuestionsImplicitCacheTTL(question), [question], @@ -73,3 +73,5 @@ export function QuestionCacheTTLField({ field, question, ...props }) { } QuestionCacheTTLField.propTypes = propTypes; + +export default QuestionCacheTTLField; diff --git a/enterprise/frontend/src/metabase-enterprise/caching/components/QuestionCacheTTLField/QuestionCacheTTLField.styled.jsx b/enterprise/frontend/src/metabase-enterprise/caching/components/QuestionCacheTTLField/QuestionCacheTTLField.styled.jsx index b10280682c6..d45fe16ef15 100644 --- a/enterprise/frontend/src/metabase-enterprise/caching/components/QuestionCacheTTLField/QuestionCacheTTLField.styled.jsx +++ b/enterprise/frontend/src/metabase-enterprise/caching/components/QuestionCacheTTLField/QuestionCacheTTLField.styled.jsx @@ -3,7 +3,7 @@ import { t } from "ttag"; import styled from "@emotion/styled"; import { space } from "metabase/styled-components/theme"; import Radio from "metabase/core/components/Radio"; -import { CacheTTLField } from "../CacheTTLField"; +import CacheTTLField from "../CacheTTLField"; export function CacheTTLInput(props) { return <CacheTTLField {...props} message={t`Cache results for`} />; diff --git a/enterprise/frontend/src/metabase-enterprise/caching/components/QuestionCacheTTLField/QuestionCacheTTLField.unit.spec.js b/enterprise/frontend/src/metabase-enterprise/caching/components/QuestionCacheTTLField/QuestionCacheTTLField.unit.spec.js index c893c54c128..4ef6867209d 100644 --- a/enterprise/frontend/src/metabase-enterprise/caching/components/QuestionCacheTTLField/QuestionCacheTTLField.unit.spec.js +++ b/enterprise/frontend/src/metabase-enterprise/caching/components/QuestionCacheTTLField/QuestionCacheTTLField.unit.spec.js @@ -3,7 +3,7 @@ import { render, screen } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { msToMinutes, msToHours } from "metabase/lib/time"; import MetabaseSettings from "metabase/lib/settings"; -import { QuestionCacheTTLField } from "./QuestionCacheTTLField"; +import QuestionCacheTTLField from "./QuestionCacheTTLField"; const TEN_MINUTES = 10 * 60 * 1000; diff --git a/enterprise/frontend/src/metabase-enterprise/caching/components/QuestionCacheTTLField/index.js b/enterprise/frontend/src/metabase-enterprise/caching/components/QuestionCacheTTLField/index.js index bbc522fae52..ce8a3adf3bb 100644 --- a/enterprise/frontend/src/metabase-enterprise/caching/components/QuestionCacheTTLField/index.js +++ b/enterprise/frontend/src/metabase-enterprise/caching/components/QuestionCacheTTLField/index.js @@ -1 +1 @@ -export * from "./QuestionCacheTTLField"; +export { default } from "./QuestionCacheTTLField"; diff --git a/enterprise/frontend/src/metabase-enterprise/caching/index.js b/enterprise/frontend/src/metabase-enterprise/caching/index.js index d3b4b61fedf..a1c91a17b24 100644 --- a/enterprise/frontend/src/metabase-enterprise/caching/index.js +++ b/enterprise/frontend/src/metabase-enterprise/caching/index.js @@ -3,11 +3,11 @@ import { t, jt } from "ttag"; import { hasPremiumFeature } from "metabase-enterprise/settings"; import { PLUGIN_CACHING, PLUGIN_FORM_WIDGETS } from "metabase/plugins"; import Link from "metabase/core/components/Link"; -import { CacheTTLField } from "./components/CacheTTLField"; -import { DatabaseCacheTTLField } from "./components/DatabaseCacheTTLField"; -import { QuestionCacheTTLField } from "./components/QuestionCacheTTLField"; -import { QuestionCacheSection } from "./components/QuestionCacheSection"; -import { DashboardCacheSection } from "./components/DashboardCacheSection"; +import CacheTTLField from "./components/CacheTTLField"; +import DatabaseCacheTTLField from "./components/DatabaseCacheTTLField"; +import QuestionCacheTTLField from "./components/QuestionCacheTTLField"; +import QuestionCacheSection from "./components/QuestionCacheSection"; +import DashboardCacheSection from "./components/DashboardCacheSection"; import { getQuestionsImplicitCacheTTL, diff --git a/frontend/src/metabase-lib/lib/Question.ts b/frontend/src/metabase-lib/lib/Question.ts index 6b2b0a9efd7..19390191428 100644 --- a/frontend/src/metabase-lib/lib/Question.ts +++ b/frontend/src/metabase-lib/lib/Question.ts @@ -879,6 +879,10 @@ class QuestionInner { return this._card && this._card["last-edit-info"]; } + lastQueryStart() { + return this._card?.last_query_start; + } + isSaved(): boolean { return !!this.id(); } diff --git a/frontend/src/metabase-types/api/card.ts b/frontend/src/metabase-types/api/card.ts index 1b910ae11a2..250d77fab28 100644 --- a/frontend/src/metabase-types/api/card.ts +++ b/frontend/src/metabase-types/api/card.ts @@ -1,3 +1,21 @@ +import { DatasetQuery } from "./query"; + +export interface Card extends UnsavedCard { + id: CardId; + name: string; + description: string | null; + dataset: boolean; + can_write: boolean; + cache_ttl: number | null; + last_query_start: string | null; +} + +export interface UnsavedCard { + display: string; + dataset_query: DatasetQuery; + visualization_settings: VisualizationSettings; +} + export type VisualizationSettings = { [key: string]: any; }; @@ -9,4 +27,5 @@ export interface ModerationReview { most_recent: boolean; } +export type CardId = number; export type ModerationReviewStatus = "verified"; diff --git a/frontend/src/metabase-types/api/foreignKey.ts b/frontend/src/metabase-types/api/foreign-key.ts similarity index 100% rename from frontend/src/metabase-types/api/foreignKey.ts rename to frontend/src/metabase-types/api/foreign-key.ts diff --git a/frontend/src/metabase-types/api/index.ts b/frontend/src/metabase-types/api/index.ts index 8364d41e44f..32fe209bd10 100644 --- a/frontend/src/metabase-types/api/index.ts +++ b/frontend/src/metabase-types/api/index.ts @@ -5,16 +5,17 @@ export * from "./card"; export * from "./collection"; export * from "./dashboard"; export * from "./database"; -export * from "./table"; +export * from "./dataset"; export * from "./field"; -export * from "./timeline"; -export * from "./settings"; -export * from "./slack"; -export * from "./user"; +export * from "./foreign-key"; export * from "./group"; -export * from "./permissions"; -export * from "./question"; -export * from "./dataset"; export * from "./models"; export * from "./notifications"; +export * from "./permissions"; +export * from "./query"; export * from "./revision"; +export * from "./settings"; +export * from "./slack"; +export * from "./table"; +export * from "./timeline"; +export * from "./user"; diff --git a/frontend/src/metabase-types/api/mocks/card.ts b/frontend/src/metabase-types/api/mocks/card.ts index deed6e5220a..41bdb3a80e3 100644 --- a/frontend/src/metabase-types/api/mocks/card.ts +++ b/frontend/src/metabase-types/api/mocks/card.ts @@ -1,4 +1,39 @@ -import { ModerationReview } from "metabase-types/api"; +import { + ModerationReview, + Card, + UnsavedCard, + VisualizationSettings, +} from "metabase-types/api"; +import { createMockStructuredDatasetQuery } from "./query"; + +export const createMockCard = (opts?: Partial<Card>): Card => ({ + id: 1, + name: "Question", + description: null, + display: "table", + dataset_query: createMockStructuredDatasetQuery(), + visualization_settings: createMockVisualizationSettings(), + dataset: false, + can_write: false, + cache_ttl: null, + last_query_start: null, + ...opts, +}); + +export const createMockUnsavedCard = ( + opts?: Partial<UnsavedCard>, +): UnsavedCard => ({ + display: "table", + dataset_query: createMockStructuredDatasetQuery(), + visualization_settings: createMockVisualizationSettings(), + ...opts, +}); + +export const createMockVisualizationSettings = ( + opts?: Partial<VisualizationSettings>, +): VisualizationSettings => ({ + ...opts, +}); export const createMockModerationReview = ( opts?: Partial<ModerationReview>, diff --git a/frontend/src/metabase-types/api/mocks/index.ts b/frontend/src/metabase-types/api/mocks/index.ts index da5f4b52d92..8aabfb3f245 100644 --- a/frontend/src/metabase-types/api/mocks/index.ts +++ b/frontend/src/metabase-types/api/mocks/index.ts @@ -6,6 +6,7 @@ export * from "./dashboard"; export * from "./database"; export * from "./dataset"; export * from "./models"; +export * from "./query"; export * from "./table"; export * from "./timeline"; export * from "./settings"; diff --git a/frontend/src/metabase-types/api/mocks/query.ts b/frontend/src/metabase-types/api/mocks/query.ts new file mode 100644 index 00000000000..0ae85cf9d09 --- /dev/null +++ b/frontend/src/metabase-types/api/mocks/query.ts @@ -0,0 +1,37 @@ +import { + NativeDatasetQuery, + NativeQuery, + StructuredDatasetQuery, + StructuredQuery, +} from "metabase-types/api"; + +export const createMockStructuredQuery = ( + opts?: Partial<StructuredQuery>, +): StructuredQuery => ({ + ...opts, +}); + +export const createMockNativeQuery = ( + opts?: Partial<NativeQuery>, +): NativeQuery => ({ + query: "SELECT 1", + ...opts, +}); + +export const createMockStructuredDatasetQuery = ( + opts?: Partial<StructuredDatasetQuery>, +): StructuredDatasetQuery => ({ + type: "query", + database: 1, + query: createMockStructuredQuery(), + ...opts, +}); + +export const createMockNativeDatasetQuery = ( + opts?: Partial<NativeDatasetQuery>, +): NativeDatasetQuery => ({ + type: "native", + database: 1, + query: createMockNativeQuery(), + ...opts, +}); diff --git a/frontend/src/metabase-types/api/query.ts b/frontend/src/metabase-types/api/query.ts new file mode 100644 index 00000000000..a8a490bee16 --- /dev/null +++ b/frontend/src/metabase-types/api/query.ts @@ -0,0 +1,24 @@ +import { DatabaseId } from "./database"; +import { TableId } from "./table"; + +export interface StructuredQuery { + "source-table"?: TableId; +} + +export interface NativeQuery { + query: string; +} + +export interface StructuredDatasetQuery { + type: "query"; + database: DatabaseId; + query: StructuredQuery; +} + +export interface NativeDatasetQuery { + type: "native"; + database: DatabaseId; + query: NativeQuery; +} + +export type DatasetQuery = StructuredDatasetQuery | NativeDatasetQuery; diff --git a/frontend/src/metabase-types/api/question.ts b/frontend/src/metabase-types/api/question.ts deleted file mode 100644 index 464b6cddaa5..00000000000 --- a/frontend/src/metabase-types/api/question.ts +++ /dev/null @@ -1 +0,0 @@ -export type CardId = number; diff --git a/frontend/src/metabase-types/api/table.ts b/frontend/src/metabase-types/api/table.ts index 003007194f9..91e63785d68 100644 --- a/frontend/src/metabase-types/api/table.ts +++ b/frontend/src/metabase-types/api/table.ts @@ -1,7 +1,9 @@ -import { ForeignKey } from "../api/foreignKey"; +import { ForeignKey } from "./foreign-key"; import { Database } from "./database"; import { Field } from "./field"; +export type TableId = number | string; // can be string for virtual questions (e.g. "card__17") + export type VisibilityType = | null | "details-only" @@ -13,7 +15,7 @@ export type VisibilityType = | "cruft"; export interface Table { - id: number | string; // can be string for virtual questions (e.g. "card__17") + id: TableId; db_id: number; db?: Database; name: string; diff --git a/frontend/src/metabase-types/types/Table.ts b/frontend/src/metabase-types/types/Table.ts index 4597c4ffbbc..6a63bca3379 100644 --- a/frontend/src/metabase-types/types/Table.ts +++ b/frontend/src/metabase-types/types/Table.ts @@ -4,7 +4,7 @@ import { Field } from "./Field"; import { Segment } from "./Segment"; import { Metric } from "./Metric"; import { DatabaseId } from "./Database"; -import { ForeignKey } from "../api/foreignKey"; +import { ForeignKey } from "../api/foreign-key"; export type TableId = number; export type SchemaName = string; diff --git a/frontend/src/metabase/visualizations/components/ObjectDetail/ObjectDetail.tsx b/frontend/src/metabase/visualizations/components/ObjectDetail/ObjectDetail.tsx index d68c5f60c91..8202d0ec56f 100644 --- a/frontend/src/metabase/visualizations/components/ObjectDetail/ObjectDetail.tsx +++ b/frontend/src/metabase/visualizations/components/ObjectDetail/ObjectDetail.tsx @@ -6,7 +6,7 @@ import Question from "metabase-lib/lib/Question"; import { isPK } from "metabase/lib/schema_metadata"; import { Table } from "metabase-types/types/Table"; -import { ForeignKey } from "metabase-types/api/foreignKey"; +import { ForeignKey } from "metabase-types/api"; import { DatasetData } from "metabase-types/types/Dataset"; import { ObjectId, OnVisualizationClickType } from "./types"; diff --git a/frontend/src/metabase/visualizations/components/ObjectDetail/ObjectRelationships.tsx b/frontend/src/metabase/visualizations/components/ObjectDetail/ObjectRelationships.tsx index 1a6c20c4db7..3aca8c44144 100644 --- a/frontend/src/metabase/visualizations/components/ObjectDetail/ObjectRelationships.tsx +++ b/frontend/src/metabase/visualizations/components/ObjectDetail/ObjectRelationships.tsx @@ -3,7 +3,7 @@ import { t, jt } from "ttag"; import cx from "classnames"; import { inflect } from "inflection"; -import { ForeignKey } from "metabase-types/api/foreignKey"; +import { ForeignKey } from "metabase-types/api"; import IconBorder from "metabase/components/IconBorder"; import LoadingSpinner from "metabase/components/LoadingSpinner"; -- GitLab