Skip to content
This repository was archived by the owner on Sep 6, 2025. It is now read-only.

Commit 2921fe2

Browse files
authored
Managed Databases: Firewall and SQL Mode Management (#82)
* New endpoint to add/list firewall rules for databases * New endpoints to configure/get sql_modes for MySQL databases And endpoints to configure/get eviction_policy for Redis databases
1 parent 2a22b83 commit 2921fe2

10 files changed

Lines changed: 291 additions & 0 deletions

File tree

DigitalOcean.API.Tests/Clients/DatabasesClientTest.cs

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,5 +285,74 @@ public void CorrectRequestForDeleteConnectionPool() {
285285
var parameters = Arg.Is<List<Parameter>>(list => (string)list[0].Value == "1" && (string)list[1].Value == "name");
286286
factory.Received().ExecuteRaw("databases/{id}/pools/{name}", parameters, null, Method.DELETE);
287287
}
288+
289+
[Fact]
290+
public void CorrectRequestForUpdateFirewallRules() {
291+
var factory = Substitute.For<IConnection>();
292+
var client = new DatabasesClient(factory);
293+
294+
var body = new Models.Requests.UpdateDatabaseFirewallRules();
295+
client.UpdateFirewallRules("1", body);
296+
297+
var parameters = Arg.Is<List<Parameter>>(list => (string)list[0].Value == "1");
298+
factory.Received().ExecuteRaw("databases/{id}/firewall", parameters, body, Method.PUT);
299+
}
300+
301+
[Fact]
302+
public void CorrectRequestForListFirewallRules() {
303+
var factory = Substitute.For<IConnection>();
304+
var client = new DatabasesClient(factory);
305+
306+
client.ListFirewallRules("1");
307+
308+
var parameters = Arg.Is<List<Parameter>>(list => (string)list[0].Value == "1");
309+
factory.Received().GetPaginated<DatabaseFirewallRule>("databases/{id}/firewall", parameters, "rules");
310+
}
311+
312+
[Fact]
313+
public void CorrectRequestForRetrieveRedisEviction() {
314+
var factory = Substitute.For<IConnection>();
315+
var client = new DatabasesClient(factory);
316+
317+
client.RetrieveEvictionPolicy("1");
318+
319+
var parameters = Arg.Is<List<Parameter>>(list => (string)list[0].Value == "1");
320+
factory.Received().ExecuteRequest<RedisEvictionPolicy>("databases/{id}/eviction_policy", parameters, null, null);
321+
}
322+
323+
[Fact]
324+
public void CorrectRequestForConfigureRedisEviction() {
325+
var factory = Substitute.For<IConnection>();
326+
var client = new DatabasesClient(factory);
327+
328+
var body = new Models.Requests.RedisEvictionPolicy();
329+
client.ConfigureEvictionPolicy("1", body);
330+
331+
var parameters = Arg.Is<List<Parameter>>(list => (string)list[0].Value == "1");
332+
factory.Received().ExecuteRaw("databases/{id}/eviction_policy", parameters, body, Method.PUT);
333+
}
334+
335+
[Fact]
336+
public void CorrectRequestForRetrieveMySqlModes() {
337+
var factory = Substitute.For<IConnection>();
338+
var client = new DatabasesClient(factory);
339+
340+
client.RetrieveSqlModes("1");
341+
342+
var parameters = Arg.Is<List<Parameter>>(list => (string)list[0].Value == "1");
343+
factory.Received().ExecuteRequest<MySqlModes>("databases/{id}/sql_mode", parameters, null, null);
344+
}
345+
346+
[Fact]
347+
public void CorrectRequestForConfigureMySqlModes() {
348+
var factory = Substitute.For<IConnection>();
349+
var client = new DatabasesClient(factory);
350+
351+
var body = new Models.Requests.MySqlModes();
352+
client.ConfigureSqlModes("1", body);
353+
354+
var parameters = Arg.Is<List<Parameter>>(list => (string)list[0].Value == "1");
355+
factory.Received().ExecuteRaw("databases/{id}/sql_mode", parameters, body, Method.PUT);
356+
}
288357
}
289358
}

DigitalOcean.API/Clients/DatabasesClient.cs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,70 @@ public Task<DatabaseCluster> Restore(Models.Requests.DatabaseBackup backup) {
285285
return _connection.ExecuteRequest<DatabaseCluster>("databases", null, backup, "database", Method.POST);
286286
}
287287

288+
/// <summary>
289+
/// To update a database cluster's firewall rules (known as "trusted sources" in the control panel).
290+
/// Specify which resources should be able to open connections to the database.
291+
/// You may limit connections to specific Droplets, Kubernetes clusters, or IP addresses.
292+
/// When a tag is provided, any Droplet or Kubernetes node with that tag applied to it will have access.
293+
/// </summary>
294+
public Task UpdateFirewallRules(string databaseId, Models.Requests.UpdateDatabaseFirewallRules updateRules) {
295+
var parameters = new List<Parameter> {
296+
new Parameter("id", databaseId, ParameterType.UrlSegment)
297+
};
298+
return _connection.ExecuteRaw("databases/{id}/firewall", parameters, updateRules, Method.PUT);
299+
}
300+
301+
/// <summary>
302+
/// To list all of a database cluster's firewall rules (known as "trusted sources" in the control panel).
303+
/// </summary>
304+
public Task<IReadOnlyList<DatabaseFirewallRule>> ListFirewallRules(string databaseId) {
305+
var parameters = new List<Parameter> {
306+
new Parameter("id", databaseId, ParameterType.UrlSegment)
307+
};
308+
return _connection.GetPaginated<DatabaseFirewallRule>("databases/{id}/firewall", parameters, "rules");
309+
}
310+
311+
/// <summary>
312+
/// To retrieve the configured eviction policy for an existing Redis cluster.
313+
/// </summary>
314+
public Task<RedisEvictionPolicy> RetrieveEvictionPolicy(string databaseId) {
315+
var parameters = new List<Parameter> {
316+
new Parameter("id", databaseId, ParameterType.UrlSegment)
317+
};
318+
return _connection.ExecuteRequest<RedisEvictionPolicy>("databases/{id}/eviction_policy", parameters, null, null);
319+
}
320+
321+
/// <summary>
322+
/// To configure an eviction policy for an existing Redis cluster.
323+
/// </summary>
324+
public Task ConfigureEvictionPolicy(string databaseId, Models.Requests.RedisEvictionPolicy evictionPolicy) {
325+
var parameters = new List<Parameter> {
326+
new Parameter("id", databaseId, ParameterType.UrlSegment)
327+
};
328+
return _connection.ExecuteRaw("databases/{id}/eviction_policy", parameters, evictionPolicy, Method.PUT);
329+
}
330+
331+
/// <summary>
332+
/// To retrieve the configured SQL modes for an existing MySQL cluster.
333+
/// </summary>
334+
public Task<MySqlModes> RetrieveSqlModes(string databaseId) {
335+
var parameters = new List<Parameter> {
336+
new Parameter("id", databaseId, ParameterType.UrlSegment)
337+
};
338+
return _connection.ExecuteRequest<MySqlModes>("databases/{id}/sql_mode", parameters, null, null);
339+
}
340+
341+
/// <summary>
342+
/// To configure the SQL modes for an existing MySQL cluster.
343+
/// See the official MySQL 8 documentation for a full list of supported SQL modes.
344+
/// </summary>
345+
public Task ConfigureSqlModes(string databaseId, Models.Requests.MySqlModes sqlModes) {
346+
var parameters = new List<Parameter> {
347+
new Parameter("id", databaseId, ParameterType.UrlSegment)
348+
};
349+
return _connection.ExecuteRaw("databases/{id}/sql_mode", parameters, sqlModes, Method.PUT);
350+
}
351+
288352
#endregion
289353
}
290354
}

DigitalOcean.API/Clients/IDatabasesClient.cs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,5 +139,39 @@ public interface IDatabasesClient {
139139
/// Delete a specific connection pool
140140
/// </summary>
141141
Task DeleteConnectionPool(string databaseId, string poolName);
142+
143+
/// <summary>
144+
/// To update a database cluster's firewall rules (known as "trusted sources" in the control panel).
145+
/// Specify which resources should be able to open connections to the database.
146+
/// You may limit connections to specific Droplets, Kubernetes clusters, or IP addresses.
147+
/// When a tag is provided, any Droplet or Kubernetes node with that tag applied to it will have access.
148+
/// </summary>
149+
Task UpdateFirewallRules(string databaseId, Models.Requests.UpdateDatabaseFirewallRules updateRules);
150+
151+
/// <summary>
152+
/// To list all of a database cluster's firewall rules (known as "trusted sources" in the control panel).
153+
/// </summary>
154+
Task<IReadOnlyList<DatabaseFirewallRule>> ListFirewallRules(string databaseId);
155+
156+
/// <summary>
157+
/// To retrieve the configured eviction policy for an existing Redis cluster.
158+
/// </summary>
159+
Task<RedisEvictionPolicy> RetrieveEvictionPolicy(string databaseId);
160+
161+
/// <summary>
162+
/// To configure an eviction policy for an existing Redis cluster.
163+
/// </summary>
164+
Task ConfigureEvictionPolicy(string databaseId, Models.Requests.RedisEvictionPolicy evictionPolicy);
165+
166+
/// <summary>
167+
/// To retrieve the configured SQL modes for an existing MySQL cluster.
168+
/// </summary>
169+
Task<MySqlModes> RetrieveSqlModes(string databaseId);
170+
171+
/// <summary>
172+
/// To configure the SQL modes for an existing MySQL cluster.
173+
/// See the official MySQL 8 documentation for a full list of supported SQL modes.
174+
/// </summary>
175+
Task ConfigureSqlModes(string databaseId, Models.Requests.MySqlModes sqlModes);
142176
}
143177
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
using Newtonsoft.Json;
2+
3+
namespace DigitalOcean.API.Models.Requests {
4+
public class DatabaseFirewallRule {
5+
/// <summary>
6+
/// The type of resource that the firewall rule allows to access the database cluster.
7+
/// The possible values are: 'droplet', 'k8s', 'ip_addr', or 'tag'.
8+
/// </summary>
9+
[JsonProperty("type")]
10+
public string Type { get; set; }
11+
12+
/// <summary>
13+
/// The ID of the specific resource, the name of a tag applied to a group of resources, or the IP address that the firewall rule allows to access the database cluster.
14+
/// </summary>
15+
[JsonProperty("value")]
16+
public string Value { get; set; }
17+
18+
/// <summary>
19+
/// A unique ID for the firewall rule itself when updating an existing rule.
20+
/// </summary>
21+
[JsonProperty("uuid")]
22+
public string Uuid { get; set; }
23+
24+
/// <summary>
25+
/// A unique ID for the database cluster to which the rule is applied.
26+
/// </summary>
27+
[JsonProperty("cluster_uuid")]
28+
public string ClusterUuid { get; set; }
29+
}
30+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
using Newtonsoft.Json;
2+
3+
namespace DigitalOcean.API.Models.Requests {
4+
public class MySqlModes {
5+
/// <summary>
6+
/// A single string specifying the desired SQL modes for the MySQL cluster separated by commas.
7+
/// </summary>
8+
[JsonProperty("sql_mode")]
9+
public string SqlMode { get; set; }
10+
}
11+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using Newtonsoft.Json;
2+
3+
namespace DigitalOcean.API.Models.Requests {
4+
public class RedisEvictionPolicy {
5+
/// <summary>
6+
/// A string specifying the desired eviction policy for the Redis cluster.
7+
/// Valid vaules are: noeviction, allkeys_lru, allkeys_random, volatile_lru, volatile_random, or volatile_ttl.
8+
/// </summary>
9+
[JsonProperty("eviction_policy")]
10+
public string EvictionPolicy { get; set; }
11+
}
12+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using System.Collections.Generic;
2+
using Newtonsoft.Json;
3+
4+
namespace DigitalOcean.API.Models.Requests {
5+
public class UpdateDatabaseFirewallRules {
6+
/// <summary>
7+
/// Firewall rules to update.
8+
/// </summary>
9+
[JsonProperty("rules")]
10+
public List<DatabaseFirewallRule> Rules { get; set; }
11+
}
12+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
using System;
2+
using Newtonsoft.Json;
3+
4+
namespace DigitalOcean.API.Models.Responses {
5+
public class DatabaseFirewallRule {
6+
/// <summary>
7+
/// A unique ID for the firewall rule itself.
8+
/// </summary>
9+
[JsonProperty("uuid")]
10+
public string Uuid { get; set; }
11+
12+
/// <summary>
13+
/// A unique ID for the database cluster to which the rule is applied.
14+
/// </summary>
15+
[JsonProperty("cluster_uuid")]
16+
public string ClusterUuid { get; set; }
17+
18+
/// <summary>
19+
/// The type of resource that the firewall rule allows to access the database cluster.
20+
/// The possible values are: 'droplet', 'k8s', 'ip_addr', or 'tag'
21+
/// </summary>
22+
[JsonProperty("type")]
23+
public string Type { get; set; }
24+
25+
/// <summary>
26+
/// The ID of the specific resource, the name of a tag applied to a group of resources, or the IP address that the firewall rule allows to access the database cluster.
27+
/// </summary>
28+
[JsonProperty("value")]
29+
public string Value { get; set; }
30+
31+
/// <summary>
32+
/// A time value given in ISO8601 combined date and time format that represents when the firewall rule was created.
33+
/// </summary>
34+
[JsonProperty("created_at")]
35+
public DateTime CreatedAt { get; set; }
36+
}
37+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
using Newtonsoft.Json;
2+
3+
namespace DigitalOcean.API.Models.Responses {
4+
public class MySqlModes {
5+
/// <summary>
6+
/// A string specifying the configured SQL modes for the MySQL cluster.
7+
/// </summary>
8+
[JsonProperty("sql_mode")]
9+
public string SqlMode { get; set; }
10+
}
11+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
using Newtonsoft.Json;
2+
3+
namespace DigitalOcean.API.Models.Responses {
4+
public class RedisEvictionPolicy {
5+
/// <summary>
6+
/// A string specifying the desired eviction policy for the Redis cluster.
7+
/// </summary>
8+
[JsonProperty("eviction_policy")]
9+
public string EvictionPolicy { get; set; }
10+
}
11+
}

0 commit comments

Comments
 (0)