diff --git a/frontend/src/metabase/selectors/metadata.js b/frontend/src/metabase/selectors/metadata.js index 71853d8aaecf688de77cc16cdcc21fdd44aeee23..44e9b359f8b29ee5f09ba35d8765679add1ba5fb 100644 --- a/frontend/src/metabase/selectors/metadata.js +++ b/frontend/src/metabase/selectors/metadata.js @@ -68,6 +68,13 @@ export const getShallowFields = getNormalizedFields; export const getShallowMetrics = getNormalizedMetrics; export const getShallowSegments = getNormalizedSegments; +export const instantiateDatabase = obj => new Database(obj); +export const instantiateSchema = obj => new Schema(obj); +export const instantiateTable = obj => new Table(obj); +export const instantiateField = obj => new Field(obj); +export const instantiateSegment = obj => new Segment(obj); +export const instantiateMetric = obj => new Metric(obj); + // fully connected graph of all databases, tables, fields, segments, and metrics // TODO: do this lazily using ES6 Proxies export const getMetadata = createSelector( @@ -81,12 +88,12 @@ export const getMetadata = createSelector( ], (databases, schemas, tables, fields, segments, metrics): Metadata => { const meta = new Metadata(); - meta.databases = copyObjects(meta, databases, Database); - meta.schemas = copyObjects(meta, schemas, Schema); - meta.tables = copyObjects(meta, tables, Table); - meta.fields = copyObjects(meta, fields, Field); - meta.segments = copyObjects(meta, segments, Segment); - meta.metrics = copyObjects(meta, metrics, Metric); + meta.databases = copyObjects(meta, databases, instantiateDatabase); + meta.schemas = copyObjects(meta, schemas, instantiateSchema); + meta.tables = copyObjects(meta, tables, instantiateTable); + meta.fields = copyObjects(meta, fields, instantiateField); + meta.segments = copyObjects(meta, segments, instantiateSegment); + meta.metrics = copyObjects(meta, metrics, instantiateMetric); // database hydrateList(meta.databases, "tables", meta.tables); @@ -260,11 +267,11 @@ export const makeGetMergedParameterFieldValues = () => { // UTILS: // clone each object in the provided mapping of objects -export function copyObjects(metadata, objects, Klass) { +export function copyObjects(metadata, objects, instantiate) { const copies = {}; for (const object of Object.values(objects)) { if (object && object.id != null) { - copies[object.id] = new Klass(object); + copies[object.id] = instantiate(object); copies[object.id].metadata = metadata; } else { console.warn("Missing id:", object); diff --git a/frontend/test/metabase/selectors/metadata.unit.spec.js b/frontend/test/metabase/selectors/metadata.unit.spec.js index 79d7cb2583f60b8f4a4dbbbb5426be650bc16031..35bb83d6b30a4aea942b1507b27b623287f0b47a 100644 --- a/frontend/test/metabase/selectors/metadata.unit.spec.js +++ b/frontend/test/metabase/selectors/metadata.unit.spec.js @@ -1,5 +1,10 @@ import Metadata from "metabase-lib/lib/metadata/Metadata"; 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 Field from "metabase-lib/lib/metadata/Field"; +import Metric from "metabase-lib/lib/metadata/Metric"; +import Segment from "metabase-lib/lib/metadata/Segment"; import { metadata, // connected graph, @@ -8,7 +13,15 @@ import { ORDERS, } from "__support__/sample_dataset_fixture"; -import { copyObjects } from "metabase/selectors/metadata"; +import { + copyObjects, + instantiateDatabase, + instantiateSchema, + instantiateTable, + instantiateField, + instantiateSegment, + instantiateMetric, +} from "metabase/selectors/metadata"; const NUM_TABLES = Object.keys(state.entities.tables).length; const NUM_DBS = Object.keys(state.entities.databases).length; @@ -63,14 +76,74 @@ describe("copyObjects", () => { it("should clone each object in the provided mapping of objects", () => { const meta = new Metadata(); const databases = state.entities.databases; - const copiedDatabases = copyObjects(meta, databases, Database); + const copiedDatabases = copyObjects(meta, databases, instantiateDatabase); expect(Object.keys(copiedDatabases).length).toEqual(NUM_DBS); Object.values(copiedDatabases).map(db => { expect(db).toBeInstanceOf(Database); expect(db).toHaveProperty("metadata"); - expect(db.metadata).toBeInstanceOf(Metadata); + expect(db.metadata).toBe(meta); }); }); + + it("should exclude an `objects` entry if it does not have an id property", () => { + const meta = new Metadata(); + const objects = [{}]; + const copiedObjects = copyObjects(meta, objects, () => { + throw new Error( + "This function should not be triggered due to there being no `objects` entries with `id`", + ); + }); + + expect(copiedObjects).toEqual({}); + }); +}); + +describe("instantiateDatabase", () => { + it("should return an instance of Database", () => { + const instance = instantiateDatabase({ abc: 123 }); + expect(instance).toBeInstanceOf(Database); + expect(instance).toHaveProperty("abc", 123); + }); +}); + +describe("instantiateSchema", () => { + it("should return an instance of Schema", () => { + const instance = instantiateSchema({ abc: 123 }); + expect(instance).toBeInstanceOf(Schema); + expect(instance).toHaveProperty("abc", 123); + }); +}); + +describe("instantiateTable", () => { + it("should return an instance of Table", () => { + const instance = instantiateTable({ abc: 123 }); + expect(instance).toBeInstanceOf(Table); + expect(instance).toHaveProperty("abc", 123); + }); +}); + +describe("instantiateField", () => { + it("should return an instance of Field", () => { + const instance = instantiateField({ abc: 123 }); + expect(instance).toBeInstanceOf(Field); + expect(instance).toHaveProperty("abc", 123); + }); +}); + +describe("instantiateSegment", () => { + it("should return an instance of Segment", () => { + const instance = instantiateSegment({ abc: 123 }); + expect(instance).toBeInstanceOf(Segment); + expect(instance).toHaveProperty("abc", 123); + }); +}); + +describe("instantiateMetric", () => { + it("should return an instance of Metric", () => { + const instance = instantiateMetric({ abc: 123 }); + expect(instance).toBeInstanceOf(Metric); + expect(instance).toHaveProperty("abc", 123); + }); });