Skip to content
Snippets Groups Projects
Unverified Commit ca723115 authored by Alexander Polyankin's avatar Alexander Polyankin Committed by GitHub
Browse files

Fix broken field normalization in metadata (#44721)


* Fix broken field normalization in metadata

* Handle Fields.actionTypes.UPDATE action in tables reducer to keep table's original_fields in sync

* Add an e2e test for fields

* Format code

* Use a different table, since accounts table is not available in e2e tests

---------

Co-authored-by: default avatarKamil Mielnik <kamil@kamilmielnik.com>
parent 7ade6133
No related branches found
No related tags found
No related merge requests found
import { SAMPLE_DATABASE } from "e2e/support/cypress_sample_database";
import {
addOrUpdateDashboardCard,
createNativeQuestion,
main,
mapColumnTo,
modal,
restore,
rightSidebar,
visualize,
visitDashboard,
popover,
openColumnOptions,
openNotebook,
openQuestionActions,
popover,
queryBuilderHeader,
questionInfoButton,
addOrUpdateDashboardCard,
openColumnOptions,
renameColumn,
restore,
rightSidebar,
saveMetadataChanges,
setColumnType,
mapColumnTo,
setModelMetadata,
sidebar,
saveMetadataChanges,
main,
visitDashboard,
visitModel,
visualize,
} from "e2e/support/helpers";
import { startQuestionFromModel } from "./helpers/e2e-models-helpers";
......@@ -452,6 +455,37 @@ describe("scenarios > models metadata", () => {
.should("be.visible");
});
});
it("does not confuse the names of various native model columns mapped to the same database field", () => {
createNativeQuestion(
{
type: "model",
native: {
query: "select 1 as A, 2 as B, 3 as C",
},
},
{ idAlias: "modelId", wrapId: true },
);
cy.get("@modelId").then(modelId => {
setModelMetadata(modelId, (field, index) => ({
...field,
id: ORDERS.ID,
display_name: `ID${index + 1}`,
semantic_type: "type/PK",
}));
visitModel(modelId);
});
openNotebook();
cy.findByTestId("fields-picker").click();
popover().within(() => {
cy.findByText("ID1").should("be.visible");
cy.findByText("ID2").should("be.visible");
cy.findByText("ID3").should("be.visible");
});
});
});
function drillFK({ id }) {
......
......@@ -48,6 +48,7 @@ export interface NormalizedTable
metrics?: CardId[];
schema?: SchemaId;
schema_name?: SchemaName;
original_fields?: Field[];
}
export interface NormalizedForeignKey
......
......@@ -195,6 +195,24 @@ const Tables = createEntity({
},
reducer: (state = {}, { type, payload, error }) => {
if (type === Fields.actionTypes.UPDATE && !error) {
const updatedField = payload.field;
const tableId = updatedField.table_id;
const table = state[tableId];
if (table) {
return {
...state,
[tableId]: {
...table,
original_fields: table.original_fields?.map(field => {
return field.id === updatedField.id ? updatedField : field;
}),
},
};
}
}
if (type === Questions.actionTypes.CREATE && !error) {
const card = payload.question;
const virtualQuestionTable = convertSavedQuestionToVirtualTable(card);
......
......@@ -46,6 +46,10 @@ export const TableSchema = new schema.Entity(
};
}
if (table.fields != null && table.original_fields == null) {
table.original_fields = table.fields;
}
return table;
},
},
......
import { createSelector } from "@reduxjs/toolkit";
import { normalize } from "normalizr";
import { FieldSchema } from "metabase/schema";
import Question from "metabase-lib/v1/Question";
import Database from "metabase-lib/v1/metadata/Database";
import Field from "metabase-lib/v1/metadata/Field";
......@@ -152,11 +154,10 @@ export const getMetadata: (
segment.table = hydrateSegmentTable(segment, metadata);
});
Object.values(metadata.fields).forEach(field => {
field.table = hydrateFieldTable(field, metadata);
field.target = hydrateFieldTarget(field, metadata);
field.name_field = hydrateNameField(field, metadata);
field.values = getFieldValues(field);
field.remapping = new Map(getRemappings(field));
hydrateField(field, metadata);
});
Object.values(metadata.tables).forEach(table => {
table.fields?.forEach(field => hydrateField(field, metadata));
});
return metadata;
......@@ -305,9 +306,27 @@ function hydrateTableSchema(
return metadata.schema(schemaId) ?? undefined;
}
function hydrateTableFields(table: Table, metadata: Metadata): Field[] {
const fieldIds = table.getPlainObject().fields ?? [];
return fieldIds.map(id => metadata.field(id)).filter(isNotNull);
function hydrateTableFields(entityTable: Table, metadata: Metadata): Field[] {
const apiTable = entityTable.getPlainObject();
if (!apiTable.original_fields) {
const fieldIds = apiTable.fields ?? [];
return fieldIds.map(id => metadata.field(id)).filter(isNotNull);
}
return apiTable.original_fields.map(apiField => {
const { entities, result } = normalize(apiField, FieldSchema);
const normalizedField = entities.fields?.[result];
return createField(normalizedField, metadata);
});
}
function hydrateField(field: Field, metadata: Metadata) {
field.table = hydrateFieldTable(field, metadata);
field.target = hydrateFieldTarget(field, metadata);
field.name_field = hydrateNameField(field, metadata);
field.values = getFieldValues(field);
field.remapping = new Map(getRemappings(field));
}
function hydrateTableForeignKeys(
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment