diff --git a/PlanGrid.Api.Net45/Properties/AssemblyInfo.cs b/PlanGrid.Api.Net45/Properties/AssemblyInfo.cs index 3bf3066..e19e0f0 100644 --- a/PlanGrid.Api.Net45/Properties/AssemblyInfo.cs +++ b/PlanGrid.Api.Net45/Properties/AssemblyInfo.cs @@ -1,5 +1,5 @@ // -// Copyright (c) 2015 PlanGrid, Inc. All rights reserved. +// Copyright (c) 2016 PlanGrid, Inc. All rights reserved. // using System.Reflection; @@ -13,7 +13,7 @@ [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("PlanGrid, Inc.")] [assembly: AssemblyProduct("PlanGrid.Api")] -[assembly: AssemblyCopyright("Copyright © 2015")] +[assembly: AssemblyCopyright("Copyright © 2016")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] diff --git a/PlanGrid.Api.Tests/AttachmentTests.cs b/PlanGrid.Api.Tests/AttachmentTests.cs new file mode 100644 index 0000000..8f089d6 --- /dev/null +++ b/PlanGrid.Api.Tests/AttachmentTests.cs @@ -0,0 +1,74 @@ +// +// Copyright (c) 2016 PlanGrid, Inc. All rights reserved. +// + +using System; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Threading.Tasks; +using NUnit.Framework; + +namespace PlanGrid.Api.Tests +{ + [TestFixture] + public class AttachmentTests + { + [Test] + public async Task UploadAttachment() + { + IPlanGridApi client = PlanGridClient.Create(); + AttachmentUploadRequest request = await client.CreateAttachmentUploadRequest(TestData.Project2Uid, new AttachmentUpload + { + ContentType = AttachmentUpload.Pdf, + Name = "test name", + Folder = "test folder" + }); + + Stream payload = typeof(AttachmentTests).Assembly.GetManifestResourceStream("PlanGrid.Api.Tests.TestData.Sample.pdf"); + Attachment attachment = await client.Upload(request, payload); + + Assert.AreEqual("test name", attachment.Name); + Assert.AreEqual("test folder", attachment.Folder); + Assert.AreEqual(TestData.ApiTestsUserUid, attachment.CreatedBy.Uid); + Assert.AreNotEqual(attachment.CreatedAt, default(DateTime)); + Assert.AreEqual(request.Uid, attachment.Uid); + + using (var downloader = new HttpClient(new HttpClientHandler { AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip, AllowAutoRedirect = true })) + { + Stream returnedPayload = await downloader.GetStreamAsync(attachment.Url); + payload = typeof(AttachmentTests).Assembly.GetManifestResourceStream("PlanGrid.Api.Tests.TestData.Sample.pdf"); + var payloadBytes = new MemoryStream(); + await payload.CopyToAsync(payloadBytes); + var returnedBytes = new MemoryStream(); + await returnedPayload.CopyToAsync(returnedBytes); + Assert.IsTrue(payloadBytes.ToArray().SequenceEqual(returnedBytes.ToArray())); + } + } + + [Test] + public async Task UploadPdfAttachment() + { + IPlanGridApi client = PlanGridClient.Create(); + Stream payload = typeof(AttachmentTests).Assembly.GetManifestResourceStream("PlanGrid.Api.Tests.TestData.Sample.pdf"); + Attachment attachment = await client.UploadPdfAttachment(TestData.Project2Uid, "test name", payload, "test folder"); + + Assert.AreEqual("test name", attachment.Name); + Assert.AreEqual("test folder", attachment.Folder); + Assert.AreEqual(TestData.ApiTestsUserUid, attachment.CreatedBy.Uid); + Assert.AreNotEqual(attachment.CreatedAt, default(DateTime)); + + using (var downloader = new HttpClient(new HttpClientHandler { AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip, AllowAutoRedirect = true })) + { + Stream returnedPayload = await downloader.GetStreamAsync(attachment.Url); + payload = typeof(AttachmentTests).Assembly.GetManifestResourceStream("PlanGrid.Api.Tests.TestData.Sample.pdf"); + var payloadBytes = new MemoryStream(); + await payload.CopyToAsync(payloadBytes); + var returnedBytes = new MemoryStream(); + await returnedPayload.CopyToAsync(returnedBytes); + Assert.IsTrue(payloadBytes.ToArray().SequenceEqual(returnedBytes.ToArray())); + } + } + } +} \ No newline at end of file diff --git a/PlanGrid.Api.Tests/IssuesTests.cs b/PlanGrid.Api.Tests/IssuesTests.cs index 9b08204..3ca290a 100644 --- a/PlanGrid.Api.Tests/IssuesTests.cs +++ b/PlanGrid.Api.Tests/IssuesTests.cs @@ -1,5 +1,5 @@ // -// Copyright (c) 2015 PlanGrid, Inc. All rights reserved. +// Copyright (c) 2016 PlanGrid, Inc. All rights reserved. // using System; @@ -21,7 +21,7 @@ public async Task GetIssues() Issue issue = page.Data[0]; Assert.IsFalse(issue.IsDeleted); Assert.AreEqual(TestData.ApiTestsUserEmail, issue.AssignedTo.Single().Email); - Assert.AreEqual(DateTime.Parse("11/16/2015 10:13:49.845"), issue.CreatedAt); + Assert.AreEqual(DateTime.Parse("11/16/2015 18:13:49"), issue.CreatedAt); Assert.AreEqual(TestData.ApiTestsUserEmail, issue.CreatedBy.Email); Assert.AreEqual("Test Description", issue.Description); Assert.AreEqual(1, issue.Number); @@ -29,7 +29,7 @@ public async Task GetIssues() Assert.AreEqual(IssueStatus.Open, issue.Status); Assert.AreEqual(TestData.ApiTestsUserEmail, issue.UpdatedBy.Email); Assert.IsFalse(string.IsNullOrEmpty(issue.Uid)); - Assert.AreEqual(DateTime.Parse("11/16/2015 10:13:50.830"), issue.UpdatedAt); + Assert.AreEqual(DateTime.Parse("11/16/2015 18:13:50"), issue.UpdatedAt); Assert.IsFalse(issue.IsDeleted); Assert.AreEqual("#FF0000", issue.CurrentAnnotation.Color); Assert.IsFalse(issue.CurrentAnnotation.IsDeleted); @@ -44,7 +44,7 @@ public async Task GetIssues() Page photos = await client.Resolve(issue.Photos); Assert.AreEqual(1, photos.TotalCount); Assert.AreEqual(1, issue.Photos.TotalCount); - Assert.AreEqual(DateTime.Parse("11/16/2015 10:32:43.584"), photos.Data[0].CreatedAt); + Assert.AreEqual(DateTime.Parse("11/16/2015 18:32:43"), photos.Data[0].CreatedAt); Assert.AreEqual(TestData.ApiTestsUserEmail, photos.Data[0].CreatedBy.Email); Assert.AreEqual("Galaxy", photos.Data[0].Title); Assert.AreEqual(TestData.PhotoUrl, photos.Data[0].Url); @@ -54,7 +54,7 @@ public async Task GetIssues() Page comments = await client.Resolve(issue.Comments); Assert.AreEqual(1, comments.TotalCount); Assert.AreEqual(1, issue.Comments.TotalCount); - Assert.AreEqual(DateTime.Parse("11/16/2015 10:35:21.698"), comments.Data[0].CreatedAt); + Assert.AreEqual(DateTime.Parse("11/16/2015 18:35:21.698"), comments.Data[0].CreatedAt); Assert.AreEqual(TestData.ApiTestsUserEmail, comments.Data[0].CreatedBy.Email); Assert.AreEqual("Test Comment", comments.Data[0].Text); Assert.IsFalse(string.IsNullOrEmpty(comments.Data[0].Uid)); @@ -66,7 +66,7 @@ public async Task GetIssueComments() IPlanGridApi client = PlanGridClient.Create(); Page comments = await client.GetIssueComments(TestData.Project1Uid, TestData.Project1Issue1Uid); Assert.AreEqual(1, comments.TotalCount); - Assert.AreEqual(DateTime.Parse("11/16/2015 10:35:21.698"), comments.Data[0].CreatedAt); + Assert.AreEqual(DateTime.Parse("11/16/2015 18:35:21.698"), comments.Data[0].CreatedAt); Assert.AreEqual(TestData.ApiTestsUserEmail, comments.Data[0].CreatedBy.Email); Assert.AreEqual("Test Comment", comments.Data[0].Text); Assert.IsFalse(string.IsNullOrEmpty(comments.Data[0].Uid)); @@ -78,7 +78,7 @@ public async Task GetIssuePhotos() IPlanGridApi client = PlanGridClient.Create(); Page photos = await client.GetIssuePhotos(TestData.Project1Uid, TestData.Project1Issue1Uid); Assert.AreEqual(1, photos.TotalCount); - Assert.AreEqual(DateTime.Parse("11/16/2015 10:32:43.584"), photos.Data[0].CreatedAt); + Assert.AreEqual(DateTime.Parse("11/16/2015 18:32:43"), photos.Data[0].CreatedAt); Assert.AreEqual(TestData.ApiTestsUserEmail, photos.Data[0].CreatedBy.Email); Assert.AreEqual("Galaxy", photos.Data[0].Title); Assert.AreEqual(TestData.PhotoUrl, photos.Data[0].Url); diff --git a/PlanGrid.Api.Tests/PlanGrid.Api.Tests.csproj b/PlanGrid.Api.Tests/PlanGrid.Api.Tests.csproj index 5a37a99..6bdcb51 100644 --- a/PlanGrid.Api.Tests/PlanGrid.Api.Tests.csproj +++ b/PlanGrid.Api.Tests/PlanGrid.Api.Tests.csproj @@ -44,6 +44,7 @@ + @@ -53,8 +54,11 @@ - + + Designer + + diff --git a/PlanGrid.Api.Tests/RfisTests.cs b/PlanGrid.Api.Tests/RfisTests.cs index 2482911..ca0f8cd 100644 --- a/PlanGrid.Api.Tests/RfisTests.cs +++ b/PlanGrid.Api.Tests/RfisTests.cs @@ -1,8 +1,10 @@ // -// Copyright (c) 2015 PlanGrid, Inc. All rights reserved. +// Copyright (c) 2016 PlanGrid, Inc. All rights reserved. // using System; +using System.IO; +using System.Linq; using System.Threading.Tasks; using NUnit.Framework; @@ -23,10 +25,10 @@ public async Task GetRfis() Assert.AreEqual("Test Rfi Question", rfi.Question); Assert.AreEqual("Test Rfi", rfi.Title); Assert.AreEqual(1, rfi.Number); - Assert.AreEqual(DateTime.Parse("11/18/2015 11:30:21.000"), rfi.SentDate); - Assert.AreEqual(DateTime.Parse("11/19/2015 11:30:13.000"), rfi.DueDate); - Assert.AreEqual(DateTime.Parse("11/17/2015 12:06:47.912"), rfi.UpdatedAt); - Assert.AreEqual(DateTime.Parse("11/16/2015 13:48:26.641"), rfi.CreatedAt); + Assert.AreEqual(DateTime.Parse("11/18/2015 19:30:21.000"), rfi.SentDate); + Assert.AreEqual(DateTime.Parse("11/19/2015 19:30:13.000"), rfi.DueDate); + Assert.AreEqual(DateTime.Parse("11/17/2015 20:06:47.912"), rfi.UpdatedAt); + Assert.AreEqual(DateTime.Parse("11/16/2015 21:48:26.641"), rfi.CreatedAt); Assert.AreEqual("kirk+apitests@plangrid.com", rfi.AssignedTo[0].Email); Assert.AreEqual("kirk+apitests@plangrid.com", rfi.UpdatedBy.Email); Assert.AreEqual("kirk+apitests@plangrid.com", rfi.CreatedBy.Email); @@ -187,5 +189,63 @@ public async Task UpdateRfi() Assert.AreEqual(TestData.ApiTestsUserUid, rfi.UpdatedBy.Uid); Assert.AreNotEqual(rfi.UpdatedAt, default(DateTime)); } + + [Test] + public async Task ReferenceAttachment() + { + IPlanGridApi client = PlanGridClient.Create(); + var rfiInsert = new RfiUpsert + { + Question = "test question", + Answer = "test answer", + AssignedTo = new[] { TestData.ApiTestsUserUid }, + DueDate = new DateTime(2020, 1, 1), + IsLocked = false, + SentDate = new DateTime(2019, 1, 1), + StatusUid = TestData.Project2DraftRfiStatusUid, + Title = "test title" + }; + Rfi rfi = await client.CreateRfi(TestData.Project2Uid, rfiInsert); + + AttachmentUploadRequest request = await client.CreateAttachmentUploadRequest(TestData.Project2Uid, new AttachmentUpload + { + ContentType = AttachmentUpload.Pdf, + Name = "test name", + Folder = "test folder" + }); + + Stream payload = typeof(AttachmentTests).Assembly.GetManifestResourceStream("PlanGrid.Api.Tests.TestData.Sample.pdf"); + Attachment attachment = await client.Upload(request, payload); + + await client.ReferenceAttachmentFromRfi(TestData.Project2Uid, rfi.Uid, new AttachmentReference { AttachmentUid = attachment.Uid }); + + Page attachments = await client.GetRfiAttachments(TestData.Project2Uid, rfi.Uid); + Attachment rfiAttachment = attachments.Data.Single(); + Assert.AreEqual(attachment.Uid, rfiAttachment.Uid); + } + + [Test] + public async Task ReferencePhoto() + { + IPlanGridApi client = PlanGridClient.Create(); + var rfiInsert = new RfiUpsert + { + Question = "test question", + Answer = "test answer", + AssignedTo = new[] { TestData.ApiTestsUserUid }, + DueDate = new DateTime(2020, 1, 1), + IsLocked = false, + SentDate = new DateTime(2019, 1, 1), + StatusUid = TestData.Project2DraftRfiStatusUid, + Title = "test title" + }; + Rfi rfi = await client.CreateRfi(TestData.Project2Uid, rfiInsert); + + await client.ReferencePhotoFromRfi(TestData.Project2Uid, rfi.Uid, new PhotoReference { PhotoUid = TestData.Project2PhotoUid }); + + Page photos = await client.GetRfiPhotos(TestData.Project2Uid, rfi.Uid); + Photo rfiPhoto = photos.Data.Single(); + Assert.AreEqual(TestData.Project2PhotoUid, rfiPhoto.Uid); + } } } \ No newline at end of file diff --git a/PlanGrid.Api.Tests/RoleTests.cs b/PlanGrid.Api.Tests/RoleTests.cs index 3e91313..e1e676d 100644 --- a/PlanGrid.Api.Tests/RoleTests.cs +++ b/PlanGrid.Api.Tests/RoleTests.cs @@ -1,5 +1,5 @@ // -// Copyright (c) 2015 PlanGrid, Inc. All rights reserved. +// Copyright (c) 2016 PlanGrid, Inc. All rights reserved. // using System.Threading.Tasks; diff --git a/PlanGrid.Api.Tests/TestData.cs b/PlanGrid.Api.Tests/TestData.cs index 83f7394..45c7a45 100644 --- a/PlanGrid.Api.Tests/TestData.cs +++ b/PlanGrid.Api.Tests/TestData.cs @@ -1,6 +1,7 @@ // -// Copyright (c) 2015 PlanGrid, Inc. All rights reserved. +// Copyright (c) 2016 PlanGrid, Inc. All rights reserved. // + namespace PlanGrid.Api.Tests { public static class TestData @@ -11,6 +12,7 @@ public static class TestData public const string Project2Uid = "269ad633-0688-395e-5c30-cc685e0ce964"; public const string Project2DraftRfiStatusUid = "00a8b880"; public const string Project2OpenRfiStatusUid = "bcadf1c9"; + public const string Project2PhotoUid = "6f976878-d243-c787-dfda-0290b7761968"; public const string PhotoUrl = "https://photo-assets-test.plangrid.com/5a16f6d9-8006-ea7d-12ee-76c778b7094f.jpg"; public const string ApiTestsUserEmail = "kirk+apitests@plangrid.com"; public const string ApiTestsUserUid = "5644e9acf0cb79476f1d48ee"; diff --git a/PlanGrid.Api.Tests/TestData/Sample.pdf b/PlanGrid.Api.Tests/TestData/Sample.pdf new file mode 100644 index 0000000..2a7c699 Binary files /dev/null and b/PlanGrid.Api.Tests/TestData/Sample.pdf differ diff --git a/PlanGrid.Api.Tests/UserTests.cs b/PlanGrid.Api.Tests/UserTests.cs index f40bd8e..ca2c3c7 100644 --- a/PlanGrid.Api.Tests/UserTests.cs +++ b/PlanGrid.Api.Tests/UserTests.cs @@ -1,5 +1,5 @@ // -// Copyright (c) 2015 PlanGrid, Inc. All rights reserved. +// Copyright (c) 2016 PlanGrid, Inc. All rights reserved. // using System.Threading.Tasks; @@ -15,9 +15,9 @@ public async Task GetUsers() { IPlanGridApi api = PlanGridClient.Create(); Page users = await api.GetUsers(TestData.Project1Uid); - Assert.AreEqual(2, users.Data.Length); + Assert.AreEqual(3, users.Data.Length); - User user = users.Data[1]; + User user = users.Data[2]; Assert.AreEqual(TestData.ApiTestsUserEmail, user.Email); Assert.IsTrue(!string.IsNullOrEmpty(user.Uid)); diff --git a/PlanGrid.Api/AnnotationVisibility.cs b/PlanGrid.Api/AnnotationVisibility.cs index aadb2ac..0b29cc4 100644 --- a/PlanGrid.Api/AnnotationVisibility.cs +++ b/PlanGrid.Api/AnnotationVisibility.cs @@ -1,5 +1,5 @@ // -// Copyright (c) 2015 PlanGrid, Inc. All rights reserved. +// Copyright (c) 2016 PlanGrid, Inc. All rights reserved. // using System.Runtime.Serialization; diff --git a/PlanGrid.Api/Attachment.cs b/PlanGrid.Api/Attachment.cs index 653226b..1c444b6 100644 --- a/PlanGrid.Api/Attachment.cs +++ b/PlanGrid.Api/Attachment.cs @@ -1,5 +1,5 @@ // -// Copyright (c) 2015 PlanGrid, Inc. All rights reserved. +// Copyright (c) 2016 PlanGrid, Inc. All rights reserved. // using System; diff --git a/PlanGrid.Api/AttachmentReference.cs b/PlanGrid.Api/AttachmentReference.cs new file mode 100644 index 0000000..93a9277 --- /dev/null +++ b/PlanGrid.Api/AttachmentReference.cs @@ -0,0 +1,10 @@ +using Newtonsoft.Json; + +namespace PlanGrid.Api +{ + public class AttachmentReference + { + [JsonProperty("attachment_uid")] + public string AttachmentUid { get; set; } + } +} diff --git a/PlanGrid.Api/AttachmentUpload.cs b/PlanGrid.Api/AttachmentUpload.cs new file mode 100644 index 0000000..31a661d --- /dev/null +++ b/PlanGrid.Api/AttachmentUpload.cs @@ -0,0 +1,22 @@ +// +// Copyright (c) 2016 PlanGrid, Inc. All rights reserved. +// + +using Newtonsoft.Json; + +namespace PlanGrid.Api +{ + public class AttachmentUpload + { + public const string Pdf = "application/pdf"; + + [JsonProperty("content_type")] + public string ContentType { get; set; } + + [JsonProperty("name")] + public string Name { get; set; } + + [JsonProperty("folder")] + public string Folder { get; set; } + } +} diff --git a/PlanGrid.Api/AttachmentUploadRequest.cs b/PlanGrid.Api/AttachmentUploadRequest.cs new file mode 100644 index 0000000..4027e6a --- /dev/null +++ b/PlanGrid.Api/AttachmentUploadRequest.cs @@ -0,0 +1,20 @@ +// +// Copyright (c) 2016 PlanGrid, Inc. All rights reserved. +// + +using Newtonsoft.Json; + +namespace PlanGrid.Api +{ + public class AttachmentUploadRequest + { + [JsonProperty("webhook_url")] + public string WebhookUrl { get; set; } + + [JsonProperty("uid")] + public string Uid { get; set; } + + [JsonProperty("aws_post_form_arguments")] + public AwsPostFormArguments AwsPostFormArguments { get; set; } + } +} diff --git a/PlanGrid.Api/AutoGeneratedIPlanGridApi.cs b/PlanGrid.Api/AutoGeneratedIPlanGridApi.cs index 0751f46..8768deb 100644 --- a/PlanGrid.Api/AutoGeneratedIPlanGridApi.cs +++ b/PlanGrid.Api/AutoGeneratedIPlanGridApi.cs @@ -1,5 +1,5 @@ // -// Copyright (c) 2015 PlanGrid, Inc. All rights reserved. +// Copyright (c) 2016 PlanGrid, Inc. All rights reserved. // using System; diff --git a/PlanGrid.Api/AwsPostFormArgument.cs b/PlanGrid.Api/AwsPostFormArgument.cs new file mode 100644 index 0000000..4062203 --- /dev/null +++ b/PlanGrid.Api/AwsPostFormArgument.cs @@ -0,0 +1,17 @@ +// +// Copyright (c) 2016 PlanGrid, Inc. All rights reserved. +// + +using Newtonsoft.Json; + +namespace PlanGrid.Api +{ + public class AwsPostFormArgument + { + [JsonProperty("name")] + public string Name { get; set; } + + [JsonProperty("value")] + public string Value { get; set; } + } +} diff --git a/PlanGrid.Api/AwsPostFormArguments.cs b/PlanGrid.Api/AwsPostFormArguments.cs new file mode 100644 index 0000000..e26b966 --- /dev/null +++ b/PlanGrid.Api/AwsPostFormArguments.cs @@ -0,0 +1,17 @@ +// +// Copyright (c) 2016 PlanGrid, Inc. All rights reserved. +// + +using Newtonsoft.Json; + +namespace PlanGrid.Api +{ + public class AwsPostFormArguments + { + [JsonProperty("action")] + public string Action { get; set; } + + [JsonProperty("fields")] + public AwsPostFormArgument[] Fields { get; set; } + } +} diff --git a/PlanGrid.Api/CollectionReference.cs b/PlanGrid.Api/CollectionReference.cs index 8b366c2..2f65681 100644 --- a/PlanGrid.Api/CollectionReference.cs +++ b/PlanGrid.Api/CollectionReference.cs @@ -1,5 +1,5 @@ // -// Copyright (c) 2015 PlanGrid, Inc. All rights reserved. +// Copyright (c) 2016 PlanGrid, Inc. All rights reserved. // using Newtonsoft.Json; diff --git a/PlanGrid.Api/Comment.cs b/PlanGrid.Api/Comment.cs index 2a0eb86..a056a34 100644 --- a/PlanGrid.Api/Comment.cs +++ b/PlanGrid.Api/Comment.cs @@ -1,5 +1,5 @@ // -// Copyright (c) 2015 PlanGrid, Inc. All rights reserved. +// Copyright (c) 2016 PlanGrid, Inc. All rights reserved. // using System; diff --git a/PlanGrid.Api/IMultipartContent.cs b/PlanGrid.Api/IMultipartContent.cs new file mode 100644 index 0000000..36f84b0 --- /dev/null +++ b/PlanGrid.Api/IMultipartContent.cs @@ -0,0 +1,10 @@ +using System.Net.Http; + +namespace PlanGrid.Api +{ + public interface IMultipartContent + { + string Name { get; } + void CreateContent(MultipartFormDataContent multipart); + } +} diff --git a/PlanGrid.Api/IPlanGridApi.cs b/PlanGrid.Api/IPlanGridApi.cs index 0566489..c2f4852 100644 --- a/PlanGrid.Api/IPlanGridApi.cs +++ b/PlanGrid.Api/IPlanGridApi.cs @@ -1,5 +1,5 @@ // -// Copyright (c) 2015 PlanGrid, Inc. All rights reserved. +// Copyright (c) 2016 PlanGrid, Inc. All rights reserved. // using System; @@ -69,5 +69,14 @@ public interface IPlanGridApi : IDisposable [Patch("/projects/{projectUid}/rfis/{rfiUid}")] Task UpdateRfi(string projectUid, string rfiUid, [Body]RfiUpsert rfi); + + [Post("/projects/{projectUid}/attachments/uploads")] + Task CreateAttachmentUploadRequest(string projectUid, [Body]AttachmentUpload upload); + + [Post("/projects/{projectUid}/rfis/{rfiUid}/attachments")] + Task ReferenceAttachmentFromRfi(string projectUid, string rfiUid, [Body]AttachmentReference attachmentReference); + + [Post("/projects/{projectUid}/rfis/{rfiUid}/photos")] + Task ReferencePhotoFromRfi(string projectUid, string rfiUid, [Body]PhotoReference photoReference); } } \ No newline at end of file diff --git a/PlanGrid.Api/Issue.cs b/PlanGrid.Api/Issue.cs index d5cdf3f..308a4dd 100644 --- a/PlanGrid.Api/Issue.cs +++ b/PlanGrid.Api/Issue.cs @@ -1,5 +1,5 @@ // -// Copyright (c) 2015 PlanGrid, Inc. All rights reserved. +// Copyright (c) 2016 PlanGrid, Inc. All rights reserved. // using System; diff --git a/PlanGrid.Api/IssueAnnotation.cs b/PlanGrid.Api/IssueAnnotation.cs index 063f132..c25cfec 100644 --- a/PlanGrid.Api/IssueAnnotation.cs +++ b/PlanGrid.Api/IssueAnnotation.cs @@ -1,5 +1,5 @@ // -// Copyright (c) 2015 PlanGrid, Inc. All rights reserved. +// Copyright (c) 2016 PlanGrid, Inc. All rights reserved. // using Newtonsoft.Json; diff --git a/PlanGrid.Api/IssueAnnotationSheet.cs b/PlanGrid.Api/IssueAnnotationSheet.cs index b5de1a0..652aa72 100644 --- a/PlanGrid.Api/IssueAnnotationSheet.cs +++ b/PlanGrid.Api/IssueAnnotationSheet.cs @@ -1,5 +1,5 @@ // -// Copyright (c) 2015 PlanGrid, Inc. All rights reserved. +// Copyright (c) 2016 PlanGrid, Inc. All rights reserved. // using Newtonsoft.Json; diff --git a/PlanGrid.Api/IssueStatus.cs b/PlanGrid.Api/IssueStatus.cs index c8ea3eb..f186598 100644 --- a/PlanGrid.Api/IssueStatus.cs +++ b/PlanGrid.Api/IssueStatus.cs @@ -1,5 +1,5 @@ // -// Copyright (c) 2015 PlanGrid, Inc. All rights reserved. +// Copyright (c) 2016 PlanGrid, Inc. All rights reserved. // using System.Runtime.Serialization; diff --git a/PlanGrid.Api/MultipartUploadException.cs b/PlanGrid.Api/MultipartUploadException.cs new file mode 100644 index 0000000..5a833fa --- /dev/null +++ b/PlanGrid.Api/MultipartUploadException.cs @@ -0,0 +1,23 @@ +// +// Copyright (c) 2016 PlanGrid, Inc. All rights reserved. +// + +using System; + +namespace PlanGrid.Api +{ + public class MultipartUploadException : Exception + { + public MultipartUploadException() + { + } + + public MultipartUploadException(string message) : base(message) + { + } + + public MultipartUploadException(string message, Exception innerException) : base(message, innerException) + { + } + } +} diff --git a/PlanGrid.Api/MultipartUploader.cs b/PlanGrid.Api/MultipartUploader.cs new file mode 100644 index 0000000..6d13231 --- /dev/null +++ b/PlanGrid.Api/MultipartUploader.cs @@ -0,0 +1,37 @@ +// +// Copyright (c) 2016 PlanGrid, Inc. All rights reserved. +// + +using System; +using System.Net; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; + +namespace PlanGrid.Api +{ + public class MultipartUploader + { + public static async Task Upload(string url, CancellationToken cancellationToken, params IMultipartContent[] values) + { + using (var client = new HttpClient(new HttpClientHandler { AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip, AllowAutoRedirect = true })) + { + string boundary = "----" + DateTime.Now.Ticks; + + var content = new MultipartFormDataContent(boundary); + foreach (IMultipartContent item in values) + { + item.CreateContent(content); + } + + HttpResponseMessage response = await client.PostAsync(new Uri(url), content, cancellationToken); + if ((int)response.StatusCode >= 400) + { + string message = await response.Content.ReadAsStringAsync(); + throw new MultipartUploadException($"Error uploading attachment to S3: {message}"); + } + return response; + } + } + } +} diff --git a/PlanGrid.Api/Page.cs b/PlanGrid.Api/Page.cs index ef85f06..fcda052 100644 --- a/PlanGrid.Api/Page.cs +++ b/PlanGrid.Api/Page.cs @@ -1,5 +1,5 @@ // -// Copyright (c) 2015 PlanGrid, Inc. All rights reserved. +// Copyright (c) 2016 PlanGrid, Inc. All rights reserved. // using Newtonsoft.Json; diff --git a/PlanGrid.Api/Photo.cs b/PlanGrid.Api/Photo.cs index e70f5ca..3cd774d 100644 --- a/PlanGrid.Api/Photo.cs +++ b/PlanGrid.Api/Photo.cs @@ -1,5 +1,5 @@ // -// Copyright (c) 2015 PlanGrid, Inc. All rights reserved. +// Copyright (c) 2016 PlanGrid, Inc. All rights reserved. // using System; diff --git a/PlanGrid.Api/PhotoReference.cs b/PlanGrid.Api/PhotoReference.cs new file mode 100644 index 0000000..4f7f831 --- /dev/null +++ b/PlanGrid.Api/PhotoReference.cs @@ -0,0 +1,10 @@ +using Newtonsoft.Json; + +namespace PlanGrid.Api +{ + public class PhotoReference + { + [JsonProperty("photo_uid")] + public string PhotoUid { get; set; } + } +} diff --git a/PlanGrid.Api/PlanGrid.Api.projitems b/PlanGrid.Api/PlanGrid.Api.projitems index 7e5e98b..3412b56 100644 --- a/PlanGrid.Api/PlanGrid.Api.projitems +++ b/PlanGrid.Api/PlanGrid.Api.projitems @@ -11,14 +11,23 @@ + + + + + + + + + @@ -34,6 +43,8 @@ + + diff --git a/PlanGrid.Api/PlanGridApiExtensions.cs b/PlanGrid.Api/PlanGridApiExtensions.cs index a226bd9..94c2869 100644 --- a/PlanGrid.Api/PlanGridApiExtensions.cs +++ b/PlanGrid.Api/PlanGridApiExtensions.cs @@ -1,8 +1,12 @@ // -// Copyright (c) 2015 PlanGrid, Inc. All rights reserved. +// Copyright (c) 2016 PlanGrid, Inc. All rights reserved. // +using System.Collections.Generic; +using System.IO; +using System.Linq; using System.Net.Http; +using System.Threading; using System.Threading.Tasks; using Newtonsoft.Json; using Refit; @@ -36,5 +40,33 @@ public static async Task> Resolve(this IPlanGridApi api, CollectionRe var result = JsonConvert.DeserializeObject>(content, settings.JsonSerializerSettings); return result; } + + public static Task UploadPdfAttachment(this IPlanGridApi api, string projectUid, string name, Stream payload, string folder = null, CancellationToken cancellationToken = default(CancellationToken)) + { + return api.UploadAttachment(projectUid, AttachmentUpload.Pdf, name, payload, folder, cancellationToken); + } + + public static async Task UploadAttachment(this IPlanGridApi api, string projectUid, string contentType, string name, Stream payload, string folder = null, CancellationToken cancellationToken = default(CancellationToken)) + { + AttachmentUploadRequest request = await api.CreateAttachmentUploadRequest(projectUid, new AttachmentUpload { ContentType = contentType, Name = name, Folder = folder }); + return await api.Upload(request, payload, cancellationToken); + } + + public static async Task Upload(this IPlanGridApi api, AttachmentUploadRequest uploadRequest, Stream payload, CancellationToken cancellationToken = default(CancellationToken)) + { + var values = new List(); + foreach (AwsPostFormArgument item in uploadRequest.AwsPostFormArguments.Fields) + { + values.Add(new StringMultipartContent(item.Name, item.Value)); + } + + var generatedApi = (AutoGeneratedIPlanGridApi)api; + RefitSettings settings = generatedApi.Settings; + + values.Add(new StreamMultipartContent("file", "data", uploadRequest.AwsPostFormArguments.Fields.Single(x => x.Name == "Content-Type").Value, payload)); + HttpResponseMessage response = await MultipartUploader.Upload(uploadRequest.AwsPostFormArguments.Action, cancellationToken, values.ToArray()); + string responseText = await response.Content.ReadAsStringAsync(); + return JsonConvert.DeserializeObject(responseText, settings.JsonSerializerSettings); + } } } diff --git a/PlanGrid.Api/PlanGridClient.cs b/PlanGrid.Api/PlanGridClient.cs index bb05fff..5f1de63 100644 --- a/PlanGrid.Api/PlanGridClient.cs +++ b/PlanGrid.Api/PlanGridClient.cs @@ -1,5 +1,5 @@ // -// Copyright (c) 2015 PlanGrid, Inc. All rights reserved. +// Copyright (c) 2016 PlanGrid, Inc. All rights reserved. // using System; diff --git a/PlanGrid.Api/PlanGridHttpHandler.cs b/PlanGrid.Api/PlanGridHttpHandler.cs index 5c5901a..387508e 100644 --- a/PlanGrid.Api/PlanGridHttpHandler.cs +++ b/PlanGrid.Api/PlanGridHttpHandler.cs @@ -1,5 +1,5 @@ // -// Copyright (c) 2015 PlanGrid, Inc. All rights reserved. +// Copyright (c) 2016 PlanGrid, Inc. All rights reserved. // using System; diff --git a/PlanGrid.Api/Project.cs b/PlanGrid.Api/Project.cs index aa017b9..f7ed709 100644 --- a/PlanGrid.Api/Project.cs +++ b/PlanGrid.Api/Project.cs @@ -1,5 +1,5 @@ // -// Copyright (c) 2015 PlanGrid, Inc. All rights reserved. +// Copyright (c) 2016 PlanGrid, Inc. All rights reserved. // using Newtonsoft.Json; diff --git a/PlanGrid.Api/ProjectUpdate.cs b/PlanGrid.Api/ProjectUpdate.cs index 16a4dd4..33dd158 100644 --- a/PlanGrid.Api/ProjectUpdate.cs +++ b/PlanGrid.Api/ProjectUpdate.cs @@ -1,5 +1,5 @@ // -// Copyright (c) 2015 PlanGrid, Inc. All rights reserved. +// Copyright (c) 2016 PlanGrid, Inc. All rights reserved. // using Newtonsoft.Json; diff --git a/PlanGrid.Api/RecordReference.cs b/PlanGrid.Api/RecordReference.cs index 87e9636..4a08ac0 100644 --- a/PlanGrid.Api/RecordReference.cs +++ b/PlanGrid.Api/RecordReference.cs @@ -1,5 +1,5 @@ // -// Copyright (c) 2015 PlanGrid, Inc. All rights reserved. +// Copyright (c) 2016 PlanGrid, Inc. All rights reserved. // using Newtonsoft.Json; diff --git a/PlanGrid.Api/Rfi.cs b/PlanGrid.Api/Rfi.cs index d2d1f9f..1908182 100644 --- a/PlanGrid.Api/Rfi.cs +++ b/PlanGrid.Api/Rfi.cs @@ -1,4 +1,8 @@ -using System; +// +// Copyright (c) 2016 PlanGrid, Inc. All rights reserved. +// + +using System; using Newtonsoft.Json; namespace PlanGrid.Api diff --git a/PlanGrid.Api/RfiStatus.cs b/PlanGrid.Api/RfiStatus.cs index 12b2370..bd3f75a 100644 --- a/PlanGrid.Api/RfiStatus.cs +++ b/PlanGrid.Api/RfiStatus.cs @@ -1,4 +1,8 @@ -using Newtonsoft.Json; +// +// Copyright (c) 2016 PlanGrid, Inc. All rights reserved. +// + +using Newtonsoft.Json; namespace PlanGrid.Api { diff --git a/PlanGrid.Api/RfiStatusUpdate.cs b/PlanGrid.Api/RfiStatusUpdate.cs index 1eea9eb..ab94bda 100644 --- a/PlanGrid.Api/RfiStatusUpdate.cs +++ b/PlanGrid.Api/RfiStatusUpdate.cs @@ -1,6 +1,7 @@ -using System; -using System.Collections.Generic; -using System.Text; +// +// Copyright (c) 2016 PlanGrid, Inc. All rights reserved. +// + using Newtonsoft.Json; namespace PlanGrid.Api diff --git a/PlanGrid.Api/RfiUpsert.cs b/PlanGrid.Api/RfiUpsert.cs index 18c5ed9..d151c49 100644 --- a/PlanGrid.Api/RfiUpsert.cs +++ b/PlanGrid.Api/RfiUpsert.cs @@ -1,6 +1,8 @@ -using System; -using System.Collections.Generic; -using System.Text; +// +// Copyright (c) 2016 PlanGrid, Inc. All rights reserved. +// + +using System; using Newtonsoft.Json; namespace PlanGrid.Api @@ -11,7 +13,7 @@ public class RfiUpsert public string StatusUid { get; set; } [JsonProperty("locked")] - public bool IsLocked { get; set; } + public bool? IsLocked { get; set; } [JsonProperty("title")] public string Title { get; set; } diff --git a/PlanGrid.Api/Role.cs b/PlanGrid.Api/Role.cs index cfc08e5..8d9bacf 100644 --- a/PlanGrid.Api/Role.cs +++ b/PlanGrid.Api/Role.cs @@ -1,5 +1,5 @@ // -// Copyright (c) 2015 PlanGrid, Inc. All rights reserved. +// Copyright (c) 2016 PlanGrid, Inc. All rights reserved. // using Newtonsoft.Json; diff --git a/PlanGrid.Api/Settings.cs b/PlanGrid.Api/Settings.cs index c2e4d11..7d60c0c 100644 --- a/PlanGrid.Api/Settings.cs +++ b/PlanGrid.Api/Settings.cs @@ -1,5 +1,5 @@ // -// Copyright (c) 2015 PlanGrid, Inc. All rights reserved. +// Copyright (c) 2016 PlanGrid, Inc. All rights reserved. // using System.Configuration; diff --git a/PlanGrid.Api/Snapshot.cs b/PlanGrid.Api/Snapshot.cs index 80b21fa..f600936 100644 --- a/PlanGrid.Api/Snapshot.cs +++ b/PlanGrid.Api/Snapshot.cs @@ -1,5 +1,5 @@ // -// Copyright (c) 2015 PlanGrid, Inc. All rights reserved. +// Copyright (c) 2016 PlanGrid, Inc. All rights reserved. // using System; diff --git a/PlanGrid.Api/StreamMultipartContent.cs b/PlanGrid.Api/StreamMultipartContent.cs new file mode 100644 index 0000000..b9e363f --- /dev/null +++ b/PlanGrid.Api/StreamMultipartContent.cs @@ -0,0 +1,29 @@ +using System.IO; +using System.Net.Http; +using System.Net.Http.Headers; + +namespace PlanGrid.Api +{ + public class StreamMultipartContent : IMultipartContent + { + public string Name { get; } + public string FileName { get; } + public string ContentType { get; } + public Stream Content { get; } + + public StreamMultipartContent(string name, string fileName, string contentType, Stream content) + { + Name = name; + FileName = fileName; + ContentType = contentType; + Content = content; + } + + public void CreateContent(MultipartFormDataContent multipart) + { + var content = new StreamContent(Content); + content.Headers.ContentType = MediaTypeHeaderValue.Parse(ContentType); + multipart.Add(content, Name, FileName); + } + } +} diff --git a/PlanGrid.Api/StringMultipartContent.cs b/PlanGrid.Api/StringMultipartContent.cs new file mode 100644 index 0000000..f71552a --- /dev/null +++ b/PlanGrid.Api/StringMultipartContent.cs @@ -0,0 +1,28 @@ +// +// Copyright (c) 2016 PlanGrid, Inc. All rights reserved. +// + +using System.Net.Http; +using System.Net.Http.Headers; + +namespace PlanGrid.Api +{ + public class StringMultipartContent : IMultipartContent + { + public string Name { get; } + public string Value { get; } + + public StringMultipartContent(string name, string value) + { + Name = name; + Value = value; + } + + public void CreateContent(MultipartFormDataContent multipart) + { + var content = new StringContent(Value); + content.Headers.ContentType = MediaTypeHeaderValue.Parse("text/plain"); + multipart.Add(content, Name); + } + } +} diff --git a/PlanGrid.Api/User.cs b/PlanGrid.Api/User.cs index 7e967d8..cc11058 100644 --- a/PlanGrid.Api/User.cs +++ b/PlanGrid.Api/User.cs @@ -1,5 +1,5 @@ // -// Copyright (c) 2015 PlanGrid, Inc. All rights reserved. +// Copyright (c) 2016 PlanGrid, Inc. All rights reserved. // using Newtonsoft.Json; diff --git a/PlanGrid.Api/UserInvitation.cs b/PlanGrid.Api/UserInvitation.cs index 913097e..48b81c5 100644 --- a/PlanGrid.Api/UserInvitation.cs +++ b/PlanGrid.Api/UserInvitation.cs @@ -1,5 +1,5 @@ // -// Copyright (c) 2015 PlanGrid, Inc. All rights reserved. +// Copyright (c) 2016 PlanGrid, Inc. All rights reserved. // using Newtonsoft.Json; diff --git a/PlanGrid.Api/UserReference.cs b/PlanGrid.Api/UserReference.cs index f459a56..f7d05bf 100644 --- a/PlanGrid.Api/UserReference.cs +++ b/PlanGrid.Api/UserReference.cs @@ -1,5 +1,5 @@ // -// Copyright (c) 2015 PlanGrid, Inc. All rights reserved. +// Copyright (c) 2016 PlanGrid, Inc. All rights reserved. // using Newtonsoft.Json;