From 1ecd14fa294f2688f96ea4ad8df632bfd7927e62 Mon Sep 17 00:00:00 2001 From: Quirijn Date: Wed, 18 Sep 2024 21:59:47 +0200 Subject: [PATCH 1/2] v3-tags (#97) * targetframework now net8.0, packages upgraded * Make it possible to get a list of tags by keyword * Allow changing tags of an existing Media item * Make it possible to remove tags from an asset * changed TagsSample to showcase the new tags-related features * updated gitignore * updated gitignore to the toptotal standard, added launchSettings to help developers run the samples * fixed merge conflict --- .gitignore | 2 +- Bynder/Sample/TagsSample.cs | 74 +++++++++++++++-- Bynder/Sdk/Query/Asset/GetTagsQuery.cs | 79 ++++++++++--------- Bynder/Sdk/Query/Asset/ModifyMediaQuery.cs | 9 ++- Bynder/Sdk/Service/Asset/AssetService.cs | 23 +++++- Bynder/Sdk/Service/Asset/IAssetService.cs | 9 +++ Bynder/Test/Service/Asset/AssetServiceTest.cs | 19 +++++ 7 files changed, 166 insertions(+), 49 deletions(-) diff --git a/.gitignore b/.gitignore index d1d96e7..b06db92 100644 --- a/.gitignore +++ b/.gitignore @@ -402,4 +402,4 @@ FodyWeavers.xsd ### VisualStudio Patch ### # Additional files built by Visual Studio -# End of https://www.toptal.com/developers/gitignore/api/visualstudio \ No newline at end of file +# End of https://www.toptal.com/developers/gitignore/api/visualstudio diff --git a/Bynder/Sample/TagsSample.cs b/Bynder/Sample/TagsSample.cs index e64cbba..9c53469 100644 --- a/Bynder/Sample/TagsSample.cs +++ b/Bynder/Sample/TagsSample.cs @@ -10,6 +10,7 @@ using System.Collections.Generic; using Bynder.Sdk.Query.Asset; using Bynder.Sdk.Model; +using Bynder.Sdk.Service.Asset; namespace Bynder.Sample { @@ -33,8 +34,9 @@ private TagsSample(Configuration configuration) { private async Task RunTagsSampleAsync() { + var assetService = _bynderClient.GetAssetService(); Console.WriteLine("Getting tags with a limit of 10: "); - var tags = await _bynderClient.GetAssetService().GetTagsAsync(new GetTagsQuery{Limit = 10}); + var tags = await assetService.GetTagsAsync(new GetTagsQuery{Limit = 10}); foreach(Tag tag in tags){ Console.WriteLine($"Tag Id: {tag.ID}"); Console.WriteLine($"Tag Name: {tag.TagName}"); @@ -42,16 +44,72 @@ private async Task RunTagsSampleAsync() } Console.WriteLine("Enter the media ID to add a tag to: "); - var mediaIdAddTag = Console.ReadLine(); + var mediaId = Console.ReadLine(); Console.WriteLine("Enter the tag ID to add to the media: "); - var tagIdAddToMedia = Console.ReadLine(); - List mediasAddTag = new List + var tagId = Console.ReadLine(); + await assetService.AddTagToMediaAsync(new AddTagToMediaQuery(tagId, [ mediaId ])); + + Console.WriteLine("Hit enter to view the asset (it may take a few seconds before the tag is registered)"); + Console.ReadKey(); + var asset = await assetService.GetMediaInfoAsync(new MediaInformationQuery() { MediaId = mediaId }); + ShowTags(asset); + + Console.WriteLine("Enter a new tag to add to the same media: "); + var anotherTag = Console.ReadLine(); + if (asset.Tags == null) + { + asset.Tags = [anotherTag]; + } + else + { + asset.Tags.Add(anotherTag); + } + + await assetService.ModifyMediaAsync(new ModifyMediaQuery(mediaId) { Tags = asset.Tags } ); + + Console.WriteLine("Hit enter to view the asset (it may take a few seconds before the tag is registered)"); + Console.ReadKey(); + asset = await assetService.GetMediaInfoAsync(new MediaInformationQuery() { MediaId = mediaId }); + ShowTags(asset); + + Console.WriteLine("Hit enter to remove the tags again"); + Console.ReadKey(); + + foreach (var tag in asset.Tags) + { + var matchingTags = await assetService.GetTagsAsync(new GetTagsQuery() { Keyword = tag }); + if (matchingTags.Any()) + { + var tagToRemove = matchingTags.FirstOrDefault(t => t.TagName.Equals(tag, StringComparison.InvariantCultureIgnoreCase)); + Console.WriteLine($"Removing tag {tagToRemove.TagName} with id {tagToRemove.ID}"); + await assetService.RemoveTagFromMediaAsync(tagToRemove.ID, [mediaId]); + } + else + { + Console.WriteLine($"Error: after adding tag with name '{tag}' to asset {mediaId}, tag cannot be found in Bynder"); + } + } + + Console.WriteLine("Hit enter to view the asset (it may take a few seconds before the tags have been removed)"); + Console.ReadKey(); + + asset = await assetService.GetMediaInfoAsync(new MediaInformationQuery() { MediaId = mediaId }); + ShowTags(asset); + + } + + private async void ShowTags(Media asset) + { + if (asset.Tags?.Any() ?? false) + { + Console.WriteLine($"Media with name {asset.Name} now has the following tags: {string.Join(',', asset.Tags)}"); + } + else { - mediaIdAddTag - }; - await _bynderClient.GetAssetService().AddTagToMediaAsync(new AddTagToMediaQuery(tagIdAddToMedia, mediasAddTag)); + Console.WriteLine($"Media with name {asset.Name} has no tags"); + } } - + private async Task AuthenticateWithOAuth2Async(bool useClientCredentials) { if (useClientCredentials) diff --git a/Bynder/Sdk/Query/Asset/GetTagsQuery.cs b/Bynder/Sdk/Query/Asset/GetTagsQuery.cs index 701ec4e..f476ad9 100644 --- a/Bynder/Sdk/Query/Asset/GetTagsQuery.cs +++ b/Bynder/Sdk/Query/Asset/GetTagsQuery.cs @@ -1,41 +1,44 @@ using Bynder.Sdk.Api.Converters; using Bynder.Sdk.Model; -using Bynder.Sdk.Query.Decoder; - -namespace Bynder.Sdk.Query.Asset +using Bynder.Sdk.Query.Decoder; + +namespace Bynder.Sdk.Query.Asset { - public class GetTagsQuery - { - /// - /// Maximum number of results. - /// - [ApiField("limit")] - public int Limit { get; set; } - - /// - /// Offset page for results: return the N-th set of limit-results. - /// - [ApiField("page")] - public int Page { get; set; } - - /// - /// Order of the returned list of tags. - /// See for possible values. - /// - [ApiField("orderBy", Converter = typeof(TagsOrderByConverter))] - public TagsOrderBy OrderBy { get; set; } - - /// - /// Search on matching names. - /// - [ApiField("keyword")] - public string Keyword { get; set; } - - /// - /// Minimum media count that the returned tags should have. - /// - [ApiField("mincount")] - public int MinCount { get; set; } - - } -} + public class GetTagsQuerySimple + { + /// + /// Maximum number of results. + /// + [ApiField("limit")] + public int Limit { get; set; } + + /// + /// Offset page for results: return the N-th set of limit-results. + /// + [ApiField("page")] + public int Page { get; set; } + + /// + /// Order of the returned list of tags. + /// See for possible values. + /// + [ApiField("orderBy", Converter = typeof(TagsOrderByConverter))] + public TagsOrderBy OrderBy { get; set; } + + /// + /// Search on matching names. + /// + [ApiField("keyword")] + public string Keyword { get; set; } + } + public class GetTagsQuery : GetTagsQuerySimple + { + + /// + /// Minimum media count that the returned tags should have. + /// + [ApiField("mincount")] + public int MinCount { get; set; } + + } +} diff --git a/Bynder/Sdk/Query/Asset/ModifyMediaQuery.cs b/Bynder/Sdk/Query/Asset/ModifyMediaQuery.cs index fe571b7..a7e4f65 100644 --- a/Bynder/Sdk/Query/Asset/ModifyMediaQuery.cs +++ b/Bynder/Sdk/Query/Asset/ModifyMediaQuery.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using Bynder.Sdk.Api.Converters; using Bynder.Sdk.Query.Decoder; @@ -68,6 +68,13 @@ public void AddMetapropertyOptions(string metapropertyId, IList optionId { MetapropertyOptions.Add(metapropertyId, optionIds); } + + /// + /// Tags that will be added to the asset + /// + [ApiField("tags", Converter = typeof(ListConverter))] + public IList Tags { get; set; } + } } diff --git a/Bynder/Sdk/Service/Asset/AssetService.cs b/Bynder/Sdk/Service/Asset/AssetService.cs index d5e15ba..e02eabf 100644 --- a/Bynder/Sdk/Service/Asset/AssetService.cs +++ b/Bynder/Sdk/Service/Asset/AssetService.cs @@ -11,6 +11,7 @@ using Bynder.Sdk.Model; using Bynder.Sdk.Query.Asset; using Bynder.Sdk.Query.Upload; +using System.Web; namespace Bynder.Sdk.Service.Asset { @@ -179,11 +180,17 @@ public async Task ModifyMediaAsync(ModifyMediaQuery query) /// Check for more information public async Task> GetTagsAsync(GetTagsQuery query) { + var queryToUse = string.IsNullOrEmpty(query.Keyword) ? query : new GetTagsQuerySimple() { + Keyword = query.Keyword, + Limit = query.Limit, + OrderBy = query.OrderBy, + Page = query.Page + }; return await _requestSender.SendRequestAsync(new ApiRequest> { Path = "/api/v4/tags/", HTTPMethod = HttpMethod.Get, - Query = query + Query = queryToUse }).ConfigureAwait(false); } @@ -201,6 +208,20 @@ public async Task AddTagToMediaAsync(AddTagToMediaQuery query) }).ConfigureAwait(false); } + /// + /// Check for more information + /// + /// Check for more information + public async Task RemoveTagFromMediaAsync(string tagId, IEnumerable assetIds) + { + var encodedIdList = HttpUtility.UrlEncode(string.Join(",", assetIds)); + return await _requestSender.SendRequestAsync(new ApiRequest + { + Path = $"/api/v4/tags/{tagId}/media/?deleteIds={encodedIdList}", + HTTPMethod = HttpMethod.Delete, + }).ConfigureAwait(false); + } + /// /// Create an asset usage operation to track usage of Bynder assets in third party applications. /// diff --git a/Bynder/Sdk/Service/Asset/IAssetService.cs b/Bynder/Sdk/Service/Asset/IAssetService.cs index 17b687a..64d554e 100644 --- a/Bynder/Sdk/Service/Asset/IAssetService.cs +++ b/Bynder/Sdk/Service/Asset/IAssetService.cs @@ -106,6 +106,15 @@ public interface IAssetService /// Can be thrown when requests to server can't be completed or HTTP code returned by server is an error Task AddTagToMediaAsync(AddTagToMediaQuery query); + /// + /// Remove tags from asset + /// + /// Id of the tag to remove + /// Ids of the assets from which the tag should be removed + /// Task representing the upload + /// Can be thrown when requests to server can't be completed or HTTP code returned by server is an error + Task RemoveTagFromMediaAsync(string tagId, IEnumerable assetIds); + /// /// Create an asset usage operation to track usage of Bynder assets in third party applications. /// diff --git a/Bynder/Test/Service/Asset/AssetServiceTest.cs b/Bynder/Test/Service/Asset/AssetServiceTest.cs index 4b633c8..2d84958 100644 --- a/Bynder/Test/Service/Asset/AssetServiceTest.cs +++ b/Bynder/Test/Service/Asset/AssetServiceTest.cs @@ -203,6 +203,25 @@ public async Task GetTagsCallsRequestSenderWithValidRequest() )); } + [Fact] + public async Task GetTagsByKeywordCallsRequestSenderWithValidRequest() + { + var result = new Status { Message = "Accepted", StatusCode = 202 }; + _apiRequestSenderMock.Setup(sender => sender.SendRequestAsync(It.IsAny())) + .ReturnsAsync(result); + var query = new GetTagsQuery { Keyword = "test" }; + await _assetService.GetTagsAsync(query); + + _apiRequestSenderMock.Verify(sender => sender.SendRequestAsync( + It.Is>>(req => + req.Path == "/api/v4/tags/" + && req.HTTPMethod == HttpMethod.Get + && req.Query is GetTagsQuerySimple + && (req.Query as GetTagsQuerySimple).Keyword == query.Keyword + ) + )); + } + [Fact] public async Task AddTagToMediaCallsRequestSenderWithValidRequest() { From e472748556d1b1090fdc49a98dd2f56f2d01a1f5 Mon Sep 17 00:00:00 2001 From: Quirijn Date: Wed, 18 Sep 2024 23:38:14 +0200 Subject: [PATCH 2/2] v3-modify-asset (#93) * targetframework now net8.0, packages upgraded * updated gitignore to the toptotal standard, added launchSettings to help developers run the samples * various fixes and enhancements regarding the modification of assets * added test for datePublished to ModifyMediaSample --- Bynder/Sample/ApiSample.cs | 9 + Bynder/Sample/ModifyMediaSample.cs | 179 ++++++++++++++++++ Bynder/Sample/Properties/launchSettings.json | 4 + .../Api/RequestSender/HttpRequestSender.cs | 8 +- Bynder/Sdk/Query/Asset/ModifyMediaQuery.cs | 10 +- Bynder/Sdk/Query/Decoder/QueryDecoder.cs | 60 +++--- Bynder/Test/Utils/QueryParameterTest.cs | 68 +++++++ 7 files changed, 302 insertions(+), 36 deletions(-) create mode 100644 Bynder/Sample/ModifyMediaSample.cs create mode 100644 Bynder/Test/Utils/QueryParameterTest.cs diff --git a/Bynder/Sample/ApiSample.cs b/Bynder/Sample/ApiSample.cs index 6c566c9..4893f04 100644 --- a/Bynder/Sample/ApiSample.cs +++ b/Bynder/Sample/ApiSample.cs @@ -54,6 +54,15 @@ public static async Task Main(string[] args) await MediaSample.MediaSampleAsync(); return; } + + // Run samples related to modifying media + if (args[0].Equals("ModifyMediaSample")) + { + Console.WriteLine("Running samples for the modification of media..."); + await ModifyMediaSample.ModifyMediaSampleAsync(); + return; + } + // Run samples related to collections if (args[0].Equals("CollectionsSample")) { Console.WriteLine("Running samples for collections..."); diff --git a/Bynder/Sample/ModifyMediaSample.cs b/Bynder/Sample/ModifyMediaSample.cs new file mode 100644 index 0000000..01afaaa --- /dev/null +++ b/Bynder/Sample/ModifyMediaSample.cs @@ -0,0 +1,179 @@ +// Copyright (c) Bynder. All rights reserved. +// Licensed under the MIT License. See LICENSE file in the project root for full license information. + +using System; +using Bynder.Sdk.Service; +using Bynder.Sample.Utils; +using Bynder.Sdk.Settings; +using System.Threading.Tasks; +using System.Linq; +using Bynder.Sdk.Query.Asset; +using Bynder.Sdk.Model; +using System.Collections.Generic; +using Newtonsoft.Json.Linq; + +namespace Bynder.Sample +{ + public class ModifyMediaSample + { + private IBynderClient _bynderClient; + + public static async Task ModifyMediaSampleAsync() + { + var configuration = Configuration.FromJson("Config.json"); + var apiSample = new ModifyMediaSample(configuration); + await apiSample.AuthenticateWithOAuth2Async( + useClientCredentials: configuration.RedirectUri == null + ); + await apiSample.RunModifyMediaSampleAsync(); + } + + private ModifyMediaSample(Configuration configuration) + { + _bynderClient = ClientFactory.Create(configuration); + } + + private async Task RunModifyMediaSampleAsync() + { + var assetService = _bynderClient.GetAssetService(); + // Get a list of media with limit 10 + Console.WriteLine("Listing media with limit of 10: "); + var mediaList = await assetService.GetMediaListAsync(new MediaQuery { Limit = 10 }); + foreach (Media media in mediaList) + { + Console.WriteLine($"Media ID: {media.Id}"); + Console.WriteLine($"Media Name: {media.Name}"); + } + + // Get the media info + Console.WriteLine("Enter the media ID to modify: "); + var mediaId = Console.ReadLine(); + var mediaInformationQuery = new MediaInformationQuery + { + MediaId = mediaId.Trim() + }; + var mediaInfo = await assetService.GetMediaInfoAsync(mediaInformationQuery); + Console.WriteLine($"ID: {mediaInfo.Id}"); + Console.WriteLine($"Name: {mediaInfo.Name}"); + + + // datePublished + Console.WriteLine($"---\r\nTest with datePublished"); + Console.WriteLine($"datePublished is currently set to: {mediaInfo.DatePublished}"); + Console.WriteLine("New value (use ISO8601 format: yyyy-mm-ddThh:mm:ssZ, or n = now, or leave empty to erase): "); + var cmd = Console.ReadLine(); + var query = new ModifyMediaQuery(mediaId); + if (cmd.ToLower().StartsWith("n")) + { + query.PublishedDate = DateTime.Now.ToString("yyyy-MM-ddTHH:mm:ssZ"); + } + else if (string.IsNullOrEmpty(cmd)) + { + query.PublishedDate = ""; + } + else + { + query.PublishedDate = cmd; + } + await assetService.ModifyMediaAsync(query); + Console.WriteLine("The asset has been modified. It takes a few seconds for changes come through. Hit enter when you want to retrieve the asset again."); + Console.ReadLine(); + mediaInfo = await assetService.GetMediaInfoAsync(mediaInformationQuery); + Console.WriteLine($"datePublished is now set to: {mediaInfo.DatePublished}"); + + // isArchived (boolean) + Console.WriteLine($"---\r\nTest with boolean value (isArchived)"); + Console.WriteLine($"isArchived is currently set to: {mediaInfo.IsArchived}"); + Console.WriteLine("New value (t=true, f=false, n=not set): "); + cmd = Console.ReadLine(); + query = new ModifyMediaQuery(mediaId); + if (cmd.ToLower().StartsWith("t")) + { + query.Archive = true; + } + else if (cmd.ToLower().StartsWith("f")) + { + query.Archive = false; + } + await assetService.ModifyMediaAsync(query); + Console.WriteLine("The asset has been modified. It takes a few seconds for changes come through. Hit enter when you want to retrieve the asset again."); + Console.ReadLine(); + mediaInfo = await assetService.GetMediaInfoAsync(mediaInformationQuery); + Console.WriteLine($"isArchived is now set to: {mediaInfo.IsArchived}"); + + // Copyright message + Console.WriteLine($"---\r\nTest with string value (copyright)"); + Console.WriteLine($"Copyright message is currently set to {mediaInfo.Copyright}"); + Console.WriteLine($"Please supply a new value for the copyright message, or hit enter to erase it"); + + cmd = Console.ReadLine(); + query = new ModifyMediaQuery(mediaId); + query.Copyright = string.IsNullOrEmpty(cmd) ? "" : cmd; + await assetService.ModifyMediaAsync(query); + Console.WriteLine("The asset has been modified. It takes a few seconds for changes come through. Hit enter when you want to retrieve the asset again."); + Console.ReadLine(); + mediaInfo = await assetService.GetMediaInfoAsync(mediaInformationQuery); + Console.WriteLine($"Copyright message is now set to {mediaInfo.Copyright}"); + + // Metaproperties + if (!(mediaInfo.PropertyOptionsDictionary?.Keys?.Any() ?? false)) + { + Console.WriteLine("This media item has no meta properties, please choose a different one the next time! Bye for now."); + return; + } + var metaprop = mediaInfo.PropertyOptionsDictionary.FirstOrDefault(); + var metaPropertyName = metaprop.Key.Replace("property_", ""); + + Console.WriteLine($"---\r\nTest with metaproperties"); + Console.WriteLine($"Meta property {metaprop.Key} is currently set to {metaprop.Value.ToString()}"); + Console.WriteLine($"Please supply a new value for the meta property, or hit enter to erase it"); + + cmd = Console.ReadLine(); + + // get ID of the meta property + var metaProperties = await assetService.GetMetapropertiesAsync(); + var metaProperty = metaProperties.Values.FirstOrDefault(mp => mp.Name == metaPropertyName); + if (metaProperty == null) + { + throw new Exception("Unable to find property with name " + metaprop.Key.Replace("property_", "")); + } + + query = new ModifyMediaQuery(mediaId); + query.MetapropertyOptions = new Dictionary>() + { + { + metaProperty.Id, + string.IsNullOrEmpty(cmd) ? [] : [cmd] + } + }; + await assetService.ModifyMediaAsync(query); + Console.WriteLine("The asset has been modified. It takes a few seconds for changes come through. Hit enter when you want to retrieve the asset again."); + Console.ReadLine(); + mediaInfo = await assetService.GetMediaInfoAsync(mediaInformationQuery); + if (mediaInfo.PropertyOptionsDictionary?.TryGetValue("property_" + metaPropertyName, out JToken value) ?? false) + { + Console.WriteLine($"Meta property {metaPropertyName} is now set to {value.ToString()}"); + } + else + { + Console.WriteLine($"Asset has no value for metaproperty {metaPropertyName}"); + } + } + + private async Task AuthenticateWithOAuth2Async(bool useClientCredentials) + { + if (useClientCredentials) + { + await _bynderClient.GetOAuthService().GetAccessTokenAsync(); + } + else + { + Browser.Launch(_bynderClient.GetOAuthService().GetAuthorisationUrl("state example")); + Console.WriteLine("Insert the code: "); + var code = Console.ReadLine(); + await _bynderClient.GetOAuthService().GetAccessTokenAsync(code); + } + } + + } +} diff --git a/Bynder/Sample/Properties/launchSettings.json b/Bynder/Sample/Properties/launchSettings.json index 865d3b8..6c8a765 100644 --- a/Bynder/Sample/Properties/launchSettings.json +++ b/Bynder/Sample/Properties/launchSettings.json @@ -36,5 +36,9 @@ "commandName": "Project", "commandLineArgs": "AssetUsageSample" }, + "Run ModifyMediaSample": { + "commandName": "Project", + "commandLineArgs": "ModifyMediaSample" + } } } \ No newline at end of file diff --git a/Bynder/Sdk/Api/RequestSender/HttpRequestSender.cs b/Bynder/Sdk/Api/RequestSender/HttpRequestSender.cs index 1eac981..dfb81c0 100644 --- a/Bynder/Sdk/Api/RequestSender/HttpRequestSender.cs +++ b/Bynder/Sdk/Api/RequestSender/HttpRequestSender.cs @@ -1,4 +1,4 @@ -// Copyright (c) Bynder. All rights reserved. +// Copyright (c) Bynder. All rights reserved. // Licensed under the MIT License. See LICENSE file in the project root for full license information. using System.Net.Http; @@ -32,7 +32,11 @@ public string UserAgent public async Task SendHttpRequest(HttpRequestMessage httpRequest) { httpRequest.Headers.Add("User-Agent", UserAgent); - var response = await _httpClient.SendAsync(httpRequest).ConfigureAwait(false); + var response = await _httpClient.SendAsync(httpRequest).ConfigureAwait(false); + if (!response.IsSuccessStatusCode) + { + var content = response.Content.ReadAsStringAsync().Result; + } response.EnsureSuccessStatusCode(); return response; } diff --git a/Bynder/Sdk/Query/Asset/ModifyMediaQuery.cs b/Bynder/Sdk/Query/Asset/ModifyMediaQuery.cs index a7e4f65..465df92 100644 --- a/Bynder/Sdk/Query/Asset/ModifyMediaQuery.cs +++ b/Bynder/Sdk/Query/Asset/ModifyMediaQuery.cs @@ -41,17 +41,23 @@ public ModifyMediaQuery(string mediaId) [ApiField("copyright")] public string Copyright { get; set; } + /// + /// Published date for the media + /// + [ApiField("datePublished")] + public string PublishedDate { get; set; } + /// /// Indicates if the media is archived /// [ApiField("archive")] - public bool Archive { get; set; } + public bool? Archive { get; set; } /// /// Indicates if the media is public /// [ApiField("isPublic")] - public bool IsPublic { get; set; } + public bool? IsPublic { get; set; } /// /// Metaproperty options to set on the asset. diff --git a/Bynder/Sdk/Query/Decoder/QueryDecoder.cs b/Bynder/Sdk/Query/Decoder/QueryDecoder.cs index 345219b..a945447 100644 --- a/Bynder/Sdk/Query/Decoder/QueryDecoder.cs +++ b/Bynder/Sdk/Query/Decoder/QueryDecoder.cs @@ -1,8 +1,9 @@ -// Copyright (c) Bynder. All rights reserved. +// Copyright (c) Bynder. All rights reserved. // Licensed under the MIT License. See LICENSE file in the project root for full license information. using System; using System.Collections.Generic; +using System.Linq; using System.Reflection; using Bynder.Sdk.Api.Converters; @@ -43,46 +44,41 @@ public IDictionary GetParameters(object query) /// collection to add the converted values private void ConvertProperty(PropertyInfo propertyInfo, object query, IDictionary parameters) { - foreach (var attribute in propertyInfo.GetCustomAttributes(true)) + var apiField = propertyInfo.GetCustomAttributes(true).FirstOrDefault(a => a is ApiField) as ApiField; + if (apiField == null) { - if (attribute is ApiField apiField) - { - object value = propertyInfo.GetValue(query); - if (value == null) - { - return; - } + return; + } - if (apiField.Converter == null) - { - AddParam(parameters, apiField.ApiName, value.ToString()); - } - else if (Activator.CreateInstance(apiField.Converter) is ITypeToStringConverter stringConverter - && stringConverter.CanConvert(propertyInfo.PropertyType)) - { - AddParam(parameters, apiField.ApiName, stringConverter.Convert(value)); - } - else if (Activator.CreateInstance(apiField.Converter) is ITypeToDictionaryConverter dictConverter - && dictConverter.CanConvert(propertyInfo.PropertyType)) - { - foreach (var item in dictConverter.Convert(value)) - { - AddParam(parameters, $"{apiField.ApiName}.{item.Key}", item.Value); - } - } + object value = propertyInfo.GetValue(query); + if (value == null) + { + return; + } - // No need to continue. Only one ApiField attribute per property - return; + if (apiField.Converter == null) + { + AddParam(parameters, apiField.ApiName, value.ToString()); + } + else if (Activator.CreateInstance(apiField.Converter) is ITypeToStringConverter stringConverter + && stringConverter.CanConvert(propertyInfo.PropertyType)) + { + AddParam(parameters, apiField.ApiName, stringConverter.Convert(value)); + } + else if (Activator.CreateInstance(apiField.Converter) is ITypeToDictionaryConverter dictConverter + && dictConverter.CanConvert(propertyInfo.PropertyType)) + { + foreach (var item in dictConverter.Convert(value)) + { + AddParam(parameters, $"{apiField.ApiName}.{item.Key}", item.Value); } } + } private void AddParam(IDictionary parameters, string key, string value) { - if (!string.IsNullOrEmpty(value)) - { - parameters.Add(key, value); - } + parameters.Add(key, value); } } diff --git a/Bynder/Test/Utils/QueryParameterTest.cs b/Bynder/Test/Utils/QueryParameterTest.cs new file mode 100644 index 0000000..3c85a66 --- /dev/null +++ b/Bynder/Test/Utils/QueryParameterTest.cs @@ -0,0 +1,68 @@ +using Bynder.Sdk.Api.Requests; +using Bynder.Sdk.Query.Asset; +using Bynder.Sdk.Query.Decoder; +using Bynder.Sdk.Utils; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Xunit; + +namespace Bynder.Test.Utils +{ + public class QueryParameterTest + { + [Fact] + public void BooleanPropertiesSetWhenModified() + { + ModifyMediaQuery query = new ModifyMediaQuery("") + { + Archive = false, + }; + + var parametersAsString = GetQueryParameters(query); + Assert.DoesNotContain("isPublic=", parametersAsString); + Assert.Contains("archive=False", parametersAsString); + } + + [Fact] + public void MetaPropertyCanBeDeleted() + { + ModifyMediaQuery query = new ModifyMediaQuery("") + { + MetapropertyOptions = new Dictionary>() + { + { + "TestProp", + [] + } + } + }; + var parametersAsString = GetQueryParameters(query); + Assert.Contains("metaproperty.TestProp=", parametersAsString); + } + + [Fact] + public void StringPropertyCanBeErased() + { + ModifyMediaQuery query = new ModifyMediaQuery("") + { + Name = "", + Copyright = null, + }; + var parametersAsString = GetQueryParameters(query); + + Assert.Contains("name=", parametersAsString); + Assert.DoesNotContain("copyright=", parametersAsString); + } + + private string GetQueryParameters(ModifyMediaQuery query) + { + QueryDecoder queryDecoder = new QueryDecoder(); + var parameters = queryDecoder.GetParameters(query); + return Url.ConvertToQuery(parameters); + } + + } +}