Newer
Older
using System.Text.RegularExpressions;
using Autofac;
using FitConnect.Models;
using FitConnect.Models.Api.Metadata;
using Microsoft.Extensions.Logging;
using Attachment = FitConnect.Models.Attachment;
using Data = FitConnect.Models.Api.Metadata.Data;
using Metadata = FitConnect.Models.Metadata;
/// <summary>
/// The fluent implementation for the <see cref="Sender" /> to encapsulate the FitConnect API.
/// </summary>
/// <example>
/// Reference for FluentSender
/// <code>
/// client.Sender
/// .Authenticate(clientId!, clientSecret!)
/// .CreateSubmission(new Submission { Attachments = new List() })
/// .UploadAttachments()
/// .WithData(data)
/// .Subm@it();
/// </code>
/// </example>
public class Sender : FitConnectClient, ISender, ISenderWithDestination,
ISenderWithAttachments, ISenderWithData, ISenderWithService {
public Sender(FitConnectEnvironment environment, string clientId, string clientSecret,
ILogger? logger = null) : base(environment, clientId, clientSecret, logger) {
public Sender(FitConnectEnvironment environment, string clientId, string clientSecret,
IContainer container) : base(environment, clientId, clientSecret,
container) {
}
public string? PublicKey { get; set; }
public ISenderWithDestination FindDestinationId(string leiaKey, string? ags = null,
string? ars = null,
string? areaId = null) {
if (ags == null && ars == null && areaId == null)
throw new ArgumentException("One of the following must be provided: ags, ars, areaId");
var destinationId = RouteService.GetDestinationIdAsync(leiaKey, ags, ars, areaId).Result;
Logger?.LogInformation("Received destinations: {Destinations}",
destinationId.Select(d => d.DestinationId).Aggregate((a, b) => a + "," + b));
return WithDestination(destinationId.First().DestinationId);
public ISenderWithDestination WithDestination(string destinationId) {
if (!Regex.IsMatch(destinationId,
"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"))
throw new ArgumentException("The destination must be a valid GUID");
Submission = CreateSubmission(destinationId).Result;
return this;
}
try {
JsonConvert.DeserializeObject(data);
}
catch (Exception e) {
throw new ArgumentException("The data must be valid JSON string", e);
}
Submission!.Data = data;
return this;
}
public Submission? Submission { get; set; }
if (Submission == null) {
Logger?.LogCritical("Submission is null on submit");
throw new InvalidOperationException("Submission is not ready");
}
var metadata = CreateMetadata(Submission);
Logger?.LogTrace("MetaData: {metadata}", metadata);
Logger?.LogInformation("Sending submission");
var encryptedMeta = Encryption.Encrypt(metadata);
Logger?.LogTrace("Encrypted metadata: {encryptedMeta}", encryptedMeta);
Submission.EncryptedMetadata = encryptedMeta;
Submission.EncryptedData = Encryption.Encrypt(Submission.Data);
var result = SubmissionService
.SubmitSubmission(Submission.Id!, (SubmitSubmissionDto)Submission).Result;
public ISenderWithService WithServiceType(string serviceName, string leikaKey) {
if (string.IsNullOrWhiteSpace(leikaKey) || !Regex.IsMatch(leikaKey,
"^urn:[a-z0-9][a-z0-9-]{0,31}:[a-z0-9()+,.:=@;$_!*'%/?#-]+$")) {
Logger?.LogError("The leikaKey must be a valid URN");
throw new ArgumentException("Invalid leika key");
}
Submission!.ServiceType = new ServiceType {
Name = serviceName,
Identifier = leikaKey
};
return this;
/// <summary>
///
/// </summary>
/// <param name="attachments"></param>
/// <returns></returns>
public ISenderWithAttachments WithAttachments(params Attachment[] attachments) =>
WithAttachments(attachments.ToList());
/// <param name="attachments"></param>
/// <exception cref="InvalidOperationException"></exception>
/// <exception cref="ArgumentException"></exception>
public ISenderWithAttachments WithAttachments(IEnumerable<Attachment> attachments) {
Submission!.Attachments = new List<Attachment>();
foreach (var attachment in attachments) Submission!.Attachments.Add(attachment);
if (Submission.ServiceType == null) {
Logger?.LogError("Submission has no service type");
throw new ArgumentException("Submission has no service type");
}
var created = SubmissionService.CreateSubmission((CreateSubmissionDto)Submission);
Submission.Id = created.SubmissionId;
Submission.CaseId = created.CaseId;
Logger?.LogInformation("Submission Id {CreatedSubmissionId}, CaseId {SubmissionCaseId}",
created.SubmissionId, Submission.CaseId);
var encryptedAttachments = Encrypt(PublicKey!, Submission.Attachments);
UploadAttachmentsAsync(Submission.Id!, encryptedAttachments).Wait();
return this;
public ISenderWithDestination FindDestinationId(Destination destination) {
throw new NotImplementedException();
}
/// Creates a new <see cref="Submission" /> on the FitConnect server.
/// <param name="destinationId">The id of the destination the submission has to be sent to</param>
/// <exception cref="InvalidOperationException">If sender is not authenticated</exception>
/// <exception cref="ArgumentException">If submission is not ready to be sent</exception>
private async Task<Submission> CreateSubmission(string destinationId) {
PublicKey = await GetPublicKeyFromDestination(destinationId);
Encryption = new FitEncryption(Logger) { PublicKeyEncryption = PublicKey };
var submission = new Submission {
DestinationId = destinationId
};
return submission;
}
private async Task<string> GetPublicKeyFromDestination(string destinationId) {
var publicKey = await DestinationService.GetPublicKey(destinationId);
return publicKey;
/// Create Metadata incl. Hash
/// <param name="submission"></param>
public static string CreateMetadata(Submission submission) {
Content = FitEncryption.CalculateHash(submission.Data ?? "")
SubmissionSchema = new Fachdatenschema {
SchemaUri = submission.ServiceType.Identifier,
}
};
var contentStructure = new ContentStructure {
Data = data,
Attachments = submission.Attachments.Select(a =>
Description = a.Description,
AttachmentId = a.Id,
ContentStructure = contentStructure
};
return JsonConvert.SerializeObject(metaData);
public IEnumerable<Area> GetAreas(string filter, out int totalCount, int offset = 0,
int limit = 100) {
var dto = RouteService.GetAreas(filter, offset, limit).Result;
totalCount = dto?.TotalCount ?? 0;
return dto?.Areas ?? new List<Area>();
}
/// Uploading the encrypted data to the server
/// <param name="submissionId">Submissions ID</param>
/// <param name="encryptedAttachments">Encrypted attachments with id and content</param>
private async Task<bool> UploadAttachmentsAsync(string submissionId,
try {
foreach (var (id, content) in encryptedAttachments) {
Logger?.LogInformation("Uploading attachment {id}", id);
SubmissionService.UploadAttachment(submissionId, id, content);
}
return true;
}
catch (Exception e) {
Logger?.LogError("Error Uploading attachment {message}", e.Message);
throw;