Skip to content

Commit e59c9f2

Browse files
committed
Add property to disable built-in post-processors, including sample code for binding third-party brokers
1 parent e46e6a5 commit e59c9f2

4 files changed

Lines changed: 178 additions & 2 deletions

File tree

src/Connectors/src/Connectors/ConnectorConfigureOptionsBuilder.cs

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,83 @@ public sealed class ConnectorConfigureOptionsBuilder
1313
/// <see cref="ConnectorAddOptionsBuilder.CacheConnection" /> is <c>false</c>.
1414
/// </summary>
1515
public bool DetectConfigurationChanges { get; set; }
16+
17+
/// <summary>
18+
/// Gets or sets a value indicating whether to turn off the built-in service broker support. This is <c>false</c> by default, but should be set to
19+
/// <c>true</c> when using custom logic to convert platform-based credentials to driver-specific configuration keys.
20+
/// <para>
21+
/// <example>
22+
/// For example, to use a third-party Cloud Foundry service broker, the following code can be used to map the PostgreSQL credentials to the format that
23+
/// <see href="https://www.npgsql.org/doc/api/Npgsql.NpgsqlConnectionStringBuilder.html">
24+
/// NpgsqlConnectionStringBuilder
25+
/// </see>
26+
/// expects:
27+
/// <code><![CDATA[
28+
/// Environment.SetEnvironmentVariable("VCAP_SERVICES", """
29+
/// {
30+
/// "custom-postgres-broker": [
31+
/// {
32+
/// "name": "products-db",
33+
/// "credentials": {
34+
/// "custom-hostname-key": "example.cloud.com",
35+
/// "custom-port-key": 2345,
36+
/// "custom-username-key": "products-user",
37+
/// "custom-password-key": "products-secret",
38+
/// "custom-database-name-key": "product-database"
39+
/// }
40+
/// },
41+
/// {
42+
/// "name": "orders-db",
43+
/// "credentials": {
44+
/// "custom-hostname-key": "example.cloud.com",
45+
/// "custom-port-key": 2345,
46+
/// "custom-username-key": "orders-user",
47+
/// "custom-password-key": "orders-secret",
48+
/// "custom-database-name-key": "order-database"
49+
/// }
50+
/// },
51+
/// ]
52+
/// }
53+
/// """);
54+
///
55+
/// var builder = WebApplication.CreateBuilder();
56+
/// builder.AddCloudFoundryConfiguration();
57+
/// MapCustomServiceBindings("custom-postgres-broker");
58+
/// builder.AddPostgreSql(configure => configure.SkipDefaultServiceBindings = true, null);
59+
/// var app = builder.Build();
60+
///
61+
/// var factory = app.Services.GetRequiredService<ConnectorFactory<PostgreSqlOptions, NpgsqlConnection>>();
62+
///
63+
/// PostgreSqlOptions productsDbOptions = factory.Get("products-db").Options;
64+
/// Console.WriteLine(productsDbOptions.ConnectionString);
65+
/// // Database=product-database;Host=example.cloud.com;Password=products-secret;Port=2345;Username=products-user
66+
///
67+
/// PostgreSqlOptions ordersDbOptions = factory.Get("orders-db").Options;
68+
/// Console.WriteLine(ordersDbOptions.ConnectionString);
69+
/// // Database=order-database;Host=example.cloud.com;Password=orders-secret;Port=2345;Username=orders-user
70+
///
71+
/// void MapCustomServiceBindings(string brokerName)
72+
/// {
73+
/// var options = builder.Configuration.GetSection("vcap").Get<CloudFoundryServicesOptions>();
74+
///
75+
/// foreach (CloudFoundryService service in options?.Services
76+
/// .Where(pair => pair.Key == brokerName)
77+
/// .SelectMany(pair => pair.Value) ?? [])
78+
/// {
79+
/// builder.Configuration.AddInMemoryCollection(new Dictionary<string, string?>
80+
/// {
81+
/// // Map credentials into the property names expected by NpgsqlConnectionStringBuilder.
82+
/// [$"steeltoe:service-bindings:postgresql:{service.Name}:host"] = service.Credentials["custom-hostname-key"].Value,
83+
/// [$"steeltoe:service-bindings:postgresql:{service.Name}:port"] = service.Credentials["custom-port-key"].Value,
84+
/// [$"steeltoe:service-bindings:postgresql:{service.Name}:username"] = service.Credentials["custom-username-key"].Value,
85+
/// [$"steeltoe:service-bindings:postgresql:{service.Name}:password"] = service.Credentials["custom-password-key"].Value,
86+
/// [$"steeltoe:service-bindings:postgresql:{service.Name}:database"] = service.Credentials["custom-database-name-key"].Value
87+
/// });
88+
/// }
89+
/// }
90+
/// ]]></code>
91+
/// </example>
92+
/// </para>
93+
/// </summary>
94+
public bool SkipDefaultServiceBindings { get; set; }
1695
}

src/Connectors/src/Connectors/ConnectorConfigurer.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,11 @@ public static void Configure<TPostProcessor>(IConfigurationBuilder builder, Acti
2020
var optionsBuilder = new ConnectorConfigureOptionsBuilder();
2121
configureAction?.Invoke(optionsBuilder);
2222

23-
builder.AddCloudFoundryServiceBindings();
24-
builder.AddKubernetesServiceBindings();
23+
if (!optionsBuilder.SkipDefaultServiceBindings)
24+
{
25+
builder.AddCloudFoundryServiceBindings();
26+
builder.AddKubernetesServiceBindings();
27+
}
2528

2629
RegisterPostProcessor(connectionStringPostProcessor, builder, optionsBuilder.DetectConfigurationChanges);
2730
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
#nullable enable
2+
Steeltoe.Connectors.ConnectorConfigureOptionsBuilder.SkipDefaultServiceBindings.get -> bool
3+
Steeltoe.Connectors.ConnectorConfigureOptionsBuilder.SkipDefaultServiceBindings.set -> void

src/Connectors/test/Connectors.Test/PostgreSql/PostgreSqlConnectorTest.cs

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
using Npgsql;
1010
using Steeltoe.Common.HealthChecks;
1111
using Steeltoe.Common.TestResources;
12+
using Steeltoe.Configuration.CloudFoundry;
1213
using Steeltoe.Configuration.CloudFoundry.ServiceBindings;
1314
using Steeltoe.Configuration.Kubernetes.ServiceBindings;
1415
using Steeltoe.Connectors.PostgreSql;
@@ -394,6 +395,97 @@ public async Task Binds_options_with_Kubernetes_service_bindings()
394395
}, options => options.WithoutStrictOrdering());
395396
}
396397

398+
[Fact]
399+
public async Task Binds_options_with_third_party_service_bindings()
400+
{
401+
var appSettings = new Dictionary<string, string?>
402+
{
403+
["Steeltoe:Client:PostgreSql:products-db:ConnectionString"] = "Include Error Detail=true;host=localhost",
404+
["Steeltoe:Client:PostgreSql:orders-db:ConnectionString"] = "Log Parameters=true;port=9999"
405+
};
406+
407+
var reader = new CloudFoundryMemorySettingsReader
408+
{
409+
ServicesJson = """
410+
{
411+
"custom-postgres-broker": [
412+
{
413+
"name": "products-db",
414+
"credentials": {
415+
"custom-hostname-key": "example.cloud.com",
416+
"custom-port-key": 2345,
417+
"custom-username-key": "products-user",
418+
"custom-password-key": "products-secret",
419+
"custom-database-name-key": "product-database"
420+
}
421+
},
422+
{
423+
"name": "orders-db",
424+
"credentials": {
425+
"custom-hostname-key": "example.cloud.com",
426+
"custom-port-key": 2345,
427+
"custom-username-key": "orders-user",
428+
"custom-password-key": "orders-secret",
429+
"custom-database-name-key": "order-database"
430+
}
431+
},
432+
]
433+
}
434+
"""
435+
};
436+
437+
WebApplicationBuilder builder = TestWebApplicationBuilderFactory.Create();
438+
builder.Configuration.AddInMemoryCollection(appSettings);
439+
builder.Configuration.AddCloudFoundry(reader);
440+
MapCustomServiceBindings("custom-postgres-broker");
441+
builder.AddPostgreSql(options => options.SkipDefaultServiceBindings = true, null);
442+
await using WebApplication app = builder.Build();
443+
444+
var optionsMonitor = app.Services.GetRequiredService<IOptionsMonitor<PostgreSqlOptions>>();
445+
446+
PostgreSqlOptions productsDbOptions = optionsMonitor.Get("products-db");
447+
448+
ExtractConnectionStringParameters(productsDbOptions.ConnectionString).Should().BeEquivalentTo(new List<string>
449+
{
450+
"Include Error Detail=True",
451+
"Host=example.cloud.com",
452+
"Port=2345",
453+
"Database=product-database",
454+
"Username=products-user",
455+
"Password=products-secret"
456+
}, options => options.WithoutStrictOrdering());
457+
458+
PostgreSqlOptions ordersDbOptions = optionsMonitor.Get("orders-db");
459+
460+
ExtractConnectionStringParameters(ordersDbOptions.ConnectionString).Should().BeEquivalentTo(new List<string>
461+
{
462+
"Log Parameters=True",
463+
"Host=example.cloud.com",
464+
"Port=2345",
465+
"Database=order-database",
466+
"Username=orders-user",
467+
"Password=orders-secret"
468+
}, options => options.WithoutStrictOrdering());
469+
470+
void MapCustomServiceBindings(string brokerName)
471+
{
472+
var options = builder.Configuration.GetSection("vcap").Get<CloudFoundryServicesOptions>();
473+
474+
foreach (CloudFoundryService service in options?.Services.Where(pair => pair.Key == brokerName).SelectMany(pair => pair.Value) ?? [])
475+
{
476+
builder.Configuration.AddInMemoryCollection(new Dictionary<string, string?>
477+
{
478+
// Map credentials into the property names expected by NpgsqlConnectionStringBuilder.
479+
[$"steeltoe:service-bindings:postgresql:{service.Name}:host"] = service.Credentials["custom-hostname-key"].Value,
480+
[$"steeltoe:service-bindings:postgresql:{service.Name}:port"] = service.Credentials["custom-port-key"].Value,
481+
[$"steeltoe:service-bindings:postgresql:{service.Name}:username"] = service.Credentials["custom-username-key"].Value,
482+
[$"steeltoe:service-bindings:postgresql:{service.Name}:password"] = service.Credentials["custom-password-key"].Value,
483+
[$"steeltoe:service-bindings:postgresql:{service.Name}:database"] = service.Credentials["custom-database-name-key"].Value
484+
});
485+
}
486+
}
487+
}
488+
397489
[Fact]
398490
public async Task Registers_ConnectorFactory()
399491
{

0 commit comments

Comments
 (0)