Beispiel #1
0
        /// <summary>
        /// Validates that the request is valid.
        /// </summary>
        /// <param name="envelopeType">Type of envelope to build.</param>
        /// <param name="scenario">(Optional) Name of the scenario we're building an envelope for.</param>
        /// <param name="requestDetails"><see cref="RequestDetails"/> instance containing additional request details.</param>
        /// <param name="logPrefix">Logging prefix to use.</param>
        /// <param name="log"><see cref="ILogger"/> instance used for logging.</param>
        /// <returns><see cref="IActionResult"/> instance or null if request is valid.</returns>
        private static IActionResult ValidateRequest(string envelopeType, string scenario, RequestDetails requestDetails, string logPrefix, ILogger log)
        {
            // Check we have a valid envelopeType
            if (!s_allowedEnvelopeTypes.Contains(envelopeType))
            {
                // Invalid EnvelopeType
                return(AzureResponseHelper.CreateFaultObjectResult(requestDetails, $"Invalid envelopeType parameter supplied - must be one of {string.Join(", ", s_allowedEnvelopeTypes)}", logPrefix, log));
            }

            // Check that ApimInstanceName is set
            if (string.IsNullOrWhiteSpace(requestDetails.ApimInstanceName))
            {
                // No ApimInstanceName set in config
                return(AzureResponseHelper.CreateFaultObjectResult(requestDetails, "No ApimInstanceName is set in config", logPrefix, log));
            }

            // Check that ApimSubscriptionKey is set
            if (string.IsNullOrWhiteSpace(requestDetails.ApimSubscriptionKey))
            {
                // No ApimSubscriptionKey set in config
                return(AzureResponseHelper.CreateFaultObjectResult(requestDetails, "No ApimSubscriptionKey is set in config", logPrefix, log));
            }

            // We need a Scenario if we have a Document envelopeType
            if (string.Compare(envelopeType, EnvelopeTypeDocument, true) == 0 && string.IsNullOrWhiteSpace(scenario))
            {
                // Missing Scenario
                return(AzureResponseHelper.CreateFaultObjectResult(requestDetails, "Scenario must be supplied if envelopeType is Document", logPrefix, log));
            }

            return(null);
        }
Beispiel #2
0
        /// <summary>
        /// Initializes a new instance of the <see cref="AzureResponseException"/> class
        /// using a <see cref="AzureResponse"/> instance.
        /// </summary>
        /// <param name="response"><see cref="AzureResponse"/> instance containing the response.</param>
        /// <param name="errorMessage">Message describing the error.</param>
        public AzureResponseException(AzureResponse response, string errorMessage) : this(errorMessage)
        {
            Response = response;

            // Build a new fault object
            Fault = AzureResponseHelper.BuildFaultMessage(ErrorMessage, response);
        }
Beispiel #3
0
        public static async Task <IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Function, "post", Route = "convertxmltojson")]
            HttpRequest req, ILogger log)
        {
            // Get Request Details
            RequestDetails requestDetails = new RequestDetails(req, LogId);

            string logPrefix = $"{LogId}:{requestDetails.TrackingId}: ";

            // Add the tracking ID to the Response Headers
            if (!string.IsNullOrWhiteSpace(requestDetails.TrackingId))
            {
                req.HttpContext.Response.Headers[HeaderConstants.AimTrackingId] = requestDetails.TrackingId;
            }

            log.LogDebug($"{logPrefix}Called with parameters messageContentType: {requestDetails.RequestContentType}, messageContentEncoding: {requestDetails.RequestContentEncoding}, messageTransferEncoding: {requestDetails.RequestTransferEncoding}, headerTrackingId: {requestDetails.TrackingId}, clearCache: {requestDetails.ClearCache}, enableTrace: {requestDetails.EnableTrace}");

            bool removeOuterEnvelope = ((string)req.Query["removeOuterEnvelope"] ?? "false") == "true";

            // Load the body into an XDocument
            XDocument xDocument;

            try
            {
                xDocument = XDocument.Parse(requestDetails.RequestBody);
            }
            catch (Exception ex)
            {
                return(await Task.FromResult <IActionResult>(AzureResponseHelper.CreateFaultObjectResult(requestDetails, $"An exception occurred trying to parse the supplied XML body - check that valid XML has been supplied", ex, logPrefix, log)));
            }

            // Remove all namespaces and prefixes from the XML (as not supported by JSON and we don't want to emit them)
            try
            {
                // Remove namespace (and prefix) from all elements and attributes
                foreach (XElement xElement in xDocument.Root.DescendantsAndSelf())
                {
                    xElement.Name = xElement.Name.LocalName;
                    var query = from xAttribute in xElement.Attributes()
                                where !xAttribute.IsNamespaceDeclaration
                                select new XAttribute(xAttribute.Name.LocalName, xAttribute.Value);
                    xElement.ReplaceAttributes(query.ToList());
                }
            }
            catch (Exception ex)
            {
                return(await Task.FromResult <IActionResult>(AzureResponseHelper.CreateFaultObjectResult(requestDetails, $"An exception occurred trying to remove namespaces and prefixes from the supplied XML", ex, logPrefix, log)));
            }

            try
            {
                JToken json = JToken.Parse(JsonConvert.SerializeXmlNode(XmlHelper.ToXmlDocument(xDocument), Newtonsoft.Json.Formatting.Indented, removeOuterEnvelope));
                return(await Task.FromResult <IActionResult>(new OkObjectResult(json)));
            }
            catch (Exception ex)
            {
                return(await Task.FromResult <IActionResult>(AzureResponseHelper.CreateFaultObjectResult(requestDetails, $"An exception occurred trying to convert XML to JSON", ex, logPrefix, log)));
            }
        }
Beispiel #4
0
        public static async Task <IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Function, "post", Route = "convertjsontoxml")]
            HttpRequest req, ILogger log)
        {
            // Get Request Details
            RequestDetails requestDetails = new RequestDetails(req, LogId);

            string logPrefix = $"{LogId}:{requestDetails.TrackingId}: ";

            // Add the tracking ID to the Response Headers
            if (!string.IsNullOrWhiteSpace(requestDetails.TrackingId))
            {
                req.HttpContext.Response.Headers[HeaderConstants.AimTrackingId] = requestDetails.TrackingId;
            }

            log.LogDebug($"{logPrefix}Called with parameters messageContentType: {requestDetails.RequestContentType}, messageContentEncoding: {requestDetails.RequestContentEncoding}, messageTransferEncoding: {requestDetails.RequestTransferEncoding}, headerTrackingId: {requestDetails.TrackingId}, clearCache: {requestDetails.ClearCache}, enableTrace: {requestDetails.EnableTrace}");

            string rootNode                      = req.Query["rootNode"];
            string rootNodeNamespace             = req.Query["rootNodeNamespace"];
            bool   writeArrayAttribute           = ((string)req.Query["writeArrayAttribute"] ?? "true") == "true";
            bool   encodeSpecialCharacters       = req.Query["encodeSpecialCharacters"] == "true";
            bool   addMessageBodyForEmptyMessage = req.Query["addMessageBodyForEmptyMessage"] == "true";

            string xmlBody = requestDetails.RequestBody;

            if (addMessageBodyForEmptyMessage && string.IsNullOrWhiteSpace(xmlBody))
            {
                xmlBody = "{}";
            }

            try
            {
                // Convert to XML
                XDocument xDocument = XmlHelper.ToXDocument(JsonConvert.DeserializeXmlNode(xmlBody, rootNode, writeArrayAttribute, encodeSpecialCharacters));
                if (!string.IsNullOrEmpty(rootNodeNamespace))
                {
                    xDocument = XmlHelper.AddNamespace(xDocument, "ns0", rootNodeNamespace);
                    xDocument = XmlHelper.UpdateRootQualifiedName(xDocument, "ns0");
                }

                // Special hack for XML content - OkObjectResult doesn't support XmlDocument in Functions at this time
                // without adding the XmlSerializerFormmatter to the list of services
                // See here: https://github.com/Azure/azure-functions-host/issues/2896
                return(await Task.FromResult <IActionResult>(new ContentResult()
                {
                    Content = xDocument.ToString(),
                    ContentType = "application/xml",
                    StatusCode = 200
                }));
            }
            catch (Exception ex)
            {
                return(await Task.FromResult <IActionResult>(AzureResponseHelper.CreateFaultObjectResult(requestDetails, $"An exception occurred trying to convert JSON to XML", ex, logPrefix, log)));
            }
        }
        /// <summary>
        /// Validates that the request is valid.
        /// </summary>
        /// <param name="requestDetails"><see cref="RequestDetails"/> instance containing additional request details.</param>
        /// <param name="logPrefix">Logging prefix to use</param>
        /// <param name="log"><see cref="ILogger"/> instance used for logging.</param>
        /// <returns><see cref="IActionResult"/> instance or null if request is valid.</returns>
        private static IActionResult ValidateRequest(RequestDetails requestDetails, string logPrefix, ILogger log)
        {
            // Check that ApimInstanceName is set
            if (string.IsNullOrWhiteSpace(requestDetails.ApimInstanceName))
            {
                // No ApimInstanceName set in config
                return(AzureResponseHelper.CreateFaultObjectResult(requestDetails, "No ApimInstanceName is set in config", logPrefix, log));
            }

            // Check that ApimSubscriptionKey is set
            if (string.IsNullOrWhiteSpace(requestDetails.ApimSubscriptionKey))
            {
                // No ApimSubscriptionKey set in config
                return(AzureResponseHelper.CreateFaultObjectResult(requestDetails, "No ApimSubscriptionKey is set in config", logPrefix, log));
            }

            return(null);
        }
Beispiel #6
0
        /// <summary>
        /// Validates that the request is valid.
        /// </summary>
        /// <param name="requestDetails"><see cref="RequestDetails"/> instance containing additional request details.</param>
        /// <param name="logPrefix">Logging prefix to use.</param>
        /// <param name="log"><see cref="ILogger"/> instance used for logging.</param>
        /// <returns><see cref="IActionResult"/> instance or null if request is valid.</returns>
        private static IActionResult ValidateRequest(RequestDetails requestDetails, string logPrefix, ILogger log)
        {
            // Check that ApimInstanceName is set
            if (string.IsNullOrWhiteSpace(requestDetails.ApimInstanceName))
            {
                // No ApimInstanceName set in config
                return(AzureResponseHelper.CreateFaultObjectResult(requestDetails, "No ApimInstanceName is set in config", logPrefix, log));
            }

            // Check that ApimSubscriptionKey is set
            if (string.IsNullOrWhiteSpace(requestDetails.ApimSubscriptionKey))
            {
                // No ApimSubscriptionKey set in config
                return(AzureResponseHelper.CreateFaultObjectResult(requestDetails, "No ApimSubscriptionKey is set in config", logPrefix, log));
            }

            // We need a Body
            if (requestDetails.RequestBody.Length == 0)
            {
                // Invalid body
                return(AzureResponseHelper.CreateFaultObjectResult(requestDetails, "A request body must be supplied", logPrefix, log));
            }

            // Request ContentType header must be supplied
            if (string.IsNullOrWhiteSpace(requestDetails.RequestContentType))
            {
                // Missing ContentType
                return(AzureResponseHelper.CreateFaultObjectResult(requestDetails, "Content-Type header must be supplied", logPrefix, log));
            }

            // Request ContentType must be JSON
            if (!requestDetails.RequestContentType.ToLower().StartsWith("text/json") && !requestDetails.RequestContentType.ToLower().StartsWith("application/json"))
            {
                // Invalid ContentType
                return(AzureResponseHelper.CreateFaultObjectResult(requestDetails, $"Content-Type header must be text/json or application/json - instead it has an unsupported value of {requestDetails.RequestContentType}", logPrefix, log));
            }

            return(null);
        }
Beispiel #7
0
 /// <summary>
 /// Builds a new NACK message envelope.
 /// </summary>
 /// <param name="ex"><see cref="Exception"/> instance containing details about an error.</param>
 /// <param name="trackingId">Tracking ID to use for this envelope.</param>
 /// <returns><see cref="JObject"/> instance representing the envelope.</returns>
 public static JObject BuildNackEnvelope(Exception ex, string trackingId = null)
 {
     return BuildNackEnvelope(ex?.Message ?? "(Unknown error)", null, AzureResponseHelper.BuildFaultMessage(ex), trackingId);
 }
Beispiel #8
0
 /// <summary>
 /// Builds a new NACK message envelope.
 /// </summary>
 /// <param name="message">Fault message.</param>
 /// <param name="code">Fault code.</param>
 /// <param name="reason">Fault reason.</param>
 /// <param name="actor">Fault actor.</param>
 /// <param name="trackingId">Tracking ID to use for this envelope.</param>
 /// <returns><see cref="JObject"/> instance representing the envelope.</returns>
 public static JObject BuildNackEnvelope(string message, string code, string reason = null, string actor = null, string trackingId = null)
 {
     return BuildNackEnvelope(message, code, AzureResponseHelper.BuildFaultMessage(message, code, reason, actor), trackingId);
 }
Beispiel #9
0
        public static async Task <IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Function, "post", Route = "decodebodycontent")]
            HttpRequest req, ILogger log)
        {
            // Get Request Details
            RequestDetails requestDetails = new RequestDetails(req, LogId);

            string logPrefix = $"{LogId}:{requestDetails.TrackingId}: ";

            // Add the tracking ID to the Response Headers
            if (!string.IsNullOrWhiteSpace(requestDetails.TrackingId))
            {
                req.HttpContext.Response.Headers[HeaderConstants.AimTrackingId] = requestDetails.TrackingId;
            }

            log.LogDebug($"{logPrefix}Called with parameters messageContentType: {requestDetails.RequestContentType}, messageContentEncoding: {requestDetails.RequestContentEncoding}, messageTransferEncoding: {requestDetails.RequestTransferEncoding}, headerTrackingId: {requestDetails.TrackingId}, clearCache: {requestDetails.ClearCache}, enableTrace: {requestDetails.EnableTrace}");

            // Validate the request
            IActionResult result = ValidateRequest(requestDetails, logPrefix, log);

            if (result != null)
            {
                return(await Task.FromResult <IActionResult>(result));
            }

            log.LogDebug($"{logPrefix}Request parameters are valid");

            // Attempt to parse the envelope
            Envelope envelope;

            try
            {
                log.LogDebug($"{logPrefix}Parsing the request body as an envelope");
                envelope = new Envelope(requestDetails);
                requestDetails.UpdateTrackingId(envelope.TrackingId);

                // Add TrackingId header
                if (!string.IsNullOrEmpty(requestDetails.TrackingId) && req?.HttpContext?.Response?.Headers?.ContainsKey(HeaderConstants.AimTrackingId) == false)
                {
                    req?.HttpContext?.Response?.Headers?.Add(HeaderConstants.AimTrackingId, requestDetails.TrackingId);
                }
            }
            catch (Exception ex)
            {
                return(await Task.FromResult <IActionResult>(AzureResponseHelper.CreateFaultObjectResult(requestDetails, $"An exception occurred trying to parse the received envelope message", ex, logPrefix, log)));
            }

            // Attempt to get the decoded root part content
            object decodedContent;

            try
            {
                decodedContent = envelope.GetDecodedRootPartContent();
            }
            catch (Exception ex)
            {
                return(await Task.FromResult <IActionResult>(AzureResponseHelper.CreateFaultObjectResult(requestDetails, $"An error occurred trying to decode the root part content", ex, logPrefix, log)));
            }

            log.LogDebug($"{logPrefix}Finished getting content - returning response");

            // Special hack for XML content - OkObjectResult doesn't support XmlDocument in Functions at this time
            // without adding the XmlSerializerFormmatter to the list of services
            // See here: https://github.com/Azure/azure-functions-host/issues/2896
            if (decodedContent is XmlDocument)
            {
                return(await Task.FromResult <IActionResult>(new ContentResult()
                {
                    Content = ((XmlDocument)decodedContent).OuterXml,
                    ContentType = "application/xml",
                    StatusCode = 200
                }));
            }

            return(await Task.FromResult <IActionResult>(new OkObjectResult(decodedContent)));
        }
Beispiel #10
0
        /// <summary>
        /// Sends the envelope to a LogicApp and returns the response, as an <see cref="IActionResult"/> instance.
        /// </summary>
        /// <param name="envelope"><see cref="Envelope"/> instance containing the envelope.</param>
        /// <param name="routeParameters"><see cref="JObject"/> instance containing the route parameters.</param>
        /// <param name="requestDetails"><see cref="RequestDetails"/> instance containing details about the request.</param>
        /// <param name="logPrefix">Logging prefix to use.</param>
        /// <param name="log"><see cref="ILogger"/> instance to use for logging.</param>
        /// <returns><see cref="Task{IActionResult}"/> instance.</returns>
        private static async Task <IActionResult> RouteToLogicApp(Envelope envelope, JObject routeParameters, RequestDetails requestDetails, string logPrefix, ILogger log)
        {
            // Get the ResourceId
            string resourceId = routeParameters["resourceId"]?.ToString();

            if (string.IsNullOrWhiteSpace(resourceId))
            {
                return(AzureResponseHelper.CreateFaultObjectResult(requestDetails, $"No resourceId is set in the parameters section for the route with index {envelope.RouteIndex}", logPrefix, log));
            }

            string[] resourceIdParts = resourceId.Split('/');
            if (resourceIdParts.Length != 3)
            {
                return(AzureResponseHelper.CreateFaultObjectResult(requestDetails, $"The resourceId that is set in the parameters section for the route with index {envelope.RouteIndex} is malformed - expected 3 parts, but received only {resourceIdParts.Length} parts", logPrefix, log));
            }

            string resourceGroupName = resourceIdParts[1];
            string logicAppName      = resourceIdParts[2];

            Uri logicAppCallbackUri;

            // Get the logicApps callback URL
            try
            {
                logicAppCallbackUri = await new ApimRestClient(requestDetails).GetLogicAppCallbackUrlAsync(resourceGroupName, logicAppName);
                log.LogDebug($"{logPrefix}Retrieved CallbackUrl for LogicApp {logicAppName}");
            }
            catch (AzureResponseException arex)
            {
                // Exception occurred
                return(AzureResponseHelper.CreateFaultObjectResult($"An AzureResponseException error occurred calling APIM to get a LogicApp URL for the route with index {envelope.RouteIndex}", arex, logPrefix, log));
            }
            catch (Exception ex)
            {
                // Exception occurred
                return(AzureResponseHelper.CreateFaultObjectResult(requestDetails, $"An error occurred calling APIM to get a LogicApp URL for the route with index {envelope.RouteIndex}", ex, logPrefix, log));
            }

            // Build headers
            IDictionary <string, string> headers = new Dictionary <string, string>();

            headers[HeaderConstants.AimClearCache]    = requestDetails.ClearCache.ToString();
            headers[HeaderConstants.AimEnableTracing] = requestDetails.EnableTrace.ToString();

            AzureResponse response;

            try
            {
                // Call the LogicApp
                log.LogDebug($"{logPrefix}Calling LogicApp {logicAppName} using Url {logicAppCallbackUri}");
                response = await new AzureRestClient(requestDetails, AzureAuthenticationEndpoints.LogicApps).SendAsync(HttpMethod.Post, logicAppCallbackUri, headers, envelope.ToString());
                log.LogDebug($"{logPrefix}Finished calling LogicApp {logicAppName} - return StatusCode is {response?.StatusCode}");
            }
            catch (AzureResponseException arex)
            {
                // Exception occurred
                return(AzureResponseHelper.CreateFaultObjectResult($"An AzureResponseException error occurred calling the LogicApp {logicAppName}", arex, logPrefix, log));
            }
            catch (Exception ex)
            {
                // Exception occurred
                return(AzureResponseHelper.CreateFaultObjectResult(requestDetails, $"An error occurred calling the LogicApp {logicAppName}", ex, logPrefix, log));
            }

            // The response from the LogicApp should be 200 (ACK or NACK).
            // If it's anything else (e.g. a 500) then it's a fault, and an envelope will need to be created.
            // Look at the StatusCode and the ResponseContent and work out what to return
            return(AzureResponseHelper.GenerateRouteResponse(requestDetails, response, "LogicApp", logicAppName, logPrefix, log));
        }
Beispiel #11
0
 /// <summary>
 /// Builds a new NACK message envelope.
 /// </summary>
 /// <param name="message">Fault message.</param>
 /// <param name="code">Fault code.</param>
 /// <param name="ex"><see cref="Exception"/> instance containing details about an error.</param>
 /// <param name="trackingId">Tracking ID to use for this envelope.</param>
 /// <returns><see cref="JObject"/> instance representing the envelope.</returns>
 public static JObject BuildNackEnvelope(string message, string code, Exception ex, string trackingId = null)
 {
     return BuildNackEnvelope(message, code, AzureResponseHelper.BuildFaultMessage(ex), trackingId);
 }
Beispiel #12
0
        public static async Task <IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Function, "post", Route = "buildenvelope/{envelopeType}/{scenario}")]
            HttpRequest req, string envelopeType, string scenario, ILogger log)
        {
            // Get Request Details
            RequestDetails requestDetails = new RequestDetails(req, LogId);

            string logPrefix = $"{LogId}:{requestDetails.TrackingId}: ";

            // Add the tracking ID to the Response Headers
            if (!string.IsNullOrWhiteSpace(requestDetails.TrackingId))
            {
                req.HttpContext.Response.Headers[HeaderConstants.AimTrackingId] = requestDetails.TrackingId;
            }

            log.LogDebug($"{logPrefix}Called with parameters envelopeType: {envelopeType}, scenario: {scenario}, messageContentType: {requestDetails.RequestContentType}, messageContentEncoding: {requestDetails.RequestContentEncoding}, messageTransferEncoding: {requestDetails.RequestTransferEncoding}, headerTrackingId: {requestDetails.TrackingId}, clearCache: {requestDetails.ClearCache}, enableTrace: {requestDetails.EnableTrace}");

            envelopeType = envelopeType.ToLower();

            // Validate the request
            IActionResult result = ValidateRequest(envelopeType, scenario, requestDetails, logPrefix, log);

            if (result != null)
            {
                return(result);
            }

            log.LogDebug($"{logPrefix}Request parameters are valid");

            JObject envelope;

            // Switch by envelopeType
            switch (envelopeType)
            {
            case EnvelopeTypeAck:
            {
                log.LogDebug($"{logPrefix}Building an ACK envelope");
                envelope = EnvelopeBuilder.BuildAckEnvelope(requestDetails);
                break;
            }

            case EnvelopeTypeNack:
            {
                log.LogDebug($"{logPrefix}Building a NACK envelope");
                envelope = EnvelopeBuilder.BuildNackEnvelope(requestDetails);
                break;
            }

            case EnvelopeTypeDocument:
            {
                try
                {
                    log.LogDebug($"{logPrefix}Building a Content envelope");
                    envelope = await BuildDocumentEnvelopeAsync(scenario, requestDetails, logPrefix, log);

                    // Update the request TrackingId if need be
                    requestDetails.UpdateTrackingId(envelope?["header"]?["properties"]?["trackingId"]?.Value <string>());
                }
                catch (AzureResponseException arex)
                {
                    // Exception occurred
                    AzureResponseHelper.SetCustomResponseHeaders(req, arex);
                    return(AzureResponseHelper.CreateFaultObjectResult("An AzureResponseException error occurred building a Document envelope", arex, logPrefix, log));
                }
                catch (Exception ex)
                {
                    // Exception occurred
                    return(AzureResponseHelper.CreateFaultObjectResult(requestDetails, "An error occurred building a Document envelope", ex, logPrefix, log));
                }
                break;
            }

            default:
            {
                // Invalid EnvelopeType
                return(AzureResponseHelper.CreateFaultObjectResult(requestDetails, $"Invalid envelopeType parameter supplied - must be one of {string.Join(", ", s_allowedEnvelopeTypes)}", logPrefix, log));
            }
            }

            // Add TrackingId header
            if (!string.IsNullOrEmpty(requestDetails.TrackingId) && req?.HttpContext?.Response?.Headers?.ContainsKey(HeaderConstants.AimTrackingId) == false)
            {
                req?.HttpContext?.Response?.Headers?.Add(HeaderConstants.AimTrackingId, requestDetails.TrackingId);
            }

            log.LogDebug($"{logPrefix}Finished building envelope - returning response");

            return(new OkObjectResult(envelope));
        }
Beispiel #13
0
        public static async Task <IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Function, "post", Route = "wrapxmlenvelope")]
            HttpRequest req, ILogger log)
        {
            // Get Request Details
            RequestDetails requestDetails = new RequestDetails(req, LogId);

            string logPrefix = $"{LogId}:{requestDetails.TrackingId}: ";

            // Add the tracking ID to the Response Headers
            if (!string.IsNullOrWhiteSpace(requestDetails.TrackingId))
            {
                req.HttpContext.Response.Headers[HeaderConstants.AimTrackingId] = requestDetails.TrackingId;
            }

            log.LogDebug($"{logPrefix}Called with parameters messageContentType: {requestDetails.RequestContentType}, messageContentEncoding: {requestDetails.RequestContentEncoding}, messageTransferEncoding: {requestDetails.RequestTransferEncoding}, headerTrackingId: {requestDetails.TrackingId}, clearCache: {requestDetails.ClearCache}, enableTrace: {requestDetails.EnableTrace}");

            bool   emitXmlDeclaration = ((string)req.Query["emitXmlDeclaration"] ?? "false") == "true";
            string envelopeSpecNames  = req.Query["envelopeSpecNames"];

            // Validate the request
            IActionResult result = ValidateRequest(requestDetails, logPrefix, log);

            if (result != null)
            {
                return(await Task.FromResult <IActionResult>(result));
            }

            if (string.IsNullOrWhiteSpace(envelopeSpecNames))
            {
                return(await Task.FromResult <IActionResult>(AzureResponseHelper.CreateFaultObjectResult(requestDetails, $"The envelopeSpecNames query string parameter is blank - this needs to be supplied and populated with a least one envelope SpecName", logPrefix, log)));
            }

            log.LogDebug($"{logPrefix}Request parameters are valid");

            // Check that BodyContent is XML
            if ((string.Compare(requestDetails.RequestContentType, "text/xml", true) != 0) && (string.Compare(requestDetails.RequestContentType, "application/xml", true) != 0))
            {
                return(await Task.FromResult <IActionResult>(AzureResponseHelper.CreateFaultObjectResult(requestDetails, $"Expected an XML content type, but instead have been supplied a ContentType of {(string.IsNullOrWhiteSpace(requestDetails.RequestContentType) ? "text" : requestDetails.RequestContentType)}", logPrefix, log)));
            }

            // Attempt to load the content into an XDocument
            XDocument bodyDocument;

            try
            {
                log.LogDebug($"{logPrefix}Parsing the request body as an XDocument");
                bodyDocument = XDocument.Parse(requestDetails.RequestBody);

                // Add TrackingId header
                if (!string.IsNullOrEmpty(requestDetails.TrackingId) && req?.HttpContext?.Response?.Headers?.ContainsKey(HeaderConstants.AimTrackingId) == false)
                {
                    req?.HttpContext?.Response?.Headers?.Add(HeaderConstants.AimTrackingId, requestDetails.TrackingId);
                }
            }
            catch (Exception ex)
            {
                return(await Task.FromResult <IActionResult>(AzureResponseHelper.CreateFaultObjectResult(requestDetails, $"An exception occurred trying to parse the received XML body into an XDocument", ex, logPrefix, log)));
            }

            // Attempt to get the first EnvelopeSchema
            string envelopeSchemaName = envelopeSpecNames.Split(",")[0];
            string envelopeSchemaContent;

            try
            {
                log.LogDebug($"{logPrefix}Getting schema content from IntegrationAccount");
                envelopeSchemaContent = await new ApimRestClient(requestDetails).GetSchemaContentByNameAsync(envelopeSchemaName);
            }
            catch (AzureResponseException arex)
            {
                // Exception occurred
                AzureResponseHelper.SetCustomResponseHeaders(req, arex);
                return(AzureResponseHelper.CreateFaultObjectResult("An AzureResponseException error occurred calling APIM", arex, logPrefix, log));
            }
            catch (Exception ex)
            {
                // Exception occurred
                return(AzureResponseHelper.CreateFaultObjectResult(requestDetails, "An error occurred calling APIM", ex, logPrefix, log));
            }

            // TODO: Load the XSD into an XmlSchema class
            // TODO: Check the BizTalk IsEnvelope property is set
            // TODO: Check the Body XPath value is set
            // TODO: Generate an instance of the envelope
            // TODO: Select the body node using the Body xPath
            // TODO: Support the emitXmlDeclarationProperty
            // TODO: Insert body content
            // TODO: Return this content

            XDocument xmlEnvelope = bodyDocument;

            log.LogDebug($"{logPrefix}Finished wrapping content - returning response");

            // Special hack for XML content - OkObjectResult doesn't support XmlDocument in Functions at this time
            // without adding the XmlSerializerFormmatter to the list of services
            // See here: https://github.com/Azure/azure-functions-host/issues/2896
            return(await Task.FromResult <IActionResult>(new ContentResult()
            {
                Content = xmlEnvelope.ToString(),
                ContentType = "application/xml",
                StatusCode = 200
            }));
        }
Beispiel #14
0
        public static async Task <IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Function, "post", Route = "mergeproperties")]
            HttpRequest req, ILogger log)
        {
            // Get Request Details
            RequestDetails requestDetails = new RequestDetails(req, LogId);

            string logPrefix = $"{LogId}:{requestDetails.TrackingId}: ";

            // Add the tracking ID to the Response Headers
            if (!string.IsNullOrWhiteSpace(requestDetails.TrackingId))
            {
                req.HttpContext.Response.Headers[HeaderConstants.AimTrackingId] = requestDetails.TrackingId;
            }

            log.LogDebug($"{logPrefix}Called with parameters messageContentType: {requestDetails.RequestContentType}, messageContentEncoding: {requestDetails.RequestContentEncoding}, messageTransferEncoding: {requestDetails.RequestTransferEncoding}, headerTrackingId: {requestDetails.TrackingId}, clearCache: {requestDetails.ClearCache}, enableTrace: {requestDetails.EnableTrace}");

            // Validate the request
            IActionResult result = ValidateRequest(requestDetails, logPrefix, log);

            if (result != null)
            {
                return(await Task.FromResult <IActionResult>(result));
            }

            log.LogDebug($"{logPrefix}Request parameters are valid");

            // We expect the body to be an array of property bags
            // e.g.
            // [
            //    {
            //       "properties":
            //        {
            //            "property1": "value1"
            //        }
            //    },
            //    {
            //       "properties":
            //        {
            //            "property2": "value2"
            //        }
            //    }
            // ]

            // Check we have a JSON array in the body content
            if (!(requestDetails.RequestBodyAsJson is JArray))
            {
                // We need an array to be supplied
                return(await Task.FromResult <IActionResult>(AzureResponseHelper.CreateFaultObjectResult(requestDetails, "The supplied request body is not a JSON array", logPrefix, log)));
            }

            JArray propertiesArray = requestDetails.RequestBodyAsJson as JArray;

            // Check we have at least one element in the array
            if (propertiesArray.Count == 0)
            {
                // We need at least one element in the supplied array
                return(await Task.FromResult <IActionResult>(AzureResponseHelper.CreateFaultObjectResult(requestDetails, "The supplied request body contains an empty JSON array", logPrefix, log)));
            }

            // Check that all the array elements are JSON Objects
            if (!propertiesArray.All(j => j is JObject))
            {
                // All elements in the array must be JSON Objects
                return(await Task.FromResult <IActionResult>(AzureResponseHelper.CreateFaultObjectResult(requestDetails, "All elements in the supplied JSON Array must be JSON Objects", logPrefix, log)));
            }

            JObject firstElement = propertiesArray[0] as JObject;

            log.LogDebug($"{logPrefix}Merging property objects");

            try
            {
                // Merge every subsequent element in the array (other than the first) with the first array element
                for (int arrayIndex = 1; arrayIndex < propertiesArray.Count; arrayIndex++)
                {
                    firstElement.Merge(propertiesArray[arrayIndex] as JObject, new JsonMergeSettings
                    {
                        // Union array values together to avoid duplicates
                        MergeArrayHandling = MergeArrayHandling.Union
                    });
                }
            }
            catch (Exception ex)
            {
                return(await Task.FromResult <IActionResult>(AzureResponseHelper.CreateFaultObjectResult(requestDetails, $"An exception occurred trying to merge the property bags", ex, logPrefix, log)));
            }

            return(await Task.FromResult <IActionResult>(new OkObjectResult(firstElement)));
        }
        public static async Task <IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Function, "post", Route = "resolveroutingproperties")]
            HttpRequest req, ILogger log)
        {
            // Get Request Details
            RequestDetails requestDetails = new RequestDetails(req, LogId);

            string logPrefix = $"{LogId}:{requestDetails.TrackingId}: ";

            // Add the tracking ID to the Response Headers
            if (!string.IsNullOrWhiteSpace(requestDetails.TrackingId))
            {
                req.HttpContext.Response.Headers[HeaderConstants.AimTrackingId] = requestDetails.TrackingId;
            }

            log.LogDebug($"{logPrefix}called with parameters messageContentType: {requestDetails.RequestContentType}, messageContentEncoding: {requestDetails.RequestContentEncoding}, messageTransferEncoding: {requestDetails.RequestTransferEncoding}, headerTrackingId: {requestDetails.TrackingId}, clearCache: {requestDetails.ClearCache}, enableTrace: {requestDetails.EnableTrace}");

            // Validate the request
            IActionResult result = ValidateRequest(requestDetails, logPrefix, log);

            if (result != null)
            {
                return(result);
            }

            log.LogDebug($"{logPrefix}Request parameters are valid");

            // Attempt to load the envelope
            Envelope envelope;

            try
            {
                log.LogDebug($"{logPrefix}Parsing the request body as an envelope");
                envelope = new Envelope(requestDetails);
                requestDetails.UpdateTrackingId(envelope.TrackingId);

                // Add TrackingId header
                if (!string.IsNullOrEmpty(requestDetails.TrackingId) && req?.HttpContext?.Response?.Headers?.ContainsKey(HeaderConstants.AimTrackingId) == false)
                {
                    req?.HttpContext?.Response?.Headers?.Add(HeaderConstants.AimTrackingId, requestDetails.TrackingId);
                }
            }
            catch (Exception ex)
            {
                return(AzureResponseHelper.CreateFaultObjectResult(requestDetails, $"An exception occurred trying to parse the received envelope message", ex, logPrefix, log));
            }

            // Check if we have a ScenarioName
            if (string.IsNullOrWhiteSpace(envelope.Scenario))
            {
                // Missing ScenarioName
                return(AzureResponseHelper.CreateFaultObjectResult(requestDetails, "ScenarioName cannot be found in the request body", logPrefix, log));
            }

            // Get the RoutingProperties from config
            JObject routingProperties;

            try
            {
                log.LogDebug($"{logPrefix}Getting full RoutingSlip from config");
                routingProperties = await new ApimRestClient(requestDetails).GetRoutingPropertiesAsync(envelope.Scenario);
            }
            catch (AzureResponseException arex)
            {
                // Exception occurred
                AzureResponseHelper.SetCustomResponseHeaders(req, arex);
                return(AzureResponseHelper.CreateFaultObjectResult("An AzureResponseException error occurred calling APIM", arex, logPrefix, log));
            }
            catch (Exception ex)
            {
                // Exception occurred
                return(AzureResponseHelper.CreateFaultObjectResult(requestDetails, "An error occurred calling APIM", ex, logPrefix, log));
            }

            try
            {
                RoutingPropertyHelper.CalculateRoutingProperties(routingProperties, envelope);
            }
            catch (Exception ex)
            {
                // Exception occurred
                return(AzureResponseHelper.CreateFaultObjectResult(requestDetails, "An error occurred calculating routing properties", ex, logPrefix, log));
            }

            log.LogDebug($"{logPrefix}Finished calculating routing properties - returning response");

            return(new OkObjectResult(envelope.ToJObject()));
        }
Beispiel #16
0
 /// <summary>
 /// Builds a new NACK message envelope.
 /// </summary>
 /// <param name="message">Fault message.</param>
 /// <param name="arex"><see cref="AzureResponseException"/> instance containing details about an error.</param>
 /// <returns><see cref="JObject"/> instance representing the envelope.</returns>
 public static JObject BuildNackEnvelope(string message, AzureResponseException arex)
 {
     return BuildNackEnvelope(message, (arex?.Response == null) ? "(Unknown)" : arex.Response?.StatusCode.ToString(), AzureResponseHelper.BuildFaultMessage(arex), arex.Response.TrackingId);
 }
Beispiel #17
0
 /// <summary>
 /// Builds a new NACK message envelope.
 /// </summary>
 /// <param name="message">Fault message.</param>
 /// <param name="code">Fault code.</param>
 /// <param name="arex"><see cref="AzureResponseException"/> instance containing details about an error.</param>
 /// <returns><see cref="JObject"/> instance representing the envelope.</returns>
 public static JObject BuildNackEnvelope(string message, string code, AzureResponseException arex)
 {
     return BuildNackEnvelope(message, code, AzureResponseHelper.BuildFaultMessage(arex), arex.Response.TrackingId);
 }
Beispiel #18
0
        public static async Task <IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Function, "post", Route = "sendtonextroute")]
            HttpRequest req, ILogger log)
        {
            // Get Request Details
            RequestDetails requestDetails = new RequestDetails(req, LogId);

            string logPrefix = $"{LogId}:{requestDetails.TrackingId}: ";

            // Add the tracking ID to the Response Headers
            if (!string.IsNullOrWhiteSpace(requestDetails.TrackingId))
            {
                req.HttpContext.Response.Headers[HeaderConstants.AimTrackingId] = requestDetails.TrackingId;
            }

            log.LogDebug($"{logPrefix}Called with parameters messageContentType: {requestDetails.RequestContentType}, messageContentEncoding: {requestDetails.RequestContentEncoding}, messageTransferEncoding: {requestDetails.RequestTransferEncoding}, headerTrackingId: {logPrefix}, clearCache: {requestDetails.ClearCache}, enableTrace: {requestDetails.EnableTrace}");

            // Validate the request
            IActionResult result = ValidateRequest(requestDetails, logPrefix, log);

            if (result != null)
            {
                return(result);
            }

            log.LogDebug($"{logPrefix}Request parameters are valid");

            // Attempt to load the envelope
            Envelope envelope;

            try
            {
                log.LogDebug($"{logPrefix}Parsing the request body as an envelope");
                envelope = new Envelope(requestDetails);
                requestDetails.UpdateTrackingId(envelope.TrackingId);

                // Add TrackingId header
                if (!string.IsNullOrEmpty(logPrefix) && req?.HttpContext?.Response?.Headers?.ContainsKey(HeaderConstants.AimTrackingId) == false)
                {
                    req?.HttpContext?.Response?.Headers?.Add(HeaderConstants.AimTrackingId, logPrefix);
                }
            }
            catch (Exception ex)
            {
                return(AzureResponseHelper.CreateFaultObjectResult(requestDetails, $"An exception occurred trying to parse the received envelope message", ex, logPrefix, log));
            }

            // We need a ScenarioName
            if (string.IsNullOrWhiteSpace(envelope.Scenario))
            {
                // Missing ScenarioName
                return(AzureResponseHelper.CreateFaultObjectResult(requestDetails, "ScenarioName cannot be found in the request body", logPrefix, log));
            }

            // Check we have a routeIndex
            if (envelope.RouteIndex == null)
            {
                // Invalid RouteIndex
                return(AzureResponseHelper.CreateFaultObjectResult(requestDetails, "RouteIndex is blank or not a number", logPrefix, log));
            }

            // RouteIndex must not be a negative number
            if (envelope.RouteIndex < 0)
            {
                // Invalid RouteIndex
                return(AzureResponseHelper.CreateFaultObjectResult(requestDetails, "RouteIndex must be 0 or greater", logPrefix, log));
            }

            // Check if we have any routes
            if (envelope.Routes == null)
            {
                // No routes to process - return an ACK
                log.LogDebug($"{logPrefix}No routes to process - returning an ACK");
                return(new OkObjectResult(EnvelopeBuilder.BuildAckEnvelope("No routes to process", null, logPrefix)));
            }

            // Check if we're past the last route
            if (envelope.NextRoute == null)
            {
                // No more routes - return an ACK
                log.LogDebug($"{logPrefix}Have processed all routes - returning an ACK");
                return(new OkObjectResult(EnvelopeBuilder.BuildAckEnvelope("Finished processing all routes", null, logPrefix)));
            }

            // Get the full RoutingSlip (including the routing parameters) from App Configuration
            JObject routingSlip;

            try
            {
                log.LogDebug($"{logPrefix}Getting the full RoutingSlip from config");
                routingSlip = await new ApimRestClient(requestDetails).GetRoutingSlipAsync(envelope.Scenario);
            }
            catch (AzureResponseException arex)
            {
                // Exception occurred
                return(AzureResponseHelper.CreateFaultObjectResult($"An AzureResponseException occurred calling APIM to get a RoutingSlip for scenario {envelope.Scenario}", arex, logPrefix, log));
            }
            catch (Exception ex)
            {
                // Exception occurred
                return(AzureResponseHelper.CreateFaultObjectResult(requestDetails, $"An exception occurred calling APIM to get a RoutingSlip for scenario {envelope.Scenario}", ex, logPrefix, log));
            }

            // Get the array of routes
            JArray routes = (JArray)routingSlip?.First?.First;

            // Get the current route
            JObject currentRoute = routes[envelope.RouteIndex] as JObject;

            if (currentRoute == null)
            {
                return(AzureResponseHelper.CreateFaultObjectResult(requestDetails, $"Unable to find the current route, with routeIndex {envelope.RouteIndex}", logPrefix, log));
            }

            // Get the route parameters
            JObject routingParameters = currentRoute["routingParameters"] as JObject;

            if (routingParameters == null)
            {
                return(AzureResponseHelper.CreateFaultObjectResult(requestDetails, $"Unable to find any parameters for the route with index {envelope.RouteIndex}", logPrefix, log));
            }

            // Get the message receiver type
            string messageReceiverType = routingParameters?["messageReceiverType"]?.ToString();

            if (string.IsNullOrWhiteSpace(messageReceiverType))
            {
                return(AzureResponseHelper.CreateFaultObjectResult(requestDetails, $"No MessageReceiverType is set for the route with index {envelope.RouteIndex}", logPrefix, log));
            }
            log.LogDebug($"{logPrefix}Next route MessageReceiverType is {messageReceiverType}");

            JObject routeParameters = routingParameters?["parameters"] as JObject;

            // Increment the routeIndex
            envelope.IncrementRouteIndex();

            // Switch by messageReceiverType
            switch (messageReceiverType.ToLower())
            {
            case "microsoft.workflows.azurelogicapp":
            {
                log.LogDebug($"{logPrefix}Calling the next route LogicApp");
                return(await RouteToLogicApp(envelope, routeParameters, requestDetails, logPrefix, log));
            }

            default:
            {
                return(AzureResponseHelper.CreateFaultObjectResult(requestDetails, $"Unsupported MessageReceiverType value of {messageReceiverType}", logPrefix, log));
            }
            }
        }
Beispiel #19
0
        public static async Task <IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Function, "post", Route = "getbodycontent")]
            HttpRequest req, ILogger log)
        {
            // Get Request Details
            RequestDetails requestDetails = new RequestDetails(req, LogId);

            string logPrefix = $"{LogId}:{requestDetails.TrackingId}: ";

            // Add the tracking ID to the Response Headers
            if (!string.IsNullOrWhiteSpace(requestDetails.TrackingId))
            {
                req.HttpContext.Response.Headers[HeaderConstants.AimTrackingId] = requestDetails.TrackingId;
            }

            log.LogDebug($"{logPrefix}Called with parameters messageContentType: {requestDetails.RequestContentType}, messageContentEncoding: {requestDetails.RequestContentEncoding}, messageTransferEncoding: {requestDetails.RequestTransferEncoding}, headerTrackingId: {requestDetails.TrackingId}, clearCache: {requestDetails.ClearCache}, enableTrace: {requestDetails.EnableTrace}");

            // Validate the request
            IActionResult result = ValidateRequest(requestDetails, logPrefix, log);

            if (result != null)
            {
                return(await Task.FromResult <IActionResult>(result));
            }

            log.LogDebug($"{logPrefix}Request parameters are valid");

            // Attempt to parse the envelope
            Envelope envelope;

            try
            {
                log.LogDebug($"{logPrefix}Parsing the request body as an envelope");
                envelope = new Envelope(requestDetails);
                requestDetails.UpdateTrackingId(envelope.TrackingId);

                // Add TrackingId header
                if (!string.IsNullOrEmpty(requestDetails.TrackingId) && req?.HttpContext?.Response?.Headers?.ContainsKey(HeaderConstants.AimTrackingId) == false)
                {
                    req?.HttpContext?.Response?.Headers?.Add(HeaderConstants.AimTrackingId, requestDetails.TrackingId);
                }
            }
            catch (Exception ex)
            {
                return(await Task.FromResult <IActionResult>(AzureResponseHelper.CreateFaultObjectResult(requestDetails, $"An exception occurred trying to parse the received envelope message", ex, logPrefix, log)));
            }

            // Attempt to get the encoded root part content
            JObject decodedContent;

            try
            {
                decodedContent = envelope.GetEncodedRootPartContent();
            }
            catch (Exception ex)
            {
                return(await Task.FromResult <IActionResult>(AzureResponseHelper.CreateFaultObjectResult(requestDetails, $"An error occurred trying to encode the root part content", ex, logPrefix, log)));
            }

            log.LogDebug($"{logPrefix}Finished getting content - returning response");

            return(await Task.FromResult <IActionResult>(new OkObjectResult(decodedContent)));
        }