Skip to content
Closed
Show file tree
Hide file tree
Changes from 3 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
4 changes: 4 additions & 0 deletions spec/core/urlParsing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ const testCases = {
// Passing invalid parameters
"/me?&test&123": "https://graph.microsoft.com/v1.0/me?&test&123",
"/me?$select($select=name)": "https://graph.microsoft.com/v1.0/me?$select($select=name)",

// National cloud deployment urls
"https://graph.microsoft.us/v1.0/me": "https://graph.microsoft.us/v1.0/me",
"https://dod-graph.microsoft.us/beta/me?$filter=a": "https://dod-graph.microsoft.us/beta/me?$filter=a",
};

describe("urlParsing.ts", () => {
Expand Down
30 changes: 26 additions & 4 deletions spec/middleware/TelemetryHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import { assert } from "chai";

import { GRAPH_BASE_URL } from "../../src/Constants";
import { Context } from "../../src/IContext";
import { MiddlewareControl } from "../../src/middleware/MiddlewareControl";
import { FeatureUsageFlag, TelemetryHandlerOptions } from "../../src/middleware/options/TelemetryHandlerOptions";
Expand All @@ -29,7 +30,7 @@ describe("TelemetryHandler.ts", () => {
try {
const uuid = "dummy_uuid";
const context: Context = {
request: "url",
request: GRAPH_BASE_URL,
options: {
headers: {
"client-request-id": uuid,
Expand All @@ -47,7 +48,7 @@ describe("TelemetryHandler.ts", () => {
it("Should create client-request-id if one is not present in the request header", async () => {
try {
const context: Context = {
request: "url",
request: GRAPH_BASE_URL,
options: {
headers: {
method: "GET",
Expand All @@ -65,7 +66,7 @@ describe("TelemetryHandler.ts", () => {
it("Should set sdk version header without feature flag usage if telemetry options is not present", async () => {
try {
const context: Context = {
request: "url",
request: GRAPH_BASE_URL,
options: {
headers: {
method: "GET",
Expand All @@ -85,7 +86,7 @@ describe("TelemetryHandler.ts", () => {
const telemetryOptions = new TelemetryHandlerOptions();
telemetryOptions["setFeatureUsage"](FeatureUsageFlag.AUTHENTICATION_HANDLER_ENABLED);
const context: Context = {
request: "url",
request: GRAPH_BASE_URL,
options: {
headers: {
method: "GET",
Expand All @@ -100,6 +101,27 @@ describe("TelemetryHandler.ts", () => {
throw error;
}
});

it("Should not set telemetry for non-graph url", async () => {
try {
const context: Context = {
request: "test url",
options: {
headers: {
method: "GET",
},
},
middlewareControl: new MiddlewareControl(),
};
dummyHTTPHandler.setResponses([okayResponse]);
await telemetryHandler.execute(context);
assert.equal(context.options.headers["client-request-id"], undefined);
assert.equal(context.options.headers["SdkVersion"], undefined);
assert.equal(context.options.headers["setFeatureUsage"], undefined);
} catch (error) {
throw error;
}
});
});
/* tslint:enable: no-string-literal */
});
5 changes: 5 additions & 0 deletions src/GraphRequestUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@
*/
export const oDataQueryNames = ["$select", "$expand", "$orderby", "$filter", "$top", "$skip", "$skipToken", "$count"];

/**
* To hold list of the service root endpoints for Microsoft Graph and Graph Explorer for each national cloud.
*/
export const graphURLs = ["https://graph.microsoft.com", "https://graph.microsoft.us", "https://dod-graph.microsoft.us", "https://graph.microsoft.de", "https://microsoftgraph.chinacloudapi.cn"];

/**
* To construct the URL by appending the segments with "/"
* @param {string[]} urlSegments - The array of strings
Expand Down
50 changes: 36 additions & 14 deletions src/middleware/TelemetryHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
* @module TelemetryHandler
*/

import { graphURLs } from "../GraphRequestUtil";
import { Context } from "../IContext";
import { PACKAGE_VERSION } from "../Version";

Expand Down Expand Up @@ -64,23 +65,44 @@ export class TelemetryHandler implements Middleware {
* @param {Context} context - The context object of the request
* @returns A Promise that resolves to nothing
*/

/**
* @private
* Checks if the request url points to the Graph service endpoints
* @param {string} url - The request url string
* @returns true if request url is a Graph url
*/
private isGraphURL(url: string): boolean {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. construct a Set() object for graphURLs where the values are only the host names.
  2. construct a typed URL object for url.
  3. Have this function return the result of graphURLs.has(urlObject.hostname)

Benefits:

  1. Efficiency: Lookup is amortized O(1).
  2. Correctness: We probably want to capture telemetry for urls like: https://graph.microsoft.com:443 and current implementation will not accept URLs with port numbers.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also consider inlining the whole function as it will be a simple call to graphURLs.has(new URL(context.request).hostname)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for suggesting an optimal approach!

I have used the approach you suggested with some modifications. Implemented a set object with the host names. I haven't used the URL object as some browsers do not support of the URL object and I am extracting the host name as it is done in GraphRequest.ts during the url parsing process.

let isGraph = false;
graphURLs.forEach((element) => {
if (url.indexOf(element) !== -1) {
isGraph = true;
}
});
return isGraph;
}

public async execute(context: Context): Promise<void> {
try {
let clientRequestId: string = getRequestHeader(context.request, context.options, TelemetryHandler.CLIENT_REQUEST_ID_HEADER);
if (clientRequestId === null) {
clientRequestId = generateUUID();
setRequestHeader(context.request, context.options, TelemetryHandler.CLIENT_REQUEST_ID_HEADER, clientRequestId);
}
let sdkVersionValue: string = `${TelemetryHandler.PRODUCT_NAME}/${PACKAGE_VERSION}`;
let options: TelemetryHandlerOptions;
if (context.middlewareControl instanceof MiddlewareControl) {
options = context.middlewareControl.getMiddlewareOptions(TelemetryHandlerOptions) as TelemetryHandlerOptions;
}
if (typeof options !== "undefined") {
const featureUsage: string = options.getFeatureUsage();
sdkVersionValue += ` (${TelemetryHandler.FEATURE_USAGE_STRING}=${featureUsage})`;
if (typeof context.request === "string" && this.isGraphURL(context.request)) {
let clientRequestId: string = getRequestHeader(context.request, context.options, TelemetryHandler.CLIENT_REQUEST_ID_HEADER);
if (clientRequestId === null) {
clientRequestId = generateUUID();
setRequestHeader(context.request, context.options, TelemetryHandler.CLIENT_REQUEST_ID_HEADER, clientRequestId);
}
let sdkVersionValue: string = `${TelemetryHandler.PRODUCT_NAME}/${PACKAGE_VERSION}`;
let options: TelemetryHandlerOptions;
if (context.middlewareControl instanceof MiddlewareControl) {
options = context.middlewareControl.getMiddlewareOptions(TelemetryHandlerOptions) as TelemetryHandlerOptions;
}
if (typeof options !== "undefined") {
const featureUsage: string = options.getFeatureUsage();
sdkVersionValue += ` (${TelemetryHandler.FEATURE_USAGE_STRING}=${featureUsage})`;
}

appendRequestHeader(context.request, context.options, TelemetryHandler.SDK_VERSION_HEADER, sdkVersionValue);
}
appendRequestHeader(context.request, context.options, TelemetryHandler.SDK_VERSION_HEADER, sdkVersionValue);

return await this.nextMiddleware.execute(context);
} catch (error) {
throw error;
Expand Down