Skip to content

Commit cd47f06

Browse files
committed
Custom Asset URL replacements. Resolves issues kontent-ai#311 and kontent-ai#193
1 parent 9327b2c commit cd47f06

File tree

8 files changed

+173
-46
lines changed

8 files changed

+173
-46
lines changed

Kontent.Ai.Delivery.Abstractions/Configuration/DeliveryOptions.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,5 +79,14 @@ public class DeliveryOptions
7979
/// </summary>
8080
[Obsolete("#312")]
8181
internal string Name { get; set; }
82+
83+
/// <summary>
84+
/// Replaces the base URL of the asset URLs in the API response. The asset URL will retain all the identifying information such as project id, asset id and file name.<br/>
85+
/// <b>NOTE: </b>Do not specify a trailing backslash in the URL.<br/>
86+
/// <b>Example where AssetUrlReplacement is defined as "https://www.example.com/assets":</b><br/>
87+
/// <b>Original Value:</b> https://preview-assets-us-01.kc-usercontent.com/7ffda7b5-bfb2-4226-8b4f-d2e333638416/614cb0cc-9e62-4572-a408-fc8715f990e8/test-asset.pdf <br/>
88+
/// <b>New Value:</b> https://www.example.com/assets/7ffda7b5-bfb2-4226-8b4f-d2e333638416/614cb0cc-9e62-4572-a408-fc8715f990e8/test-asset.pdf
89+
/// </summary>
90+
public string AssetUrlReplacement { get; set; }
8291
}
8392
}

Kontent.Ai.Delivery.Abstractions/Extensions/DeliveryOptionsExtensions.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,11 @@ public static void Configure(this DeliveryOptions o, DeliveryOptions options)
2424
o.DefaultRetryPolicyOptions = options.DefaultRetryPolicyOptions;
2525
o.IncludeTotalCount = options.IncludeTotalCount;
2626
// See #312
27-
#pragma warning disable CS0618
27+
#pragma warning disable CS0618
2828
o.Name = options.Name;
29-
#pragma warning restore CS0618
29+
#pragma warning restore CS0618
3030
o.DefaultRenditionPreset = options.DefaultRenditionPreset;
31+
o.AssetUrlReplacement = options.AssetUrlReplacement;
3132
}
3233
}
3334
}

Kontent.Ai.Delivery.Tests/Builders/Configuration/DeliveryOptionsBuilderTests.cs

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1-
using System;
2-
using Kontent.Ai.Delivery.Abstractions;
1+
using Kontent.Ai.Delivery.Abstractions;
32
using Kontent.Ai.Delivery.Configuration;
3+
using System;
44
using Xunit;
55

66
namespace Kontent.Ai.Delivery.Tests.Builders.Configuration
77
{
88
public class DeliveryOptionsBuilderTests
99
{
1010
private const string EnvironmentId = "550cec62-90a6-4ab3-b3e4-3d0bb4c04f5c";
11-
private const string PreviewApiKey =
11+
private const string PreviewApiKey =
1212
"eyJ0eXAiOiwq14X65DLCJhbGciOiJIUzI1NiJ-.eyJqdGkiOiABCjJlM2FiOTBjOGM0ODVmYjdmZTDEFRQZGM1NDIyMCIsImlhdCI6IjE1Mjg454wexiLCJleHAiOiIxODc0NDg3NjqasdfwicHJvamVjdF9pZCI6Ij" +
1313
"g1OTEwOTlkN2458198ewqewZjI3Yzg5M2FhZTJiNTE4IiwidmVyIjoiMS4wLjAiLCJhdWQiewqgsdaWV3LmRlbGl2ZXIua2VudGljb2Nsb3VkLmNvbSJ9._tSzbNDpbE55dsaLUTGsdgesg4b693TFuhRCRsDyoc";
1414

@@ -58,7 +58,7 @@ public void BuildWithEnvironmentIdAndSecuredProductionApi()
5858
Assert.True(deliveryOptions.UseSecureAccess);
5959
Assert.Equal(SecuredApiKey, deliveryOptions.SecureAccessApiKey);
6060
}
61-
61+
6262
[Fact]
6363
public void BuildWithRetryPolicyOptions()
6464
{
@@ -136,7 +136,7 @@ public void BuildWithCustomEndpointForPreviewApi()
136136
.WithCustomEndpoint(customEndpoint)
137137
.Build();
138138

139-
Assert.Equal(customEndpoint, deliveryOptions.PreviewEndpoint);
139+
Assert.Equal(customEndpoint, deliveryOptions.PreviewEndpoint);
140140
}
141141

142142
[Fact]
@@ -159,7 +159,7 @@ public void BuildWithCustomEndpointAsUriForPreviewApi()
159159
{
160160
const string customEndpoint = "http://www.custompreviewendpoint.com/";
161161
var uri = new Uri(customEndpoint, UriKind.Absolute);
162-
162+
163163
var deliveryOptions = DeliveryOptionsBuilder
164164
.CreateInstance()
165165
.WithEnvironmentId(EnvironmentId)
@@ -185,7 +185,7 @@ public void BuildWithCustomEndpointAsUriForProductionApi()
185185

186186
Assert.Equal(customEndpoint, deliveryOptions.ProductionEndpoint);
187187
}
188-
188+
189189
[Fact]
190190
public void BuildWithDefaultRenditionPreset()
191191
{
@@ -200,5 +200,20 @@ public void BuildWithDefaultRenditionPreset()
200200

201201
Assert.Equal(renditionPreset, deliveryOptions.DefaultRenditionPreset);
202202
}
203+
204+
[Fact]
205+
public void BuildWithAssetUrlReplacement()
206+
{
207+
var assetUrl = "https://www.example.com/assets";
208+
209+
var deliveryOptions = DeliveryOptionsBuilder
210+
.CreateInstance()
211+
.WithEnvironmentId(EnvironmentId)
212+
.UseProductionApi()
213+
.WithAssetUrlReplacement(assetUrl)
214+
.Build();
215+
216+
Assert.Equal(assetUrl, deliveryOptions.AssetUrlReplacement);
217+
}
203218
}
204219
}

Kontent.Ai.Delivery.Tests/Builders/DeliveryClient/DeliveryClientBuilderTests.cs

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
using System;
2-
using System.Collections.Generic;
3-
using FakeItEasy;
1+
using FakeItEasy;
42
using Kontent.Ai.Delivery.Abstractions;
53
using Kontent.Ai.Delivery.Builders.DeliveryClient;
64
using Kontent.Ai.Delivery.ContentItems;
@@ -10,6 +8,8 @@
108
using Microsoft.Extensions.Logging;
119
using Microsoft.Extensions.Logging.Abstractions;
1210
using RichardSzalay.MockHttp;
11+
using System;
12+
using System.Collections.Generic;
1313
using Xunit;
1414

1515
namespace Kontent.Ai.Delivery.Tests.Builders.DeliveryClient
@@ -27,7 +27,7 @@ public class DeliveryClientBuilderTests
2727
[Fact]
2828
public void BuildWithEnvironmentId_ReturnsDeliveryClientWithEnvironmentIdSet()
2929
{
30-
var deliveryClient = (Delivery.DeliveryClient) DeliveryClientBuilder.WithEnvironmentId(EnvironmentId).Build();
30+
var deliveryClient = (Delivery.DeliveryClient)DeliveryClientBuilder.WithEnvironmentId(EnvironmentId).Build();
3131

3232
Assert.Equal(EnvironmentId, deliveryClient.DeliveryOptions.CurrentValue.EnvironmentId);
3333
}
@@ -36,18 +36,21 @@ public void BuildWithEnvironmentId_ReturnsDeliveryClientWithEnvironmentIdSet()
3636
public void BuildWithDeliveryOptions_ReturnsDeliveryClientWithDeliveryOptions()
3737
{
3838
var guid = new Guid(EnvironmentId);
39+
var assetReplacementUrl = "https://cdn.example.com/assets";
3940

40-
var deliveryClient = (Delivery.DeliveryClient) DeliveryClientBuilder
41+
var deliveryClient = (Delivery.DeliveryClient)DeliveryClientBuilder
4142
.WithOptions(builder => builder
4243
.WithEnvironmentId(guid)
4344
.UsePreviewApi(PreviewApiKey)
4445
.WithCustomEndpoint(PreviewEndpoint)
46+
.WithAssetUrlReplacement("https://cdn.example.com/assets")
4547
.Build()
4648
).Build();
4749

4850
Assert.Equal(EnvironmentId, deliveryClient.DeliveryOptions.CurrentValue.EnvironmentId);
4951
Assert.True(deliveryClient.DeliveryOptions.CurrentValue.UsePreviewApi);
5052
Assert.Equal(PreviewEndpoint, deliveryClient.DeliveryOptions.CurrentValue.PreviewEndpoint);
53+
Assert.Equal(assetReplacementUrl, deliveryClient.DeliveryOptions.CurrentValue.AssetUrlReplacement);
5154
}
5255

5356
[Fact]
@@ -65,7 +68,7 @@ public void BuildWithOptionalSteps_ReturnsDeliveryClientWithSetInstances()
6568
var mockDeliveryHttpClient = new DeliveryHttpClient(new MockHttpMessageHandler().ToHttpClient());
6669
var mockLoggerFactory = A.Fake<ILoggerFactory>();
6770

68-
var deliveryClient = (Delivery.DeliveryClient) DeliveryClientBuilder
71+
var deliveryClient = (Delivery.DeliveryClient)DeliveryClientBuilder
6972
.WithEnvironmentId(EnvironmentId)
7073
.WithDeliveryHttpClient(mockDeliveryHttpClient)
7174
.WithContentLinkUrlResolver(mockContentLinkUrlResolver)
@@ -119,7 +122,7 @@ public void BuildWithOptionalStepsAndCustomProvider_ReturnsDeliveryClientWithSet
119122
{
120123
var modelProvider = new FakeModelProvider();
121124

122-
var deliveryClient = (Delivery.DeliveryClient) DeliveryClientBuilder
125+
var deliveryClient = (Delivery.DeliveryClient)DeliveryClientBuilder
123126
.WithEnvironmentId(EnvironmentId)
124127
.WithModelProvider(modelProvider)
125128
.Build();
@@ -137,7 +140,7 @@ public void BuildWithoutOptionalSteps_ReturnsDeliveryClientWithDefaultImplementa
137140
typeof(UnknownContentItem)
138141
};
139142

140-
var deliveryClient = (Delivery.DeliveryClient) DeliveryClientBuilder
143+
var deliveryClient = (Delivery.DeliveryClient)DeliveryClientBuilder
141144
.WithEnvironmentId(_guid)
142145
.Build();
143146
var actualResolvableInlineContentItemTypes = GetResolvableInlineContentItemTypes(deliveryClient);

Kontent.Ai.Delivery.Tests/ValueConverterTests.cs

Lines changed: 77 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,4 @@
1-
using System;
2-
using System.Collections.Generic;
3-
using System.IO;
4-
using System.Linq;
5-
using System.Net.Http;
6-
using System.Reflection;
7-
using System.Threading.Tasks;
8-
using AngleSharp.Html.Parser;
1+
using AngleSharp.Html.Parser;
92
using FakeItEasy;
103
using Kontent.Ai.Delivery.Abstractions;
114
using Kontent.Ai.Delivery.ContentItems;
@@ -14,6 +7,13 @@
147
using Kontent.Ai.Urls.Delivery.QueryParameters;
158
using NodaTime;
169
using RichardSzalay.MockHttp;
10+
using System;
11+
using System.Collections.Generic;
12+
using System.IO;
13+
using System.Linq;
14+
using System.Net.Http;
15+
using System.Reflection;
16+
using System.Threading.Tasks;
1717
using Xunit;
1818

1919
namespace Kontent.Ai.Delivery.Tests
@@ -72,7 +72,7 @@ public async void LinkedItemCodenamesValueConverter()
7272

7373
var article = await client.GetItemAsync<Article>("on_roasts");
7474

75-
Assert.Equal(new[]{"coffee_processing_techniques", "origins_of_arabica_bourbon"}, article.Item.RelatedArticleCodenames);
75+
Assert.Equal(new[] { "coffee_processing_techniques", "origins_of_arabica_bourbon" }, article.Item.RelatedArticleCodenames);
7676
}
7777

7878
[Fact]
@@ -120,7 +120,7 @@ public async void RichTextViaValueConverter()
120120
Assert.NotNull(hostedVideo);
121121
Assert.NotNull(tweet);
122122
}
123-
123+
124124
[Fact]
125125
public async Task AssetElementValueConverter_NoPresetSpecifiedInConfig_AssetUrlIsUntouched()
126126
{
@@ -138,7 +138,26 @@ public async Task AssetElementValueConverter_NoPresetSpecifiedInConfig_AssetUrlI
138138

139139
Assert.Equal(assetUrl, teaserImage.Url);
140140
}
141-
141+
142+
[Fact]
143+
public async Task AssetElementValueConverter_NoPresetSpecifiedInConfig_AssetUrlReplacementSpecifiedInConfig_AssetUrlIsTouched()
144+
{
145+
var mockHttp = new MockHttpMessageHandler();
146+
mockHttp
147+
.When($"{_baseUrl}/items/coffee_beverages_explained")
148+
.Respond("application/json", await File.ReadAllTextAsync(Path.Combine(Environment.CurrentDirectory, $"Fixtures{Path.DirectorySeparatorChar}DeliveryClient{Path.DirectorySeparatorChar}coffee_beverages_explained.json")));
149+
150+
var client = InitializeDeliveryClient(mockHttp);
151+
client.DeliveryOptions.CurrentValue.AssetUrlReplacement = "https://cdn.example.com/assets";
152+
153+
var response = await client.GetItemAsync<Article>("coffee_beverages_explained");
154+
var teaserImage = response.Item.TeaserImage.FirstOrDefault();
155+
156+
var assetUrl = "https://cdn.example.com/assets/975bf280-fd91-488c-994c-2f04416e5ee3/e700596b-03b0-4cee-ac5c-9212762c027a/coffee-beverages-explained-1080px.jpg";
157+
158+
Assert.Equal(assetUrl, teaserImage.Url);
159+
}
160+
142161
[Fact]
143162
public async Task AssetElementValueConverter_DefaultPresetSpecifiedInConfig_AssetUrlContainsDefaultRenditionQuery()
144163
{
@@ -149,7 +168,7 @@ public async Task AssetElementValueConverter_DefaultPresetSpecifiedInConfig_Asse
149168

150169
var defaultRenditionPreset = "default";
151170

152-
var client = InitializeDeliveryClient(mockHttp, new DeliveryOptions { EnvironmentId = _guid, DefaultRenditionPreset = defaultRenditionPreset});
171+
var client = InitializeDeliveryClient(mockHttp, new DeliveryOptions { EnvironmentId = _guid, DefaultRenditionPreset = defaultRenditionPreset });
153172

154173
var response = await client.GetItemAsync<Article>("coffee_beverages_explained");
155174
var teaserImage = response.Item.TeaserImage.FirstOrDefault();
@@ -159,7 +178,29 @@ public async Task AssetElementValueConverter_DefaultPresetSpecifiedInConfig_Asse
159178

160179
Assert.Equal($"{assetUrl}?{defaultRenditionQuery}", teaserImage.Url);
161180
}
162-
181+
182+
[Fact]
183+
public async Task AssetElementValueConverter_DefaultPresetSpecifiedInConfig_AssetUrlReplacementSpecifiedInConfig_AssetUrlContainsDefaultRenditionQuery()
184+
{
185+
var mockHttp = new MockHttpMessageHandler();
186+
mockHttp
187+
.When($"{_baseUrl}/items/coffee_beverages_explained")
188+
.Respond("application/json", await File.ReadAllTextAsync(Path.Combine(Environment.CurrentDirectory, $"Fixtures{Path.DirectorySeparatorChar}DeliveryClient{Path.DirectorySeparatorChar}coffee_beverages_explained.json")));
189+
190+
var defaultRenditionPreset = "default";
191+
192+
var client = InitializeDeliveryClient(mockHttp, new DeliveryOptions { EnvironmentId = _guid, DefaultRenditionPreset = defaultRenditionPreset });
193+
client.DeliveryOptions.CurrentValue.AssetUrlReplacement = "https://cdn.example.com/assets";
194+
195+
var response = await client.GetItemAsync<Article>("coffee_beverages_explained");
196+
var teaserImage = response.Item.TeaserImage.FirstOrDefault();
197+
198+
var assetUrl = "https://cdn.example.com/assets/975bf280-fd91-488c-994c-2f04416e5ee3/e700596b-03b0-4cee-ac5c-9212762c027a/coffee-beverages-explained-1080px.jpg";
199+
var defaultRenditionQuery = "w=200&h=150&fit=clip&rect=7,23,300,200";
200+
201+
Assert.Equal($"{assetUrl}?{defaultRenditionQuery}", teaserImage.Url);
202+
}
203+
163204
[Fact]
164205
public async Task AssetElementValueConverter_MobilePresetSpecifiedInConfig_AssetUrlIsUntouchedAsThereIsNoMobileRenditionSpecified()
165206
{
@@ -170,8 +211,8 @@ public async Task AssetElementValueConverter_MobilePresetSpecifiedInConfig_Asset
170211

171212
var defaultRenditionPreset = "mobile";
172213

173-
var client = InitializeDeliveryClient(mockHttp, new DeliveryOptions { EnvironmentId = _guid, DefaultRenditionPreset = defaultRenditionPreset});
174-
214+
var client = InitializeDeliveryClient(mockHttp, new DeliveryOptions { EnvironmentId = _guid, DefaultRenditionPreset = defaultRenditionPreset });
215+
175216
var response = await client.GetItemAsync<Article>("coffee_beverages_explained");
176217
var teaserImage = response.Item.TeaserImage.FirstOrDefault();
177218

@@ -180,6 +221,27 @@ public async Task AssetElementValueConverter_MobilePresetSpecifiedInConfig_Asset
180221
Assert.Equal(assetUrl, teaserImage.Url);
181222
}
182223

224+
[Fact]
225+
public async Task AssetElementValueConverter_MobilePresetSpecifiedInConfig_AssetUrlReplacementSpecifiedInConfig_AssetUrlIsUntouchedAsThereIsNoMobileRenditionSpecified()
226+
{
227+
var mockHttp = new MockHttpMessageHandler();
228+
mockHttp
229+
.When($"{_baseUrl}/items/coffee_beverages_explained")
230+
.Respond("application/json", await File.ReadAllTextAsync(Path.Combine(Environment.CurrentDirectory, $"Fixtures{Path.DirectorySeparatorChar}DeliveryClient{Path.DirectorySeparatorChar}coffee_beverages_explained.json")));
231+
232+
var defaultRenditionPreset = "mobile";
233+
234+
var client = InitializeDeliveryClient(mockHttp, new DeliveryOptions { EnvironmentId = _guid, DefaultRenditionPreset = defaultRenditionPreset });
235+
client.DeliveryOptions.CurrentValue.AssetUrlReplacement = "https://cdn.example.com/assets";
236+
237+
var response = await client.GetItemAsync<Article>("coffee_beverages_explained");
238+
var teaserImage = response.Item.TeaserImage.FirstOrDefault();
239+
240+
var assetUrl = "https://cdn.example.com/assets/975bf280-fd91-488c-994c-2f04416e5ee3/e700596b-03b0-4cee-ac5c-9212762c027a/coffee-beverages-explained-1080px.jpg";
241+
242+
Assert.Equal(assetUrl, teaserImage.Url);
243+
}
244+
183245
private DeliveryClient InitializeDeliveryClient(MockHttpMessageHandler mockHttp, DeliveryOptions options = null)
184246
{
185247
var deliveryHttpClient = new DeliveryHttpClient(mockHttp.ToHttpClient());

Kontent.Ai.Delivery/Configuration/DeliveryOptionsBuilder.cs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
using System;
2-
using Kontent.Ai.Delivery.Abstractions;
1+
using Kontent.Ai.Delivery.Abstractions;
2+
using System;
33
using DefaultRetryPolicyOptions = Kontent.Ai.Delivery.Abstractions.DefaultRetryPolicyOptions;
44

55
namespace Kontent.Ai.Delivery.Configuration
@@ -101,7 +101,13 @@ IOptionalDeliveryConfiguration IOptionalDeliveryConfiguration.WithCustomEndpoint
101101
IOptionalDeliveryConfiguration IOptionalDeliveryConfiguration.WithDefaultRenditionPreset(string presetCodename)
102102
{
103103
_deliveryOptions.DefaultRenditionPreset = presetCodename;
104-
104+
105+
return this;
106+
}
107+
108+
IOptionalDeliveryConfiguration IOptionalDeliveryConfiguration.WithAssetUrlReplacement(string url)
109+
{
110+
_deliveryOptions.AssetUrlReplacement = url;
105111
return this;
106112
}
107113

Kontent.Ai.Delivery/Configuration/IDeliveryOptionsBuilder.cs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
using System;
1+
using Kontent.Ai.Delivery.Abstractions;
22
using Kontent.Ai.Urls.Delivery.QueryParameters;
3-
using Kontent.Ai.Delivery.Abstractions;
3+
using System;
44

55

66
namespace Kontent.Ai.Delivery.Configuration
@@ -69,7 +69,7 @@ public interface IOptionalDeliveryConfiguration : IDeliveryOptionsBuild
6969
/// Please note that using this option might increase the response time.
7070
/// </summary>
7171
IOptionalDeliveryConfiguration IncludeTotalCount();
72-
72+
7373
/// <summary>
7474
/// Change configuration of the default retry policy.
7575
/// </summary>
@@ -104,6 +104,17 @@ public interface IOptionalDeliveryConfiguration : IDeliveryOptionsBuild
104104
/// </remarks>
105105
/// <param name="presetCodename">Codename of the rendition preset to be applied automatically.</param>
106106
IOptionalDeliveryConfiguration WithDefaultRenditionPreset(string presetCodename);
107+
108+
/// <summary>
109+
/// Replaces the base URL of the asset URLs in the API response. The asset URL will retain all the identifying information such as project id, asset id and file name.<br/>
110+
/// <b>NOTE: </b>Do not specify a trailing backslash in the URL.<br/>
111+
/// <b>Example where url is defined as "https://www.example.com/assets":</b><br/>
112+
/// <b>Original Value:</b> https://preview-assets-us-01.kc-usercontent.com/7ffda7b5-bfb2-4226-8b4f-d2e333638416/614cb0cc-9e62-4572-a408-fc8715f990e8/test-asset.pdf <br/>
113+
/// <b>New Value:</b> https://www.example.com/assets/7ffda7b5-bfb2-4226-8b4f-d2e333638416/614cb0cc-9e62-4572-a408-fc8715f990e8/test-asset.pdf
114+
/// </summary>
115+
/// <param name="url">URL to substitute for asset urls</param>
116+
/// <returns></returns>
117+
IOptionalDeliveryConfiguration WithAssetUrlReplacement(string url);
107118
}
108119

109120
/// <summary>

0 commit comments

Comments
 (0)