From 63c900d369b710280d46f49cd8b62d73a74425b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eivind=20Bergst=C3=B8l?= Date: Wed, 8 Nov 2023 09:54:23 +0100 Subject: [PATCH] Add feature to get the document as stream or url to open in webbrowser A Link relation changed it name in Digipost. Changed name i api for type SharedDocumentRequestState -> ShareDocumentRequestState Add support for Stop sharing The ShareDocumentsRequestEvent data type can be added to the ShareDocumentRequest. Fetch the ShareDocumentRequestState and that will have a url to add Data to the request. Fixes after rebase on main-branch Use new Digipost DataType types for metadata and additional data. This is a breaking change for how we use DataTypes in this library. Before it was a stringy dependency where the user of the client library had to Serialize the metadata them selves. Now this is done behind the scenes. This will create better abstractions and type safety in addition to better description of how metadata is supposed to be used. * Separated smart post to a new header in the menu since the Send messages menu-point was getting very big. * Added the documentation for the process for Share document request. Use latest beta of data types Clean up after rebase on main --- .../DataTransferObjectConverter.cs | 53 +++- .../Entrypoint/Entrypoint.cs | 5 + .../Generated/Apidomain/V8.cs | 266 ++++++++++++++++++ Digipost.Api.Client.Common/IRestLinkable.cs | 9 + .../Relations/ApiRelations.cs | 53 +++- .../Share/ShareDocumentsRequestState.cs | 35 +++ .../Share/SharedDocument.cs | 84 ++++++ .../Share/SharedDocumentContent.cs | 17 ++ Digipost.Api.Client.Docs/SendExamples.cs | 37 +++ .../Xsd/Data/api_v8.xsd | 70 +++++ .../Actions/AddAdditionalDataAction.cs | 37 +++ Digipost.Api.Client.Send/Document.cs | 2 +- Digipost.Api.Client.Send/IAdditionalData.cs | 34 +++ .../SendDataTransferObjectConverter.cs | 18 +- Digipost.Api.Client.Send/SendRequestHelper.cs | 9 + .../Smoke/ClientSmokeTestHelper.cs | 44 ++- .../Smoke/ClientSmokeTests.cs | 26 +- Digipost.Api.Client/Api/DocumentsApi.cs | 52 +++- Digipost.Api.Client/Api/SendMessage.cs | 15 + Digipost.Api.Client/DigipostClient.cs | 14 + docs/_v14_0/3_send_smart_post.md | 101 +++++++ 21 files changed, 964 insertions(+), 17 deletions(-) create mode 100644 Digipost.Api.Client.Common/Share/ShareDocumentsRequestState.cs create mode 100644 Digipost.Api.Client.Common/Share/SharedDocument.cs create mode 100644 Digipost.Api.Client.Common/Share/SharedDocumentContent.cs create mode 100644 Digipost.Api.Client.Send/Actions/AddAdditionalDataAction.cs create mode 100644 Digipost.Api.Client.Send/IAdditionalData.cs diff --git a/Digipost.Api.Client.Common/DataTransferObjectConverter.cs b/Digipost.Api.Client.Common/DataTransferObjectConverter.cs index 0351c6f2..1094d163 100755 --- a/Digipost.Api.Client.Common/DataTransferObjectConverter.cs +++ b/Digipost.Api.Client.Common/DataTransferObjectConverter.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; +using Digipost.Api.Client.Common.Entrypoint; using Digipost.Api.Client.Common.Enums; using Digipost.Api.Client.Common.Extensions; using Digipost.Api.Client.Common.Identify; @@ -9,12 +10,11 @@ using Digipost.Api.Client.Common.Recipient; using Digipost.Api.Client.Common.Search; using Digipost.Api.Client.Common.SenderInfo; -using Digipost.Api.Client.Common.Extensions; +using Digipost.Api.Client.Common.Share; using Digipost.Api.Client.Send; using V8; using Identification = V8.Identification; using Link = Digipost.Api.Client.Common.Entrypoint.Link; -using Root = Digipost.Api.Client.Common.Entrypoint.Root; namespace Digipost.Api.Client.Common { @@ -182,7 +182,7 @@ internal static Print_Details ToDataTransferObject(this IPrintDetails printDetai return printDetailsDataTransferObject; } - internal static List ToDataTransferObject(this IPrintInstructions printInstructions) + private static List ToDataTransferObject(this IPrintInstructions printInstructions) { if (printInstructions == null || printInstructions.PrintInstruction.Count == 0) return new List(); @@ -190,7 +190,7 @@ internal static List ToDataTransferObject(this IPrintInstruct return printInstructions.PrintInstruction.Select(ToDataTransferObject).ToList(); } - internal static Print_Instruction ToDataTransferObject(IPrintInstruction printInstruction) + private static Print_Instruction ToDataTransferObject(IPrintInstruction printInstruction) { if (printInstruction == null) return null; @@ -273,7 +273,8 @@ internal static Foreign_Address ToDataTransferObject(this IForeignAddress foreig if (foreignAddress.CountryIdentifier == CountryIdentifier.Country) { result.Country = foreignAddress.CountryIdentifierValue; - } else if (foreignAddress.CountryIdentifier == CountryIdentifier.Countrycode) + } + else if (foreignAddress.CountryIdentifier == CountryIdentifier.Countrycode) { result.Country_Code = foreignAddress.CountryIdentifierValue; } @@ -361,6 +362,7 @@ internal static IIdentificationResult FromDataTransferObject(this Identification { return new IdentificationResult(IdentificationResultType.InvalidReason, identificationResultDto.Invalid_Reason.ToString()); } + if (identificationResultDto.Unidentified_ReasonSpecified) { return new IdentificationResult(IdentificationResultType.UnidentifiedReason, identificationResultDto.Unidentified_Reason.ToString()); @@ -384,6 +386,7 @@ private static IdentificationResult IdentificationResultForDigipostOrPersonalIde default: throw new ArgumentOutOfRangeException(); } + return identificationResult; } @@ -397,7 +400,7 @@ internal static Error FromDataTransferObject(this V8.Error error) }; } - internal static SearchDetailsResult FromDataTransferObject(this V8.Recipients recipients) + internal static SearchDetailsResult FromDataTransferObject(this Recipients recipients) { return new SearchDetailsResult { @@ -424,5 +427,43 @@ internal static SearchDetailsResult FromDataTransferObject(this V8.Recipients re }; } + + internal static ShareDocumentsRequestState FromDataTransferObject(this Share_Documents_Request_State dto) + { + return new ShareDocumentsRequestState( + dto.Shared_At_TimeSpecified ? dto.Shared_At_Time : (DateTime?) null, + dto.Expiry_TimeSpecified ? dto.Expiry_Time : (DateTime?) null, + dto.Withdrawn_TimeSpecified ? dto.Withdrawn_Time : (DateTime?) null, + dto.Shared_Documents.FromDataTransferObject(), + dto.Link.FromDataTransferObject() + ); + } + + private static IEnumerable FromDataTransferObject(this Collection dto) + { + return dto.Select(document => new SharedDocument( + document.Delivery_Time, + document.Subject, + document.File_Type, + Convert.ToInt32(document.File_Size_Bytes), + document.Origin.FromDataTransferObject(), + document.Link.FromDataTransferObject() + )).ToList(); + } + + private static IOrigin FromDataTransferObject(this Shared_Document_Origin dto) + { + if (dto.Organisation != null) + { + return new OrganisationOrigin(dto.Organisation.Name, dto.Organisation.Organisation_Number); + } + + return new PrivatePersonOrigin(dto.Private_Person.Name); + } + + internal static SharedDocumentContent FromDataTransferObject(this Shared_Document_Content dto) + { + return new SharedDocumentContent(dto.Content_Type, new Uri(dto.Uri, UriKind.Absolute)); + } } } diff --git a/Digipost.Api.Client.Common/Entrypoint/Entrypoint.cs b/Digipost.Api.Client.Common/Entrypoint/Entrypoint.cs index 42c80bd4..5ba1577d 100644 --- a/Digipost.Api.Client.Common/Entrypoint/Entrypoint.cs +++ b/Digipost.Api.Client.Common/Entrypoint/Entrypoint.cs @@ -89,6 +89,11 @@ public DocumentStatusUri GetDocumentStatusUri(Guid guid) { return new DocumentStatusUri(Links["DOCUMENT_STATUS"], guid); } + + public ShareDocumentsRequestStateUri GetShareDocumentsRequestStateUri(Guid guid) + { + return new ShareDocumentsRequestStateUri(Links["GET_SHARE_DOCUMENTS_REQUEST_STATE"], guid); + } } public class Link diff --git a/Digipost.Api.Client.Common/Generated/Apidomain/V8.cs b/Digipost.Api.Client.Common/Generated/Apidomain/V8.cs index d87dbc77..b4fb4124 100644 --- a/Digipost.Api.Client.Common/Generated/Apidomain/V8.cs +++ b/Digipost.Api.Client.Common/Generated/Apidomain/V8.cs @@ -3213,6 +3213,272 @@ public partial class Archive_Document_Content public string Uri { get; set; } } + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.594.0")] + [System.SerializableAttribute()] + [System.Xml.Serialization.XmlTypeAttribute("share-documents-request-state", Namespace="http://api.digipost.no/schema/v8")] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.ComponentModel.DesignerCategoryAttribute("code")] + [System.Xml.Serialization.XmlRootAttribute("share-documents-request-state", Namespace="http://api.digipost.no/schema/v8")] + public partial class Share_Documents_Request_State + { + + [System.Xml.Serialization.XmlIgnoreAttribute()] + private System.Collections.ObjectModel.Collection _shared_Documents; + + [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.Xml.Serialization.XmlArrayAttribute("shared-documents")] + [System.Xml.Serialization.XmlArrayItemAttribute("shared-document", Namespace="http://api.digipost.no/schema/v8")] + public System.Collections.ObjectModel.Collection Shared_Documents + { + get + { + return this._shared_Documents; + } + private set + { + this._shared_Documents = value; + } + } + + /// + /// Initializes a new instance of the class. + /// + public Share_Documents_Request_State() + { + this._shared_Documents = new System.Collections.ObjectModel.Collection(); + this._link = new System.Collections.ObjectModel.Collection(); + } + + [System.Xml.Serialization.XmlElementAttribute("shared-at-time", DataType="dateTime")] + public System.DateTime Shared_At_Time { get; set; } + + /// + /// Gets or sets a value indicating whether the Shared_At_Time property is specified. + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool Shared_At_TimeSpecified { get; set; } + + [System.Xml.Serialization.XmlElementAttribute("expiry-time", DataType="dateTime")] + public System.DateTime Expiry_Time { get; set; } + + /// + /// Gets or sets a value indicating whether the Expiry_Time property is specified. + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool Expiry_TimeSpecified { get; set; } + + [System.Xml.Serialization.XmlElementAttribute("withdrawn-time", DataType="dateTime")] + public System.DateTime Withdrawn_Time { get; set; } + + /// + /// Gets or sets a value indicating whether the Withdrawn_Time property is specified. + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool Withdrawn_TimeSpecified { get; set; } + + [System.Xml.Serialization.XmlIgnoreAttribute()] + private System.Collections.ObjectModel.Collection _link; + + [System.Xml.Serialization.XmlElementAttribute("link")] + public System.Collections.ObjectModel.Collection Link + { + get + { + return this._link; + } + private set + { + this._link = value; + } + } + + /// + /// Gets a value indicating whether the Link collection is empty. + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool LinkSpecified + { + get + { + return (this.Link.Count != 0); + } + } + } + + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.594.0")] + [System.SerializableAttribute()] + [System.Xml.Serialization.XmlTypeAttribute("shared-documents", Namespace="http://api.digipost.no/schema/v8")] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.ComponentModel.DesignerCategoryAttribute("code")] + public partial class Shared_Documents + { + + [System.Xml.Serialization.XmlIgnoreAttribute()] + private System.Collections.ObjectModel.Collection _shared_Document; + + [System.Xml.Serialization.XmlElementAttribute("shared-document")] + public System.Collections.ObjectModel.Collection Shared_Document + { + get + { + return this._shared_Document; + } + private set + { + this._shared_Document = value; + } + } + + /// + /// Gets a value indicating whether the Shared_Document collection is empty. + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool Shared_DocumentSpecified + { + get + { + return (this.Shared_Document.Count != 0); + } + } + + /// + /// Initializes a new instance of the class. + /// + public Shared_Documents() + { + this._shared_Document = new System.Collections.ObjectModel.Collection(); + } + } + + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.594.0")] + [System.SerializableAttribute()] + [System.Xml.Serialization.XmlTypeAttribute("shared-document", Namespace="http://api.digipost.no/schema/v8")] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.ComponentModel.DesignerCategoryAttribute("code")] + public partial class Shared_Document + { + + [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.Xml.Serialization.XmlElementAttribute("delivery-time", DataType="dateTime")] + public System.DateTime Delivery_Time { get; set; } + + /// + /// Maximum length: 255. + /// + [System.ComponentModel.DataAnnotations.MaxLengthAttribute(255)] + [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.Xml.Serialization.XmlElementAttribute("subject")] + public string Subject { get; set; } + + [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.Xml.Serialization.XmlElementAttribute("file-type")] + public string File_Type { get; set; } + + [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.Xml.Serialization.XmlElementAttribute("file-size-bytes")] + public string File_Size_Bytes { get; set; } + + [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.Xml.Serialization.XmlElementAttribute("origin")] + public Shared_Document_Origin Origin { get; set; } + + [System.Xml.Serialization.XmlIgnoreAttribute()] + private System.Collections.ObjectModel.Collection _link; + + [System.Xml.Serialization.XmlElementAttribute("link")] + public System.Collections.ObjectModel.Collection Link + { + get + { + return this._link; + } + private set + { + this._link = value; + } + } + + /// + /// Gets a value indicating whether the Link collection is empty. + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool LinkSpecified + { + get + { + return (this.Link.Count != 0); + } + } + + /// + /// Initializes a new instance of the class. + /// + public Shared_Document() + { + this._link = new System.Collections.ObjectModel.Collection(); + } + } + + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.594.0")] + [System.SerializableAttribute()] + [System.Xml.Serialization.XmlTypeAttribute("shared-document-origin", Namespace="http://api.digipost.no/schema/v8")] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.ComponentModel.DesignerCategoryAttribute("code")] + public partial class Shared_Document_Origin + { + + [System.Xml.Serialization.XmlElementAttribute("organisation")] + public Shared_Document_Origin_Organisation Organisation { get; set; } + + [System.Xml.Serialization.XmlElementAttribute("private-person")] + public Shared_Document_Origin_Private_Person Private_Person { get; set; } + } + + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.594.0")] + [System.SerializableAttribute()] + [System.Xml.Serialization.XmlTypeAttribute("shared-document-origin-organisation", Namespace="http://api.digipost.no/schema/v8")] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.ComponentModel.DesignerCategoryAttribute("code")] + public partial class Shared_Document_Origin_Organisation + { + + [System.Xml.Serialization.XmlAttributeAttribute("organisation-number")] + public string Organisation_Number { get; set; } + + [System.Xml.Serialization.XmlAttributeAttribute("name")] + public string Name { get; set; } + } + + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.594.0")] + [System.SerializableAttribute()] + [System.Xml.Serialization.XmlTypeAttribute("shared-document-origin-private-person", Namespace="http://api.digipost.no/schema/v8")] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.ComponentModel.DesignerCategoryAttribute("code")] + public partial class Shared_Document_Origin_Private_Person + { + + [System.Xml.Serialization.XmlAttributeAttribute("name")] + public string Name { get; set; } + } + + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.594.0")] + [System.SerializableAttribute()] + [System.Xml.Serialization.XmlTypeAttribute("shared-document-content", Namespace="http://api.digipost.no/schema/v8")] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.ComponentModel.DesignerCategoryAttribute("code")] + [System.Xml.Serialization.XmlRootAttribute("shared-document-content", Namespace="http://api.digipost.no/schema/v8")] + public partial class Shared_Document_Content + { + + [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.Xml.Serialization.XmlElementAttribute("content-type")] + public string Content_Type { get; set; } + + [System.ComponentModel.DataAnnotations.RequiredAttribute()] + [System.Xml.Serialization.XmlElementAttribute("uri")] + public string Uri { get; set; } + } + [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.0.594.0")] [System.SerializableAttribute()] [System.Xml.Serialization.XmlTypeAttribute("autocomplete", Namespace="http://api.digipost.no/schema/v8", AnonymousType=true)] diff --git a/Digipost.Api.Client.Common/IRestLinkable.cs b/Digipost.Api.Client.Common/IRestLinkable.cs index df00b12a..38d58a3c 100644 --- a/Digipost.Api.Client.Common/IRestLinkable.cs +++ b/Digipost.Api.Client.Common/IRestLinkable.cs @@ -5,6 +5,15 @@ namespace Digipost.Api.Client.Common { public abstract class RestLinkable { + protected RestLinkable() + { + } + + protected RestLinkable(Dictionary links) + { + Links = links; + } + internal Dictionary Links { get; set; } } } diff --git a/Digipost.Api.Client.Common/Relations/ApiRelations.cs b/Digipost.Api.Client.Common/Relations/ApiRelations.cs index a4043ba7..d4b16830 100644 --- a/Digipost.Api.Client.Common/Relations/ApiRelations.cs +++ b/Digipost.Api.Client.Common/Relations/ApiRelations.cs @@ -8,6 +8,19 @@ namespace Digipost.Api.Client.Common.Relations { + /// + /// A guid-based uri is typically one that is supposed to be appended by a guid that only the sender/broker can know. + /// eg. + /// api/1234/document/uuid/{append-guid-here} + /// + public abstract class GuidBasedUri : Uri + { + protected GuidBasedUri(Link link, Guid? guid) + : base($"{link.Uri}{guid.ToString()}", UriKind.Absolute) + { + } + } + public class ApiRootUri : Uri { public ApiRootUri(Sender senderId = null) @@ -53,6 +66,15 @@ public RecipientSearchUri(Link link, string search) } } + public class AddAdditionalDataUri : Uri + { + public AddAdditionalDataUri(Link link) + : base(link.Uri, UriKind.Absolute) + { + + } + } + public class GetInboxUri : Uri { public GetInboxUri(Link link, int offset = 0, int limit = 100) @@ -93,15 +115,15 @@ public GetArchiveDocumentByReferenceIdUri(Link link, string referenceId) } } - public class GetArchiveDocumentByUuidUri : Uri + public class GetArchiveDocumentByUuidUri : GuidBasedUri { public GetArchiveDocumentByUuidUri(Link link, Guid guid) - : base($"{link.Uri}{guid.ToString()}", UriKind.Absolute) + : base(link, guid) { } public GetArchiveDocumentByUuidUri(Link link) - : base(link.Uri, UriKind.Absolute) + : base(link, null) { } } @@ -187,14 +209,33 @@ private static string DatetimeFormatter(DateTime? dt) } } - public class DocumentStatusUri : Uri + public class DocumentStatusUri : GuidBasedUri { public DocumentStatusUri(Link link, Guid guid) - : base($"{link.Uri}{guid.ToString()}", UriKind.Absolute) + : base(link, guid) + { + } + } + + public class ShareDocumentsRequestStateUri : GuidBasedUri + { + public ShareDocumentsRequestStateUri(Link link, Guid guid) + : base(link, guid) + { + } + } + + public class GetSharedDocumentContentStreamUri : Uri + { + public GetSharedDocumentContentStreamUri(Link link) + : base(link.Uri, UriKind.Absolute) { } + } - public DocumentStatusUri(Link link) + public class GetSharedDocumentContentUri : Uri + { + public GetSharedDocumentContentUri(Link link) : base(link.Uri, UriKind.Absolute) { } diff --git a/Digipost.Api.Client.Common/Share/ShareDocumentsRequestState.cs b/Digipost.Api.Client.Common/Share/ShareDocumentsRequestState.cs new file mode 100644 index 00000000..e1cbfe0d --- /dev/null +++ b/Digipost.Api.Client.Common/Share/ShareDocumentsRequestState.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using Digipost.Api.Client.Common.Entrypoint; +using Digipost.Api.Client.Common.Relations; + +namespace Digipost.Api.Client.Common.Share +{ + public class ShareDocumentsRequestState : RestLinkable + { + public DateTime? SharedAtTime { get; } + + public DateTime? ExpiryTime { get; } + + public DateTime? WithdrawnTime { get; } + + public IEnumerable SharedDocuments { get; } + + public ShareDocumentsRequestState(DateTime? sharedAtTime, + DateTime? expiryTime, + DateTime? withdrawnTime, + IEnumerable sharedDocuments, Dictionary links) + { + SharedAtTime = sharedAtTime; + ExpiryTime = expiryTime; + WithdrawnTime = withdrawnTime; + SharedDocuments = sharedDocuments; + Links = links; + } + + public AddAdditionalDataUri GetStopSharingUri() + { + return new AddAdditionalDataUri(Links["STOP_SHARING"]); + } + } +} diff --git a/Digipost.Api.Client.Common/Share/SharedDocument.cs b/Digipost.Api.Client.Common/Share/SharedDocument.cs new file mode 100644 index 00000000..6776c913 --- /dev/null +++ b/Digipost.Api.Client.Common/Share/SharedDocument.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +using Digipost.Api.Client.Common.Entrypoint; +using Digipost.Api.Client.Common.Relations; + +namespace Digipost.Api.Client.Common.Share +{ + public class SharedDocument : RestLinkable + { + public DateTime DeliveryTime { get; } + + public string Subject { get; } + + public string FileType { get; } + + public int FileSizeBytes { get; } + + public IOrigin Origin { get; } + + public SharedDocument( + DateTime deliveryTime, + String subject, + String fileType, + int fileSizeBytes, + IOrigin origin, + Dictionary links) + : base(links) + { + DeliveryTime = deliveryTime; + Subject = subject; + FileType = fileType; + FileSizeBytes = fileSizeBytes; + Origin = origin; + } + + public GetSharedDocumentContentStreamUri GetSharedDocumentContentStreamUri() + { + return new GetSharedDocumentContentStreamUri(Links["GET_SHARED_DOCUMENT_CONTENT_STREAM"]); + } + + public GetSharedDocumentContentUri GetSharedDocumentContentUri() + { + return new GetSharedDocumentContentUri(Links["GET_SHARED_DOCUMENT_CONTENT"]); + } + } + + public interface IOrigin + { + string Name { get; } + } + + public class OrganisationOrigin : IOrigin + { + public string Name { get; } + + public string OrganisationNumber { get; } + + public OrganisationOrigin(string name, string organisationNumber) + { + Name = name; + OrganisationNumber = organisationNumber; + } + + public override string ToString() + { + return Name + "[" + OrganisationNumber + "]"; + } + } + + public class PrivatePersonOrigin : IOrigin + { + public string Name { get; } + + public PrivatePersonOrigin(string name) + { + Name = name; + } + + public override string ToString() + { + return Name; + } + } +} diff --git a/Digipost.Api.Client.Common/Share/SharedDocumentContent.cs b/Digipost.Api.Client.Common/Share/SharedDocumentContent.cs new file mode 100644 index 00000000..21cb03e2 --- /dev/null +++ b/Digipost.Api.Client.Common/Share/SharedDocumentContent.cs @@ -0,0 +1,17 @@ +using System; + +namespace Digipost.Api.Client.Common.Share +{ + public class SharedDocumentContent + { + public SharedDocumentContent(string contentType, Uri uri) + { + ContentType = contentType; + Uri = uri; + } + + public string ContentType { get; } + + public Uri Uri { get; } + } +} diff --git a/Digipost.Api.Client.Docs/SendExamples.cs b/Digipost.Api.Client.Docs/SendExamples.cs index 1fbeceee..caa732ba 100755 --- a/Digipost.Api.Client.Docs/SendExamples.cs +++ b/Digipost.Api.Client.Docs/SendExamples.cs @@ -1,10 +1,13 @@ using System; +using System.IO; +using System.Linq; using Digipost.Api.Client.Common; using Digipost.Api.Client.Common.Enums; using Digipost.Api.Client.Common.Identify; using Digipost.Api.Client.Common.Print; using Digipost.Api.Client.Common.Recipient; using Digipost.Api.Client.Common.Utilities; +using Digipost.Api.Client.Common.Share; using Digipost.Api.Client.DataTypes.Core; using Digipost.Api.Client.Send; using Address = Digipost.Api.Client.DataTypes.Core.Common.Address; @@ -386,6 +389,40 @@ public void SendMessageWithExternalLinkMetadata() // Create Message and send using the client as specified in other examples. } + public async void SendShareDocumentRequestMessage() + { + var shareDocReq = new ShareDocumentsRequest( + maxShareDurationSeconds: 60 * 60 * 24 * 5, // Five calendar days + purpose: "The purpose for my use of the document"); + + var requestGuid = Guid.NewGuid(); // Keep this in your database as reference to this particular user interaction + + var document = new Document( + subject: "Your appointment", + fileType: "pdf", + path: @"c:\...\document.pdf", + dataType: shareDocReq + ) + { + Guid = requestGuid.ToString() + }; + + // Create Message and send using the client as specified in other examples. + + // when you the user has shared a document: + var shareDocumentsRequestState = await client.GetDocumentSharing(sender) + .GetShareDocumentsRequestState(requestGuid); + + SharedDocumentContent sharedDocumentContent = await client.GetDocumentSharing(sender).GetShareDocumentContent(shareDocumentsRequestState.SharedDocuments.First().GetSharedDocumentContentUri()); + + Stream stream = await client.GetDocumentSharing(sender).FetchSharedDocument(shareDocumentsRequestState.SharedDocuments.First().GetSharedDocumentContentStreamUri()); + Uri uri = sharedDocumentContent.Uri; + // sharedDocumentContent.Uri is a url to a document that can be shown in an internet browser. + + var additionalData = new AdditionalData(sender, new ShareDocumentsRequestSharingStopped()); + client.AddAdditionalData(additionalData, shareDocumentsRequestState.GetStopSharingUri()); + } + public void SendMessageWithSenderInformation() { var senderInformation = client.GetSenderInformation(new SenderOrganisation("9876543210", "thePartId")); diff --git a/Digipost.Api.Client.Resources/Xsd/Data/api_v8.xsd b/Digipost.Api.Client.Resources/Xsd/Data/api_v8.xsd index 4f4564c0..2c1b3176 100644 --- a/Digipost.Api.Client.Resources/Xsd/Data/api_v8.xsd +++ b/Digipost.Api.Client.Resources/Xsd/Data/api_v8.xsd @@ -1387,4 +1387,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Digipost.Api.Client.Send/Actions/AddAdditionalDataAction.cs b/Digipost.Api.Client.Send/Actions/AddAdditionalDataAction.cs new file mode 100644 index 00000000..e549d1fa --- /dev/null +++ b/Digipost.Api.Client.Send/Actions/AddAdditionalDataAction.cs @@ -0,0 +1,37 @@ +using System; +using System.Net.Http; +using System.Net.Http.Headers; +using Digipost.Api.Client.Common; +using Digipost.Api.Client.Common.Actions; +using Digipost.Api.Client.Common.Identify; +using Digipost.Api.Client.Common.Utilities; +using V8; + +namespace Digipost.Api.Client.Send.Actions +{ + internal class AddAdditionalDataAction : DigipostAction + { + public AddAdditionalDataAction(IAdditionalData additionalData) + : base(additionalData) + { + } + + internal override HttpContent Content(IAdditionalData requestContent) + { + var xmlMessage = Serialize(requestContent); + var messageContent = new StringContent(xmlMessage); + + var boundary = Guid.NewGuid().ToString(); + var mediaTypeHeaderValue = new MediaTypeHeaderValue(DigipostVersion.V8); + mediaTypeHeaderValue.Parameters.Add(new NameValueWithParametersHeaderValue("boundary", boundary)); + messageContent.Headers.ContentType = mediaTypeHeaderValue; + + return messageContent; + } + + protected override string Serialize(IAdditionalData requestContent) + { + return SerializeUtil.Serialize(requestContent.ToDataTransferObject()); + } + } +} diff --git a/Digipost.Api.Client.Send/Document.cs b/Digipost.Api.Client.Send/Document.cs index 3fb941f0..7a2c67c0 100755 --- a/Digipost.Api.Client.Send/Document.cs +++ b/Digipost.Api.Client.Send/Document.cs @@ -5,7 +5,7 @@ namespace Digipost.Api.Client.Send { - public class Document : IDocument + public class Document : RestLinkable, IDocument { /// The subject of the document. /// The file type of the file (e.g pdf,txt.. ). diff --git a/Digipost.Api.Client.Send/IAdditionalData.cs b/Digipost.Api.Client.Send/IAdditionalData.cs new file mode 100644 index 00000000..d46110ec --- /dev/null +++ b/Digipost.Api.Client.Send/IAdditionalData.cs @@ -0,0 +1,34 @@ +using Digipost.Api.Client.Common; +using Digipost.Api.Client.DataTypes.Core; + +namespace Digipost.Api.Client.Send +{ + public interface IAdditionalData : IRequestContent + { + /// + /// The sender of the message, i.e. what the receiver of the message sees as the sender of the message. + /// If you are delivering a message on behalf of an organization with id 5555, set this property + /// to 5555. If you are delivering on behalf of yourself, set this to your organization`s sender id. + /// The id is created by Digipost. + /// + Sender Sender { get; } + + /// + /// Optional metadata to enrich the document in Digipost. See https://github.com/digipost/digipost-data-types for valid data-types. + /// + IDigipostDataType DataType { get; } + } + + public class AdditionalData : IAdditionalData + { + public Sender Sender { get; } + + public IDigipostDataType DataType { get; } + + public AdditionalData(Sender sender, IDigipostDataType dataType) + { + Sender = sender; + DataType = dataType; + } + } +} diff --git a/Digipost.Api.Client.Send/SendDataTransferObjectConverter.cs b/Digipost.Api.Client.Send/SendDataTransferObjectConverter.cs index 468b36d9..62358e13 100755 --- a/Digipost.Api.Client.Send/SendDataTransferObjectConverter.cs +++ b/Digipost.Api.Client.Send/SendDataTransferObjectConverter.cs @@ -132,7 +132,8 @@ internal static IDocument FromDataTransferObject(this V8.Document documentDto) return new Document(documentDto.Subject, documentDto.File_Type, documentDto.Authentication_Level.ToAuthenticationLevel(), documentDto.Sensitivity_Level.ToSensitivityLevel(), FromDataTransferObject(documentDto.Sms_Notification)) { Guid = documentDto.Uuid, - ContentHash = new ContentHash {HashAlgoritm = documentDto.Content_Hash.Hash_Algorithm, Value = documentDto.Content_Hash.Value} + ContentHash = new ContentHash {HashAlgoritm = documentDto.Content_Hash.Hash_Algorithm, Value = documentDto.Content_Hash.Value}, + Links = documentDto.Link.FromDataTransferObject() }; } @@ -191,5 +192,20 @@ private static DocumentStatus.Read ToRead(this Read read) throw new ArgumentOutOfRangeException(); } } + + internal static Additional_Data ToDataTransferObject(this IAdditionalData additionalData) + { + var dto = new Additional_Data + { + Sender_Id = additionalData.Sender.Id, + Sender_IdSpecified = true, + Data_Type = new Data_Type() + { + Any = additionalData.DataType.ToXmlDocument().DocumentElement + } + }; + + return dto; + } } } diff --git a/Digipost.Api.Client.Send/SendRequestHelper.cs b/Digipost.Api.Client.Send/SendRequestHelper.cs index 0c327a9d..e938934e 100755 --- a/Digipost.Api.Client.Send/SendRequestHelper.cs +++ b/Digipost.Api.Client.Send/SendRequestHelper.cs @@ -2,6 +2,7 @@ using System.Threading.Tasks; using Digipost.Api.Client.Common.Actions; using Digipost.Api.Client.Common.Identify; +using Digipost.Api.Client.Common.Relations; using Digipost.Api.Client.Common.Utilities; using Digipost.Api.Client.Send.Actions; @@ -29,6 +30,14 @@ internal Task PostMessage(IMessage message, Uri uri, bool skipMetaDataVali return _requestHelper.Post(httpContent, messageAction.RequestContent, uri, skipMetaDataValidation); } + internal Task PostAdditionalData(IAdditionalData additionalData, AddAdditionalDataUri uri) + { + var action = new AddAdditionalDataAction(additionalData); + var httpContent = action.Content(additionalData); + + return _requestHelper.Post(httpContent, action.RequestContent, uri); + } + internal Task PostIdentification(IIdentification identification, Uri uri) { var messageAction = new IdentificationAction(identification); diff --git a/Digipost.Api.Client.Tests/Smoke/ClientSmokeTestHelper.cs b/Digipost.Api.Client.Tests/Smoke/ClientSmokeTestHelper.cs index 5114f956..65a957f9 100644 --- a/Digipost.Api.Client.Tests/Smoke/ClientSmokeTestHelper.cs +++ b/Digipost.Api.Client.Tests/Smoke/ClientSmokeTestHelper.cs @@ -10,6 +10,7 @@ using Digipost.Api.Client.Common.Relations; using Digipost.Api.Client.Common.Search; using Digipost.Api.Client.Common.SenderInfo; +using Digipost.Api.Client.Common.Share; using Digipost.Api.Client.Common.Utilities; using Digipost.Api.Client.DataTypes.Core; using Digipost.Api.Client.Send; @@ -17,6 +18,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Xunit; +using Sender = Digipost.Api.Client.Common.Sender; namespace Digipost.Api.Client.Tests.Smoke { @@ -51,6 +53,8 @@ internal class ClientSmokeTestHelper private DocumentStatus _documentStatus; private SenderInformation _senderInformation; private DocumentEvents _documentEvents; + private ShareDocumentsRequestState _shareDocumentsRequestState; + private ILogger _testLogger; public ClientSmokeTestHelper(TestSender testSender, bool withoutDataTypesProject = false) { @@ -59,9 +63,12 @@ public ClientSmokeTestHelper(TestSender testSender, bool withoutDataTypesProject var serviceProvider = LoggingUtility.CreateServiceProviderAndSetUpLogging(); - _digipostClient = new DigipostClient(new ClientConfig(broker, testSender.Environment) {TimeoutMilliseconds = 900000000, LogRequestAndResponse = true, SkipMetaDataValidation = withoutDataTypesProject}, testSender.Certificate, serviceProvider.GetService()); + var loggerFactory = serviceProvider.GetService(); + _digipostClient = new DigipostClient(new ClientConfig(broker, testSender.Environment) {TimeoutMilliseconds = 900000000, LogRequestAndResponse = true, SkipMetaDataValidation = withoutDataTypesProject}, testSender.Certificate, loggerFactory); _root = _digipostClient.GetRoot(new ApiRootUri()); + + _testLogger = loggerFactory.CreateLogger(); } public ClientSmokeTestHelper HasRoot() @@ -228,6 +235,32 @@ public void Expect_search_to_have_result() Assert.InRange(_searchResult.PersonDetails.ToList().Count, 1, 11); } + public ClientSmokeTestHelper FetchShareDocumentRequestState() + { + Assert_state(_messageDeliveryResult); + Assert_state(_primary); + + _shareDocumentsRequestState = _digipostClient.GetDocumentSharing(new Sender(_testSender.Id)).GetShareDocumentsRequestState(Guid.Parse(_primary.Guid)).Result; + return this; + } + + public void ExpectShareDocumentsRequestState() + { + Assert_state(_shareDocumentsRequestState); + if (_shareDocumentsRequestState.SharedDocuments.Any()) + { + var sharedDocument = _shareDocumentsRequestState.SharedDocuments.First(); + var sharedDocumentContent = _digipostClient.GetDocumentSharing(new Sender(_testSender.Id)) + .GetShareDocumentContent(sharedDocument.GetSharedDocumentContentUri()).Result; + + _testLogger.LogInformation($"Uri til dokument (pressthelink): '{sharedDocumentContent.Uri}'"); + + var documentStream = _digipostClient.GetDocumentSharing(new Sender(_testSender.Id)).FetchSharedDocument(sharedDocument.GetSharedDocumentContentStreamUri()).Result; + Assert.Equal(true, documentStream.CanRead); + Assert.True(documentStream.Length > 500); + } + } + public ClientSmokeTestHelper FetchDocumentStatus() { Assert_state(_messageDeliveryResult); @@ -275,5 +308,14 @@ public void Expect_valid_sender_information() Assert_state(_senderInformation); Assert.True(_senderInformation.IsValidSender); } + + public void PerformStopSharing() + { + Assert_state(_shareDocumentsRequestState); + + var additionalData = new AdditionalData(new Sender(_testSender.Id), new ShareDocumentsRequestSharingStopped()); + + _digipostClient.AddAdditionalData(additionalData, _shareDocumentsRequestState.GetStopSharingUri()); + } } } diff --git a/Digipost.Api.Client.Tests/Smoke/ClientSmokeTests.cs b/Digipost.Api.Client.Tests/Smoke/ClientSmokeTests.cs index fc9ebf20..716be66d 100644 --- a/Digipost.Api.Client.Tests/Smoke/ClientSmokeTests.cs +++ b/Digipost.Api.Client.Tests/Smoke/ClientSmokeTests.cs @@ -122,7 +122,7 @@ public void Can_send_document_with_object_datatype_to_digipost_user() } [Fact(Skip = "SmokeTest")] - public void Can_send_document_share_request_to_user() + public void Can_send_document_and_check_document_status() { _client .Create_message_with_primary_document() @@ -146,5 +146,29 @@ public void Check_document_status() DeliveryMethod.PENDING ); } + + [Fact(Skip = "SmokeTest")] + public void Can_send_document_share_request_to_user() + { + var shareDocReq = new ShareDocumentsRequest(maxShareDurationSeconds: 60 * 60 * 24 * 2, purpose: "The purpose for my use of the document"); // Two days + + _client + .CreateMessageWithPrimaryDataTypeDocument(shareDocReq) + .To_Digital_Recipient() + .SendMessage() + .Expect_message_to_have_status(MessageStatus.Delivered); + + _client.FetchDocumentStatus() + .Expect_document_status_to_be( + DocumentStatus.DocumentDeliveryStatus.DELIVERED, + DeliveryMethod.Digipost + ); + + //Set breakpoint her og del et dokument i qa.digipost.no! + _client.FetchShareDocumentRequestState() + .ExpectShareDocumentsRequestState(); + + _client.PerformStopSharing(); + } } } diff --git a/Digipost.Api.Client/Api/DocumentsApi.cs b/Digipost.Api.Client/Api/DocumentsApi.cs index d4941bec..aa74d1d3 100644 --- a/Digipost.Api.Client/Api/DocumentsApi.cs +++ b/Digipost.Api.Client/Api/DocumentsApi.cs @@ -1,8 +1,11 @@ using System; using System.Collections.Generic; +using System.IO; using System.Threading.Tasks; using Digipost.Api.Client.Common; using Digipost.Api.Client.Common.Entrypoint; +using Digipost.Api.Client.Common.Relations; +using Digipost.Api.Client.Common.Share; using Digipost.Api.Client.Common.Utilities; using Digipost.Api.Client.Send; using Microsoft.Extensions.Logging; @@ -21,8 +24,16 @@ public interface IDocumentsApi Task GetDocumentEvents(DateTime from, DateTime to, int offset, int maxResults); Task GetDocumentEventsAsync(DateTime from, DateTime to, int offset, int maxResults); } + public interface IShareDocumentsApi + { + Task GetShareDocumentsRequestState(Guid guid); + Task GetShareDocumentsRequestStateAsync(Guid guid); + Task GetShareDocumentContent(GetSharedDocumentContentUri uri); + Task GetShareDocumentContentAsync(GetSharedDocumentContentUri uri); + Task FetchSharedDocument(GetSharedDocumentContentStreamUri uri); + } - internal class DocumentsApi : IDocumentsApi + internal class DocumentsApi : IDocumentsApi, IShareDocumentsApi { private readonly RequestHelper _requestHelper; private readonly ILoggerFactory _loggerFactory; @@ -72,5 +83,44 @@ public async Task GetDocumentEventsAsync(DateTime from, DateTime return result.FromDataTransferObject(); } + + public Task GetShareDocumentsRequestState(Guid guid) + { + var result = GetShareDocumentsRequestStateAsync(guid); + + if (result.IsFaulted && result.Exception != null) + throw result.Exception.InnerException; + + return result; + } + + public async Task GetShareDocumentsRequestStateAsync(Guid guid) + { + var shareDocumentsRequestStateUri = _root.GetShareDocumentsRequestStateUri(guid); + var result = await _requestHelper.Get(shareDocumentsRequestStateUri).ConfigureAwait(false); + + return result.FromDataTransferObject(); + } + + public Task GetShareDocumentContent(GetSharedDocumentContentUri uri) + { + var result = GetShareDocumentContentAsync(uri); + if (result.IsFaulted && result.Exception != null) + throw result.Exception.InnerException; + + return result; + } + + public async Task GetShareDocumentContentAsync(GetSharedDocumentContentUri uri) + { + var result = await _requestHelper.Get(uri).ConfigureAwait(false); + + return result.FromDataTransferObject(); + } + + public async Task FetchSharedDocument(GetSharedDocumentContentStreamUri uri) + { + return await _requestHelper.GetStream(uri).ConfigureAwait(false); + } } } diff --git a/Digipost.Api.Client/Api/SendMessage.cs b/Digipost.Api.Client/Api/SendMessage.cs index 5c00ffec..6d838acd 100644 --- a/Digipost.Api.Client/Api/SendMessage.cs +++ b/Digipost.Api.Client/Api/SendMessage.cs @@ -3,6 +3,7 @@ using Digipost.Api.Client.Common; using Digipost.Api.Client.Common.Entrypoint; using Digipost.Api.Client.Common.Identify; +using Digipost.Api.Client.Common.Relations; using Digipost.Api.Client.Common.Search; using Digipost.Api.Client.Extensions; using Digipost.Api.Client.Send; @@ -110,5 +111,19 @@ public async Task SearchAsync(string search) return searchDetailsResult; } + + public void SendAdditionalData(IAdditionalData additionalData, AddAdditionalDataUri uri) + { + SendAdditionalDataAsync(additionalData, uri).GetAwaiter().GetResult(); + } + + public async Task SendAdditionalDataAsync(IAdditionalData additionalData, AddAdditionalDataUri uri) + { + _logger.LogDebug("Sending additional data '{uri}'", uri); + + await RequestHelper.PostAdditionalData(additionalData, uri).ConfigureAwait(false); + + _logger.LogDebug("Additional data added to '{uri}'", uri); + } } } diff --git a/Digipost.Api.Client/DigipostClient.cs b/Digipost.Api.Client/DigipostClient.cs index 05420298..a995e111 100644 --- a/Digipost.Api.Client/DigipostClient.cs +++ b/Digipost.Api.Client/DigipostClient.cs @@ -143,6 +143,15 @@ public IDocumentsApi DocumentsApi(Sender sender = null) return new DocumentsApi(_requestHelper, _loggerFactory, GetRoot(new ApiRootUri(sender)), senderToUse); } + public IDocumentsApi GetDocumentApi(Sender sender = null) + { + return new DocumentsApi(_requestHelper, _loggerFactory, GetRoot(new ApiRootUri(sender)), sender); + } + public IShareDocumentsApi GetDocumentSharing(Sender sender = null) + { + return new DocumentsApi(_requestHelper, _loggerFactory, GetRoot(new ApiRootUri(sender)), sender); + } + public IIdentificationResult Identify(IIdentification identification) { return _sendMessageApi().Identify(identification); @@ -163,6 +172,11 @@ public Task SendMessageAsync(IMessage message) return _sendMessageApi().SendMessageAsync(message, _clientConfig.SkipMetaDataValidation); } + public void AddAdditionalData(AdditionalData additionalData, AddAdditionalDataUri uri) + { + _sendMessageApi().SendAdditionalData(additionalData, uri); + } + public ISearchDetailsResult Search(string query) { return _sendMessageApi().Search(query); diff --git a/docs/_v14_0/3_send_smart_post.md b/docs/_v14_0/3_send_smart_post.md index 6c5e613c..37242ca1 100644 --- a/docs/_v14_0/3_send_smart_post.md +++ b/docs/_v14_0/3_send_smart_post.md @@ -104,3 +104,104 @@ var document = new Document( // Create Message and send using the client as specified in other examples. ``` + +### Datatype ShareDocumentsRequest + +This datatype facilitates exchange of documents between an organisation and a Digipost end user. The sender +first sends a message of datatype ShareDocumentsRequest, to which the end user can attach a list of documents. When +new documents are shared, a DocumentEvent is generated. The organisation can retrieve the status of their +ShareDocumentsRequest. If documents are shared and the sharing is not cancelled, the documents can either be downloaded +or viewed on the digipostdata.no domain. Active requests can be cancelled both by the end user and the organisation. + +The `purpose` attribute of the ShareDocumentsRequest should briefly explain why the sender organisation want to gain +access to the relevant documents. The primary document should contain a more detailed explanation. + +#### Send ShareDocumentsRequest + +```csharp +var shareDocReq = new ShareDocumentsRequest( + maxShareDurationSeconds: 60 * 60 * 24 * 5, // Five calendar days + purpose: "The purpose for my use of the document"); + +var requestGuid = Guid.NewGuid(); // Keep this in your database as reference to this particular user interaction + +var document = new Document( + subject: "Information about document sharing", + fileType: "pdf", + path: @"c:\...\document.pdf", + dataType: shareDocReq +) +{ + Guid = requestGuid.ToString() +}; + +var message = new Message( + senderInformation.Sender, + new RecipientById(IdentificationType.PersonalIdentificationNumber, "311084xxxx"), + new Document(subject: "Attachment", fileType: "pdf", path: @"c:\...\attachment.pdf") +); + +IMessageDeliveryResult result = client.SendMessage(message); +``` + +#### Be notified of new shared documents +The sender organisation can be notified of new shared documents by polling document events regularly. Use the `uuid` attribute +of the DocumentEvent to match with the `messageUUID` of the origin ShareDocumentsRequest. + +This particular api is still not developed for .NET client. Periodically check the below api to check current status for +sent shared request. + +#### Get state of ShareDocumentsRequest + +* Use the `requestGuid` that you stored from previous. + +```csharp +var shareDocumentsRequestState = await client.GetDocumentSharing(sender) + .GetShareDocumentsRequestState(requestGuid); +``` + +If the `shareDocumentsRequestState.SharedDocuments.Count() > 0` is true, then the user has shared a +document for this share request. + +#### Get documents + +Each `SharedDocument` has attributes describing the document and its origin. If `SharedDocumentOrigin` is of type +`OrganisationOrigin`, the corresponding document was received by the end user through Digipost from the organisation +with the provided organisation number. If the origin is of type `PrivatePersonOrigin`, the document was received either +from another end user or uploaded by the user itself. + +Get a single document as stream: + +```csharp +Stream stream = await client.GetDocumentSharing(sender) + .FetchSharedDocument( + shareDocumentsRequestState.SharedDocuments.First().GetSharedDocumentContentStreamUri() + ); +``` + +Get link to view a single document on digipostdata.no: + +```csharp +SharedDocumentContent sharedDocumentContent = await client.GetDocumentSharing(sender) + .GetShareDocumentContent( + shareDocumentsRequestState.SharedDocuments.First().GetSharedDocumentContentUri() + ); + +Uri uriToOpenInBrowser = sharedDocumentContent.Uri; +``` + +#### Stop sharing + +Use the `requestGuid` from before. Top stop the sharing you add more information on the original +message you sent to start this process. Stop sharing can be done when you definitely don't need the +document to be shared any more. The sharing will in any case stop at the originally +specified max share duration automatically. + +```csharp +var shareDocumentsRequestState = await client.GetDocumentSharing(sender) + .GetShareDocumentsRequestState(requestGuid); + +var additionalData = new AdditionalData(sender, new ShareDocumentsRequestSharingStopped()); + +client.AddAdditionalData(additionalData, shareDocumentsRequestState.GetStopSharingUri()); +```