/// <summary>
        /// Creates a new comment on a story with a pre-defined <see cref="PivotalNewComment"/>.
        /// </summary>
        /// <remarks>
        /// Note: <paramref name="pivotalComment"/> must include ProjectId and StoryId.
        /// </remarks>
        /// <param name="pivotalComment">The pre-defined PivotalNewComment to create.</param>
        /// <returns></returns>
        public async Task <PivotalComment> CreateNewCommentAsync(PivotalComment pivotalComment)
        {
            // Try and send our whole comment object to Pivotal
            var response = await HttpService.PostAsync(StringUtil.PivotalCommentsUrl(pivotalComment.ProjectId, pivotalComment.StoryId), pivotalComment);

            return(HandleResponse <PivotalComment>(response));
        }
        /// <summary>
        /// Creates a new comment on a story with from a pre-defined <see cref="PivotalStory"/>
        /// </summary>
        /// <param name="pivotalStory">The pre-defined <see cref="PivotalStory"/> to add the comment to.</param>
        /// <param name="bodyText">The main descrtiption text of the comment.</param>
        /// <returns></returns>
        public async Task <PivotalComment> CreateNewCommentAsync(PivotalStory pivotalStory, string bodyText)
        {
            // Attempts to get the story for our pivotalStory object
            var storyFromPivotal = await GetStoryByIdAsync(pivotalStory.ProjectId, pivotalStory.Id.GetValueOrDefault());

            // Make sure our story actually exists, throws exception if false.
            if (storyFromPivotal != null)
            {
                // Setup a new comment to send to Pivotal
                var newComment = new PivotalComment
                {
                    ProjectId       = storyFromPivotal.ProjectId,
                    StoryId         = storyFromPivotal.Id.Value,
                    Text            = bodyText,
                    FileAttachments = new List <PivotalAttachment>()
                };

                // Try and add the comment to our story
                var response =
                    await HttpService.PostAsync(
                        StringUtil.PivotalCommentsUrl(storyFromPivotal.ProjectId, storyFromPivotal.Id.Value), newComment);

                return(HandleResponse <PivotalComment>(response));
            }

            // Failure, our story does not exist on this project.
            throw new PivotalNotFoundException($"Story with Id {pivotalStory.Id} does not exist on project with Id {pivotalStory.ProjectId}");
        }
        /// <summary>
        /// Creates a new <see cref="PivotalComment"/> and adds to to a story.
        /// </summary>
        /// <param name="projectId">Id of the project to work with.</param>
        /// <param name="storyId">Id of the story to add the comment to.</param>
        /// <param name="bodyText">The main description text of the comment.</param>
        /// <returns></returns>
        public async Task <PivotalComment> CreateNewCommentAsync(int?projectId, int storyId, string bodyText)
        {
            int properProjectId = GetProjectIdToUse(projectId);
            // TODO: Implement include attachments.
            var pivotalComment = new PivotalComment
            {
                ProjectId       = properProjectId,
                StoryId         = storyId,
                Text            = bodyText,
                FileAttachments = new List <PivotalAttachment>()
            };

            // Try and send our new comment to Pivotal
            var response = await HttpService.PostAsync(StringUtil.PivotalCommentsUrl(properProjectId, storyId), pivotalComment);

            return(HandleResponse <PivotalComment>(response));
        }
        public async Task <PivotalComment> UpdateCommentAsync(int?projectId, int storyId, PivotalComment comment)
        {
            int properProjectId = GetProjectIdToUse(projectId);

            if (comment.PersonId != null)
            {
                comment.PersonId = null;
            }

            var response = await HttpService.PutAsync(StringUtil.PivotalCommentsUrl(properProjectId, storyId, comment.Id), comment);

            return(HandleResponse <PivotalComment>(response));
        }
        /// <summary>
        /// Creates a new <see cref="PivotalComment"/> and adds to to a story.
        /// </summary>
        /// <param name="projectId">Id of the project to work with.</param>
        /// <param name="storyId">Id of the story to add the comment to.</param>
        /// <param name="bodyText">The main description text of the comment.</param>
        /// <param name="fileData">(optional) File data you want to add to the comment as an attachment as Stream.</param>
        /// <returns></returns>
        public async Task <PivotalComment> CreateNewCommentAsync(int?projectId, int storyId, string bodyText, Stream fileData, bool closeFileDataStream = true)
        {
            int properProjectId = GetProjectIdToUse(projectId);
            // Create a new comment on our story before we go any further
            var pivotalComment = await CreateNewCommentAsync(properProjectId, storyId, bodyText);

            using (var ms = new MemoryStream())
            {
                // Setup a new HTTP form to post to the server with our attachment(s)
                var form = new MultipartFormDataContent();
                // Copy our fileData to our MemoryStream to create a Byte array
                fileData.CopyTo(ms);
                // Try and cast our fileData as a FileStream so that we can access the real file name.
                FileStream fileStream = (FileStream)fileData;
                if (closeFileDataStream)
                {
                    fileData.Dispose();
                }
                // Create a new byte array from our MemoryStream with our fileData in
                byte[] data = ms.ToArray();

                // Create a ByteArrayContent from our byte array
                var content = new ByteArrayContent(data);
                // Setup Headers of our content
                content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("form-data")
                {
                    Name     = "file",
                    FileName = fileStream.Name
                };

                // Make sure we dispose of our FileStream as we no longer need it
                fileStream.Dispose();

                // Add our finalised ByteArrayContent to our HTTP form ready to post
                form.Add(content);

                // Try and send our form to Pivotal
                var uploadResponse = await HttpService.PostContentAsync(StringUtil.PivotalUploadsUrl(properProjectId), form);

                if (uploadResponse.IsSuccessStatusCode)
                {
                    // Success, our file was uploaded to Pivotal, we need to serialise it so that we can construct a new comment with the attachment
                    var uploadedFile = Serialize <PivotalAttachment>(uploadResponse);
                    // Create a new List<PivotalAttachment> to add to our comment to handle multiple attachments (TODO)
                    var attachments = new List <PivotalAttachment> {
                        uploadedFile
                    };
                    // Create our fully constructed comment to send to Pivotal
                    var newComment = new PivotalComment()
                    {
                        ProjectId       = properProjectId,
                        StoryId         = storyId,
                        Text            = pivotalComment.Text,
                        FileAttachments = attachments
                    };

                    // Try and send our comment to Pivotal
                    var commentResponse = await HttpService.PostAsync($"projects/{projectId}/stories/{storyId}/comments", newComment);

                    if (commentResponse.IsSuccessStatusCode)
                    {
                        // Serialise response to object
                        var serializedComment = Serialize <PivotalComment>(commentResponse);
                        // Add our attachments as the response from Pivotal doesn't return them
                        serializedComment.FileAttachments = attachments;
                        // Success, return serialised response
                        return(serializedComment);
                    }

                    // Failure, throw generic exception with response
                    throw ThrowException(commentResponse);
                }

                // Failure, throw generic exception with response
                throw ThrowException(uploadResponse);
            }
        }