diff --git a/frontend/src/metabase-lib/lib/metadata/Field.ts b/frontend/src/metabase-lib/lib/metadata/Field.ts index 3394eb1f694bed198ea28f6d9ac9e45ebf3db6ab..74707a42e5fb5bb84c7ecfac259163451b03b913 100644 --- a/frontend/src/metabase-lib/lib/metadata/Field.ts +++ b/frontend/src/metabase-lib/lib/metadata/Field.ts @@ -32,11 +32,14 @@ import { getIconForField, getFilterOperators, } from "metabase/lib/schema_metadata"; -import { FieldFingerprint } from "metabase-types/api/field"; -import { Field as FieldRef } from "metabase-types/types/Query"; import { FieldDimension } from "../Dimension"; -import Table from "./Table"; import Base from "./Base"; +import type { FieldFingerprint } from "metabase-types/api/field"; +import type { Field as FieldRef } from "metabase-types/types/Query"; +import type StructuredQuery from "metabase-lib/lib/queries/StructuredQuery"; +import type NativeQuery from "metabase-lib/lib/queries/NativeQuery"; +import type Table from "./Table"; +import type Metadata from "./Metadata"; export const LONG_TEXT_MIN = 80; @@ -57,9 +60,14 @@ class FieldInner extends Base { fingerprint?: FieldFingerprint; base_type: string | null; table?: Table; + table_id?: Table["id"]; target?: Field; has_field_values?: "list" | "search" | "none"; values: any[]; + metadata?: Metadata; + + // added when creating "virtual fields" that are associated with a given query + query?: StructuredQuery | NativeQuery; getId() { if (Array.isArray(this.id)) { diff --git a/frontend/src/metabase-lib/lib/metadata/Metadata.ts b/frontend/src/metabase-lib/lib/metadata/Metadata.ts index 8d3d14e63d77e0cb433caacc8f0011656416efd4..ded4bbafb41047abd0f12aa7f5adee33b2e54f80 100644 --- a/frontend/src/metabase-lib/lib/metadata/Metadata.ts +++ b/frontend/src/metabase-lib/lib/metadata/Metadata.ts @@ -2,8 +2,11 @@ // @ts-nocheck import _ from "underscore"; import Base from "./Base"; -import Question from "../Question"; -import Database from "./Database"; +import type Question from "../Question"; +import type Database from "./Database"; +import type Table from "./Table"; +import type Schema from "./Schema"; + /** * @typedef { import("./metadata").DatabaseId } DatabaseId * @typedef { import("./metadata").SchemaId } SchemaId @@ -18,7 +21,9 @@ import Database from "./Database"; */ export default class Metadata extends Base { - databases: Database[]; + databases: { [databaseId: string]: Database }; + questions: { [cardId: string]: Question }; + tables: { [tableId: string]: Table }; /** * @deprecated this won't be sorted or filtered in a meaningful way @@ -76,7 +81,7 @@ export default class Metadata extends Base { * @param {DatabaseId} databaseId * @returns {?Database} */ - database(databaseId) { + database(databaseId): Database | null { return (databaseId != null && this.databases[databaseId]) || null; } @@ -84,7 +89,7 @@ export default class Metadata extends Base { * @param {SchemaId} schemaId * @returns {Schema} */ - schema(schemaId) { + schema(schemaId): Schema | null { return (schemaId != null && this.schemas[schemaId]) || null; } @@ -93,7 +98,7 @@ export default class Metadata extends Base { * @param {TableId} tableId * @returns {?Table} */ - table(tableId) { + table(tableId): Table | null { return (tableId != null && this.tables[tableId]) || null; } @@ -101,11 +106,11 @@ export default class Metadata extends Base { * @param {FieldId} fieldId * @returns {?Field} */ - field(fieldId) { + field(fieldId): Field | null { return (fieldId != null && this.fields[fieldId]) || null; } - question(cardId) { + question(cardId): Question | null { return (cardId != null && this.questions[cardId]) || null; } diff --git a/frontend/src/metabase-lib/lib/metadata/Schema.ts b/frontend/src/metabase-lib/lib/metadata/Schema.ts index 4426e151adfe7b778998faf931816c4fd6492db2..67d62c3901ae075e6d0b7ed4d2e74db86a361000 100644 --- a/frontend/src/metabase-lib/lib/metadata/Schema.ts +++ b/frontend/src/metabase-lib/lib/metadata/Schema.ts @@ -2,8 +2,8 @@ // @ts-nocheck import Base from "./Base"; import { titleize, humanize } from "metabase/lib/formatting"; -import Database from "./Database"; -import Table from "./Table"; +import type Database from "./Database"; +import type Table from "./Table"; /** * Wrapper class for a {@link Database} schema. Contains {@link Table}s. */ diff --git a/frontend/src/metabase-lib/lib/metadata/Table.ts b/frontend/src/metabase-lib/lib/metadata/Table.ts index c2b3ef5b2d57852d58d9d46d8cfb478d0be51672..ee924b2cdb68dfd6acadacbce74f8b4816dd57fa 100644 --- a/frontend/src/metabase-lib/lib/metadata/Table.ts +++ b/frontend/src/metabase-lib/lib/metadata/Table.ts @@ -2,13 +2,15 @@ // @ts-nocheck // NOTE: this needs to be imported first due to some cyclical dependency nonsense import Question from "../Question"; -import Schema from "./Schema"; import Base from "./Base"; import { singularize } from "metabase/lib/formatting"; import { getAggregationOperators } from "metabase/lib/schema_metadata"; import { createLookupByProperty, memoizeClass } from "metabase-lib/lib/utils"; -import Field from "./Field"; - +import type Metadata from "./Metadata"; +import type Schema from "./Schema"; +import type Field from "./Field"; +import type Database from "./Database"; +import type { TableId } from "metabase-types/types/Table"; /** * @typedef { import("./metadata").SchemaName } SchemaName * @typedef { import("./metadata").EntityType } EntityType @@ -18,7 +20,8 @@ import Field from "./Field"; /** This is the primary way people interact with tables */ class TableInner extends Base { - id: number; + id: TableId; + name: string; description?: string; fks?: any[]; schema?: Schema; @@ -26,6 +29,8 @@ class TableInner extends Base { schema_name: string; db_id: number; fields: Field[]; + metadata?: Metadata; + db?: Database | undefined | null; hasSchema() { return (this.schema_name && this.db && this.db.schemas.length > 1) || false; diff --git a/frontend/src/metabase-types/api/table.ts b/frontend/src/metabase-types/api/table.ts index 91e63785d68f8d3d748fb19e7a82ee0f148f04ab..790afe8bdb616a3a097582c90393aa93a1c61987 100644 --- a/frontend/src/metabase-types/api/table.ts +++ b/frontend/src/metabase-types/api/table.ts @@ -2,7 +2,9 @@ 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 ConcreteTableId = number; +export type VirtualTableId = string; // e.g. "card__17" where 17 is a card id +export type TableId = ConcreteTableId | VirtualTableId; export type VisibilityType = | null diff --git a/frontend/src/metabase-types/types/Table.ts b/frontend/src/metabase-types/types/Table.ts index 1819378bd55349d57224eefc255eda5d15d7c426..59f0bd8395c0756b88ffa5a8e4abc2f7aa2c104a 100644 --- a/frontend/src/metabase-types/types/Table.ts +++ b/frontend/src/metabase-types/types/Table.ts @@ -10,8 +10,10 @@ import { Segment } from "./Segment"; import { Metric } from "./Metric"; import { DatabaseId } from "./Database"; import { ForeignKey } from "../api/foreign-key"; +import { TableId as _TableId } from "metabase-types/api"; + +export type TableId = _TableId; -export type TableId = number; export type SchemaName = string; type TableVisibilityType = string; // FIXME diff --git a/frontend/src/metabase/admin/permissions/selectors/confirmations.ts b/frontend/src/metabase/admin/permissions/selectors/confirmations.ts index a1d5e2621c79bce7a4c1a05146eb416e359de606..f55405ad8ffbae8b3d5aef30ca18501930948de3 100644 --- a/frontend/src/metabase/admin/permissions/selectors/confirmations.ts +++ b/frontend/src/metabase/admin/permissions/selectors/confirmations.ts @@ -6,9 +6,13 @@ import { getNativePermission, getSchemasPermission, } from "metabase/admin/permissions/utils/graph"; -import { Group, GroupsPermissions } from "metabase-types/api"; -import { EntityId } from "../types"; -import Database from "metabase-lib/lib/metadata/Database"; +import type { + Group, + GroupsPermissions, + ConcreteTableId, +} from "metabase-types/api"; +import type { EntityId } from "../types"; +import type Database from "metabase-lib/lib/metadata/Database"; export const getDefaultGroupHasHigherAccessText = (defaultGroup: Group) => t`The "${defaultGroup.name}" group has a higher level of access than this, which will override this setting. You should limit or revoke the "${defaultGroup.name}" group's access to this item.`; @@ -139,7 +143,7 @@ export function getRevokingAccessToAllTablesWarningModal( const allTableEntityIds = database.tables.map(table => ({ databaseId: table.db_id, schemaName: table.schema_name || "", - tableId: table.id, + tableId: table.id as ConcreteTableId, })); // Show the warning only if user tries to revoke access to the very last table of all schemas diff --git a/frontend/src/metabase/admin/permissions/selectors/data-permissions/breadcrumbs.ts b/frontend/src/metabase/admin/permissions/selectors/data-permissions/breadcrumbs.ts index 903713c778d4d84bfeb702c71e8399fadf708ebc..0f69c67425f5e3f66532b34ffb5099a568ed012c 100644 --- a/frontend/src/metabase/admin/permissions/selectors/data-permissions/breadcrumbs.ts +++ b/frontend/src/metabase/admin/permissions/selectors/data-permissions/breadcrumbs.ts @@ -1,4 +1,6 @@ -import Metadata from "metabase-lib/lib/metadata/Metadata"; +import type Metadata from "metabase-lib/lib/metadata/Metadata"; +import type Schema from "metabase-lib/lib/metadata/Schema"; +import type Table from "metabase-lib/lib/metadata/Table"; import { Group } from "metabase-types/api"; import _ from "underscore"; @@ -46,7 +48,7 @@ export const getDatabasesEditorBreadcrumbs = ( return [groupItem, databaseItem]; } - const schema = database.schema(schemaName); + const schema = database.schema(schemaName) as Schema; const schemaItem = { id: schema.name, text: schema.name, @@ -79,7 +81,7 @@ export const getGroupsDataEditorBreadcrumbs = ( return [databaseItem]; } - const schema = database.schema(schemaName); + const schema = database.schema(schemaName) as Schema; const schemaItem = { id: schema.id, text: schema.name, @@ -92,7 +94,7 @@ export const getGroupsDataEditorBreadcrumbs = ( return [databaseItem, hasMultipleSchemas && schemaItem].filter(Boolean); } - const table = metadata.table(tableId); + const table = metadata.table(tableId) as Table; const tableItem = { id: table.id, diff --git a/frontend/src/metabase/admin/permissions/selectors/data-permissions/permission-editor.ts b/frontend/src/metabase/admin/permissions/selectors/data-permissions/permission-editor.ts index ddf3bc6dbc428f2e9d2283fefaeabf58c0729014..c57955aaee4c78ae0fd24dee4605a7348125a3a9 100644 --- a/frontend/src/metabase/admin/permissions/selectors/data-permissions/permission-editor.ts +++ b/frontend/src/metabase/admin/permissions/selectors/data-permissions/permission-editor.ts @@ -164,7 +164,7 @@ export const getDatabasesPermissionEditor = createSelector( if (database && (schemaName != null || hasSingleSchema)) { const schema: Schema = hasSingleSchema ? database.getSchemas()[0] - : database.schema(schemaName); + : (database.schema(schemaName) as Schema); entities = schema .getTables() diff --git a/frontend/src/metabase/admin/permissions/utils/data-entity-id.ts b/frontend/src/metabase/admin/permissions/utils/data-entity-id.ts index 423c403114bddf99f805fb23d4c0fd4eb95eefc0..240bc6cb6c0ecbbb9a71c84359e613652218e23a 100644 --- a/frontend/src/metabase/admin/permissions/utils/data-entity-id.ts +++ b/frontend/src/metabase/admin/permissions/utils/data-entity-id.ts @@ -1,7 +1,8 @@ -import Database from "metabase-lib/lib/metadata/Database"; -import Schema from "metabase-lib/lib/metadata/Schema"; -import Table from "metabase-lib/lib/metadata/Table"; -import { EntityId, PermissionSubject } from "../types"; +import type Database from "metabase-lib/lib/metadata/Database"; +import type Schema from "metabase-lib/lib/metadata/Schema"; +import type Table from "metabase-lib/lib/metadata/Table"; +import type { EntityId, PermissionSubject } from "../types"; +import type { ConcreteTableId } from "metabase-types/api"; export const getDatabaseEntityId = (databaseEntity: Database) => ({ databaseId: databaseEntity.id, @@ -15,7 +16,7 @@ export const getSchemaEntityId = (schemaEntity: Schema) => ({ export const getTableEntityId = (tableEntity: Table) => ({ databaseId: tableEntity.db_id, schemaName: tableEntity.schema_name, - tableId: tableEntity.id, + tableId: tableEntity.id as ConcreteTableId, }); export const isTableEntityId = (entityId: Partial<EntityId>) => diff --git a/frontend/src/metabase/admin/permissions/utils/graph/data-permissions.ts b/frontend/src/metabase/admin/permissions/utils/graph/data-permissions.ts index 7914b7e87e3332860a10f2efd116adb3ea95a73a..e5f37bfb0fe94ebcc216fd9efeb014dda5dd145e 100644 --- a/frontend/src/metabase/admin/permissions/utils/graph/data-permissions.ts +++ b/frontend/src/metabase/admin/permissions/utils/graph/data-permissions.ts @@ -5,10 +5,10 @@ import { PLUGIN_ADMIN_PERMISSIONS_TABLE_FIELDS_PERMISSION_VALUE, PLUGIN_ADVANCED_PERMISSIONS, } from "metabase/plugins"; -import { GroupsPermissions } from "metabase-types/api"; -import Database from "metabase-lib/lib/metadata/Database"; -import Table from "metabase-lib/lib/metadata/Table"; -import { +import type { GroupsPermissions, ConcreteTableId } from "metabase-types/api"; +import type Database from "metabase-lib/lib/metadata/Database"; +import type Table from "metabase-lib/lib/metadata/Table"; +import type { DatabaseEntityId, DataPermission, EntityId, @@ -202,7 +202,7 @@ export function downgradeNativePermissionsIfNeeded( const metadataTableToTableEntityId = (table: Table) => ({ databaseId: table.db_id, schemaName: table.schema_name || "", - tableId: table.id, + tableId: table.id as ConcreteTableId, }); // TODO Atte Keinänen 6/24/17 See if this method could be simplified diff --git a/frontend/src/metabase/admin/permissions/utils/graph/permissions-diff.ts b/frontend/src/metabase/admin/permissions/utils/graph/permissions-diff.ts index 0d18ccd3b2c61c05780f29c3c9c4bc164f935181..fb6ab6a738d792cfbc743b1376d303f4d2a17c17 100644 --- a/frontend/src/metabase/admin/permissions/utils/graph/permissions-diff.ts +++ b/frontend/src/metabase/admin/permissions/utils/graph/permissions-diff.ts @@ -1,7 +1,11 @@ import _ from "underscore"; -import { Group, GroupsPermissions } from "metabase-types/api"; -import Database from "metabase-lib/lib/metadata/Database"; +import type { + Group, + GroupsPermissions, + ConcreteTableId, +} from "metabase-types/api"; +import type Database from "metabase-lib/lib/metadata/Database"; import { getFieldsPermission, getNativePermission, @@ -46,7 +50,7 @@ function diffDatabasePermissions( { databaseId: database.id, schemaName: table.schema_name || "", - tableId: table.id, + tableId: table.id as ConcreteTableId, }, "data", ); @@ -56,7 +60,7 @@ function diffDatabasePermissions( { databaseId: database.id, schemaName: table.schema_name || "", - tableId: table.id, + tableId: table.id as ConcreteTableId, }, "data", ); diff --git a/frontend/src/metabase/components/MetadataInfo/TableInfo/TableInfo.tsx b/frontend/src/metabase/components/MetadataInfo/TableInfo/TableInfo.tsx index bfb09b515257452095535083bcabc35608cc15da..5bb61f47cb6f5f335d238ec0fbf4876c21e8b2b7 100644 --- a/frontend/src/metabase/components/MetadataInfo/TableInfo/TableInfo.tsx +++ b/frontend/src/metabase/components/MetadataInfo/TableInfo/TableInfo.tsx @@ -20,7 +20,7 @@ import ConnectedTables from "./ConnectedTables"; type OwnProps = { className?: string; - tableId: number; + tableId: Table["id"]; onConnectedTableClick?: (table: Table) => void; }; @@ -33,8 +33,8 @@ const mapStateToProps = (state: any, props: OwnProps): { table?: Table } => { }; const mapDispatchToProps: { - fetchForeignKeys: (args: { id: number }) => Promise<any>; - fetchMetadata: (args: { id: number }) => Promise<any>; + fetchForeignKeys: (args: { id: Table["id"] }) => Promise<any>; + fetchMetadata: (args: { id: Table["id"] }) => Promise<any>; } = { fetchForeignKeys: Tables.actions.fetchForeignKeys, fetchMetadata: Tables.actions.fetchMetadata, diff --git a/frontend/src/metabase/components/MetadataInfo/TableInfo/TableInfo.unit.spec.tsx b/frontend/src/metabase/components/MetadataInfo/TableInfo/TableInfo.unit.spec.tsx index 8f448a882a82a67bfbfbdf8c00dae6e356aac532..da8a66ab179e467fe2c0e3cdd12a39145f836d99 100644 --- a/frontend/src/metabase/components/MetadataInfo/TableInfo/TableInfo.unit.spec.tsx +++ b/frontend/src/metabase/components/MetadataInfo/TableInfo/TableInfo.unit.spec.tsx @@ -30,7 +30,7 @@ const tableWithoutDescription = new Table({ const fetchForeignKeys = jest.fn(); const fetchMetadata = jest.fn(); -function setup({ id, table }: { table: Table | undefined; id: number }) { +function setup({ id, table }: { table: Table | undefined; id: Table["id"] }) { fetchForeignKeys.mockReset(); fetchMetadata.mockReset(); diff --git a/frontend/src/metabase/visualizations/components/ObjectDetail/ObjectDetail.tsx b/frontend/src/metabase/visualizations/components/ObjectDetail/ObjectDetail.tsx index aa02c3d4bc0af4624e452ae101a3d94788b69e17..9492d27ca480c40192d7def9180371e6b5da0d72 100644 --- a/frontend/src/metabase/visualizations/components/ObjectDetail/ObjectDetail.tsx +++ b/frontend/src/metabase/visualizations/components/ObjectDetail/ObjectDetail.tsx @@ -7,7 +7,7 @@ import { isPK } from "metabase/lib/schema_metadata"; import Table from "metabase-lib/lib/metadata/Table"; import { State } from "metabase-types/store"; -import { ForeignKey } from "metabase-types/api"; +import type { ForeignKey, ConcreteTableId } from "metabase-types/api"; import { DatasetData } from "metabase-types/types/Dataset"; import { ObjectId, OnVisualizationClickType } from "./types"; @@ -35,6 +35,7 @@ import { getCanZoomNextRow, } from "metabase/query_builder/selectors"; import { columnSettings } from "metabase/visualizations/lib/settings/column"; +import { isVirtualCardId } from "metabase/lib/saved-questions"; import { getObjectName, @@ -159,8 +160,8 @@ export function ObjectDetailFn({ return; } - if (table && table.fks == null) { - fetchTableFks(table.id); + if (table && table.fks == null && !isVirtualCardId(table.id)) { + fetchTableFks(table.id as ConcreteTableId); } // load up FK references if (tableForeignKeys) {