Skip to content
Open
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
35 changes: 35 additions & 0 deletions lib/sanbase/available_metrics/available_metrics.ex
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ defmodule Sanbase.AvailableMetrics do
metric_to_supported_assets_map = metric_to_available_slugs_maps()
access_map = Sanbase.Metric.Helper.access_map()
metric_to_categories = metric_to_categories_map()
hidden_metrics = Sanbase.Metric.hidden_metrics()

metrics
|> Enum.map(fn metric ->
Expand All @@ -99,6 +100,12 @@ defmodule Sanbase.AvailableMetrics do
internal_name: m.internal_metric,
status: m.status,
docs: Map.get(m, :docs) || [],
# `is_deprecated` is the derived flag: the registry's own column, or a
# `hard_deprecate_after` that is set - including a date still in the
# future, which is a metric on its way out and not worth advertising.
is_deprecated: Map.get(m, :is_deprecated) == true,
hard_deprecate_after: Map.get(m, :hard_deprecate_after),
is_hidden: MapSet.member?(hidden_metrics, m.metric),
available_assets: Map.get(metric_to_supported_assets_map, m.metric) || [],
default_aggregation: m.default_aggregation,
frequency: m.min_interval,
Expand Down Expand Up @@ -205,6 +212,7 @@ defmodule Sanbase.AvailableMetrics do
def apply_filters(metrics_list, filters) when is_list(metrics_list) do
metrics_list
|> reject_hidden_group_metrics()
|> reject_hidden_and_deprecated_metrics()
|> maybe_apply_filter(:docs, filters)
|> maybe_apply_filter(:only_with_docs, filters)
|> maybe_apply_filter(:only_intraday_metrics, filters)
Expand All @@ -225,6 +233,33 @@ defmodule Sanbase.AvailableMetrics do
end)
end

# The page lists what the API will actually serve.
#
# `is_hidden` means "keep out of metric lists", so it is dropped here. A
# `hard_deprecate_after` that has passed makes every request fail, so it is
# dropped too - `Sanbase.Metric.available_metrics/0` normally does that already,
# but it filters at refresh time into a cache with no TTL, so a date that passes
# between refreshes leaves the metric listed. Filtering again here is what keeps
# the page honest in that window, and it also catches a name that a code adapter
# keeps advertising while the registry deprecates it.
#
# A metric that is only `is_deprecated` is deliberately kept: it still returns
# data, with no end date, so it belongs in the inventory. Note that it is not in
# any package - `Bundle.PackageSnapshot.sellable?/1` rejects the flag - so the
# page shows it while nobody can buy it any more.
defp reject_hidden_and_deprecated_metrics(metrics) do
now = DateTime.utc_now()

Enum.reject(metrics, fn metric ->
metric[:is_hidden] == true or hard_deprecated?(metric[:hard_deprecate_after], now)
end)
end

defp hard_deprecated?(%DateTime{} = deprecate_after, now),
do: DateTime.before?(deprecate_after, now)

defp hard_deprecated?(_deprecate_after, _now), do: false

# "with" | "without" | "all". `only_with_docs` is the older boolean form of the
# same thing - the page sends `docs`, links and saved exports may still send the
# checkbox.
Expand Down
11 changes: 11 additions & 0 deletions lib/sanbase/billing/plan/bundle/package_snapshot.ex
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,17 @@ defmodule Sanbase.Billing.Plan.Bundle.PackageSnapshot do

defp sellable?(%Registry{is_deprecated: true}), do: false
defp sellable?(%Registry{is_hidden: true}), do: false

# A `hard_deprecate_after` that has passed is enforced on every request by
# `Sanbase.Metric.hard_deprecated?/1`, which returns an error rather than data.
# Selling it would put a metric in a package that no customer can fetch, and
# `is_deprecated` is a separate column that is often left unset when the date is
# scheduled - so this cannot rely on the flag above.
#
# A date still in the future is left sellable: the metric works until then.
defp sellable?(%Registry{hard_deprecate_after: %DateTime{} = deprecate_after}),
do: DateTime.after?(deprecate_after, DateTime.utc_now())

defp sellable?(%Registry{}), do: true
defp sellable?(_), do: false

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,23 @@ defmodule Sanbase.AvailableMetricsCategorizationFilterTest do
assert result == ["also_sellable"]
end

test "hides hidden metrics and the ones the API already refuses" do
metrics = [
metric("live", []),
# Still serves data, with no end date, so it stays in the inventory even
# though it can no longer be sold.
metric("soft_deprecated", []) |> Map.put(:is_deprecated, true),
# Works until the date, so it is still listed.
metric("scheduled", []) |> Map.put(:hard_deprecate_after, ~U[2100-01-01 00:00:00Z]),
# Every request fails - listing it would advertise an error.
metric("hard_deprecated", []) |> Map.put(:hard_deprecate_after, ~U[2020-01-01 00:00:00Z]),
metric("internal", []) |> Map.put(:is_hidden, true)
]

assert AvailableMetrics.apply_filters(metrics, %{}) |> Enum.map(& &1.metric) ==
["live", "soft_deprecated", "scheduled"]
end

test "does not match category and group across different mappings" do
metrics = [
metric("multi", [
Expand Down
30 changes: 30 additions & 0 deletions test/sanbase/billing/plan/bundle/package_snapshot_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,36 @@ defmodule Sanbase.Billing.Plan.Bundle.PackageSnapshotTest do
assert contents["market"] == ["bundle_test_kept"]
end

test "leaves out a metric whose hard_deprecate_after has passed", %{categories: categories} do
# The date is enforced per request by `Sanbase.Metric.hard_deprecated?/1`, and
# `is_deprecated` is a separate column that is usually left unset when a date
# is scheduled - so the snapshot has to read the date itself or it sells a
# metric the API refuses.
categorize("bundle_test_kept", categories["market"])

categorize(
%{metric: "bundle_test_hard_deprecated", hard_deprecate_after: ~U[2020-01-01 00:00:00Z]},
categories["market"]
)

{:ok, contents} = PackageSnapshot.materialize()

assert contents["market"] == ["bundle_test_kept"]
end

test "keeps a metric whose hard_deprecate_after is still in the future", %{
categories: categories
} do
categorize(
%{metric: "bundle_test_scheduled", hard_deprecate_after: ~U[2100-01-01 00:00:00Z]},
categories["market"]
)

{:ok, contents} = PackageSnapshot.materialize()

assert contents["market"] == ["bundle_test_scheduled"]
end

test "includes metrics served by adapter modules rather than the registry", %{
categories: categories
} do
Expand Down