示例#1
0
        private static async Task <ClientResponse <MembershipContainerPage> > GetFilteredMembershipPageAsync(
            HttpClient client, string serviceUrl, string consumerKey, string consumerSecret,
            string contentType, SignatureMethod signatureMethod,
            EventHandler <Newtonsoft.Json.Serialization.ErrorEventArgs> deserializationErrorHandler)
        {
            try
            {
                client.DefaultRequestHeaders.Accept.Clear();
                client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(contentType));

                await SecuredClient.SignRequest(client, HttpMethod.Get, serviceUrl, new StringContent(string.Empty), consumerKey,
                                                consumerSecret, signatureMethod);

                var outcomeResponse = new ClientResponse <MembershipContainerPage>();
                try
                {
                    using (var response = await client.GetAsync(serviceUrl).ConfigureAwait(false))
                    {
                        outcomeResponse.StatusCode = response.StatusCode;
                        if (response.StatusCode == HttpStatusCode.OK)
                        {
                            outcomeResponse.Response = await response.DeserializeJsonObjectAsync <MembershipContainerPage>(deserializationErrorHandler)
                                                       .ConfigureAwait(false);
                        }
#if DEBUG
                        outcomeResponse.HttpRequest = await response.RequestMessage.ToFormattedRequestStringAsync()
                                                      .ConfigureAwait(false);

                        outcomeResponse.HttpResponse = await response.ToFormattedResponseStringAsync()
                                                       .ConfigureAwait(false);
#endif
                    }
                }
                catch (HttpRequestException ex)
                {
                    outcomeResponse.Exception  = ex;
                    outcomeResponse.StatusCode = HttpStatusCode.BadRequest;
                }
                catch (Exception ex)
                {
                    outcomeResponse.Exception  = ex;
                    outcomeResponse.StatusCode = HttpStatusCode.InternalServerError;
                }
                return(outcomeResponse);
            }
            catch (Exception ex)
            {
                return(new ClientResponse <MembershipContainerPage>
                {
                    Exception = ex,
                    StatusCode = HttpStatusCode.InternalServerError
                });
            }
        }
示例#2
0
        private static async Task <ClientResponse <T> > PostOutcomeAsync <T>(HttpClient client, string serviceUrl,
                                                                             string consumerKey, string consumerSecret, T content, string contentType, SignatureMethod signatureMethod,
                                                                             EventHandler <Newtonsoft.Json.Serialization.ErrorEventArgs> deserializationErrorHandler) where T : class
        {
            try
            {
                HttpRequestMessage webRequest = new HttpRequestMessage(HttpMethod.Post, serviceUrl)
                {
                    Content = new StringContent(content.ToJsonLdString(), Encoding.UTF8, contentType)
                };
                await SecuredClient.SignRequest(client, webRequest, consumerKey, consumerSecret, signatureMethod).ConfigureAwait(false);

                var outcomeResponse = new ClientResponse <T>();
                try
                {
                    using (var response = await client.SendAsync(webRequest).ConfigureAwait(false))
                    {
                        outcomeResponse.StatusCode = response.StatusCode;
                        if (response.StatusCode == HttpStatusCode.Created)
                        {
                            outcomeResponse.Response = await response.DeserializeJsonObjectAsync <T>(deserializationErrorHandler).ConfigureAwait(false);
                        }
#if DEBUG
                        outcomeResponse.HttpRequest = await response.RequestMessage.ToFormattedRequestStringAsync().ConfigureAwait(false);

                        outcomeResponse.HttpResponse = await response.ToFormattedResponseStringAsync().ConfigureAwait(false);
#endif
                    }
                }
                catch (HttpRequestException ex)
                {
                    outcomeResponse.Exception  = ex;
                    outcomeResponse.StatusCode = HttpStatusCode.BadRequest;
                }
                catch (Exception ex)
                {
                    outcomeResponse.Exception  = ex;
                    outcomeResponse.StatusCode = HttpStatusCode.InternalServerError;
                }
                return(outcomeResponse);
            }
            catch (Exception ex)
            {
                return(new ClientResponse <T>
                {
                    Exception = ex,
                    StatusCode = HttpStatusCode.InternalServerError
                });
            }
        }
示例#3
0
        private static async Task <ClientResponse <T> > GetOutcomeAsync <T>(HttpClient client, string serviceUrl, string consumerKey,
                                                                            string consumerSecret, string contentType, SignatureMethod signatureMethod) where T : class
        {
            try
            {
                client.DefaultRequestHeaders.Accept.Clear();
                client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(contentType));

                await SecuredClient.SignRequest(client, HttpMethod.Get, serviceUrl, new StringContent(string.Empty), consumerKey, consumerSecret, signatureMethod);

                var outcomeResponse = new ClientResponse <T>();
                try
                {
                    using (var response = await client.GetAsync(serviceUrl))
                    {
                        outcomeResponse.StatusCode = response.StatusCode;
                        if (response.StatusCode == HttpStatusCode.OK)
                        {
                            outcomeResponse.Response = await response.DeserializeJsonObjectAsync <T>();
                        }
#if DEBUG
                        outcomeResponse.HttpRequest = await response.RequestMessage.ToFormattedRequestStringAsync();

                        outcomeResponse.HttpResponse = await response.ToFormattedResponseStringAsync();
#endif
                    }
                }
                catch (HttpRequestException ex)
                {
                    outcomeResponse.Exception  = ex;
                    outcomeResponse.StatusCode = HttpStatusCode.BadRequest;
                }
                catch (Exception ex)
                {
                    outcomeResponse.Exception  = ex;
                    outcomeResponse.StatusCode = HttpStatusCode.InternalServerError;
                }
                return(outcomeResponse);
            }
            catch (Exception ex)
            {
                return(new ClientResponse <T>
                {
                    Exception = ex,
                    StatusCode = HttpStatusCode.InternalServerError
                });
            }
        }
示例#4
0
        private static async Task <ClientResponse <T> > PostOutcomeAsync <T>(HttpClient client, string serviceUrl,
                                                                             string consumerKey, string consumerSecret, T content, string contentType, SignatureMethod signatureMethod) where T : class
        {
            try
            {
                var httpContent = new StringContent(content.ToJsonLdString(), Encoding.UTF8, contentType);

                await SecuredClient.SignRequest(client, HttpMethod.Post, serviceUrl, httpContent, consumerKey, consumerSecret, signatureMethod);

                var outcomeResponse = new ClientResponse <T>();
                try
                {
                    using (var response = await client.PostAsync(serviceUrl, httpContent))
                    {
                        outcomeResponse.StatusCode = response.StatusCode;
                        if (response.StatusCode == HttpStatusCode.Created)
                        {
                            outcomeResponse.Response = await response.DeserializeJsonObjectAsync <T>();
                        }
#if DEBUG
                        outcomeResponse.HttpRequest = await response.RequestMessage.ToFormattedRequestStringAsync();

                        outcomeResponse.HttpResponse = await response.ToFormattedResponseStringAsync();
#endif
                    }
                }
                catch (HttpRequestException ex)
                {
                    outcomeResponse.Exception  = ex;
                    outcomeResponse.StatusCode = HttpStatusCode.BadRequest;
                }
                catch (Exception ex)
                {
                    outcomeResponse.Exception  = ex;
                    outcomeResponse.StatusCode = HttpStatusCode.InternalServerError;
                }
                return(outcomeResponse);
            }
            catch (Exception ex)
            {
                return(new ClientResponse <T>
                {
                    Exception = ex,
                    StatusCode = HttpStatusCode.InternalServerError
                });
            }
        }
示例#5
0
        private static async Task <ClientResponse> PutOutcomeAsync <T>(HttpClient client, string serviceUrl, string consumerKey,
                                                                       string consumerSecret, T content, string contentType, SignatureMethod signatureMethod)
        {
            try
            {
                HttpRequestMessage webRequest = new HttpRequestMessage(HttpMethod.Put, serviceUrl)
                {
                    Content = new StringContent(content.ToJsonLdString(), Encoding.UTF8, contentType)
                };
                await SecuredClient.SignRequest(client, webRequest, consumerKey, consumerSecret, signatureMethod).ConfigureAwait(false);

                var outcomeResponse = new ClientResponse();
                try
                {
                    using (var response = await client.SendAsync(webRequest).ConfigureAwait(false))
                    {
                        outcomeResponse.StatusCode = response.StatusCode;
#if DEBUG
                        outcomeResponse.HttpRequest = await response.RequestMessage.ToFormattedRequestStringAsync().ConfigureAwait(false);

                        outcomeResponse.HttpResponse = await response.ToFormattedResponseStringAsync().ConfigureAwait(false);
#endif
                    }
                }
                catch (HttpRequestException ex)
                {
                    outcomeResponse.Exception  = ex;
                    outcomeResponse.StatusCode = HttpStatusCode.BadRequest;
                }
                catch (Exception ex)
                {
                    outcomeResponse.Exception  = ex;
                    outcomeResponse.StatusCode = HttpStatusCode.InternalServerError;
                }
                return(outcomeResponse);
            }
            catch (Exception ex)
            {
                return(new ClientResponse
                {
                    Exception = ex,
                    StatusCode = HttpStatusCode.InternalServerError
                });
            }
        }
示例#6
0
        private static async Task <ClientResponse> DeleteOutcomeAsync(HttpClient client, string serviceUrl, string consumerKey,
                                                                      string consumerSecret, SignatureMethod signatureMethod)
        {
            try
            {
                HttpRequestMessage webRequest = new HttpRequestMessage(HttpMethod.Delete, serviceUrl);
                await SecuredClient.SignRequest(client, webRequest, consumerKey, consumerSecret, signatureMethod).ConfigureAwait(false);

                var outcomeResponse = new ClientResponse();
                try
                {
                    // HttpClient does not send content in a DELETE request. So there is no Content-Type
                    // header. Therefore, all representations of the resource will be deleted.
                    // See https://www.imsglobal.org/lti/model/uml/purl.imsglobal.org/vocab/lis/v2/outcomes/LineItem/service.html#DELETE
                    using (var response = await client.SendAsync(webRequest).ConfigureAwait(false))
                    {
                        outcomeResponse.StatusCode = response.StatusCode;
#if DEBUG
                        outcomeResponse.HttpRequest = await response.RequestMessage.ToFormattedRequestStringAsync().ConfigureAwait(false);

                        outcomeResponse.HttpResponse = await response.ToFormattedResponseStringAsync().ConfigureAwait(false);
#endif
                    }
                }
                catch (HttpRequestException ex)
                {
                    outcomeResponse.Exception  = ex;
                    outcomeResponse.StatusCode = HttpStatusCode.BadRequest;
                }
                catch (Exception ex)
                {
                    outcomeResponse.Exception  = ex;
                    outcomeResponse.StatusCode = HttpStatusCode.InternalServerError;
                }
                return(outcomeResponse);
            }
            catch (Exception ex)
            {
                return(new ClientResponse
                {
                    Exception = ex,
                    StatusCode = HttpStatusCode.InternalServerError
                });
            }
        }
示例#7
0
        /// <summary>
        /// Send an Outcomes 1.0 DeleteResult request.
        /// </summary>
        /// <param name="client">The HttpClient that will be used to process the request.</param>
        /// <param name="serviceUrl">The URL to send the request to.</param>
        /// <param name="consumerKey">The OAuth key to sign the request.</param>
        /// <param name="consumerSecret">The OAuth secret to sign the request.</param>
        /// <param name="sourcedId">The LisResultSourcedId to be deleted.</param>
        /// <returns>A <see cref="ClientResponse"/>.</returns>
        public static async Task <ClientResponse> DeleteResultAsync(HttpClient client, string serviceUrl, string consumerKey, string consumerSecret, string sourcedId)
        {
            try
            {
                var imsxEnvelope = new imsx_POXEnvelopeType
                {
                    imsx_POXHeader = new imsx_POXHeaderType {
                        Item = new imsx_RequestHeaderInfoType()
                    },
                    imsx_POXBody = new imsx_POXBodyType {
                        Item = new deleteResultRequest()
                    }
                };

                var imsxHeader = (imsx_RequestHeaderInfoType)imsxEnvelope.imsx_POXHeader.Item;
                imsxHeader.imsx_version           = imsx_GWSVersionValueType.V10;
                imsxHeader.imsx_messageIdentifier = Guid.NewGuid().ToString();

                var imsxBody = (deleteResultRequest)imsxEnvelope.imsx_POXBody.Item;
                imsxBody.resultRecord = new ResultRecordType
                {
                    sourcedGUID = new SourcedGUIDType {
                        sourcedId = sourcedId
                    }
                };

                var outcomeResponse = new ClientResponse();
                try
                {
                    client.DefaultRequestHeaders.Accept.Clear();
                    client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(LtiConstants.ImsxOutcomeMediaType));

                    // Create a UTF8 encoding of the request
                    var xml = await GetXmlAsync(imsxEnvelope);

                    var xmlContent = new StringContent(xml, Encoding.UTF8, LtiConstants.ImsxOutcomeMediaType);
                    await SecuredClient.SignRequest(client, HttpMethod.Post, serviceUrl, xmlContent, consumerKey, consumerSecret);

                    // Post the request and check the response
                    using (var response = await client.PostAsync(serviceUrl, xmlContent))
                    {
                        outcomeResponse.StatusCode = response.StatusCode;
                        if (response.IsSuccessStatusCode)
                        {
                            var imsxResponseEnvelope = (imsx_POXEnvelopeType)ImsxResponseSerializer.Deserialize(await response.Content.ReadAsStreamAsync());
                            var imsxResponseHeader   = (imsx_ResponseHeaderInfoType)imsxResponseEnvelope.imsx_POXHeader.Item;
                            var imsxResponseStatus   = imsxResponseHeader.imsx_statusInfo.imsx_codeMajor;

                            outcomeResponse.StatusCode = imsxResponseStatus == imsx_CodeMajorType.success
                                ? HttpStatusCode.OK
                                : HttpStatusCode.BadRequest;
                        }
#if DEBUG
                        outcomeResponse.HttpRequest = await response.RequestMessage.ToFormattedRequestStringAsync(new StringContent(xml, Encoding.UTF8, LtiConstants.ImsxOutcomeMediaType));

                        outcomeResponse.HttpResponse = await response.ToFormattedResponseStringAsync();
#endif
                    }
                }
                catch (HttpRequestException ex)
                {
                    outcomeResponse.Exception  = ex;
                    outcomeResponse.StatusCode = HttpStatusCode.BadRequest;
                }
                catch (Exception ex)
                {
                    outcomeResponse.Exception  = ex;
                    outcomeResponse.StatusCode = HttpStatusCode.InternalServerError;
                }
                return(outcomeResponse);
            }
            catch (Exception ex)
            {
                return(new ClientResponse
                {
                    Exception = ex,
                    StatusCode = HttpStatusCode.InternalServerError
                });
            }
        }
示例#8
0
        /// <summary>
        /// Send an Outcomes 1.0 ReplaceResult request.
        /// </summary>
        /// <param name="client">The HttpClient that will be used to process the request.</param>
        /// <param name="serviceUrl">The URL to send the request to.</param>
        /// <param name="consumerKey">The OAuth key to sign the request.</param>
        /// <param name="consumerSecret">The OAuth secret to sign the request.</param>
        /// <param name="lisResultSourcedId">The LisResult to receive the score.</param>
        /// <param name="score">The score.</param>
        /// <returns>A <see cref="ClientResponse"/>.</returns>
        public static async Task <ClientResponse> ReplaceResultAsync(HttpClient client, string serviceUrl, string consumerKey, string consumerSecret, string lisResultSourcedId, double?score)
        {
            try
            {
                var imsxEnvelope = new imsx_POXEnvelopeType
                {
                    imsx_POXHeader = new imsx_POXHeaderType {
                        Item = new imsx_RequestHeaderInfoType()
                    },
                    imsx_POXBody = new imsx_POXBodyType {
                        Item = new replaceResultRequest()
                    }
                };

                var imsxHeader = (imsx_RequestHeaderInfoType)imsxEnvelope.imsx_POXHeader.Item;
                imsxHeader.imsx_version           = imsx_GWSVersionValueType.V10;
                imsxHeader.imsx_messageIdentifier = Guid.NewGuid().ToString();

                var imsxBody = (replaceResultRequest)imsxEnvelope.imsx_POXBody.Item;
                imsxBody.resultRecord = new ResultRecordType
                {
                    sourcedGUID = new SourcedGUIDType {
                        sourcedId = lisResultSourcedId
                    },
                    result = new ResultType
                    {
                        resultScore = new TextType
                        {
                            language = LtiConstants.ScoreLanguage,
                            // The LTI 1.1 specification states in 6.1.1. that the score in replaceResult should
                            // always be formatted using “en” formatting
                            // (http://www.imsglobal.org/LTI/v1p1p1/ltiIMGv1p1p1.html#_Toc330273034).
                            textString = score?.ToString(new CultureInfo(LtiConstants.ScoreLanguage))
                        }
                    }
                };

                var outcomeResponse = new ClientResponse();
                try
                {
                    client.DefaultRequestHeaders.Accept.Clear();
                    client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(LtiConstants.ImsxOutcomeMediaType));

                    // Create a UTF8 encoding of the request
                    var xml = await GetXmlAsync(imsxEnvelope);

                    var xmlContent = new StringContent(xml, Encoding.UTF8, LtiConstants.ImsxOutcomeMediaType);
                    await SecuredClient.SignRequest(client, HttpMethod.Post, serviceUrl, xmlContent, consumerKey, consumerSecret);

                    // Post the request and check the response
                    using (var response = await client.PostAsync(serviceUrl, xmlContent))
                    {
                        outcomeResponse.StatusCode = response.StatusCode;
                        if (response.IsSuccessStatusCode)
                        {
                            var imsxResponseEnvelope = (imsx_POXEnvelopeType)ImsxResponseSerializer.Deserialize(await response.Content.ReadAsStreamAsync());
                            var imsxResponseHeader   = (imsx_ResponseHeaderInfoType)imsxResponseEnvelope.imsx_POXHeader.Item;
                            var imsxResponseStatus   = imsxResponseHeader.imsx_statusInfo.imsx_codeMajor;

                            outcomeResponse.StatusCode = imsxResponseStatus == imsx_CodeMajorType.success
                                ? HttpStatusCode.OK
                                : HttpStatusCode.BadRequest;
                        }
#if DEBUG
                        outcomeResponse.HttpRequest = await response.RequestMessage.ToFormattedRequestStringAsync(new StringContent(xml, Encoding.UTF8, LtiConstants.ImsxOutcomeMediaType));

                        outcomeResponse.HttpResponse = await response.ToFormattedResponseStringAsync();
#endif
                    }
                }
                catch (HttpRequestException ex)
                {
                    outcomeResponse.Exception  = ex;
                    outcomeResponse.StatusCode = HttpStatusCode.BadRequest;
                }
                catch (Exception ex)
                {
                    outcomeResponse.Exception  = ex;
                    outcomeResponse.StatusCode = HttpStatusCode.InternalServerError;
                }
                return(outcomeResponse);
            }
            catch (Exception ex)
            {
                return(new ClientResponse
                {
                    Exception = ex,
                    StatusCode = HttpStatusCode.InternalServerError
                });
            }
        }
示例#9
0
        /// <summary>
        /// Send an Outcomes 1.0 ReadScore request and return the LisResult.
        /// </summary>
        /// <param name="client">The HttpClient that will be used to process the request.</param>
        /// <param name="serviceUrl">The URL to send the request to.</param>
        /// <param name="consumerKey">The OAuth key to sign the request.</param>
        /// <param name="consumerSecret">The OAuth secret to sign the request.</param>
        /// <param name="lisResultSourcedId">The LisResult to read.</param>
        /// <param name="signatureMethod">The signatureMethod. Defaults to <see cref="SignatureMethod.HmacSha1"/></param>
        /// <returns>A <see cref="ClientResponse"/>.</returns>
        public static async Task<ClientResponse<Result>> ReadResultAsync(HttpClient client, string serviceUrl, string consumerKey, string consumerSecret, 
            string lisResultSourcedId, SignatureMethod signatureMethod = SignatureMethod.HmacSha1)
        {
            try
            {
                var imsxEnvelope = new imsx_POXEnvelopeType
                {
                    imsx_POXHeader = new imsx_POXHeaderType { Item = new imsx_RequestHeaderInfoType() },
                    imsx_POXBody = new imsx_POXBodyType { Item = new readResultRequest() }
                };

                var imsxHeader = (imsx_RequestHeaderInfoType)imsxEnvelope.imsx_POXHeader.Item;
                imsxHeader.imsx_version = imsx_GWSVersionValueType.V10;
                imsxHeader.imsx_messageIdentifier = Guid.NewGuid().ToString();

                var imsxBody = (readResultRequest)imsxEnvelope.imsx_POXBody.Item;
                imsxBody.resultRecord = new ResultRecordType
                {
                    sourcedGUID = new SourcedGUIDType { sourcedId = lisResultSourcedId }
                };

                var outcomeResponse = new ClientResponse<Result>();
                try
                {
                    client.DefaultRequestHeaders.Accept.Clear();
                    client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(LtiConstants.ImsxOutcomeMediaType));

                    // Create a UTF8 encoding of the request
                    var xml = await GetXmlAsync(imsxEnvelope).ConfigureAwait(false);
                    var xmlContent = new StringContent(xml, Encoding.UTF8, LtiConstants.ImsxOutcomeMediaType);
                    await SecuredClient.SignRequest
                        (client, HttpMethod.Post, serviceUrl, xmlContent, consumerKey, consumerSecret, signatureMethod)
                        .ConfigureAwait(false);

                    // Post the request and check the response
                    using (var response = await client.PostAsync(serviceUrl, xmlContent).ConfigureAwait(false))
                    {
                        outcomeResponse.StatusCode = response.StatusCode;
                        if (response.IsSuccessStatusCode)
                        {
                            var imsxResponseEnvelope = (imsx_POXEnvelopeType)ImsxResponseSerializer.Deserialize
                                (await response.Content.ReadAsStreamAsync().ConfigureAwait(false));
                            var imsxResponseHeader = (imsx_ResponseHeaderInfoType)imsxResponseEnvelope.imsx_POXHeader.Item;
                            var imsxResponseStatus = imsxResponseHeader.imsx_statusInfo.imsx_codeMajor;

                            if (imsxResponseStatus == imsx_CodeMajorType.success)
                            {
                                var imsxResponseBody = (readResultResponse)imsxResponseEnvelope.imsx_POXBody.Item;
                                if (imsxResponseBody?.result == null)
                                {
                                    outcomeResponse.Response = new Result { Score = null };
                                }
                                else
                                {
                                    // The TP is supposed to use "en" language format, but this allows
                                    // a little bit of misbehaving. If the TP does not include a language, "en" will
                                    // be used. If the TP does include a language (even a non-en language), it will
                                    // be used.
                                    var cultureInfo = new CultureInfo(imsxResponseBody.result.resultScore.language??"en");
                                    outcomeResponse.Response = double.TryParse(imsxResponseBody.result.resultScore.textString, NumberStyles.Number, cultureInfo, out var result) 
                                        ? new Result { Score = result, SourcedId = lisResultSourcedId } 
                                        : new Result { Score = null, SourcedId = lisResultSourcedId };
                                }
                            }
                            else
                            {
                                outcomeResponse.StatusCode = HttpStatusCode.BadRequest;
                            }
                        }
#if DEBUG
                        outcomeResponse.HttpRequest = await response.RequestMessage.ToFormattedRequestStringAsync
                            (new StringContent(xml, Encoding.UTF8, LtiConstants.ImsxOutcomeMediaType))
                            .ConfigureAwait(false);
#endif
                        outcomeResponse.HttpResponse = await response.ToFormattedResponseStringAsync()
                            .ConfigureAwait(false);
                    }
                }
                catch (HttpRequestException ex)
                {
                    outcomeResponse.Exception = ex;
                    outcomeResponse.StatusCode = HttpStatusCode.BadRequest;
                }
                catch (Exception ex)
                {
                    outcomeResponse.Exception = ex;
                    outcomeResponse.StatusCode = HttpStatusCode.InternalServerError;
                }
                return outcomeResponse;
            }
            catch (Exception ex)
            {
                return new ClientResponse<Result>
                {
                    Exception = ex,
                    StatusCode = HttpStatusCode.InternalServerError
                };
            }
        }
示例#10
0
        /// <summary>
        /// Send an Outcomes 1.0 ReplaceResult request.
        /// </summary>
        /// <param name="client">The HttpClient that will be used to process the request.</param>
        /// <param name="serviceUrl">The URL to send the request to.</param>
        /// <param name="consumerKey">The OAuth key to sign the request.</param>
        /// <param name="consumerSecret">The OAuth secret to sign the request.</param>
        /// <param name="lisResultSourcedId">The LisResult to receive the score.</param>
        /// <param name="score">The score.</param>
        /// <param name="text">Optional text data (Canvas extension)</param>
        /// <param name="url">Optional url data</param>
        /// <param name="ltiLaunchUrl">Optional LTI launch URL data</param>
        /// <param name="signatureMethod">The signatureMethod. Defaults to <see cref="SignatureMethod.HmacSha1"/></param>
        /// <returns>A <see cref="ClientResponse"/>.</returns>
        public static async Task <ClientResponse> ReplaceResultAsync(HttpClient client, string serviceUrl, string consumerKey, string consumerSecret,
                                                                     string lisResultSourcedId, double?score, string text = null, string url = null, string ltiLaunchUrl = null, SignatureMethod signatureMethod = SignatureMethod.HmacSha1)
        {
            try
            {
                var imsxEnvelope = new imsx_POXEnvelopeType
                {
                    imsx_POXHeader = new imsx_POXHeaderType {
                        Item = new imsx_RequestHeaderInfoType()
                    },
                    imsx_POXBody = new imsx_POXBodyType {
                        Item = new replaceResultRequest()
                    }
                };

                var imsxHeader = (imsx_RequestHeaderInfoType)imsxEnvelope.imsx_POXHeader.Item;
                imsxHeader.imsx_version           = imsx_GWSVersionValueType.V10;
                imsxHeader.imsx_messageIdentifier = Guid.NewGuid().ToString();

                var imsxBody = (replaceResultRequest)imsxEnvelope.imsx_POXBody.Item;
                imsxBody.resultRecord = new ResultRecordType
                {
                    sourcedGUID = new SourcedGUIDType {
                        sourcedId = lisResultSourcedId
                    },
                    result = new ResultType
                    {
                        resultScore = new TextType
                        {
                            language = LtiConstants.ScoreLanguage,
                            // The LTI 1.1 specification states in 6.1.1. that the score in replaceResult should
                            // always be formatted using “en” formatting
                            // (http://www.imsglobal.org/LTI/v1p1p1/ltiIMGv1p1p1.html#_Toc330273034).
                            textString = score?.ToString(new CultureInfo(LtiConstants.ScoreLanguage))
                        }
                    }
                };

                // If any ResultData is supplied, add the ResultData element
                if (!string.IsNullOrEmpty(text + url + ltiLaunchUrl))
                {
                    var resultData = imsxBody.resultRecord.result.ResultData = new DataType();
                    resultData.LtiLaunchUrl = ltiLaunchUrl;
                    resultData.Text         = text;
                    resultData.Url          = url;
                }

                var outcomeResponse = new ClientResponse();
                try
                {
                    // Create a UTF8 encoding of the request
                    var xml = await GetXmlAsync(imsxEnvelope).ConfigureAwait(false);

                    var xmlContent = new StringContent(xml, Encoding.UTF8, LtiConstants.ImsxOutcomeMediaType);
                    HttpRequestMessage webRequest = new HttpRequestMessage(HttpMethod.Post, serviceUrl)
                    {
                        Content = xmlContent
                    };
                    webRequest.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(LtiConstants.ImsxOutcomeMediaType));
                    await SecuredClient.SignRequest(client, webRequest, consumerKey, consumerSecret, signatureMethod)
                    .ConfigureAwait(false);

                    // Post the request and check the response
                    using (var response = await client.SendAsync(webRequest).ConfigureAwait(false))
                    {
                        outcomeResponse.StatusCode = response.StatusCode;
                        if (response.IsSuccessStatusCode)
                        {
                            var imsxResponseEnvelope = (imsx_POXEnvelopeType)ImsxResponseSerializer.Deserialize
                                                           (await response.Content.ReadAsStreamAsync().ConfigureAwait(false));
                            var imsxResponseHeader = (imsx_ResponseHeaderInfoType)imsxResponseEnvelope.imsx_POXHeader.Item;
                            var imsxResponseStatus = imsxResponseHeader.imsx_statusInfo.imsx_codeMajor;

                            outcomeResponse.StatusCode = imsxResponseStatus == imsx_CodeMajorType.success
                                ? HttpStatusCode.OK
                                : HttpStatusCode.BadRequest;
                            outcomeResponse.Severity  = imsxResponseHeader.imsx_statusInfo.imsx_severity;
                            outcomeResponse.MinorCode = imsxResponseHeader.imsx_statusInfo.imsx_codeMinor;
                        }
#if DEBUG
                        outcomeResponse.HttpRequest = await response.RequestMessage.ToFormattedRequestStringAsync
                                                          (new StringContent(xml, Encoding.UTF8, LtiConstants.ImsxOutcomeMediaType)).ConfigureAwait(false);
#endif
                        outcomeResponse.HttpResponse = await response.ToFormattedResponseStringAsync().ConfigureAwait(false);
                    }
                }
                catch (HttpRequestException ex)
                {
                    outcomeResponse.Exception  = ex;
                    outcomeResponse.StatusCode = HttpStatusCode.BadRequest;
                }
                catch (Exception ex)
                {
                    outcomeResponse.Exception  = ex;
                    outcomeResponse.StatusCode = HttpStatusCode.InternalServerError;
                }
                return(outcomeResponse);
            }
            catch (Exception ex)
            {
                return(new ClientResponse
                {
                    Exception = ex,
                    StatusCode = HttpStatusCode.InternalServerError
                });
            }
        }