Skip to content

Commit

Permalink
attach appSettings object to Essence so it can be used during visuali…
Browse files Browse the repository at this point in the history
…zation resolving
  • Loading branch information
adrianmroz-allegro committed Nov 23, 2022
1 parent 98d61cf commit b54ac4a
Show file tree
Hide file tree
Showing 17 changed files with 122 additions and 56 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@
*/

import { NamedArray } from "immutable-class";
import memoizeOne from "memoize-one";
import React from "react";
import { ClientAppSettings } from "../../../common/models/app-settings/app-settings";
import { ClientCustomization } from "../../../common/models/customization/customization";
import { ClientDataCube } from "../../../common/models/data-cube/data-cube";
import { Essence } from "../../../common/models/essence/essence";
import { isEnabled as isOAuthEnabled } from "../../../common/models/oauth/oauth";
Expand All @@ -34,13 +36,11 @@ import { Ajax } from "../../utils/ajax/ajax";
import { reportError } from "../../utils/error-reporter/error-reporter";
import { replaceHash } from "../../utils/url/url";
import { CubeView } from "../../views/cube-view/cube-view";
import { SettingsContext, SettingsValue } from "../../views/cube-view/settings-context";
import { GeneralError } from "../../views/error-view/general-error";
import { HomeView } from "../../views/home-view/home-view";
import "./turnilo-application.scss";
import { cube, generalError, home, oauthCodeHandler, oauthMessageView, View } from "./view";
import { SettingsContext, SettingsValue } from "../../views/cube-view/settings-context";
import memoizeOne from "memoize-one";
import { ClientCustomization } from "../../../common/models/customization/customization";

export interface TurniloApplicationProps {
version: string;
Expand Down
26 changes: 26 additions & 0 deletions src/client/deserializers/app-settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,29 @@ export function deserialize({ oauth, clientTimeout, customization, version }: Se
oauth: deserializeOauth(oauth)
};
}

/*
NOTE: Function is used only for serialize-deserialize cycle in Essence class.
Now it's hard to remove that functionality but in the end, Essence does not need to serialize itself.
*/
export function serialize(appSettings: ClientAppSettings): SerializedAppSettings {
const { clientTimeout, version, customization, oauth } = appSettings;
const { visualizationColors, messages, customLogoSvg, hasUrlShortener, locale, headerBackground, sentryDSN, timezones, externalViews } = customization;

return {
clientTimeout,
version,
oauth,
customization: {
visualizationColors,
messages,
customLogoSvg,
locale,
hasUrlShortener,
headerBackground,
sentryDSN,
timezones: timezones.map(t => t.toJS()),
externalViews
}
};
}
9 changes: 5 additions & 4 deletions src/client/views/cube-view/cube-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ export interface CubeViewProps {
hash: string;
changeCubeAndEssence: Ternary<ClientDataCube, Essence, boolean, void>;
urlForCubeAndEssence: Binary<ClientDataCube, Essence, string>;
getEssenceFromHash: Binary<string, ClientDataCube, Essence>;
getEssenceFromHash: Ternary<string, ClientDataCube, ClientAppSettings, Essence>;
dataCube: ClientDataCube;
dataCubes: ClientDataCube[];
openAboutModal: Fn;
Expand Down Expand Up @@ -292,7 +292,8 @@ export class CubeView extends React.Component<CubeViewProps, CubeViewState> {
}

getEssenceFromDataCube(dataCube: ClientDataCube): Essence {
return Essence.fromDataCube(dataCube);
const { appSettings } = this.props;
return Essence.fromDataCube(dataCube, appSettings);
}

getEssenceFromHash(hash: string, dataCube: ClientDataCube): Essence {
Expand All @@ -304,8 +305,8 @@ export class CubeView extends React.Component<CubeViewProps, CubeViewState> {
throw new Error("Hash is required.");
}

const { getEssenceFromHash } = this.props;
return getEssenceFromHash(hash, dataCube);
const { getEssenceFromHash, appSettings } = this.props;
return getEssenceFromHash(hash, dataCube, appSettings);
}

globalResizeListener = () => {
Expand Down
22 changes: 20 additions & 2 deletions src/common/models/essence/essence.fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,23 @@ import { HEAT_MAP_MANIFEST } from "../../visualization-manifests/heat-map/heat-m
import { LINE_CHART_MANIFEST } from "../../visualization-manifests/line-chart/line-chart";
import { TABLE_MANIFEST } from "../../visualization-manifests/table/table";
import { TOTALS_MANIFEST } from "../../visualization-manifests/totals/totals";
import { clientAppSettings } from "../app-settings/app-settings.fixtures";
import { customClientCube, twitterClientDataCube, wikiClientDataCube } from "../data-cube/data-cube.fixtures";
import { NumberFilterClause, NumberRange, RelativeTimeFilterClause, TimeFilterPeriod } from "../filter-clause/filter-clause";
import { boolean, numberRange, stringContains, stringIn, stringMatch, timePeriod, timeRange } from "../filter-clause/filter-clause.fixtures";
import {
NumberFilterClause,
NumberRange,
RelativeTimeFilterClause,
TimeFilterPeriod
} from "../filter-clause/filter-clause";
import {
boolean,
numberRange,
stringContains,
stringIn,
stringMatch,
timePeriod,
timeRange
} from "../filter-clause/filter-clause.fixtures";
import { Filter } from "../filter/filter";
import { EMPTY_SERIES, SeriesList } from "../series-list/series-list";
import { measureSeries } from "../series/series.fixtures";
Expand All @@ -35,6 +49,7 @@ import { VisualizationManifest } from "../visualization-manifest/visualization-m
import { Essence, EssenceValue, VisStrategy } from "./essence";

const defaultEssence: EssenceValue = {
appSettings: clientAppSettings,
dataCube: customClientCube("essence-fixture-data-cube", "essence-fixture-data-cube"),
visualization: null,
visualizationSettings: null,
Expand Down Expand Up @@ -96,6 +111,7 @@ export class EssenceFixtures {
stringSplitCombine("namespace", { sort: { reference: "added", direction: SortDirection.descending }, limit: 5 })
];
return new Essence({
appSettings: clientAppSettings,
dataCube: wikiClientDataCube,
visualization: HEAT_MAP_MANIFEST,
visualizationSettings: HEAT_MAP_MANIFEST.visualizationSettings.defaults,
Expand Down Expand Up @@ -130,6 +146,7 @@ export class EssenceFixtures {
measureSeries("added")
];
return new Essence({
appSettings: clientAppSettings,
dataCube: wikiClientDataCube,
visualization: TABLE_MANIFEST as unknown as VisualizationManifest,
visualizationSettings: TABLE_MANIFEST.visualizationSettings.defaults,
Expand Down Expand Up @@ -158,6 +175,7 @@ export class EssenceFixtures {
measureSeries("added")
];
return new Essence({
appSettings: clientAppSettings,
dataCube: wikiClientDataCube,
visualization: LINE_CHART_MANIFEST as unknown as VisualizationManifest,
visualizationSettings: LINE_CHART_MANIFEST.visualizationSettings.defaults,
Expand Down
9 changes: 6 additions & 3 deletions src/common/models/essence/essence.mocha.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { GRID_MANIFEST } from "../../visualization-manifests/grid/grid";
import { LINE_CHART_MANIFEST } from "../../visualization-manifests/line-chart/line-chart";
import { TABLE_MANIFEST } from "../../visualization-manifests/table/table";
import { TOTALS_MANIFEST } from "../../visualization-manifests/totals/totals";
import { clientAppSettings } from "../app-settings/app-settings.fixtures";
import { twitterClientDataCube } from "../data-cube/data-cube.fixtures";
import { TimeFilterPeriod } from "../filter-clause/filter-clause";
import { timePeriod } from "../filter-clause/filter-clause.fixtures";
Expand All @@ -33,6 +34,7 @@ import { DimensionSort, SortDirection } from "../sort/sort";
import { Split, SplitType } from "../split/split";
import { Splits } from "../splits/splits";
import { TimeShift } from "../time-shift/time-shift";
import { VisualizationManifest } from "../visualization-manifest/visualization-manifest";
import { Essence, VisStrategy } from "./essence";
import { EssenceFixtures } from "./essence.fixtures";

Expand All @@ -41,7 +43,7 @@ describe("EssenceProps", () => {

describe(".fromDataCube", () => {
it.skip("works in the base case", () => {
const essence = Essence.fromDataCube(dataCube);
const essence = Essence.fromDataCube(dataCube, clientAppSettings);

// TODO: don't test toJS
expect(essence.toJS()).to.deep.equal({
Expand Down Expand Up @@ -135,14 +137,15 @@ describe("EssenceProps", () => {
sort: new DimensionSort({ reference: "time", direction: SortDirection.ascending })
})],
series,
current: null,
current: null as VisualizationManifest,
expected: LINE_CHART_MANIFEST
}
];

tests.forEach(({ splits, current, series, expected }) => {
it(`chooses ${expected.name} given splits: [${splits}] with current ${current && current.name}`, () => {
const { visualization } = Essence.getBestVisualization(
clientAppSettings,
twitterClientDataCube,
Splits.fromSplits(splits),
SeriesList.fromSeries(series),
Expand Down Expand Up @@ -269,7 +272,7 @@ describe("EssenceProps", () => {
describe("#changeVisualisation", () => {
[TABLE_MANIFEST, LINE_CHART_MANIFEST, BAR_CHART_MANIFEST].forEach(manifest => {
it("it sets visResolve to manual", () => {
const essence = EssenceFixtures.twitterNoVisualisation().changeVisualization(manifest);
const essence = EssenceFixtures.twitterNoVisualisation().changeVisualization(manifest as VisualizationManifest);
expect(essence.visualization.name).to.deep.equal(manifest.name);
expect(essence.visResolve.isManual()).to.be.true;
});
Expand Down
35 changes: 21 additions & 14 deletions src/common/models/essence/essence.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,13 @@

import { Timezone } from "chronoshift";
import { List, OrderedSet, Record as ImmutableRecord, Set } from "immutable";
import { serialize } from "../../../client/deserializers/data-cube";
import { serialize as serializeAppSettings } from "../../../client/deserializers/app-settings";
import { serialize as serializeDataCube } from "../../../client/deserializers/data-cube";
import { thread } from "../../utils/functional/functional";
import nullableEquals from "../../utils/immutable-utils/nullable-equals";
import { visualizationIndependentEvaluator } from "../../utils/rules/visualization-independent-evaluator";
import { MANIFESTS } from "../../visualization-manifests";
import { ClientAppSettings } from "../app-settings/app-settings";
import {
ClientDataCube,
getDefaultFilter,
Expand Down Expand Up @@ -78,6 +80,7 @@ export enum VisStrategy {
type DimensionId = string;

export interface EssenceValue {
appSettings: ClientAppSettings;
dataCube: ClientDataCube;
visualization: VisualizationManifest;
visualizationSettings: VisualizationSettings | null;
Expand All @@ -92,6 +95,7 @@ export interface EssenceValue {
}

const defaultEssence: EssenceValue = {
appSettings: null,
dataCube: null,
visualization: null,
visualizationSettings: null,
Expand All @@ -111,18 +115,18 @@ export interface EffectiveFilterOptions {
}

type VisualizationResolverResult = Pick<EssenceValue, "splits" | "visualization" | "visResolve">;
type VisualizationResolverParameters = Pick<EssenceValue, "visualization" | "dataCube" | "splits" | "series">;
type VisualizationResolverParameters = Pick<EssenceValue, "visualization" | "dataCube" | "splits" | "series" | "appSettings">;

function resolveVisualization({ visualization, dataCube, splits, series }: VisualizationResolverParameters): VisualizationResolverResult {
function resolveVisualization({ visualization, dataCube, splits, series, appSettings }: VisualizationResolverParameters): VisualizationResolverResult {

let visResolve: Resolve;
// Place vis here because it needs to know about splits and colors (and maybe later other things)
if (!visualization) {
const visAndResolve = Essence.getBestVisualization(dataCube, splits, series, null);
const visAndResolve = Essence.getBestVisualization(appSettings, dataCube, splits, series, null);
visualization = visAndResolve.visualization;
}

const ruleVariables = { dataCube, series, splits, isSelectedVisualization: true };
const ruleVariables = { appSettings, dataCube, series, splits, isSelectedVisualization: true };
visResolve = visualization.evaluateRules(ruleVariables);
if (visResolve.isAutomatic()) {
const adjustment = visResolve.adjustment;
Expand All @@ -143,14 +147,15 @@ function resolveVisualization({ visualization, dataCube, splits, series }: Visua
export class Essence extends ImmutableRecord<EssenceValue>(defaultEssence) {

static getBestVisualization(
appSettings: ClientAppSettings,
dataCube: ClientDataCube,
splits: Splits,
series: SeriesList,
currentVisualization: VisualizationManifest
): VisualizationAndResolve {
const visAndResolves = MANIFESTS.map(visualization => {
const isSelectedVisualization = visualization === currentVisualization;
const ruleVariables = { dataCube, splits, series, isSelectedVisualization };
const ruleVariables = { appSettings, dataCube, splits, series, isSelectedVisualization };
return {
visualization,
resolve: visualization.evaluateRules(ruleVariables)
Expand All @@ -160,8 +165,9 @@ export class Essence extends ImmutableRecord<EssenceValue>(defaultEssence) {
return visAndResolves.sort((vr1, vr2) => Resolve.compare(vr1.resolve, vr2.resolve))[0];
}

static fromDataCube(dataCube: ClientDataCube): Essence {
static fromDataCube(dataCube: ClientDataCube, appSettings: ClientAppSettings): Essence {
const essence = new Essence({
appSettings,
dataCube,
visualization: null,
visualizationSettings: null,
Expand Down Expand Up @@ -238,9 +244,10 @@ export class Essence extends ImmutableRecord<EssenceValue>(defaultEssence) {

public toJS() {
return {
appSettings: serializeAppSettings(this.appSettings),
visualization: this.visualization,
visualizationSettings: this.visualizationSettings,
dataCube: serialize(this.dataCube),
dataCube: serializeDataCube(this.dataCube),
timezone: this.timezone.toJS(),
filter: this.filter && this.filter.toJS(),
splits: this.splits && this.splits.toJS(),
Expand Down Expand Up @@ -435,9 +442,9 @@ export class Essence extends ImmutableRecord<EssenceValue>(defaultEssence) {
}

function adjustVisualization(essence: Essence): Essence {
const { dataCube, visualization, splits, series } = essence;
const { dataCube, visualization, splits, series, appSettings } = essence;
const { visualization: newVis } = Essence.getBestVisualization(
dataCube, splits, series, visualization);
appSettings, dataCube, splits, series, visualization);
if (newVis === visualization) return essence;
return essence.changeVisualization(newVis, newVis.visualizationSettings.defaults);
}
Expand Down Expand Up @@ -501,7 +508,7 @@ export class Essence extends ImmutableRecord<EssenceValue>(defaultEssence) {
}

public changeSplits(splits: Splits, strategy: VisStrategy): Essence {
const { splits: oldSplits, dataCube, visualization, visResolve, filter, series } = this;
const { splits: oldSplits, appSettings, dataCube, visualization, visResolve, filter, series } = this;

const newSplits = this.setSortOnSplits(splits).updateWithFilter(filter, dataCube.dimensions);

Expand All @@ -518,7 +525,7 @@ export class Essence extends ImmutableRecord<EssenceValue>(defaultEssence) {

function adjustVisualization(essence: Essence): Essence {
if (adjustStrategy(strategy) !== VisStrategy.FairGame) return essence;
const { visualization: newVis } = Essence.getBestVisualization(dataCube, newSplits, series, visualization);
const { visualization: newVis } = Essence.getBestVisualization(appSettings, dataCube, newSplits, series, visualization);
if (newVis === visualization) return essence;
return essence.changeVisualization(newVis, newVis.visualizationSettings.defaults);
}
Expand Down Expand Up @@ -612,8 +619,8 @@ export class Essence extends ImmutableRecord<EssenceValue>(defaultEssence) {
}

public resolveVisualizationAndUpdate() {
const { visualization, splits, dataCube, series } = this;
const result = resolveVisualization({ splits, dataCube, visualization, series });
const { visualization, splits, dataCube, series, appSettings } = this;
const result = resolveVisualization({ appSettings, splits, dataCube, visualization, series });
return this
.set("visResolve", result.visResolve)
.set("visualization", result.visualization)
Expand Down
3 changes: 2 additions & 1 deletion src/common/utils/rules/split-adjustments.mocha.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
adjustLimit,
adjustSort
} from "./split-adjustments";
import { DEFAULT_COLORS } from "../../models/colors/colors";

describe("Split adjustment utilities", () => {
describe("adjustContinuousTimeSplit", () => {
Expand Down Expand Up @@ -118,7 +119,7 @@ describe("Split adjustment utilities", () => {
limits: [42, 100]
};
const split = stringSplitCombine("foobar", { limit: 50 });
const adjusted = adjustColorSplit(split, dimension, SeriesList.fromSeries([]));
const adjusted = adjustColorSplit(split, dimension, SeriesList.fromSeries([]), DEFAULT_COLORS);

expect(adjusted.limit).to.be.equal(10);
});
Expand Down
9 changes: 4 additions & 5 deletions src/common/utils/rules/split-adjustments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,20 @@
* limitations under the License.
*/

import { NORMAL_COLORS } from "../../models/colors/colors";
import { VisualizationColors } from "../../models/colors/colors";
import { Dimension } from "../../models/dimension/dimension";
import { SeriesList } from "../../models/series-list/series-list";
import { DimensionSort, SeriesSort, SortDirection } from "../../models/sort/sort";
import { Split, SplitType } from "../../models/split/split";
import { thread } from "../functional/functional";

const COLORS_COUNT = NORMAL_COLORS.length;

export function adjustColorSplit(split: Split, dimension: Dimension, series: SeriesList): Split {
export function adjustColorSplit(split: Split, dimension: Dimension, series: SeriesList, visualizationColors: VisualizationColors): Split {
const colorsCount = visualizationColors.series.length;
return thread(
split,
adjustSort(dimension, series),
// TODO: This magic 5 will disappear in #756
adjustFiniteLimit([5, COLORS_COUNT], COLORS_COUNT)
adjustFiniteLimit([5, colorsCount], colorsCount)
);
}

Expand Down
2 changes: 2 additions & 0 deletions src/common/utils/rules/visualization-dependent-evaluator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
* limitations under the License.
*/

import { ClientAppSettings } from "../../models/app-settings/app-settings";
import { ClientDataCube } from "../../models/data-cube/data-cube";
import { SeriesList } from "../../models/series-list/series-list";
import { Splits } from "../../models/splits/splits";
Expand All @@ -27,6 +28,7 @@ export interface PredicateVariables {
}

export interface ActionVariables {
appSettings: ClientAppSettings;
dataCube: ClientDataCube;
splits: Splits;
series: SeriesList;
Expand Down
Loading

0 comments on commit b54ac4a

Please sign in to comment.