Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(): attempt a telemetry metric (hack, dnm) #1578

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
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
296 changes: 282 additions & 14 deletions packages/common/package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions packages/common/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"dist/**"
],
"dependencies": {
"@esri/telemetry-reporting-client": "^3.2.4",
"@terraformer/arcgis": "^2.1.2",
"@types/arcgis-js-api": "~4.28.0",
"abab": "^2.0.5",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ export const MetricSchema: IConfigurationSchema = {
dynamicMetric: {
type: "object",
},
telemetryMetric: {
type: "object",
},
allowUnitFormatting: {
type: "boolean",
default: false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,11 @@ export const buildUiSchema = async (
rules: [
undefined,
undefined,
[
{
effect: UiSchemaRuleEffects.SHOW,
// only show in alpha
conditions: [
checkPermission("hub:availability:alpha", context).access,
],
},
],
undefined,
{
effect: UiSchemaRuleEffects.HIDE,
conditions: [true],
},
],
enum: {
i18nScope: `statistic.type.enum`,
Expand All @@ -68,6 +64,14 @@ export const buildUiSchema = async (
control: "hub-composite-input-service-query-metric",
},
},
{
scope: "/properties/telemetryMetric",
type: "Control",
rule: SHOW_FOR_TELEMETRY_RULE,
options: {
control: "hub-composite-input-telemetry-query-metric",
},
},
{
type: "Section",
labelKey: `formatting.sectionTitle`,
Expand Down Expand Up @@ -446,6 +450,14 @@ const SHOW_FOR_DYNAMIC_RULE = {
effect: UiSchemaRuleEffects.SHOW,
};

const SHOW_FOR_TELEMETRY_RULE = {
condition: {
scope: "/properties/type",
schema: { const: "telemetry" },
},
effect: UiSchemaRuleEffects.SHOW,
};

const SHOW_FOR_STATIC_RULE = {
condition: {
scope: "/properties/type",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,6 @@ export enum ICONS {
export enum SOURCE {
dynamic = "dynamic",
static = "static",
telemetry = "telemetry",
itemQuery = "itemQuery",
}
1 change: 1 addition & 0 deletions packages/common/src/core/schemas/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ export type UiSchemaElementOptions = Pick<

export interface IUiSchemaComboboxItem {
value: string;
description?: string;
label?: string;
icon?: string;
selected?: boolean;
Expand Down
15 changes: 14 additions & 1 deletion packages/common/src/core/types/Metrics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { IQuery } from "../../search/types/IHubCatalog";
import { FieldType, IField } from "@esri/arcgis-rest-types";
import { IReference } from "./IReference";
import { ServiceAggregation } from "../../core/types/DynamicValues";
import { ITelemetryRequestParams } from "@esri/telemetry-reporting-client";

/**
* Defines the information required from a Metric
Expand Down Expand Up @@ -109,7 +110,8 @@ export interface IServiceQuery {
export type MetricSource =
| IStaticValueMetricSource
| IServiceQueryMetricSource
| IItemQueryMetricSource;
| IItemQueryMetricSource
| ITelemetryQueryMetricSource;

/**
* Defines a metric source that is a static value
Expand Down Expand Up @@ -164,6 +166,17 @@ export interface IItemQueryMetricSource {
scope?: IQuery;
}

export interface ITelemetryQueryMetricSource {
type: "telemetry-query";

requestParams: ITelemetryRequestParams;

telemetryContext: {
customDimensionsConfig?: Record<string, number>;
customMetricsConfig?: Record<string, number>;
};
}

export interface IMetricAttributes extends IEntityInfo {
[key: string]: number | string | null;
}
Expand Down
48 changes: 48 additions & 0 deletions packages/common/src/metrics/resolveMetric.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
IResolvedMetric,
IServiceQueryMetricSource,
IStaticValueMetricSource,
ITelemetryQueryMetricSource,
MetricSource,
} from "../core/types/Metrics";
import { queryFeatures } from "@esri/arcgis-rest-feature-layer";
Expand All @@ -15,6 +16,11 @@ import { IPredicate, IQuery } from "../search/types/IHubCatalog";
import { combineQueries } from "../search/_internal/combineQueries";
import { IHubSearchOptions } from "../search/types/IHubSearchOptions";
import { portalSearchItemsAsItems } from "../search/_internal/portalSearchItems";
import {
ITelemetryDataEntry,
ITelemetryRequestOptions,
getTelemetryReport,
} from "@esri/telemetry-reporting-client";

/**
* Resolve a Metric into an array of `IMetricFeature` objects.
Expand Down Expand Up @@ -43,6 +49,9 @@ export async function resolveMetric(
case "item-query":
return resolveItemQueryMetric(metric, context);

case "telemetry-query":
return resolveTelemetryQueryMetric(metric, context);

default:
throw new Error(`Unknown metric type passed in.`);
}
Expand Down Expand Up @@ -77,6 +86,45 @@ function resolveStaticValueMetric(
});
}

async function resolveTelemetryQueryMetric(
metric: IMetric,
context: IArcGISContext
): Promise<IResolvedMetric> {
const source = metric.source as ITelemetryQueryMetricSource;
// cut off the parent identifier from the metric id and use that
// as the output field name
const fieldName = metric.id.split("_")[0];
const requestOptions = {
...(context && context?.hubRequestOptions),
...source.telemetryContext,
} as ITelemetryRequestOptions;

// Execute the report query
const response = await getTelemetryReport(
source.requestParams,
requestOptions
);

const results = response.data.map((entry: ITelemetryDataEntry) => {
const key = Object.keys(entry)[0];
const result: IMetricFeature = {
attributes: {
id: metric.entityInfo.id,
name: metric.entityInfo.name,
type: metric.entityInfo.type,
[fieldName]: entry[key],
key,
},
};
return result;
});

return Promise.resolve({
features: results,
generatedAt: new Date().getTime(),
});
}

/**
* Exequte a query against a service and return the aggregate value
* in an `IMetricFeature`
Expand Down
85 changes: 85 additions & 0 deletions packages/common/test/metrics/resolveMetric.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
IMetric,
IQuery,
IServiceQueryMetricSource,
ITelemetryQueryMetricSource,
clearMemoizedCache,
cloneObject,
resolveMetric,
Expand All @@ -13,6 +14,7 @@ import { MOCK_AUTH } from "../mocks/mock-auth";
import * as PortalModule from "@esri/arcgis-rest-portal";
import * as FLModule from "@esri/arcgis-rest-feature-layer";
import * as PSModule from "../../src/search/_internal/portalSearchItems";
import * as TelemetryModule from "@esri/telemetry-reporting-client";

describe("resolveMetric:", () => {
let ctx: IArcGISContext;
Expand Down Expand Up @@ -313,4 +315,87 @@ describe("resolveMetric:", () => {
});
});
});

describe("telemetry-query", () => {
let querySpy: jasmine.Spy;

const telemetryMetric: IMetric = {
id: "initiativeTelemetry_cc0",
name: "Initiative Telemetry",
source: {
type: "telemetry-query",
requestParams: {
scope: {
hostname:
"a-site-of-initiatives-and-projects-qa-pre-a-hub.hubqa.arcgis.com",
},
dateRange: {
startDate: "2024-06-05T13:58:22.564Z",
endDate: "2024-07-05T13:58:22.565Z",
},
metrics: ["page-views:count"],
dimensionFilters: [
{
name: "contentId",
any: ["portal:bf7c258ad5bc46cea4c1764ca6d59955"],
},
{
name: "action",
not: ["Manage"],
},
],
timeDimension: "day",
orderBy: [
{
name: "day",
direction: "asc",
},
],
emptyRows: true,
},
telemetryContext: {},
} as ITelemetryQueryMetricSource,
entityInfo: {
id: "00f",
name: "Some Initiative Name",
type: "Hub Initiative",
},
};

const MockResponse = {
data: [{ 12345: 100 }, { 123456: 200 }],
};

beforeEach(() => {
querySpy = spyOn(TelemetryModule, "getTelemetryReport").and.callFake(
() => {
return Promise.resolve(MockResponse);
}
);
});

it("resolves a telemetry metric", async () => {
const chk = await resolveMetric(telemetryMetric, ctx);
expect(chk.features).toEqual([
{
attributes: {
id: "00f",
name: "Some Initiative Name",
type: "Hub Initiative",
initiativeTelemetry: 100,
key: "12345",
},
},
{
attributes: {
id: "00f",
name: "Some Initiative Name",
type: "Hub Initiative",
initiativeTelemetry: 200,
key: "123456",
},
},
]);
});
});
});
Loading