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
103 changes: 96 additions & 7 deletions glam/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ def validate_request_legacy(**kwargs):
validated_data["os"] = kwargs.get("os", "*")
validated_data["num_versions"] = kwargs.get("versions", 3)
validated_data["process"] = kwargs.get("process")
validated_data["metric_key"] = kwargs.get("metric_key")

if validated_data["channel"] not in ["beta", "nightly", "release"]:
raise ValidationError("Invalid channel: {}".format(validated_data["channel"]))
Expand Down Expand Up @@ -116,6 +117,7 @@ def validate_request_glean(**kwargs):
validated_data["os"] = kwargs.get("os", "*")
validated_data["num_versions"] = kwargs.get("versions", 3)
validated_data["ping_type"] = kwargs.get("ping_type")
validated_data["metric_key"] = kwargs.get("metric_key")

if validated_data["channel"] not in ["beta", "nightly", "release"]:
raise ValidationError("Invalid channel: {}".format(validated_data["channel"]))
Expand Down Expand Up @@ -146,6 +148,36 @@ def validate_request_glean(**kwargs):
return validated_data


def validate_metric_keys_request(**kwargs):
REQUIRED_QUERY_PARAMETERS = [
"probe",
"channel",
"product",
]
if any([k not in kwargs.keys() for k in REQUIRED_QUERY_PARAMETERS]):
missing = set(REQUIRED_QUERY_PARAMETERS) - set(kwargs.keys())
raise ValidationError(
"Missing required query parameters: {}".format(", ".join(sorted(missing)))
)

validated_data = {}
validated_data["probe"] = kwargs.get("probe")[0]
validated_data["channel"] = kwargs.get("channel")[0]
validated_data["product"] = kwargs.get("product")[0]
validated_data["os"] = kwargs.get("os")[0]

if validated_data["channel"] not in ["beta", "nightly", "release"]:
raise ValidationError("Invalid channel: {}".format(validated_data["channel"]))

if validated_data["product"] not in ["fog", "fenix"]:
raise ValidationError("Invalid product: {}".format(validated_data["product"]))

if validated_data["os"] not in ["Windows", "Darwin", "Mac", "Linux", "*"]:
raise ValidationError("Invalid os: {}".format(validated_data["os"]))

return validated_data


def get_firefox_aggregations(source, request, **kwargs):
if source == "BigQuery":
bqClient = get_bq_client()
Expand Down Expand Up @@ -218,6 +250,8 @@ def get_firefox_aggregations_from_pg(request, **kwargs):

if "process" in kwargs:
dimensions.append(Q(process=kwargs["process"]))
if "metric_key" in kwargs and kwargs["metric_key"]:
dimensions.append(Q(metric_key=kwargs["metric_key"]))
result = model.objects.filter(*dimensions)

response = []
Expand Down Expand Up @@ -291,6 +325,15 @@ def get_firefox_aggregations_from_bq(bqClient, request, req_data):
)
process_filter = "AND process = @process"

metric_key_filter = ""
if "metric_key" in req_data and req_data["metric_key"]:
query_parameters.append(
bigquery.ScalarQueryParameter(
"metric_key", "STRING", req_data["metric_key"]
)
)
metric_key_filter = "AND metric_key = @metric_key"

table = f"glam_desktop_{channel}_aggregates"
query = f"""
WITH versions AS (
Expand All @@ -314,6 +357,7 @@ def get_firefox_aggregations_from_bq(bqClient, request, req_data):
metric = @metric
AND os = @os
{process_filter}
{metric_key_filter}
{build_id_filter}
AND version IN UNNEST(versions.selected_versions)
"""
Expand Down Expand Up @@ -461,6 +505,11 @@ def get_glean_aggregations_from_pg(request, **kwargs):
Q(os=os),
]

# Add metric_key filtering if provided
metric_key = kwargs.get("metric_key")
if metric_key:
dimensions.append(Q(metric_key=metric_key))

aggregation_level = kwargs["aggregationLevel"]
# Whether to pull aggregations by version or build_id.
if aggregation_level == "version":
Expand Down Expand Up @@ -519,6 +568,7 @@ def get_glean_aggregations_from_bq(bqClient, request, req_data):
ping_type = req_data["ping_type"]
os = req_data["os"]
aggregation_level = req_data["aggregation_level"]
metric_key = req_data["metric_key"] or ""

table_id = f"glam_{product}_{channel}_aggregates"
shas = {}
Expand All @@ -528,6 +578,10 @@ def get_glean_aggregations_from_bq(bqClient, request, req_data):
if product == "fog":
shas = _get_firefox_shas(channel, hourly=True)
build_id_filter = 'AND build_id != "*"'
if metric_key:
metric_key_filter = "AND metric_key = @metric_key"
else:
metric_key_filter = ""
# Build the SQL query with parameters
query = f"""
WITH versions AS (
Expand All @@ -549,18 +603,24 @@ def get_glean_aggregations_from_bq(bqClient, request, req_data):
versions
WHERE
metric = @metric
{metric_key_filter}
AND ping_type = @ping_type
AND os = @os
{build_id_filter}
AND version IN UNNEST(versions.selected_versions)
"""
query_parameters = [
bigquery.ScalarQueryParameter("metric", "STRING", probe),
bigquery.ScalarQueryParameter("ping_type", "STRING", ping_type),
bigquery.ScalarQueryParameter("os", "STRING", os),
bigquery.ScalarQueryParameter("num_versions", "INT64", num_versions),
]
if metric_key:
query_parameters.append(
bigquery.ScalarQueryParameter("metric_key", "STRING", metric_key)
)
job_config = bigquery.QueryJobConfig(
query_parameters=[
bigquery.ScalarQueryParameter("metric", "STRING", probe),
bigquery.ScalarQueryParameter("ping_type", "STRING", ping_type),
bigquery.ScalarQueryParameter("os", "STRING", os),
bigquery.ScalarQueryParameter("num_versions", "INT64", num_versions),
]
query_parameters=query_parameters,
)
with bqClient as client:
query_job = client.query(query, job_config=job_config)
Expand Down Expand Up @@ -666,6 +726,34 @@ def _get_fog_counts(app_id, versions, ping_type, os, by_build):
return data


@api_view(["GET"])
def metric_keys(request):
"""
Fetches metric keys from BigQuery for a given probe.
"""
req_data = validate_metric_keys_request(**request.GET)
product = req_data["product"]
channel = req_data["channel"]
probe = req_data["probe"]
os = req_data["os"]

query = f"""
SELECT DISTINCT metric_key
FROM `{GLAM_BQ_PROD_PROJECT}.glam_etl.glam_{product}_{channel}_aggregates`
WHERE metric = @probe
AND os = @os
"""
job_config = bigquery.QueryJobConfig(
query_parameters=[
bigquery.ScalarQueryParameter("probe", "STRING", probe),
bigquery.ScalarQueryParameter("os", "STRING", os),
]
)
with get_bq_client() as client:
query_job = client.query(query, job_config=job_config)
return Response({"metric_keys": [row.metric_key for row in query_job]})


@api_view(["POST"])
def aggregations(request):
"""
Expand All @@ -676,7 +764,8 @@ def aggregations(request):
{
"query": {
"channel": "nightly",
"probe": "gc_ms",
"probe": "<probe_name>",
"metric_key": "<metric_key>",
"process": "content"
"versions": 5, # Defaults to 3 versions.
"aggregationLevel": "version" # OR "build_id"
Expand Down
1 change: 1 addition & 0 deletions glam/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

urlpatterns = [
path("api/v1/data/", api_views.aggregations, name="v1-data"),
path("api/v1/metric-keys/", api_views.metric_keys, name="v1-metric-keys"),
path("api/v1/probes/", api_views.probes, name="v1-probes"),
path("api/v1/probes/random", api_views.random_probes, name="v1-random-probes"),
path("api/v1/updates/", api_views.updates, name="v1-updates"),
Expand Down
11 changes: 11 additions & 0 deletions public/static/global.css
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,7 @@ main .details {
padding: var(--space-1h);
padding-left: var(--space-2x);
padding-right: var(--space-2x);
z-index: -1;
}

/* probe labels */
Expand Down Expand Up @@ -660,6 +661,16 @@ main .details {
background-color: var(--memory_distribution-bg);
}

.label--labeled_custom_distribution {
color: var(--memory_distribution-color);
background-color: var(--memory_distribution-bg);
}

.label--labeled_timing_distribution {
color: var(--memory_distribution-color);
background-color: var(--memory_distribution-bg);
}

.label--uuid {
color: var(--uuid-color);
background-color: var(--uuid-bg);
Expand Down
51 changes: 33 additions & 18 deletions src/components/controls/ProbeKeySelector.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,18 @@
import { CaretDown } from '@graph-paper/icons';
import { FloatingMenu, MenuList, MenuListItem } from '@graph-paper/menu';
import { tooltip as tooltipAction } from '@graph-paper/core/actions';
import { createEventDispatcher } from 'svelte';

import { store } from '../../state/store';

const dispatch = createEventDispatcher();

export let options;
export let currentKey;
export let active;
export let tooltipText = 'this probe has multiple keys associated with it';
export let fieldName = 'metricKey';
export let disableStoreUpdate = false;

let button;
let width;
Expand All @@ -20,14 +25,16 @@
}

function setValue(event) {
currentKey = event.detail.key;
store.setField('aggKey', currentKey);
active = false;
}
const selectedKey = event.detail.key;
currentKey = selectedKey;

// Dispatch selection event to parent
dispatch('selection', { key: selectedKey });
if (!disableStoreUpdate) {
store.setField(fieldName, selectedKey);
}

// initialize aggregation key if none has been selected yet
if (!$store.aggKey && options.length) {
store.setField('aggKey', options[0]);
active = false;
}
</script>

Expand All @@ -41,12 +48,19 @@
text-align: left;
/* min-width: var(--space-16x); */
background-color: white;
border: 1px solid var(--cool-gray-300);
display: grid;
grid-auto-flow: column;
width: max-content;
grid-column-gap: var(--space-base);
color: var(--subhead-gray-02);
border-radius: var(--space-1h);
cursor: pointer;
}

.activating-button:hover {
border-color: var(--cool-gray-400);
background-color: var(--cool-gray-50);
}
.menu-list-item__title {
font-size: var(--text-01);
Expand All @@ -58,7 +72,11 @@
}
</style>

<div class="menu-button" bind:this={button}>
<div
class="menu-button"
bind:this={button}
style="position: relative; overflow: visible;"
>
<button
class="activating-button"
on:click={toggle}
Expand All @@ -68,16 +86,13 @@
}}
>
<div>
{#each options as opt}
<div
style="
visibility: {opt === currentKey ? 'visible' : 'hidden'};
height: {opt === currentKey ? 'inherit' : 0};
"
>
{opt}
</div>
{/each}
{#if currentKey && options.includes(currentKey)}
{currentKey}
{:else}
<span style="color: var(--cool-gray-400); font-style: italic;">
Select an option...
</span>
{/if}
</div>
<CaretDown size="16" />
</button>
Expand Down
Loading