Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -666,6 +666,81 @@ describe("metrics view names change", () => {
});
});

describe("yaml config change", () => {
// A canvas applies its metrics views and then its config, both from the same ResolveCanvas
// response. When the metrics view specs are already loaded, applying the names parses the
// queued params right away, before the config is known.
it("adds required and pinned chips when the config lands after the params", async () => {
const provider = await createTestMetricsViewsProvider([
AD_BIDS_METRICS_NAME,
]);
provider.value.setMetricsViewNames([]);
const yamlConfigProvider = new YAMLConfigProvider();
const { value: filterManager, destroy } = createInEffectRoot(
() => new ExpressionFilterManager(provider.value, yamlConfigProvider),
);
cleanups.push(() => {
destroy();
provider.value.cleanup();
provider.destroy();
});
expect(filterManager.ready).toBe(false);

// Queued until the specs are ready, which they are as soon as the names are applied.
filterManager.storeSync.setUrlParams(new URLSearchParams());
provider.value.setMetricsViewNames([AD_BIDS_METRICS_NAME]);
expect(filterManager.ready).toBe(true);
expect(filterManager.sortedFilterManagers.dimensions).toEqual([]);

yamlConfigProvider.update(
{},
[AD_BIDS_PUBLISHER_DIMENSION, AD_BIDS_IMPRESSIONS_MEASURE],
[AD_BIDS_DOMAIN_DIMENSION],
);

expect(names(filterManager.sortedFilterManagers.dimensions)).toEqual([
AD_BIDS_DOMAIN_DIMENSION,
AD_BIDS_PUBLISHER_DIMENSION,
]);
expect(names(filterManager.sortedFilterManagers.measures)).toEqual([
AD_BIDS_IMPRESSIONS_MEASURE,
]);
expect(exprOf(filterManager, AD_BIDS_METRICS_NAME)).toBeUndefined();
});

it("keeps the chips it has when the config is applied again", () => {
const yamlConfigProvider = new YAMLConfigProvider();
yamlConfigProvider.update({}, [AD_BIDS_PUBLISHER_DIMENSION], []);
const filterManager = createFilterManager(yamlConfigProvider);
filterManager.storeSync.setUrlParams(
perMetricsViewParams({
[AD_BIDS_METRICS_NAME]: `${AD_BIDS_DOMAIN_DIMENSION} IN ('google.com')`,
}),
);
const chipsBefore = filterManager.sortedFilterManagers.dimensions;
expect(names(chipsBefore)).toEqual([
AD_BIDS_PUBLISHER_DIMENSION,
AD_BIDS_DOMAIN_DIMENSION,
]);

yamlConfigProvider.update({}, [AD_BIDS_PUBLISHER_DIMENSION], []);

const chipsAfter = filterManager.sortedFilterManagers.dimensions;
expect(chipsAfter.length).toBe(chipsBefore.length);
chipsAfter.forEach((chip, i) => expect(chip).toBe(chipsBefore[i]));
});

it("skips pinned filters the metrics views do not define", () => {
const yamlConfigProvider = new YAMLConfigProvider();
const filterManager = createFilterManager(yamlConfigProvider);

yamlConfigProvider.update({}, ["not_a_field"], []);

expect(filterManager.sortedFilterManagers.dimensions).toEqual([]);
expect(filterManager.sortedFilterManagers.measures).toEqual([]);
});
});

describe("applyFilterToParams", () => {
it("writes a param per metrics view and drops the legacy singular one", () => {
const filterManager = createFilterManager();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export class ExpressionFilterManager implements UrlParamsStore {
public paramKeys = new Set<string>([ExploreStateURLParams.Filters]);
public readonly storeSync: UrlParamsChangeTracker;

// Unsubscribes from `metricsViewsProvider`, which can outlive this manager. See `cleanup`.
// Unsubscribes from the providers, which can outlive this manager. See `cleanup`.
private readonly unsubscribers: (() => void)[];

public constructor(
Expand Down Expand Up @@ -100,7 +100,11 @@ export class ExpressionFilterManager implements UrlParamsStore {
this.ready = true;
this.events.emit("ready");
});
this.unsubscribers = [unsubUpdate, unsubSpecsLoaded];
// The config can land after the params were parsed. See `addRequiredAndPinnedFilters`.
const unsubYamlConfig = yamlConfigProvider.on("update", () =>
this.topLevelJoiner.addRequiredAndPinnedFilters(),
);
this.unsubscribers = [unsubUpdate, unsubSpecsLoaded, unsubYamlConfig];

this.topLevelJoiner = $state(
JoinerFilterManager.parse(
Expand Down Expand Up @@ -157,8 +161,8 @@ export class ExpressionFilterManager implements UrlParamsStore {
}

/**
* Removes the listeners on `metricsViewsProvider`.
* Call this when the manager is discarded before the provider, e.g. a temporary `clone`.
* Removes the listeners on `metricsViewsProvider` and `yamlConfigProvider`.
* Call this when the manager is discarded before the providers, e.g. a temporary `clone`.
*/
public cleanup() {
this.unsubscribers.forEach((unsub) => unsub());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,39 @@ export class JoinerFilterManager {
};
}

/**
* Adds a chip for each required and pinned filter that does not have one yet.
* `parse` only adds the filters the YAML config held at the time, and the config can land after
* the params: a canvas applies its metrics views before its config, both from the same spec,
* and when the metrics view specs are already loaded the queued params are parsed in between.
*/
public addRequiredAndPinnedFilters() {
if (!this.yamlConfigProvider) return;

Object.keys(this.yamlConfigProvider.requiredFilters)
.concat(Object.keys(this.yamlConfigProvider.pinnedFilters))
.forEach((name) => {
if (this.managerLookup[name]) return;

const dimensionManager = DimensionFilterManager.createForMetricsViews(
this.metricsViewsProvider,
name,
{ events: this.events },
);
if (dimensionManager) {
this.maybeAddDimensionFilter(dimensionManager);
return;
}

const measureManager = MeasureFilterManager.createForMetricsViews(
this.metricsViewsProvider,
name,
{ events: this.events },
);
if (measureManager) this.maybeAddMeasureFilter(measureManager);
});
}

public removeEmptyManagers() {
this.managers = {
...this.managers,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { type V1Expression } from "@rilldata/web-common/runtime-client";
import { EventEmitter } from "@rilldata/web-common/lib/event-emitter.ts";

type YAMLConfigProviderEvents = {
/** Fired after `update` has applied the config from a spec. */
update: void;
};

/**
* A provider for YAML only configuration. These are only mutable during yaml editing.
Expand All @@ -13,6 +19,11 @@ export class YAMLConfigProvider {

public cleanup: (() => void) | undefined = undefined;

private events = new EventEmitter<YAMLConfigProviderEvents>();
public readonly on = this.events.on.bind(
this.events,
) as typeof this.events.on;

public update(
defaultFilters: Record<string, V1Expression | undefined>,
pinnedFilters: string[],
Expand All @@ -31,6 +42,8 @@ export class YAMLConfigProvider {
);
this.requiredFilters = { ...requiredFiltersRec };
this.specRequiredFilters = { ...requiredFiltersRec };

this.events.emit("update");
}

public setEditable(newEditable: boolean) {
Expand Down
Loading