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

Commit c4b7caf

Browse files
committed
Add Projects api
1 parent 8308641 commit c4b7caf

9 files changed

Lines changed: 416 additions & 0 deletions

File tree

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
using System.Collections.Generic;
2+
using DigitalOcean.API.Clients;
3+
using DigitalOcean.API.Http;
4+
using DigitalOcean.API.Models.Responses;
5+
using NSubstitute;
6+
using RestSharp;
7+
using Xunit;
8+
9+
namespace DigitalOcean.API.Tests.Clients {
10+
public class ProjectsClientTest {
11+
[Fact]
12+
public void CorrectRequestForGetAll() {
13+
var factory = Substitute.For<IConnection>();
14+
var client = new ProjectsClient(factory);
15+
16+
client.GetAll();
17+
factory.Received().GetPaginated<Project>("projects", null, "projects");
18+
}
19+
20+
[Fact]
21+
public void CorrectRequireForCreate() {
22+
var factory = Substitute.For<IConnection>();
23+
var client = new ProjectsClient(factory);
24+
25+
var data = new Models.Requests.Project();
26+
client.Create(data);
27+
28+
factory.Received().ExecuteRequest<Project>("projects", null, data, "project", Method.POST);
29+
}
30+
31+
[Fact]
32+
public void CorrectRequestForGet() {
33+
var factory = Substitute.For<IConnection>();
34+
var client = new ProjectsClient(factory);
35+
36+
client.Get("project:abc123");
37+
38+
var parameters = Arg.Is<List<Parameter>>(list => (string)list[0].Value == "project:abc123");
39+
factory.Received().ExecuteRequest<Project>("projects/{project_id}", parameters, null, "project");
40+
}
41+
42+
[Fact]
43+
public void CorrectRequestForGetDefault() {
44+
var factory = Substitute.For<IConnection>();
45+
var client = new ProjectsClient(factory);
46+
47+
client.GetDefault();
48+
49+
factory.Received().ExecuteRequest<Project>("projects/default", null, null, "project");
50+
}
51+
52+
[Fact]
53+
public void CorrectRequestForUpdate() {
54+
var factory = Substitute.For<IConnection>();
55+
var client = new ProjectsClient(factory);
56+
57+
var data = new Models.Requests.UpdateProject();
58+
client.Update("project:abc123", data);
59+
60+
var parameters = Arg.Is<List<Parameter>>(list => (string)list[0].Value == "project:abc123");
61+
factory.Received().ExecuteRequest<Project>("projects/{project_id}", parameters, data, "project", Method.PUT);
62+
}
63+
64+
[Fact]
65+
public void CorrectRequestForUpdateDefault() {
66+
var factory = Substitute.For<IConnection>();
67+
var client = new ProjectsClient(factory);
68+
69+
var data = new Models.Requests.UpdateProject();
70+
client.UpdateDefault(data);
71+
72+
factory.Received().ExecuteRequest<Project>("projects/default", null, data, "project", Method.PUT);
73+
}
74+
75+
[Fact]
76+
public void CorrectRequestForPatch() {
77+
var factory = Substitute.For<IConnection>();
78+
var client = new ProjectsClient(factory);
79+
80+
var data = new Models.Requests.PatchProject();
81+
client.Patch("project:abc123", data);
82+
83+
var parameters = Arg.Is<List<Parameter>>(list => (string)list[0].Value == "project:abc123");
84+
factory.Received().ExecuteRequest<Project>("projects/{project_id}", parameters, data, "project", Method.PATCH);
85+
}
86+
87+
[Fact]
88+
public void CorrectRequestForPatchDefault() {
89+
var factory = Substitute.For<IConnection>();
90+
var client = new ProjectsClient(factory);
91+
92+
var data = new Models.Requests.PatchProject();
93+
client.PatchDefault(data);
94+
95+
factory.Received().ExecuteRequest<Project>("projects/default", null, data, "project", Method.PATCH);
96+
}
97+
}
98+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
using System.Collections.Generic;
2+
using System.Threading.Tasks;
3+
using DigitalOcean.API.Models.Responses;
4+
5+
namespace DigitalOcean.API.Clients {
6+
public interface IProjectsClient {
7+
/// <summary>
8+
/// To list all your projects.
9+
/// </summary>
10+
Task<IReadOnlyList<Project>> GetAll();
11+
12+
/// <summary>
13+
/// To create a project.
14+
/// </summary>
15+
Task<Project> Create(Models.Requests.Project project);
16+
17+
/// <summary>
18+
/// To get a project.
19+
/// </summary>
20+
Task<Project> Get(string projectId);
21+
22+
/// <summary>
23+
/// To get the default project.
24+
/// </summary>
25+
Task<Project> GetDefault();
26+
27+
/// <summary>
28+
/// To update a project.
29+
/// </summary>
30+
Task<Project> Update(string projectId, Models.Requests.UpdateProject updateProject);
31+
32+
/// <summary>
33+
/// To update the default project.
34+
/// </summary>
35+
Task<Project> UpdateDefault(Models.Requests.UpdateProject updateProject);
36+
37+
/// <summary>
38+
/// To update only specific attributes of a project.
39+
/// </summary>
40+
Task<Project> Patch(string projectId, Models.Requests.PatchProject patchProject);
41+
42+
/// <summary>
43+
/// To update only specific attributes of the default project.
44+
/// </summary>
45+
Task<Project> PatchDefault(Models.Requests.PatchProject patchProject);
46+
}
47+
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
using System.Collections.Generic;
2+
using System.Threading.Tasks;
3+
using DigitalOcean.API.Http;
4+
using DigitalOcean.API.Models.Responses;
5+
using RestSharp;
6+
7+
namespace DigitalOcean.API.Clients {
8+
public class ProjectsClient : IProjectsClient {
9+
private readonly IConnection _connection;
10+
11+
public ProjectsClient(IConnection connection) {
12+
_connection = connection;
13+
}
14+
15+
/// <summary>
16+
/// To list all your projects.
17+
/// </summary>
18+
public Task<IReadOnlyList<Project>> GetAll() {
19+
return _connection.GetPaginated<Project>("projects", null, "projects");
20+
}
21+
22+
/// <summary>
23+
/// To create a project.
24+
/// </summary>
25+
public Task<Project> Create(Models.Requests.Project project) {
26+
return _connection.ExecuteRequest<Project>("projects", null, project, "project", Method.POST);
27+
}
28+
29+
/// <summary>
30+
/// To get a project.
31+
/// </summary>
32+
public Task<Project> Get(string projectId) {
33+
var parameters = new List<Parameter> {
34+
new Parameter { Name = "project_id", Value = projectId, Type = ParameterType.UrlSegment }
35+
};
36+
return _connection.ExecuteRequest<Project>("projects/{project_id}", parameters, null, "project");
37+
}
38+
39+
/// <summary>
40+
/// To get the default project.
41+
/// </summary>
42+
public Task<Project> GetDefault() {
43+
return _connection.ExecuteRequest<Project>("projects/default", null, null, "project");
44+
}
45+
46+
/// <summary>
47+
/// To update a project.
48+
/// </summary>
49+
public Task<Project> Update(string projectId, Models.Requests.UpdateProject updateProject) {
50+
var parameters = new List<Parameter> {
51+
new Parameter { Name = "project_id", Value = projectId, Type = ParameterType.UrlSegment }
52+
};
53+
return _connection.ExecuteRequest<Project>("projects/{project_id}", parameters, updateProject, "project", Method.PUT);
54+
}
55+
56+
/// <summary>
57+
/// To update the default project.
58+
/// </summary>
59+
public Task<Project> UpdateDefault(Models.Requests.UpdateProject updateProject) {
60+
// implied to always be true
61+
updateProject.IsDefault = true;
62+
return _connection.ExecuteRequest<Project>("projects/default", null, updateProject, "project", Method.PUT);
63+
}
64+
65+
/// <summary>
66+
/// To update only specific attributes of a project.
67+
/// </summary>
68+
public Task<Project> Patch(string projectId, Models.Requests.PatchProject patchProject) {
69+
var parameters = new List<Parameter> {
70+
new Parameter { Name = "project_id", Value = projectId, Type = ParameterType.UrlSegment }
71+
};
72+
return _connection.ExecuteRequest<Project>("projects/{project_id}", parameters, patchProject, "project", Method.PATCH);
73+
}
74+
75+
/// <summary>
76+
/// To update only specific attributes of the default project.
77+
/// </summary>
78+
public Task<Project> PatchDefault(Models.Requests.PatchProject patchProject) {
79+
// implied to always be true
80+
patchProject.IsDefault = true;
81+
return _connection.ExecuteRequest<Project>("projects/default", null, patchProject, "project", Method.PATCH);
82+
}
83+
}
84+
}
85+

DigitalOcean.API/DigitalOceanClient.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ public DigitalOceanClient(string token) {
2424
ImageActions = new ImageActionsClient(_connection);
2525
Images = new ImagesClient(_connection);
2626
LoadBalancers = new LoadBalancerClient(_connection);
27+
Projects = new ProjectsClient(_connection);
2728
ProjectResources = new ProjectResourcesClient(_connection);
2829
Keys = new KeysClient(_connection);
2930
Regions = new RegionsClient(_connection);
@@ -47,6 +48,7 @@ public IRateLimit Rates {
4748
public IImagesClient Images { get; private set; }
4849
public IKeysClient Keys { get; private set; }
4950
public ILoadBalancerClient LoadBalancers { get; private set; }
51+
public IProjectsClient Projects { get; private set; }
5052
public IProjectResourcesClient ProjectResources { get; private set; }
5153
public IRegionsClient Regions { get; private set; }
5254
public ISizesClient Sizes { get; private set; }

DigitalOcean.API/IDigitalOceanClient.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ public interface IDigitalOceanClient {
1313
IImagesClient Images { get; }
1414
IKeysClient Keys { get; }
1515
ILoadBalancerClient LoadBalancers { get;}
16+
IProjectsClient Projects { get; }
1617
IProjectResourcesClient ProjectResources { get; }
1718
IRegionsClient Regions { get; }
1819
ISizesClient Sizes { get; }
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
using Newtonsoft.Json;
2+
3+
namespace DigitalOcean.API.Models.Requests {
4+
public class PatchProject {
5+
/// <summary>
6+
/// The human-readable name for the project. The maximum length is 175 characters.
7+
/// </summary>
8+
[JsonProperty("name")]
9+
public string Name { get; set; }
10+
11+
/// <summary>
12+
/// The description of the project. The maximum length is 255 characters.
13+
/// </summary>
14+
[JsonProperty("description")]
15+
public string Description { get; set; }
16+
17+
/// <summary>
18+
/// The purpose of the project. The maximum length is 255 characters. For examples of valid purposes, see <see cref="Project.Purposes"/>
19+
/// </summary>
20+
[JsonProperty("purpose")]
21+
public string Purpose { get; set; }
22+
23+
/// <summary>
24+
/// The environment of the project's resources. List of valid environments, see <see cref="Project.Enviroments"/>
25+
/// </summary>
26+
[JsonProperty("environment")]
27+
public string Environment { get; set; }
28+
29+
/// <summary>
30+
/// If true, all resources will be added to this project if no project is specified.
31+
/// </summary>
32+
[JsonProperty("is_default")]
33+
public bool? IsDefault { get; set; }
34+
}
35+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
using System;
2+
using System.Security.Cryptography.X509Certificates;
3+
using Newtonsoft.Json;
4+
5+
namespace DigitalOcean.API.Models.Requests {
6+
public class Project {
7+
/// <summary>
8+
/// The human-readable name for the project. The maximum length is 175 characters and the name must be unique.
9+
/// </summary>
10+
[JsonProperty("name")]
11+
public string Name { get; set; }
12+
13+
/// <summary>
14+
/// The description of the project. The maximum length is 255 characters.
15+
/// </summary>
16+
[JsonProperty("description")]
17+
public string Description { get; set; }
18+
19+
/// <summary>
20+
/// The purpose of the project. The maximum length is 255 characters. For examples of valid purposes, see <see cref="Purposes"/>
21+
/// </summary>
22+
[JsonProperty("purpose")]
23+
public string Purpose { get; set; }
24+
25+
/// <summary>
26+
/// The environment of the project's resources. List of valid environments, see <see cref="Enviroments"/>
27+
/// </summary>
28+
[JsonProperty("environment")]
29+
public string Environment { get; set; }
30+
31+
/// <summary>
32+
/// The purpose attribute can have one of the following values.
33+
/// If specify another value for purpose, for example "your custom purpose",
34+
/// your purpose will be stored as Other: your custom purpose
35+
/// </summary>
36+
public static class Purposes {
37+
public const string Trying = "Just trying out DigitalOcean";
38+
public const string Educational = "Class project / Educational purposes";
39+
public const string Website = "Website or blog";
40+
public const string WebApp = "Web Application";
41+
public const string ServiceOrApi = "Service or API";
42+
public const string Mobile = "Mobile Application";
43+
public const string DataProcessing = "Machine learning / AI / Data processing";
44+
public const string IoT = "IoT";
45+
public const string Operational = "Operational / Developer tooling";
46+
}
47+
48+
/// <summary>
49+
/// The environment attribute must have one of the following values.
50+
/// If another value is specified, a 400 Bad Request is returned.
51+
/// </summary>
52+
public static class Enviroments {
53+
public const string Development = "Development";
54+
public const string Staging = "Staging";
55+
public const string Production = "Production";
56+
}
57+
}
58+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
using Newtonsoft.Json;
2+
3+
namespace DigitalOcean.API.Models.Requests {
4+
public class UpdateProject {
5+
/// <summary>
6+
/// The human-readable name for the project. The maximum length is 175 characters.
7+
/// </summary>
8+
[JsonProperty("name")]
9+
public string Name { get; set; }
10+
11+
/// <summary>
12+
/// The description of the project. The maximum length is 255 characters.
13+
/// </summary>
14+
[JsonProperty("description")]
15+
public string Description { get; set; }
16+
17+
/// <summary>
18+
/// The purpose of the project. The maximum length is 255 characters. For examples of valid purposes, see <see cref="Project.Purposes"/>
19+
/// </summary>
20+
[JsonProperty("purpose")]
21+
public string Purpose { get; set; }
22+
23+
/// <summary>
24+
/// The environment of the project's resources. List of valid environments, see <see cref="Project.Enviroments"/>
25+
/// </summary>
26+
[JsonProperty("environment")]
27+
public string Environment { get; set; }
28+
29+
/// <summary>
30+
/// If true, all resources will be added to this project if no project is specified.
31+
/// </summary>
32+
[JsonProperty("is_default")]
33+
public bool IsDefault { get; set; }
34+
}
35+
}

0 commit comments

Comments
 (0)