diff --git a/Digipost.Api.Client.Archive.Tests/Smoke/ArchiveSmokeTestsHelper.cs b/Digipost.Api.Client.Archive.Tests/Smoke/ArchiveSmokeTestsHelper.cs index 7bcaf00b..b2438acc 100755 --- a/Digipost.Api.Client.Archive.Tests/Smoke/ArchiveSmokeTestsHelper.cs +++ b/Digipost.Api.Client.Archive.Tests/Smoke/ArchiveSmokeTestsHelper.cs @@ -16,7 +16,7 @@ public class ArchiveSmokeTestsHelper { public static readonly string ArchiveName = "SmokeTestArchive"; private readonly TestSender _testSender; - private readonly ArchiveApi _archiveApi; + private readonly IArchiveApi _archiveApi; private Archive _archivesWithDocuments; private Archive _archive; private Archive _byAttribute; diff --git a/Digipost.Api.Client.Archive/ArchiveApi.cs b/Digipost.Api.Client.Archive/ArchiveApi.cs index 9e62678d..84f71154 100755 --- a/Digipost.Api.Client.Archive/ArchiveApi.cs +++ b/Digipost.Api.Client.Archive/ArchiveApi.cs @@ -12,7 +12,7 @@ namespace Digipost.Api.Client.Archive { - internal interface IArchiveApi + public interface IArchiveApi { /// /// List all the archives available to the current sender @@ -52,7 +52,7 @@ internal interface IArchiveApi Task GetDocumentContent(ArchiveDocumentContentUri archiveDocumentContentUri); } - public class ArchiveApi : IArchiveApi + internal class ArchiveApi : IArchiveApi { private readonly Root _root; private readonly RequestHelper _requestHelper; diff --git a/Digipost.Api.Client.Common/DataTransferObjectConverter.cs b/Digipost.Api.Client.Common/DataTransferObjectConverter.cs index 33d99666..b0bbd7aa 100755 --- a/Digipost.Api.Client.Common/DataTransferObjectConverter.cs +++ b/Digipost.Api.Client.Common/DataTransferObjectConverter.cs @@ -414,5 +414,20 @@ public static SearchDetailsResult FromDataTransferObject(Recipients recipients) }) }; } + + public static HashAlgoritm ToHashAlgoritm(this V8.Hash_Algorithm hashAlgorithm) + { + switch (hashAlgorithm) + { + case Hash_Algorithm.NONE: + return HashAlgoritm.NONE; + case Hash_Algorithm.MD5: + return HashAlgoritm.MD5; + case Hash_Algorithm.SHA256: + return HashAlgoritm.SHA256; + default: + throw new ArgumentOutOfRangeException(); + } + } } } diff --git a/Digipost.Api.Client.Common/Entrypoint/Entrypoint.cs b/Digipost.Api.Client.Common/Entrypoint/Entrypoint.cs index cd091d77..b8ee5a0b 100644 --- a/Digipost.Api.Client.Common/Entrypoint/Entrypoint.cs +++ b/Digipost.Api.Client.Common/Entrypoint/Entrypoint.cs @@ -70,6 +70,11 @@ public GetArchiveDocumentByUuidUri GetGetArchiveDocumentsByUuidUri(Guid guid) { return new GetArchiveDocumentByUuidUri(Links["GET_ARCHIVE_DOCUMENT_BY_UUID"], guid); } + + public DocumentStatusUri GetDocumentStatusUri(Guid guid) + { + return new DocumentStatusUri(Links["DOCUMENT_STATUS"], guid); + } } public class Link diff --git a/Digipost.Api.Client.Common/Enums/HashAlgoritm.cs b/Digipost.Api.Client.Common/Enums/HashAlgoritm.cs new file mode 100644 index 00000000..0630bfa2 --- /dev/null +++ b/Digipost.Api.Client.Common/Enums/HashAlgoritm.cs @@ -0,0 +1,9 @@ +namespace Digipost.Api.Client.Common.Enums +{ + public enum HashAlgoritm + { + NONE, + MD5, + SHA256 + } +} diff --git a/Digipost.Api.Client.Common/Relations/ApiRelations.cs b/Digipost.Api.Client.Common/Relations/ApiRelations.cs index edc588b7..e067f0f7 100644 --- a/Digipost.Api.Client.Common/Relations/ApiRelations.cs +++ b/Digipost.Api.Client.Common/Relations/ApiRelations.cs @@ -16,7 +16,6 @@ public ApiRootUri(Sender senderId = null) } } - public class SendMessageUri : Uri { public SendMessageUri(Link link) @@ -160,4 +159,17 @@ public ArchiveDocumentDeleteUri(Link link) { } } + + public class DocumentStatusUri : Uri + { + public DocumentStatusUri(Link link, Guid guid) + : base($"{link.Uri}{guid.ToString()}", UriKind.Absolute) + { + } + + public DocumentStatusUri(Link link) + : base(link.Uri, UriKind.Absolute) + { + } + } } diff --git a/Digipost.Api.Client.Docs/ArchiveExamples.cs b/Digipost.Api.Client.Docs/ArchiveExamples.cs index 14bc6c21..1b5327d5 100644 --- a/Digipost.Api.Client.Docs/ArchiveExamples.cs +++ b/Digipost.Api.Client.Docs/ArchiveExamples.cs @@ -26,7 +26,7 @@ private async Task ArchiveADocument() new ArchiveDocument(Guid.NewGuid(), "attachment_123123.pdf", "pdf", "application/psd", readFileFromDisk("attachment_123123.pdf")) }); - var savedArchive = await client.GetArchive().ArchiveDocuments(archive); + var savedArchive = client.GetArchive().ArchiveDocuments(archive); } private async Task ArchiveADocumentWithAutoDelete() diff --git a/Digipost.Api.Client.Docs/DocumentsExamples.cs b/Digipost.Api.Client.Docs/DocumentsExamples.cs new file mode 100644 index 00000000..88f2e678 --- /dev/null +++ b/Digipost.Api.Client.Docs/DocumentsExamples.cs @@ -0,0 +1,23 @@ +using System; +using Digipost.Api.Client.Common; +using Digipost.Api.Client.Send; + +namespace Digipost.Api.Client.Docs +{ + public class DocumentsExamples + { + private static readonly DigipostClient client; + private static readonly Sender sender; + + public void Hent_document_status() + { + DocumentStatus documentStatus = client.GetDocumentStatus(sender) + .GetDocumentStatus(Guid.Parse("10ff4c99-8560-4741-83f0-1093dc4deb1c")) + .Result; + + // example information: + // documentStatus.DeliveryStatus => DELIVERED + // documentStatus.DeliveryMethod => PRINT + } + } +} diff --git a/Digipost.Api.Client.Send/DocumentStatus.cs b/Digipost.Api.Client.Send/DocumentStatus.cs new file mode 100644 index 00000000..60e21c9b --- /dev/null +++ b/Digipost.Api.Client.Send/DocumentStatus.cs @@ -0,0 +1,83 @@ +using System; +using Digipost.Api.Client.Common; +using Digipost.Api.Client.Common.Enums; + +namespace Digipost.Api.Client.Send +{ + public class DocumentStatus + { + public DocumentStatus( + string guid, + long senderId, + DateTime created, + DocumentDeliveryStatus documentDeliveryStatus, + Read? read, + DeliveryMethod deliveryMethod, + string contentHash, + DateTime? delivered, + Boolean? isPrimaryDocument, + HashAlgoritm? contentHashAlgoritm + ) + { + Guid = guid; + Sender = new Sender(senderId); + Created = created; + DeliveryStatus = documentDeliveryStatus; + DocumentRead = read; + DeliveryMethod = deliveryMethod; + ContentHash = contentHash; + Delivered = delivered; + IsPrimaryDocument = isPrimaryDocument; + ContentHashAlgoritm = contentHashAlgoritm; + } + + public string Guid { get; } + + public Sender Sender { get; } + + public DateTime Created { get; } + + /** + * If DeliveryStatus is NOT_DELIVERED, Delivered will not have a value + */ + public DateTime? Delivered { get; } + + public DocumentDeliveryStatus DeliveryStatus { get; } + + public Read? DocumentRead { get; } + + public DeliveryMethod DeliveryMethod { get; } + + public String ContentHash { get; } + + public HashAlgoritm? ContentHashAlgoritm { get; } + + /** + * isPrimaryDocument has value only if you ask api are the actual sender asking for DocumentStatus. + * If you are, then this will be true for the primary document else false. + */ + public Boolean? IsPrimaryDocument { get; } + + public enum DocumentDeliveryStatus + { + /** + * The document has been delivered + */ + DELIVERED, + + /** + * The document is still being processed + */ + NOT_DELIVERED + } + + /** + * Indicates whether the document is read or not + */ + public enum Read + { + YES, + NO + } + } +} diff --git a/Digipost.Api.Client.Send/SendDataTransferObjectConverter.cs b/Digipost.Api.Client.Send/SendDataTransferObjectConverter.cs index 9677d1b6..9cac583d 100755 --- a/Digipost.Api.Client.Send/SendDataTransferObjectConverter.cs +++ b/Digipost.Api.Client.Send/SendDataTransferObjectConverter.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Xml; using Digipost.Api.Client.Common; +using Digipost.Api.Client.Common.Enums; using Digipost.Api.Client.Common.Extensions; using V8; @@ -86,16 +87,16 @@ public static V8.Document ToDataTransferObject(IDocument document) return documentDto; } - public static V8.Sms_Notification ToDataTransferObject(ISmsNotification smsNotification) + public static Sms_Notification ToDataTransferObject(ISmsNotification smsNotification) { if (smsNotification == null) return null; - var smsNotificationDto = new V8.Sms_Notification(); + var smsNotificationDto = new Sms_Notification(); if (smsNotification.NotifyAtTimes.Count > 0) { - var timesAsListedTimes = smsNotification.NotifyAtTimes.Select(dateTime => new V8.Listed_Time {Time = dateTime, TimeSpecified = true}); + var timesAsListedTimes = smsNotification.NotifyAtTimes.Select(dateTime => new Listed_Time {Time = dateTime, TimeSpecified = true}); foreach (var timesAsListedTime in timesAsListedTimes) { smsNotificationDto.At.Add(timesAsListedTime); @@ -113,7 +114,7 @@ public static V8.Sms_Notification ToDataTransferObject(ISmsNotification smsNotif return smsNotificationDto; } - public static IMessageDeliveryResult FromDataTransferObject(V8.Message_Delivery messageDeliveryDto) + public static IMessageDeliveryResult FromDataTransferObject(Message_Delivery messageDeliveryDto) { IMessageDeliveryResult messageDeliveryResult = new MessageDeliveryResult { @@ -138,7 +139,7 @@ public static IDocument FromDataTransferObject(V8.Document documentDto) }; } - public static ISmsNotification FromDataTransferObject(V8.Sms_Notification smsNotificationDto) + public static ISmsNotification FromDataTransferObject(Sms_Notification smsNotificationDto) { if (smsNotificationDto == null) return null; @@ -151,5 +152,47 @@ public static ISmsNotification FromDataTransferObject(V8.Sms_Notification smsNot return smsNotification; } + + public static DocumentStatus FromDataTransferObject(Document_Status dto) + { + return new DocumentStatus( + dto.Uuid, + dto.Sender_Id, + dto.Created, + dto.Status.ToDeliveryStatus(), + dto.ReadSpecified ? dto.Read.ToRead() : (DocumentStatus.Read?) null, + dto.Channel.ToDeliveryMethod(), + dto.Content_Hash, + dto.DeliveredSpecified ? dto.Delivered : (DateTime?) null, + dto.Is_Primary_DocumentSpecified ? dto.Is_Primary_Document : (bool?) null, + dto.Content_Hash_AlgorithmSpecified ? dto.Content_Hash_Algorithm.ToHashAlgoritm() : (HashAlgoritm?) null + ); + } + + private static DocumentStatus.DocumentDeliveryStatus ToDeliveryStatus(this Delivery_Status deliveryStatus) + { + switch (deliveryStatus) + { + case Delivery_Status.DELIVERED: + return DocumentStatus.DocumentDeliveryStatus.DELIVERED; + case Delivery_Status.NOT_DELIVERED: + return DocumentStatus.DocumentDeliveryStatus.NOT_DELIVERED; + default: + throw new ArgumentOutOfRangeException(); + } + } + + private static DocumentStatus.Read ToRead(this Read read) + { + switch (read) + { + case Read.Y: + return DocumentStatus.Read.YES; + case Read.N: + return DocumentStatus.Read.NO; + default: + throw new ArgumentOutOfRangeException(); + } + } } } diff --git a/Digipost.Api.Client.Tests/Smoke/ClientSmokeTestHelper.cs b/Digipost.Api.Client.Tests/Smoke/ClientSmokeTestHelper.cs index 0b412858..755902ec 100644 --- a/Digipost.Api.Client.Tests/Smoke/ClientSmokeTestHelper.cs +++ b/Digipost.Api.Client.Tests/Smoke/ClientSmokeTestHelper.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Threading; using Digipost.Api.Client.Common; using Digipost.Api.Client.Common.Entrypoint; using Digipost.Api.Client.Common.Enums; @@ -47,6 +46,8 @@ internal class ClientSmokeTestHelper //Gradually built state, requestForRegistration private RequestForRegistration _requestForRegistration; + private DocumentStatus _documentStatus; + public ClientSmokeTestHelper(TestSender testSender, bool withoutDataTypesProject = false) { var broker = new Broker(testSender.Id); @@ -222,5 +223,26 @@ public void Expect_search_to_have_result() Assert.InRange(_searchResult.PersonDetails.ToList().Count, 1, 11); } + + public ClientSmokeTestHelper FetchDocumentStatus() + { + Assert_state(_messageDeliveryResult); + Assert_state(_primary); + + _documentStatus = _digipostClient.GetDocumentStatus(new Sender(_testSender.Id)).GetDocumentStatus(Guid.Parse(_primary.Guid)).Result; + return this; + } + public ClientSmokeTestHelper FetchDocumentStatus(Guid guid) + { + _documentStatus = _digipostClient.GetDocumentStatus(new Sender(_testSender.Id)).GetDocumentStatus(guid).Result; + return this; + } + + public void Expect_document_status_to_be(DocumentStatus.DocumentDeliveryStatus deliveryStatus, DeliveryMethod deliveryMethod) + { + Assert_state(_documentStatus); + Assert.Equal(_documentStatus.DeliveryMethod, deliveryMethod); + Assert.Equal(_documentStatus.DeliveryStatus, deliveryStatus); + } } } diff --git a/Digipost.Api.Client.Tests/Smoke/ClientSmokeTests.cs b/Digipost.Api.Client.Tests/Smoke/ClientSmokeTests.cs index e065bdc9..1e64fe92 100644 --- a/Digipost.Api.Client.Tests/Smoke/ClientSmokeTests.cs +++ b/Digipost.Api.Client.Tests/Smoke/ClientSmokeTests.cs @@ -84,7 +84,6 @@ public void Can_send_document_with_raw_datatype_to_digipost_user() [Fact(Skip = "SmokeTest")] public void Can_send_document_with_object_datatype_to_digipost_user() { - var externalLink = new ExternalLink {Url = "https://www.test.no", Description = "This is a link"}; var linkXml = SerializeUtil.Serialize(externalLink); @@ -94,5 +93,31 @@ public void Can_send_document_with_object_datatype_to_digipost_user() .SendMessage() .Expect_message_to_have_status(MessageStatus.Delivered); } + + [Fact(Skip = "SmokeTest")] + public void Can_send_document_share_request_to_user() + { + _client + .Create_message_with_primary_document() + .To_Digital_Recipient() + .SendMessage() + .Expect_message_to_have_status(MessageStatus.Delivered); + + _client.FetchDocumentStatus() + .Expect_document_status_to_be( + DocumentStatus.DocumentDeliveryStatus.DELIVERED, + DeliveryMethod.Digipost + ); + } + + [Fact(Skip = "SmokeTest")] + public void Check_document_status() + { + _client.FetchDocumentStatus(Guid.Parse("92c95fa4-dc74-4196-95e9-4dc580017588")) //Use a guid that you know of. This is just a random one. + .Expect_document_status_to_be( + DocumentStatus.DocumentDeliveryStatus.NOT_DELIVERED, + DeliveryMethod.PENDING + ); + } } } diff --git a/Digipost.Api.Client/Api/DocumentsApi.cs b/Digipost.Api.Client/Api/DocumentsApi.cs new file mode 100644 index 00000000..b2fa6a28 --- /dev/null +++ b/Digipost.Api.Client/Api/DocumentsApi.cs @@ -0,0 +1,51 @@ +using System; +using System.Threading.Tasks; +using Digipost.Api.Client.Common.Entrypoint; +using Digipost.Api.Client.Common.Utilities; +using Digipost.Api.Client.Send; +using Microsoft.Extensions.Logging; +using V8; + +namespace Digipost.Api.Client.Api +{ + public interface IDocumentsApi + { + /** + * Guid should be the Guid added to the actual document, usually the main document + */ + Task GetDocumentStatus(Guid guid); + Task GetDocumentStatusAsync(Guid guid); + } + + internal class DocumentsApi : IDocumentsApi + { + private readonly RequestHelper _requestHelper; + private readonly ILoggerFactory _loggerFactory; + private readonly Root _root; + + public DocumentsApi(RequestHelper requestHelper, ILoggerFactory loggerFactory, Root root) + { + _requestHelper = requestHelper; + _loggerFactory = loggerFactory; + _root = root; + } + + public Task GetDocumentStatus(Guid guid) + { + var result = GetDocumentStatusAsync(guid); + + if (result.IsFaulted && result.Exception != null) + throw result.Exception.InnerException; + + return result; + } + + public async Task GetDocumentStatusAsync(Guid guid) + { + var documentStatusUri = _root.GetDocumentStatusUri(guid); + var result = await _requestHelper.Get(documentStatusUri).ConfigureAwait(false); + + return SendDataTransferObjectConverter.FromDataTransferObject(result); + } + } +} diff --git a/Digipost.Api.Client/DigipostClient.cs b/Digipost.Api.Client/DigipostClient.cs index 7e2fc136..21ad4bc8 100644 --- a/Digipost.Api.Client/DigipostClient.cs +++ b/Digipost.Api.Client/DigipostClient.cs @@ -105,11 +105,16 @@ public Inbox.Inbox GetInbox(Sender senderId) return new Inbox.Inbox(_requestHelper, GetRoot(new ApiRootUri(senderId))); } - public Archive.ArchiveApi GetArchive(Sender senderId = null) + public Archive.IArchiveApi GetArchive(Sender senderId = null) { return new Archive.ArchiveApi(_requestHelper, _loggerFactory, GetRoot(new ApiRootUri(senderId))); } + public IDocumentsApi GetDocumentStatus(Sender senderId = null) + { + return new DocumentsApi(_requestHelper, _loggerFactory, GetRoot(new ApiRootUri(senderId))); + } + public IIdentificationResult Identify(IIdentification identification) { return _sendMessageApi().Identify(identification); diff --git a/docs/Gemfile.lock b/docs/Gemfile.lock index b9210e2f..76759ba8 100644 --- a/docs/Gemfile.lock +++ b/docs/Gemfile.lock @@ -1,14 +1,20 @@ GEM remote: https://rubygems.org/ specs: - activesupport (7.0.8) + activesupport (7.1.1) + base64 + bigdecimal concurrent-ruby (~> 1.0, >= 1.0.2) + connection_pool (>= 2.2.5) + drb i18n (>= 1.6, < 2) minitest (>= 5.1) + mutex_m tzinfo (~> 2.0) addressable (2.8.5) public_suffix (>= 2.0.2, < 6.0) base64 (0.1.1) + bigdecimal (3.1.4) coffee-script (2.4.1) coffee-script-source execjs @@ -16,8 +22,11 @@ GEM colorator (1.1.0) commonmarker (0.23.10) concurrent-ruby (1.2.2) + connection_pool (2.4.1) dnsruby (1.70.0) simpleidn (~> 0.2.1) + drb (2.1.1) + ruby2_keywords em-websocket (0.5.3) eventmachine (>= 0.12.9) http_parser.rb (~> 0) @@ -212,6 +221,7 @@ GEM jekyll-feed (~> 0.9) jekyll-seo-tag (~> 2.1) minitest (5.20.0) + mutex_m (0.1.2) nokogiri (1.15.4-x86_64-darwin) racc (~> 1.4) octokit (4.25.1) diff --git a/docs/_v14_0/2_send_messages.md b/docs/_v14_0/2_send_messages.md index f7cf908e..46d0ae13 100644 --- a/docs/_v14_0/2_send_messages.md +++ b/docs/_v14_0/2_send_messages.md @@ -546,3 +546,35 @@ var document = new Document( // Create Message and send using the client as specified in other examples. ``` + + +### Get status of a sent document + +You can for any given document check its status to see if it and when has been delivered, status of +read approval, delivery method etc. + +To do this you need to have the Guid given to the document. + +Send the message: + +```csharp +var documentGuid = Guid.NewGuid(); +var message = new Message( + sender, + new RecipientById(IdentificationType.PersonalIdentificationNumber, "311084xxxx"), + new Document(subject: "Attachment", fileType: "txt", path: @"c:\...\document.txt") + { + Guid = documentGuid.ToString() + } +); + +client.SendMessage(message); +``` + +To fetch fhe DocumentStatus later: + +```csharp +var documentStatus = _digipostClient.GetDocumentStatus(sender).GetDocumentStatus(documentGuid).Result; +``` + +This can be useful if you use fallback to print, print-if-unread, request for registration etc.