/// <summary>
        /// Retrieve the raw JSON response from the
        /// <see cref="CloudRequestEngine"/> in this pipeline, extract
        /// the data for this specific engine and populate the
        /// <code>TData</code> instance accordingly.
        /// </summary>
        /// <param name="data">
        /// The <see cref="IFlowData"/> to get the raw JSON data from.
        /// </param>
        /// <param name="aspectData">
        /// The <code>TData</code> instance to populate with values.
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// Thrown if a required parameter is null.
        /// </exception>
        protected override void ProcessEngine(IFlowData data, T aspectData)
        {
            if (data == null)
            {
                throw new ArgumentNullException(nameof(data));
            }

            CloudRequestData requestData;

            // Get requestData from CloudRequestEngine. If requestData does not
            // exist in the element data TypedKeyMap then the engine either
            // does not exist in the Pipeline or is not run before this engine.
            try
            {
                requestData = data.GetFromElement(RequestEngine.GetInstance());
            }
            catch (KeyNotFoundException ex)
            {
                throw new PipelineConfigurationException(
                          $"The '{GetType().Name}' requires a 'CloudRequestEngine' " +
                          $"before it in the Pipeline. This engine will be unable " +
                          $"to produce results until this is corrected.", ex);
            }

            // Check the requestData ProcessStarted flag which informs whether
            // the cloud request engine process method was called.
            if (requestData?.ProcessStarted == false)
            {
                throw new PipelineConfigurationException(
                          $"The '{GetType().Name}' requires a 'CloudRequestEngine' " +
                          $"before it in the Pipeline. This engine will be unable " +
                          $"to produce results until this is corrected.");
            }

            var json = requestData?.JsonResponse;

            // If the JSON is empty or null then do not Process the CloudAspectEngine.
            // Empty or null JSON indicates that an error has occurred in the
            // CloudRequestEngine. The error will have been reported by the
            // CloudRequestEngine so just log a warning that this
            // CloudAspectEngine did not process.
            if (string.IsNullOrEmpty(json) == false)
            {
                ProcessCloudEngine(data, aspectData, json);
            }
            else
            {
                Logger.LogInformation($"The '{GetType().Name}' did not process " +
                                      $"as the JSON response from the CloudRequestEngine was null " +
                                      $"or empty. Please refer to errors generated by the " +
                                      $"CloudRequestEngine in the logs as this indicates an error " +
                                      $"occurred there.");
            }
        }
        /// <summary>
        /// Retrieve the raw JSON response from the
        /// <see cref="CloudRequestEngine"/> in this pipeline, extract
        /// the data for this specific engine and populate the
        /// <code>TData</code> instance accordingly.
        /// </summary>
        /// <param name="data">
        /// The <see cref="IFlowData"/> to get the raw JSON data from.
        /// </param>
        /// <param name="aspectData">
        /// The <code>TData</code> instance to populate with values.
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// Thrown if a required parameter is null.
        /// </exception>
        protected override void ProcessEngine(IFlowData data, TData aspectData)
        {
            if (data == null)
            {
                throw new ArgumentNullException(nameof(data));
            }

            var requestData = data.GetFromElement(RequestEngine.GetInstance());
            var json        = requestData?.JsonResponse;

            if (string.IsNullOrEmpty(json))
            {
                throw new PipelineConfigurationException(
                          $"Json response from cloud request engine is null. " +
                          $"This is probably because there is not a " +
                          $"'CloudRequestEngine' before the '{GetType().Name}' " +
                          $"in the Pipeline. This engine will be unable " +
                          $"to produce results until this is corrected.");
            }
            else
            {
                // Extract data from json to the aspectData instance.
                var dictionary = JsonConvert.DeserializeObject <Dictionary <string, object> >(json);
                // Access the data relating to this engine.
                var propertyKeyed = dictionary[ElementDataKey] as JObject;
                // Access the 'Profiles' property
                foreach (var entry in propertyKeyed["profiles"])
                {
                    // Iterate through the devices, parsing each one and
                    // adding it to the result.
                    var propertyValues = JsonConvert.DeserializeObject <Dictionary <string, object> >(entry.ToString(),
                                                                                                      new JsonSerializerSettings()
                    {
                        Converters = JSON_CONVERTERS,
                    });
                    var device = CreateProfileData();
                    // Get the meta-data for properties on device instances.
                    var propertyMetaData = Properties
                                           .Single(p => p.Name == "Profiles").ItemProperties;

                    var deviceData = CreateAPVDictionary(
                        propertyValues,
                        propertyMetaData);

                    device.PopulateFromDictionary(deviceData);
                    //device.SetNoValueReasons(nullReasons);
                    aspectData.AddProfile(device);
                }
            }
        }