diff --git a/Scr/Sdk4me.GraphQL/Helpers/ExecutionQueryBuilder.cs b/Scr/Sdk4me.GraphQL/Helpers/ExecutionQueryBuilder.cs index 8eae6f8..a3db1b3 100644 --- a/Scr/Sdk4me.GraphQL/Helpers/ExecutionQueryBuilder.cs +++ b/Scr/Sdk4me.GraphQL/Helpers/ExecutionQueryBuilder.cs @@ -451,8 +451,7 @@ private static string SerializeObject(string? value) private static string[] SerializeObject(params string?[] values) { List retval = new(); - if (values == null) - values = Array.Empty(); + values ??= Array.Empty(); foreach (string? value in values) retval.Add(SerializeObject(value)); return retval.ToArray(); diff --git a/Scr/Sdk4me.GraphQL/Queries/RequestQuery.cs b/Scr/Sdk4me.GraphQL/Queries/RequestQuery.cs index fee9453..46961ee 100644 --- a/Scr/Sdk4me.GraphQL/Queries/RequestQuery.cs +++ b/Scr/Sdk4me.GraphQL/Queries/RequestQuery.cs @@ -347,7 +347,7 @@ public RequestQuery SelectWorkflow(WorkflowQuery query) /// public RequestQuery CustomFilter(string name, FilterOperator filterOperator, params string?[] values) { - return AddCustomFilter(name, filterOperator, values ?? new string?[] { null }); + return AddCustomFilter(name, filterOperator, values); } } } diff --git a/Scr/Sdk4me.GraphQL/Sdk4meClient.cs b/Scr/Sdk4me.GraphQL/Sdk4meClient.cs index c54cbdc..32c96b3 100644 --- a/Scr/Sdk4me.GraphQL/Sdk4meClient.cs +++ b/Scr/Sdk4me.GraphQL/Sdk4meClient.cs @@ -1,7 +1,6 @@ using Newtonsoft.Json.Converters; using System.Diagnostics; using System.Net.Http.Headers; -using System.Reflection; using System.Text; using System.Text.RegularExpressions; @@ -312,29 +311,61 @@ public async Task UploadAttachment(FileInfo file, stri if (!file.Exists) throw new FileNotFoundException(file.FullName); + using (FileStream stream = file.OpenRead()) + return await UploadAttachment(stream as Stream, file.Name, contentType); + } + + /// + /// Upload a file to the 4me AWS S3 storage. + /// + /// The content to upload. The stream needs to support seeking in order to determine the HTTP Content-Length header. + /// The file name. + /// The content type of the file. + /// A containing the 4me AWS S3 file storage reference key and file size. + /// + /// + public async Task UploadAttachment(StreamReader stream, string fileName, string contentType) + { + return await UploadAttachment(stream.BaseStream, fileName, contentType); + } + + /// + /// Upload a file to the 4me AWS S3 storage. + /// + /// The content to upload. The stream needs to support seeking in order to determine the HTTP Content-Length header. + /// The file name. + /// The content type of the file. + /// A containing the 4me AWS S3 file storage reference key and file size. + /// + public async Task UploadAttachment(Stream stream, string fileName, string contentType) + { + if (!stream.CanSeek) + throw new Sdk4meException("The stream needs to support seeking in order to determine the HTTP Content-Length header."); + DataList attachmentStorages = await Get(Query.AttachmentStorage); if (attachmentStorages.FirstOrDefault() is AttachmentStorage attachmentStorage) { - if (attachmentStorage.AllowedExtensions != null && attachmentStorage.AllowedExtensions.Contains(file.Extension.TrimStart('.'))) + string fileExtension = Path.GetExtension(fileName).TrimStart('.'); + if (attachmentStorage.AllowedExtensions != null && attachmentStorage.AllowedExtensions.Contains(fileExtension, StringComparer.InvariantCultureIgnoreCase)) { - if (file.Length >= attachmentStorage.SizeLimit) + if (stream.Length >= attachmentStorage.SizeLimit) throw new Sdk4meException($"File size exceeded, the maximum size is {attachmentStorage.SizeLimit} byte"); Dictionary storageFacility = attachmentStorage?.ProviderParameters?.ToObject>() ?? throw new Sdk4meException("File upload failed, invalid AttachmentStorage.ProviderParameters value."); MultipartFormDataContent multipartContent = new() - { - { new StringContent(contentType), "Content-Type" }, - { new StringContent(storageFacility["acl"]), "acl" }, - { new StringContent(storageFacility["key"]), "key" }, - { new StringContent(storageFacility["policy"]), "policy" }, - { new StringContent(storageFacility["success_action_status"]), "success_action_status" }, - { new StringContent(storageFacility["x-amz-algorithm"]), "x-amz-algorithm" }, - { new StringContent(storageFacility["x-amz-credential"]), "x-amz-credential" }, - { new StringContent(storageFacility["x-amz-date"]), "x-amz-date" }, - { new StringContent(storageFacility["x-amz-server-side-encryption"]), "x-amz-server-side-encryption" }, - { new StringContent(storageFacility["x-amz-signature"]), "x-amz-signature" }, - { new ByteArrayContent(File.ReadAllBytes(file.FullName)), "file", file.Name } - }; + { + { new StringContent(contentType), "Content-Type" }, + { new StringContent(storageFacility["acl"]), "acl" }, + { new StringContent(storageFacility["key"]), "key" }, + { new StringContent(storageFacility["policy"]), "policy" }, + { new StringContent(storageFacility["success_action_status"]), "success_action_status" }, + { new StringContent(storageFacility["x-amz-algorithm"]), "x-amz-algorithm" }, + { new StringContent(storageFacility["x-amz-credential"]), "x-amz-credential" }, + { new StringContent(storageFacility["x-amz-date"]), "x-amz-date" }, + { new StringContent(storageFacility["x-amz-server-side-encryption"]), "x-amz-server-side-encryption" }, + { new StringContent(storageFacility["x-amz-signature"]), "x-amz-signature" }, + { new StreamContent(stream), "file", fileName } + }; using (HttpRequestMessage requestMessage = new(HttpMethod.Post, attachmentStorage.UploadUri) { Content = multipartContent }) { @@ -351,7 +382,7 @@ public async Task UploadAttachment(FileInfo file, stri return new() { Key = match.Value, - Size = file.Length + Size = stream.Length }; } else @@ -362,7 +393,10 @@ public async Task UploadAttachment(FileInfo file, stri } } } - throw new Sdk4meException($"The {file.Extension} extension is not allowed on this 4me instance."); + if (fileExtension == string.Empty) + throw new Sdk4meException($"A file name without extension is not allowed on this 4me instance."); + else + throw new Sdk4meException($"The '{fileExtension}' extension is not allowed on this 4me instance."); } throw new Sdk4meException("No AttachmentStorage object returned by the GraphQL API."); }