/// <summary> /// Proxies the specified request to the actual Store REST API. /// </summary> /// <param name="pathAndQuery"> /// The UriAbsolutePath and Query properties. Simply, this is the entire Uri except for /// the protocol and domain information. /// </param> /// <param name="method">The <see cref="HttpMethod"/> of the REST API.</param> /// <param name="onBehalfOf"> /// The <see cref="IPrincipal"/> of the user that we're performing the API request on behalf of. /// </param> /// <param name="body">The body content of the REST request (if needed).</param> /// <param name="tenantId"> /// [Optional] The tenantId that should be used for the request if the proxy supports /// multiple tenants. Mutually exclusive with <paramref name="tenantName"/> since /// <paramref name="tenantName"/> is a "friendly" version of this value. If neither this /// nor <paramref name="tenantName"/> are provided, the default TenantId will be used for /// this request. /// </param> /// <param name="tenantName"> /// [Optional] The friendly name of the <paramref name="tenantId"/> that should be used for /// the request if the proxy supports multiple tenants. Mutually exclusive with /// <paramref name="tenantId"/> since this is a friendly name version of that value. /// If neither this nor <paramref name="tenantId"/> are provided, the default TenantId will /// be used for this request. /// </param> /// <param name="endpointType">The type of endpoint that should be used for the request.</param> /// <param name="correlationId"> /// An ID that a client may have set in the header (which we must proxy) to track a string of related requests. /// </param> /// <param name="clientRequestId"> /// An ID that a client may have set in the header (which we must proxy) to track an individual request. /// </param> /// <returns>The <see cref="HttpResponseMessage"/> to be sent to the user.</returns> public static async Task <HttpResponseMessage> PerformRequestAsync( string pathAndQuery, HttpMethod method, IPrincipal onBehalfOf, string body = null, string tenantId = null, string tenantName = null, EndpointType endpointType = EndpointType.Prod, string correlationId = null, string clientRequestId = null) { // We'll track how long this takes, for telemetry purposes. Stopwatch stopwatch = Stopwatch.StartNew(); // Assume ok unless we find out otherwise. HttpStatusCode statusCode = HttpStatusCode.OK; // We want to record the request header for every request in case we need to look up failures later. string requestId = string.Empty; // We also want to store the ClientId so that it's easier to see distribution of requests across Clients. string clientId = string.Empty; try { Endpoint endpoint = null; HttpResponseMessage response; if (ProxyManager.TryGetEndpoint(tenantId, tenantName, endpointType, out endpoint, out response)) { clientId = endpoint.ClientId; response = await endpoint.PerformRequestAsync(pathAndQuery, method, onBehalfOf, body, correlationId, clientRequestId); } // We'll capture the status code for use in the finally block. statusCode = response.StatusCode; // Get any of the request ID headers that can be used for post-mortem diagnostics. We'll use them in the finally block. IEnumerable <string> headerValues; if (response.Headers.TryGetValues(ProxyManager.MSCorrelationIdHeader, out headerValues)) { // If the client supplied a correlationId, the value we're getting back from the API should be identical. correlationId = headerValues.FirstOrDefault(); } if (response.Headers.TryGetValues(ProxyManager.MSRequestIdHeader, out headerValues)) { requestId = headerValues.FirstOrDefault(); } if (response.Headers.TryGetValues(ProxyManager.MSClientRequestIdHeader, out headerValues)) { // If the client supplied a clientRequestId, the value we're getting back from the API should be identical. clientRequestId = headerValues.FirstOrDefault(); } return(response); } finally { stopwatch.Stop(); ProxyManager.LogTelemetryEvent( onBehalfOf.Identity.Name, pathAndQuery, method, tenantId, tenantName, clientId, endpointType, statusCode, correlationId, requestId, clientRequestId, stopwatch.Elapsed.TotalSeconds); } }