Skip to content

Commit 04b9fe1

Browse files
Remove content type check for appconfig (#235)
1 parent 5378691 commit 04b9fe1

File tree

6 files changed

+210
-11
lines changed

6 files changed

+210
-11
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"Projects": [
3+
{
4+
"Name": "Amazon.Extensions.Configuration.SystemsManager",
5+
"Type": "Patch",
6+
"ChangelogMessages": [
7+
"Removed content type check for app config. We now always try to parse json first"
8+
]
9+
}
10+
]
11+
}

src/Amazon.Extensions.Configuration.SystemsManager/Amazon.Extensions.Configuration.SystemsManager.csproj

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,11 @@
3838
<None Include="../../icon.png" Pack="true" PackagePath="" />
3939
</ItemGroup>
4040

41-
<PropertyGroup>
41+
<ItemGroup>
42+
<InternalsVisibleTo Include="Amazon.Extensions.Configuration.SystemsManager.Tests,PublicKey=0024000004800000940000000602000000240000525341310004000001000100db5f59f098d27276c7833875a6263a3cc74ab17ba9a9df0b52aedbe7252745db7274d5271fd79c1f08f668ecfa8eaab5626fa76adc811d3c8fc55859b0d09d3bc0a84eecd0ba891f2b8a2fc55141cdcc37c2053d53491e650a479967c3622762977900eddbf1252ed08a2413f00a28f3a0752a81203f03ccb7f684db373518b4" />
43+
</ItemGroup>
44+
45+
<PropertyGroup>
4246
<SignAssembly>true</SignAssembly>
4347
<AssemblyOriginatorKeyFile>..\..\public.snk</AssemblyOriginatorKeyFile>
4448
</PropertyGroup>

src/Amazon.Extensions.Configuration.SystemsManager/AppConfig/AppConfigProcessor.cs

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
using System.Collections.Generic;
1818
using System.IO;
1919
using System.Net.Http;
20+
using System.Text.Json;
2021
using System.Threading;
2122
using System.Threading.Tasks;
2223
using Amazon.AppConfigData;
@@ -162,20 +163,17 @@ private async Task<string> GetInitialConfigurationTokenAsync(IAmazonAppConfigDat
162163
return (await appConfigClient.StartConfigurationSessionAsync(request).ConfigureAwait(false)).InitialConfigurationToken;
163164
}
164165

165-
private static IDictionary<string, string> ParseConfig(string contentType, Stream configuration)
166+
internal static IDictionary<string, string> ParseConfig(string contentType, Stream configuration)
166167
{
167-
// Content-Type has format "media-type; charset" or "media-type; boundary" (for multipart entities).
168-
if (contentType != null)
168+
try
169169
{
170-
contentType = contentType.Split(';')[0];
170+
return JsonConfigurationParser.Parse(configuration);
171171
}
172-
173-
switch (contentType)
172+
catch (JsonException ex)
174173
{
175-
case "application/json":
176-
return JsonConfigurationParser.Parse(configuration);
177-
default:
178-
throw new NotImplementedException($"Not implemented AppConfig type: {contentType}");
174+
throw new InvalidOperationException(
175+
$"Failed to parse AppConfig content as JSON. Content-Type was '{contentType}'. {ex.Message}",
176+
ex);
179177
}
180178
}
181179
}

test/Amazon.Extensions.Configuration.SystemsManager.Integ/AppConfigEndToEndTests.cs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,42 @@ public async Task JsonWithCharsetConfiguration()
8484
}
8585
}
8686

87+
[Fact]
88+
public async Task AppConfigWithOctetStreamContentType()
89+
{
90+
var configSettings = new Dictionary<string, string>
91+
{
92+
{ "key1", "value1" },
93+
{ "key2", "value2" },
94+
{ "database:connectionString", "server=localhost;database=test" },
95+
{ "api:timeout", "30" }
96+
};
97+
98+
// Create AppConfig with application/octet-stream content type (simulates Parameter Store backend)
99+
(string applicationId, string environmentId, string configProfileId) =
100+
await CreateAppConfigResourcesAsync("OctetStreamTest", configSettings, "application/octet-stream");
101+
102+
try
103+
{
104+
var builder = new ConfigurationBuilder()
105+
.AddAppConfig(applicationId, environmentId, configProfileId,
106+
new AWSOptions { Region = RegionEndpoint.USWest2 },
107+
TimeSpan.FromSeconds(5));
108+
109+
var configuration = builder.Build();
110+
111+
// Verify configuration loads correctly despite application/octet-stream content type
112+
Assert.Equal("value1", configuration["key1"]);
113+
Assert.Equal("value2", configuration["key2"]);
114+
Assert.Equal("server=localhost;database=test", configuration["database:connectionString"]);
115+
Assert.Equal("30", configuration["api:timeout"]);
116+
}
117+
finally
118+
{
119+
await CleanupAppConfigResourcesAsync(applicationId, environmentId, configProfileId);
120+
}
121+
}
122+
87123
private async Task CleanupAppConfigResourcesAsync(string applicationId, string environmentId, string configProfileId)
88124
{
89125
await _appConfigClient.DeleteEnvironmentAsync(new DeleteEnvironmentRequest {ApplicationId = applicationId, EnvironmentId = environmentId });

test/Amazon.Extensions.Configuration.SystemsManager.Tests/Amazon.Extensions.Configuration.SystemsManager.Tests.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
<TargetFramework>net8.0</TargetFramework>
55
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
66
<IsPackable>false</IsPackable>
7+
<SignAssembly>true</SignAssembly>
8+
<AssemblyOriginatorKeyFile>..\..\public.snk</AssemblyOriginatorKeyFile>
79
</PropertyGroup>
810

911
<ItemGroup>
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
using System;
17+
using System.Collections.Generic;
18+
using System.IO;
19+
using System.Text;
20+
using Amazon.Extensions.Configuration.SystemsManager.AppConfig;
21+
using Xunit;
22+
23+
namespace Amazon.Extensions.Configuration.SystemsManager.Tests
24+
{
25+
public class AppConfigProcessorTests
26+
{
27+
[Fact]
28+
public void ParseConfig_ApplicationJson_ParsesSuccessfully()
29+
{
30+
var jsonContent = "{\"key1\":\"value1\",\"key2\":\"value2\"}";
31+
using var stream = new MemoryStream(Encoding.UTF8.GetBytes(jsonContent));
32+
33+
var result = AppConfigProcessor.ParseConfig("application/json", stream);
34+
35+
Assert.Equal("value1", result["key1"]);
36+
Assert.Equal("value2", result["key2"]);
37+
}
38+
39+
[Fact]
40+
public void ParseConfig_ApplicationOctetStream_ParsesJsonSuccessfully()
41+
{
42+
var jsonContent = "{\"key1\":\"value1\",\"key2\":\"value2\"}";
43+
using var stream = new MemoryStream(Encoding.UTF8.GetBytes(jsonContent));
44+
45+
var result = AppConfigProcessor.ParseConfig("application/octet-stream", stream);
46+
47+
Assert.Equal("value1", result["key1"]);
48+
Assert.Equal("value2", result["key2"]);
49+
}
50+
51+
[Fact]
52+
public void ParseConfig_ApplicationJsonWithCharset_ParsesSuccessfully()
53+
{
54+
var jsonContent = "{\"key1\":\"value1\",\"key2\":\"value2\"}";
55+
using var stream = new MemoryStream(Encoding.UTF8.GetBytes(jsonContent));
56+
57+
var result = AppConfigProcessor.ParseConfig("application/json; charset=utf-8", stream);
58+
59+
Assert.Equal("value1", result["key1"]);
60+
Assert.Equal("value2", result["key2"]);
61+
}
62+
63+
[Fact]
64+
public void ParseConfig_UnknownContentType_ParsesJsonSuccessfully()
65+
{
66+
var jsonContent = "{\"key1\":\"value1\",\"key2\":\"value2\"}";
67+
using var stream = new MemoryStream(Encoding.UTF8.GetBytes(jsonContent));
68+
69+
var result = AppConfigProcessor.ParseConfig("application/unknown", stream);
70+
71+
Assert.Equal("value1", result["key1"]);
72+
Assert.Equal("value2", result["key2"]);
73+
}
74+
75+
[Fact]
76+
public void ParseConfig_NullContentType_ParsesJsonSuccessfully()
77+
{
78+
var jsonContent = "{\"key1\":\"value1\",\"key2\":\"value2\"}";
79+
using var stream = new MemoryStream(Encoding.UTF8.GetBytes(jsonContent));
80+
81+
var result = AppConfigProcessor.ParseConfig(null, stream);
82+
83+
Assert.Equal("value1", result["key1"]);
84+
Assert.Equal("value2", result["key2"]);
85+
}
86+
87+
[Fact]
88+
public void ParseConfig_NestedJson_ParsesSuccessfully()
89+
{
90+
var jsonContent = "{\"section1\":{\"key1\":\"value1\",\"key2\":\"value2\"},\"section2\":{\"key3\":\"value3\"}}";
91+
using var stream = new MemoryStream(Encoding.UTF8.GetBytes(jsonContent));
92+
93+
var result = AppConfigProcessor.ParseConfig("application/octet-stream", stream);
94+
95+
Assert.Equal("value1", result["section1:key1"]);
96+
Assert.Equal("value2", result["section1:key2"]);
97+
Assert.Equal("value3", result["section2:key3"]);
98+
}
99+
100+
[Fact]
101+
public void ParseConfig_InvalidJson_ThrowsInvalidOperationException()
102+
{
103+
var invalidJsonContent = "{ invalid json content }";
104+
using var stream = new MemoryStream(Encoding.UTF8.GetBytes(invalidJsonContent));
105+
106+
var exception = Assert.Throws<InvalidOperationException>(() =>
107+
AppConfigProcessor.ParseConfig("application/json", stream));
108+
109+
Assert.Contains("Failed to parse AppConfig content as JSON", exception.Message);
110+
Assert.Contains("Content-Type was 'application/json'", exception.Message);
111+
}
112+
113+
[Fact]
114+
public void ParseConfig_InvalidJsonWithOctetStream_ThrowsInvalidOperationException()
115+
{
116+
var invalidJsonContent = "not json at all";
117+
using var stream = new MemoryStream(Encoding.UTF8.GetBytes(invalidJsonContent));
118+
119+
var exception = Assert.Throws<InvalidOperationException>(() =>
120+
AppConfigProcessor.ParseConfig("application/octet-stream", stream));
121+
122+
Assert.Contains("Failed to parse AppConfig content as JSON", exception.Message);
123+
Assert.Contains("Content-Type was 'application/octet-stream'", exception.Message);
124+
}
125+
126+
[Fact]
127+
public void ParseConfig_EmptyJson_ReturnsEmptyDictionary()
128+
{
129+
var jsonContent = "{}";
130+
using var stream = new MemoryStream(Encoding.UTF8.GetBytes(jsonContent));
131+
132+
var result = AppConfigProcessor.ParseConfig("application/json", stream);
133+
134+
Assert.Empty(result);
135+
}
136+
137+
[Fact]
138+
public void ParseConfig_EmptyStream_ThrowsInvalidOperationException()
139+
{
140+
using var stream = new MemoryStream();
141+
142+
var exception = Assert.Throws<InvalidOperationException>(() =>
143+
AppConfigProcessor.ParseConfig("application/json", stream));
144+
145+
Assert.Contains("Failed to parse AppConfig content as JSON", exception.Message);
146+
}
147+
}
148+
}

0 commit comments

Comments
 (0)