diff --git a/src/Enclave.Sdk/Clients/AuthorityClient.cs b/src/Enclave.Sdk/Clients/AuthorityClient.cs deleted file mode 100644 index c413012..0000000 --- a/src/Enclave.Sdk/Clients/AuthorityClient.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Enclave.Sdk.Api.Clients.Interfaces; - -namespace Enclave.Sdk.Api.Clients; - -public class AuthorityClient : ClientBase, IAuthorityClient -{ - private string _orgRoute; - - public AuthorityClient(HttpClient httpClient, string orgRoute) - : base(httpClient) - { - _orgRoute = orgRoute; - } -} diff --git a/src/Enclave.Sdk/Clients/ClientBase.cs b/src/Enclave.Sdk/Clients/ClientBase.cs index 3afbd75..3fafe03 100644 --- a/src/Enclave.Sdk/Clients/ClientBase.cs +++ b/src/Enclave.Sdk/Clients/ClientBase.cs @@ -1,5 +1,4 @@ using System.Diagnostics.CodeAnalysis; -using System.Net; using System.Net.Http.Json; using System.Net.Mime; using System.Text; @@ -10,7 +9,7 @@ namespace Enclave.Sdk.Api.Clients; /// /// Base class used for commonly accessed methods and properties for all clients. /// -public abstract class ClientBase +internal abstract class ClientBase { /// /// HttpClient used for all clients API calls. @@ -33,7 +32,7 @@ protected ClientBase(HttpClient httpClient) /// the object to encode. /// String content of object. /// throws if data provided is null. - protected StringContent CreateJsonContent(TModel data) + protected static StringContent CreateJsonContent(TModel data) { if (data is null) { @@ -52,7 +51,7 @@ protected StringContent CreateJsonContent(TModel data) /// the object type to deserialise to. /// httpContent from the API call. /// the object of type specified. - protected async Task DeserialiseAsync(HttpContent httpContent) + protected static async Task DeserialiseAsync(HttpContent httpContent) { if (httpContent is null) { diff --git a/src/Enclave.Sdk/Clients/DNSClient.cs b/src/Enclave.Sdk/Clients/DNSClient.cs index 73c8e42..2c0508d 100644 --- a/src/Enclave.Sdk/Clients/DNSClient.cs +++ b/src/Enclave.Sdk/Clients/DNSClient.cs @@ -1,14 +1,249 @@ +using System.Net.Http.Json; +using System.Web; using Enclave.Sdk.Api.Clients.Interfaces; +using Enclave.Sdk.Api.Data; +using Enclave.Sdk.Api.Data.Dns; +using Enclave.Sdk.Api.Data.Pagination; +using Enclave.Sdk.Api.Data.PatchModel; namespace Enclave.Sdk.Api.Clients; -public class DnsClient : ClientBase, IDnsClient +/// +internal class DnsClient : ClientBase, IDnsClient { - private string _orgRoute; + private readonly string _orgRoute; + /// + /// Constructor which will be called by when it's created. + /// + /// an instance of httpClient with a baseURL referencing the API. + /// The organisation API route. public DnsClient(HttpClient httpClient, string orgRoute) : base(httpClient) { _orgRoute = orgRoute; } + + /// + public async Task GetPropertiesSummaryAsync() + { + var model = await HttpClient.GetFromJsonAsync($"{_orgRoute}/dns", Constants.JsonSerializerOptions); + + EnsureNotNull(model); + + return model; + } + + /// + public async Task> GetZonesAsync(int? pageNumber = null, int? perPage = null) + { + var queryString = BuildQueryString(null, null, pageNumber, perPage); + + var model = await HttpClient.GetFromJsonAsync>($"{_orgRoute}/dns/zones?{queryString}"); + + EnsureNotNull(model); + + return model; + } + + /// + public async Task CreateZoneAsync(DnsZoneCreate createModel) + { + if (createModel is null) + { + throw new ArgumentNullException(nameof(createModel)); + } + + var result = await HttpClient.PostAsJsonAsync($"{_orgRoute}/dns/zones", createModel, Constants.JsonSerializerOptions); + + var model = await DeserialiseAsync(result.Content); + + EnsureNotNull(model); + + return model; + } + + /// + public async Task GetZoneAsync(DnsZoneId dnsZoneId) + { + var model = await HttpClient.GetFromJsonAsync($"{_orgRoute}/dns/zones/{dnsZoneId}", Constants.JsonSerializerOptions); + + EnsureNotNull(model); + + return model; + } + + /// + public async Task UpdateZoneAsync(DnsZoneId dnsZoneId, PatchBuilder builder) + { + if (builder is null) + { + throw new ArgumentNullException(nameof(builder)); + } + + using var encoded = CreateJsonContent(builder.Send()); + var result = await HttpClient.PatchAsync($"{_orgRoute}/dns/zones/{dnsZoneId}", encoded); + + result.EnsureSuccessStatusCode(); + + var model = await DeserialiseAsync(result.Content); + + EnsureNotNull(model); + + return model; + } + + /// + public async Task DeleteZoneAsync(DnsZoneId dnsZoneId) + { + var result = await HttpClient.DeleteAsync($"{_orgRoute}/dns/zones/{dnsZoneId}"); + + result.EnsureSuccessStatusCode(); + + var model = await DeserialiseAsync(result.Content); + + EnsureNotNull(model); + + return model; + } + + /// + public async Task> GetRecordsAsync( + DnsZoneId? dnsZoneId = null, + string? hostname = null, + int? pageNumber = null, + int? perPage = null) + { + var queryString = BuildQueryString(dnsZoneId, hostname, pageNumber, perPage); + + var model = await HttpClient.GetFromJsonAsync>($"{_orgRoute}/dns/records?{queryString}"); + + EnsureNotNull(model); + + return model; + } + + /// + public async Task CreateRecordAsync(DnsRecordCreate createModel) + { + if (createModel is null) + { + throw new ArgumentNullException(nameof(createModel)); + } + + var result = await HttpClient.PostAsJsonAsync($"{_orgRoute}/dns/records", createModel, Constants.JsonSerializerOptions); + + var model = await DeserialiseAsync(result.Content); + + EnsureNotNull(model); + + return model; + } + + /// + public async Task DeleteRecordsAsync(params DnsRecordId[] records) + { + using var content = CreateJsonContent(new + { + recordIds = records, + }); + + using var request = new HttpRequestMessage + { + Content = content, + Method = HttpMethod.Delete, + RequestUri = new Uri($"{HttpClient.BaseAddress}{_orgRoute}/dns/records"), + }; + + var result = await HttpClient.SendAsync(request); + + result.EnsureSuccessStatusCode(); + + var model = await DeserialiseAsync(result.Content); + + EnsureNotNull(model); + + return model.DnsRecordsDeleted; + } + + /// + public async Task DeleteRecordsAsync(IEnumerable records) + { + if (records is null) + { + throw new ArgumentNullException(nameof(records)); + } + + return await DeleteRecordsAsync(records.ToArray()); + } + + /// + public async Task GetRecordAsync(DnsRecordId dnsRecordId) + { + var model = await HttpClient.GetFromJsonAsync($"{_orgRoute}/dns/records/{dnsRecordId}", Constants.JsonSerializerOptions); + + EnsureNotNull(model); + + return model; + } + + /// + public async Task UpdateRecordAsync(DnsRecordId dnsRecordId, PatchBuilder builder) + { + if (builder is null) + { + throw new ArgumentNullException(nameof(builder)); + } + + using var encoded = CreateJsonContent(builder.Send()); + var result = await HttpClient.PatchAsync($"{_orgRoute}/dns/records/{dnsRecordId}", encoded); + + result.EnsureSuccessStatusCode(); + + var model = await DeserialiseAsync(result.Content); + + EnsureNotNull(model); + + return model; + } + + /// + public async Task DeleteRecordAsync(DnsRecordId dnsRecordId) + { + var result = await HttpClient.DeleteAsync($"{_orgRoute}/dns/records/{dnsRecordId}"); + + result.EnsureSuccessStatusCode(); + + var model = await DeserialiseAsync(result.Content); + + EnsureNotNull(model); + + return model; + } + + private static string? BuildQueryString(DnsZoneId? dnsZoneId, string? hostname, int? pageNumber, int? perPage) + { + var queryString = HttpUtility.ParseQueryString(string.Empty); + if (dnsZoneId is not null) + { + queryString.Add("zoneId", dnsZoneId.ToString()); + } + + if (hostname is not null) + { + queryString.Add("hostname", hostname); + } + + if (pageNumber is not null) + { + queryString.Add("page", pageNumber.ToString()); + } + + if (perPage is not null) + { + queryString.Add("per_page", perPage.ToString()); + } + + return queryString.ToString(); + } } diff --git a/src/Enclave.Sdk/Clients/EnrolledSystemsClient.cs b/src/Enclave.Sdk/Clients/EnrolledSystemsClient.cs new file mode 100644 index 0000000..0ade948 --- /dev/null +++ b/src/Enclave.Sdk/Clients/EnrolledSystemsClient.cs @@ -0,0 +1,245 @@ +using System.Collections.Generic; +using System.Net.Http.Json; +using System.Web; +using Enclave.Sdk.Api.Clients.Interfaces; +using Enclave.Sdk.Api.Data; +using Enclave.Sdk.Api.Data.EnrolledSystems; +using Enclave.Sdk.Api.Data.EnrolledSystems.Enum; +using Enclave.Sdk.Api.Data.Pagination; +using Enclave.Sdk.Api.Data.PatchModel; + +namespace Enclave.Sdk.Api.Clients; + +/// +internal class EnrolledSystemsClient : ClientBase, IEnrolledSystemsClient +{ + private readonly string _orgRoute; + + /// + /// Consutructor which will be called by when it's created. + /// + /// an instance of httpClient with a baseURL referencing the API. + /// The organisation API route. + public EnrolledSystemsClient(HttpClient httpClient, string orgRoute) + : base(httpClient) + { + _orgRoute = orgRoute; + } + + /// + public async Task> GetSystemsAsync( + int? enrolmentKeyId = null, + string? searchTerm = null, + bool? includeDisabled = null, + SystemQuerySortMode? sortOrder = null, + string? dnsName = null, + int? pageNumber = null, + int? perPage = null) + { + var queryString = BuildQueryString(enrolmentKeyId, searchTerm, includeDisabled, sortOrder, dnsName, pageNumber, perPage); + + var model = await HttpClient.GetFromJsonAsync>($"{_orgRoute}/systems?{queryString}", Constants.JsonSerializerOptions); + + EnsureNotNull(model); + + return model; + } + + /// + public async Task RevokeSystemsAsync(params SystemId[] systemIds) + { + using var content = CreateJsonContent(new + { + systemIds = systemIds, + }); + + using var request = new HttpRequestMessage + { + Content = content, + Method = HttpMethod.Delete, + RequestUri = new Uri($"{HttpClient.BaseAddress}{_orgRoute}/systems"), + }; + + var result = await HttpClient.SendAsync(request); + + result.EnsureSuccessStatusCode(); + + var model = await DeserialiseAsync(result.Content); + + EnsureNotNull(model); + + return model.SystemsRevoked; + } + + /// + public async Task RevokeSystemsAsync(IEnumerable systemIds) + { + return await RevokeSystemsAsync(systemIds.ToArray()); + } + + /// + public async Task GetAsync(SystemId systemId) + { + var model = await HttpClient.GetFromJsonAsync($"{_orgRoute}/systems/{systemId}", Constants.JsonSerializerOptions); + + EnsureNotNull(model); + + return model; + } + + /// + public async Task UpdateAsync(SystemId systemId, PatchBuilder builder) + { + if (builder is null) + { + throw new ArgumentNullException(nameof(builder)); + } + + using var encoded = CreateJsonContent(builder.Send()); + var result = await HttpClient.PatchAsync($"{_orgRoute}/systems/{systemId}", encoded); + + result.EnsureSuccessStatusCode(); + + var model = await DeserialiseAsync(result.Content); + + EnsureNotNull(model); + + return model; + } + + /// + public async Task RevokeAsync(SystemId systemId) + { + var result = await HttpClient.DeleteAsync($"{_orgRoute}/systems/{systemId}"); + + result.EnsureSuccessStatusCode(); + + var model = await DeserialiseAsync(result.Content); + + EnsureNotNull(model); + + return model; + } + + /// + public async Task EnableAsync(SystemId systemId) + { + var result = await HttpClient.PutAsync($"{_orgRoute}/systems/{systemId}/enable", null); + + var model = await DeserialiseAsync(result.Content); + + EnsureNotNull(model); + + return model; + } + + /// + public async Task DisableAsync(SystemId systemId) + { + var result = await HttpClient.PutAsync($"{_orgRoute}/systems/{systemId}/disable", null); + + var model = await DeserialiseAsync(result.Content); + + EnsureNotNull(model); + + return model; + } + + /// + public async Task BulkEnableAsync(params SystemId[] systemIds) + { + var requestModel = new + { + systemIds = systemIds, + }; + + var result = await HttpClient.PutAsJsonAsync($"{_orgRoute}/systems/enable", requestModel, Constants.JsonSerializerOptions); + + result.EnsureSuccessStatusCode(); + + var model = await DeserialiseAsync(result.Content); + + EnsureNotNull(model); + + return model.SystemsUpdated; + } + + /// + public async Task BulkEnableAsync(IEnumerable systemIds) + { + return await BulkEnableAsync(systemIds.ToArray()); + } + + /// + public async Task BulkDisableAsync(params SystemId[] systemIds) + { + var requestModel = new + { + systemIds = systemIds, + }; + + var result = await HttpClient.PutAsJsonAsync($"{_orgRoute}/systems/disable", requestModel, Constants.JsonSerializerOptions); + + result.EnsureSuccessStatusCode(); + + var model = await DeserialiseAsync(result.Content); + + EnsureNotNull(model); + + return model.SystemsUpdated; + } + + /// + public async Task BulkDisableAsync(IEnumerable systemIds) + { + return await BulkDisableAsync(systemIds.ToArray()); + } + + private static string? BuildQueryString( + int? enrolmentKeyId = null, + string? searchTerm = null, + bool? includeDisabled = null, + SystemQuerySortMode? sortOrder = null, + string? dnsName = null, + int? pageNumber = null, + int? perPage = null) + { + var queryString = HttpUtility.ParseQueryString(string.Empty); + if (enrolmentKeyId is not null) + { + queryString.Add("enrolment_key", enrolmentKeyId.ToString()); + } + + if (searchTerm is not null) + { + queryString.Add("search", searchTerm); + } + + if (includeDisabled is not null) + { + queryString.Add("include_disabled", includeDisabled.ToString()); + } + + if (sortOrder is not null) + { + queryString.Add("sort", sortOrder.ToString()); + } + + if (dnsName is not null) + { + queryString.Add("dns", dnsName); + } + + if (pageNumber is not null) + { + queryString.Add("page", pageNumber.ToString()); + } + + if (perPage is not null) + { + queryString.Add("per_page", perPage.ToString()); + } + + return queryString.ToString(); + } +} diff --git a/src/Enclave.Sdk/Clients/EnrolmentKeysClient.cs b/src/Enclave.Sdk/Clients/EnrolmentKeysClient.cs index bc98e38..84a3f8d 100644 --- a/src/Enclave.Sdk/Clients/EnrolmentKeysClient.cs +++ b/src/Enclave.Sdk/Clients/EnrolmentKeysClient.cs @@ -1,14 +1,196 @@ +using System.Net.Http.Json; +using System.Web; using Enclave.Sdk.Api.Clients.Interfaces; +using Enclave.Sdk.Api.Data; +using Enclave.Sdk.Api.Data.EnrolmentKeys; +using Enclave.Sdk.Api.Data.EnrolmentKeys.Enum; +using Enclave.Sdk.Api.Data.Pagination; +using Enclave.Sdk.Api.Data.PatchModel; namespace Enclave.Sdk.Api.Clients; -public class EnrolmentKeysClient : ClientBase, IEnrolmentKeysClient +/// +internal class EnrolmentKeysClient : ClientBase, IEnrolmentKeysClient { - private string _orgRoute; + private readonly string _orgRoute; + /// + /// Constructor which will be called by when it's created. + /// + /// an instance of httpClient with a baseURL referencing the API. + /// The organisation API route. public EnrolmentKeysClient(HttpClient httpClient, string orgRoute) : base(httpClient) { _orgRoute = orgRoute; } -} + + /// + public async Task> GetEnrolmentKeysAsync( + string? searchTerm = null, + bool? includeDisabled = null, + EnrolmentKeySortOrder? sortOrder = null, + int? pageNumber = null, + int? perPage = null) + { + var queryString = BuildQueryString(searchTerm, includeDisabled, sortOrder, pageNumber, perPage); + + var model = await HttpClient.GetFromJsonAsync>($"{_orgRoute}/enrolment-keys?{queryString}"); + + EnsureNotNull(model); + + return model; + } + + /// + public async Task CreateAsync(EnrolmentKeyCreate createModel) + { + if (createModel is null) + { + throw new ArgumentNullException(nameof(createModel)); + } + + var result = await HttpClient.PostAsJsonAsync($"{_orgRoute}/enrolment-keys", createModel, Constants.JsonSerializerOptions); + + var model = await DeserialiseAsync(result.Content); + + EnsureNotNull(model); + + return model; + } + + /// + public async Task GetAsync(EnrolmentKeyId enrolmentKeyId) + { + var model = await HttpClient.GetFromJsonAsync($"{_orgRoute}/enrolment-keys/{enrolmentKeyId}", Constants.JsonSerializerOptions); + + EnsureNotNull(model); + + return model; + } + + /// + public async Task UpdateAsync(EnrolmentKeyId enrolmentKeyId, PatchBuilder builder) + { + if (builder is null) + { + throw new ArgumentNullException(nameof(builder)); + } + + using var encoded = CreateJsonContent(builder.Send()); + var result = await HttpClient.PatchAsync($"{_orgRoute}/enrolment-keys/{enrolmentKeyId}", encoded); + + result.EnsureSuccessStatusCode(); + + var model = await DeserialiseAsync(result.Content); + + EnsureNotNull(model); + + return model; + } + + /// + public async Task EnableAsync(EnrolmentKeyId enrolmentKeyId) + { + var result = await HttpClient.PutAsync($"{_orgRoute}/enrolment-keys/{enrolmentKeyId}/enable", null); + + var model = await DeserialiseAsync(result.Content); + + EnsureNotNull(model); + + return model; + } + + /// + public async Task DisableAsync(EnrolmentKeyId enrolmentKeyId) + { + var result = await HttpClient.PutAsync($"{_orgRoute}/enrolment-keys/{enrolmentKeyId}/disable", null); + + var model = await DeserialiseAsync(result.Content); + + EnsureNotNull(model); + + return model; + } + + /// + public async Task BulkEnableAsync(params EnrolmentKeyId[] enrolmentKeys) + { + var requestModel = new + { + keyIds = enrolmentKeys, + }; + + var result = await HttpClient.PutAsJsonAsync($"{_orgRoute}/enrolment-keys/enable", requestModel, Constants.JsonSerializerOptions); + + result.EnsureSuccessStatusCode(); + + var model = await DeserialiseAsync(result.Content); + + EnsureNotNull(model); + + return model.KeysModified; + } + + /// + public async Task BulkEnableAsync(IEnumerable enrolmentKeys) + { + return await BulkEnableAsync(enrolmentKeys.ToArray()); + } + + /// + public async Task BulkDisableAsync(params EnrolmentKeyId[] enrolmentKeys) + { + var requestModel = new + { + keyIds = enrolmentKeys, + }; + + var result = await HttpClient.PutAsJsonAsync($"{_orgRoute}/enrolment-keys/disable", requestModel, Constants.JsonSerializerOptions); + + result.EnsureSuccessStatusCode(); + + var model = await DeserialiseAsync(result.Content); + + EnsureNotNull(model); + + return model.KeysModified; + } + + /// + public async Task BulkDisableAsync(IEnumerable enrolmentKeys) + { + return await BulkDisableAsync(enrolmentKeys.ToArray()); + } + + private static string? BuildQueryString(string? searchTerm, bool? includeDisabled, EnrolmentKeySortOrder? sortOrder, int? pageNumber, int? perPage) + { + var queryString = HttpUtility.ParseQueryString(string.Empty); + if (searchTerm is not null) + { + queryString.Add("search", searchTerm); + } + + if (includeDisabled is not null) + { + queryString.Add("include_disabled", includeDisabled.ToString()); + } + + if (sortOrder is not null) + { + queryString.Add("sort", sortOrder.ToString()); + } + + if (pageNumber is not null) + { + queryString.Add("page", pageNumber.ToString()); + } + + if (perPage is not null) + { + queryString.Add("per_page", perPage.ToString()); + } + + return queryString.ToString(); + } +} \ No newline at end of file diff --git a/src/Enclave.Sdk/Clients/Interfaces/IAuthorityClient.cs b/src/Enclave.Sdk/Clients/Interfaces/IAuthorityClient.cs deleted file mode 100644 index 49b89d6..0000000 --- a/src/Enclave.Sdk/Clients/Interfaces/IAuthorityClient.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace Enclave.Sdk.Api.Clients.Interfaces; - -public interface IAuthorityClient -{ -} \ No newline at end of file diff --git a/src/Enclave.Sdk/Clients/Interfaces/IDnsClient.cs b/src/Enclave.Sdk/Clients/Interfaces/IDnsClient.cs index c409dd2..ddedbcc 100644 --- a/src/Enclave.Sdk/Clients/Interfaces/IDnsClient.cs +++ b/src/Enclave.Sdk/Clients/Interfaces/IDnsClient.cs @@ -1,5 +1,112 @@ -namespace Enclave.Sdk.Api.Clients.Interfaces; +using Enclave.Sdk.Api.Data; +using Enclave.Sdk.Api.Data.Dns; +using Enclave.Sdk.Api.Data.Pagination; +using Enclave.Sdk.Api.Data.PatchModel; +namespace Enclave.Sdk.Api.Clients.Interfaces; + +/// +/// Provides operations to get, create, and manipulate DNS rules. +/// public interface IDnsClient { + /// + /// Gets a summary of DNS properties. + /// + /// A summary of DNS properties. + Task GetPropertiesSummaryAsync(); + + /// + /// Gets a paginated list of DNS zones. + /// + /// Which page number do you want to return. + /// How many items per page. + /// A paginated response model with links to get the previous, next, first and last pages. + Task> GetZonesAsync(int? pageNumber = null, int? perPage = null); + + /// + /// Creates a DNS Zone using a Model. + /// + /// The model needed to create a DNS Zone. + /// The created DNS Zone as a . + Task CreateZoneAsync(DnsZoneCreate createModel); + + /// + /// Gets the details of a specific DNS Zone. + /// + /// The DNS Zone ID that you want to get. + /// A full DNS Zone object. + Task GetZoneAsync(DnsZoneId dnsZoneId); + + /// + /// Patch request to update a DNS Zone. + /// + /// The DNS Zone ID that you want to update. + /// An instance of used to setup our patch request. + /// A full DNS Zone object. + Task UpdateZoneAsync(DnsZoneId dnsZoneId, PatchBuilder builder); + + /// + /// Delete a DNS Zone and it's associated record. This is irriversable. + /// + /// The DNS Zone ID that you want to update. + /// The deleted DNS Zone object. + Task DeleteZoneAsync(DnsZoneId dnsZoneId); + + /// + /// Gets a paginated list of DNS records. + /// + /// The DNS Zone ID that you want to get. + /// A partial hostname search value. + /// Which page number do you want to return. + /// How many items per page. + /// A paginated response model with links to get the previous, next, first and last pages. + Task> GetRecordsAsync( + DnsZoneId? dnsZoneId = null, + string? hostname = null, + int? pageNumber = null, + int? perPage = null); + + /// + /// Create a DNS Record using a model. + /// + /// The model needed to create a DNS Record. + /// The created DNS Record as . + Task CreateRecordAsync(DnsRecordCreate createModel); + + /// + /// Delete multiple DNS Records. + /// + /// An Array of the Record Ids to delete. + /// The number of records deleted. + Task DeleteRecordsAsync(params DnsRecordId[] records); + + /// + /// Delete multiple DNS Records. + /// + /// An IEnumerable of the Record Ids to delete. + /// The number of records deleted. + Task DeleteRecordsAsync(IEnumerable records); + + /// + /// Get a detailed DNS Record. + /// + /// The id of the DNS Record you want to get. + /// A detailed DNS Record object. + Task GetRecordAsync(DnsRecordId dnsRecordId); + + /// + /// Patch request to update a DNS Record. + /// + /// The DNS Record ID that you want to update. + /// An instance of used to setup our patch request. + /// A full DNS Record object. + Task UpdateRecordAsync(DnsRecordId dnsRecordId, PatchBuilder builder); + + /// + /// Delete a single DNS Record. + /// + /// The DNS Record ID that you want to delete. + /// The deleted DNS Record object. + Task DeleteRecordAsync(DnsRecordId dnsRecordId); } \ No newline at end of file diff --git a/src/Enclave.Sdk/Clients/Interfaces/IEnrolledSystemsClient.cs b/src/Enclave.Sdk/Clients/Interfaces/IEnrolledSystemsClient.cs new file mode 100644 index 0000000..d4547a5 --- /dev/null +++ b/src/Enclave.Sdk/Clients/Interfaces/IEnrolledSystemsClient.cs @@ -0,0 +1,111 @@ +using Enclave.Sdk.Api.Data; +using Enclave.Sdk.Api.Data.EnrolledSystems; +using Enclave.Sdk.Api.Data.EnrolledSystems.Enum; +using Enclave.Sdk.Api.Data.Pagination; +using Enclave.Sdk.Api.Data.PatchModel; + +namespace Enclave.Sdk.Api.Clients.Interfaces; + +/// +/// Provides operations to get, create, and manipulate Enrolled Systems. +/// +public interface IEnrolledSystemsClient +{ + /// + /// Gets a paginated list of Systems which can be searched and iterated upon. + /// + /// The Enrolment Key Id which the systems are associated to. + /// Search for systems with a partial match on description and system ID. + /// Should include disabled Systems. + /// Sort order for the pagination. + /// Searches for systems that will answer to the specified DNS name. + /// Which page number do you want to return. + /// How many per page. + /// A paginated response model with links to get the previous, next, first and last pages. + Task> GetSystemsAsync( + int? enrolmentKeyId = null, + string? searchTerm = null, + bool? includeDisabled = null, + SystemQuerySortMode? sortOrder = null, + string? dnsName = null, + int? pageNumber = null, + int? perPage = null); + + /// + /// Permanetly revoke multiple systems. + /// + /// The System Ids to revoke. + /// The number of systems revoked. + Task RevokeSystemsAsync(params SystemId[] systemIds); + + /// + /// Permanetly revoke multiple systems. + /// + /// The System Ids to revoke. + /// The number of systems revoked. + Task RevokeSystemsAsync(IEnumerable systemIds); + + /// + /// Retrieve a Detailed System model. + /// + /// The SystemId to Get. + /// A Full System Model. + Task GetAsync(SystemId systemId); + + /// + /// Update an Enrolled System. + /// + /// The Id of the Enrolled System to update. + /// An instance of used to setup our patch request. + /// An Enrolled System. + Task UpdateAsync(SystemId systemId, PatchBuilder builder); + + /// + /// Revoke a system permanetly. + /// + /// The id of the Enrolled System to revoke. + /// The revoked Enrolled System. + Task RevokeAsync(SystemId systemId); + + /// + /// Enable an Enrolled System. + /// + /// The Id of the Enrolled System to enable. + /// A detailed Enrolment Key. + Task EnableAsync(SystemId systemId); + + /// + /// Disable an Enrolled System. + /// + /// The Id of the Enrolled System to disable. + /// A detailed Enrolment Key. + Task DisableAsync(SystemId systemId); + + /// + /// Bulk enable mutliple Enrolled System. + /// + /// An array of Enrolled System Ids to enable. + /// The number of keys modified. + Task BulkEnableAsync(params SystemId[] systemIds); + + /// + /// Bulk enable mutliple Enrolled System. + /// + /// An array of Enrolled System Ids to enable. + /// The number of keys modified. + Task BulkEnableAsync(IEnumerable systemIds); + + /// + /// Bulk disable mutliple Enrolled System. + /// + /// An array of Enrolled System Ids to disable. + /// The number of keys modified. + Task BulkDisableAsync(params SystemId[] systemIds); + + /// + /// Bulk disable mutliple Enrolled System. + /// + /// An array of Enrolled System Ids to disable. + /// The number of keys modified. + Task BulkDisableAsync(IEnumerable systemIds); +} \ No newline at end of file diff --git a/src/Enclave.Sdk/Clients/Interfaces/IEnrolmentKeysClient.cs b/src/Enclave.Sdk/Clients/Interfaces/IEnrolmentKeysClient.cs index a11d9cd..43a11ab 100644 --- a/src/Enclave.Sdk/Clients/Interfaces/IEnrolmentKeysClient.cs +++ b/src/Enclave.Sdk/Clients/Interfaces/IEnrolmentKeysClient.cs @@ -1,5 +1,89 @@ -namespace Enclave.Sdk.Api.Clients.Interfaces; +using Enclave.Sdk.Api.Data; +using Enclave.Sdk.Api.Data.EnrolmentKeys; +using Enclave.Sdk.Api.Data.EnrolmentKeys.Enum; +using Enclave.Sdk.Api.Data.Pagination; +using Enclave.Sdk.Api.Data.PatchModel; +namespace Enclave.Sdk.Api.Clients.Interfaces; + +/// +/// Provides operations to get, create, and manipulate Enrolment Keys. +/// public interface IEnrolmentKeysClient { + /// + /// Gets a paginated list of Enrolment Keys which can be searched and interated upon. + /// + /// A partial matching search term. + /// Include the disabled Enrolment Keys in the results. + /// Sort order for the pagination. + /// Which page number do you want to return. + /// How many Enrolment Keys per page. + /// A paginated response model with links to get the previous, next, first and last pages. + Task> GetEnrolmentKeysAsync(string? searchTerm = null, bool? includeDisabled = null, EnrolmentKeySortOrder? sortOrder = null, int? pageNumber = null, int? perPage = null); + + /// + /// Creates an Enrolment Key using a Model. + /// + /// The model needed to create an Enrolment Key. + /// The created Enrolment Key as a . + Task CreateAsync(EnrolmentKeyCreate createModel); + + /// + /// Gets a detailed Enrolment Key model. + /// + /// The Id of the Enrolment Key to get. + /// A detailed Enrolment Key. + Task GetAsync(EnrolmentKeyId enrolmentKeyId); + + /// + /// Patch request to update the EnrolmentKey. + /// Use . + /// + /// The Id of the Enrolment Key to update. + /// An instance of used to setup our patch request. + /// A detailed Enrolment Key. + Task UpdateAsync(EnrolmentKeyId enrolmentKeyId, PatchBuilder builder); + + /// + /// Enable an Enrolment Key. + /// + /// The Id of the Enrolment Key to enable. + /// A detailed Enrolment Key. + Task EnableAsync(EnrolmentKeyId enrolmentKeyId); + + /// + /// Disable an Enrolment Key. + /// + /// The Id of the Enrolment Key to disable. + /// A detailed Enrolment Key. + Task DisableAsync(EnrolmentKeyId enrolmentKeyId); + + /// + /// Bulk enable mutliple Enrolment Keys. + /// + /// An array of Enrolment Key Ids to enable. + /// The number of keys modified. + Task BulkEnableAsync(params EnrolmentKeyId[] enrolmentKeys); + + /// + /// Bulk enable mutliple Enrolment Keys. + /// + /// An IEnumerable of Enrolment Key Ids to enable. + /// The number of keys modified. + Task BulkEnableAsync(IEnumerable enrolmentKeys); + + /// + /// Bulk disable mutliple Enrolment Keys. + /// + /// An array of Enrolment Key Ids to disable. + /// The number of keys modified. + Task BulkDisableAsync(params EnrolmentKeyId[] enrolmentKeys); + + /// + /// Bulk disable mutliple Enrolment Keys. + /// + /// An IEnumerable of Enrolment Key Ids to disable. + /// The number of keys modified. + Task BulkDisableAsync(IEnumerable enrolmentKeys); } \ No newline at end of file diff --git a/src/Enclave.Sdk/Clients/Interfaces/ILogsClient.cs b/src/Enclave.Sdk/Clients/Interfaces/ILogsClient.cs index 11595fd..e28c9a4 100644 --- a/src/Enclave.Sdk/Clients/Interfaces/ILogsClient.cs +++ b/src/Enclave.Sdk/Clients/Interfaces/ILogsClient.cs @@ -1,5 +1,18 @@ -namespace Enclave.Sdk.Api.Clients.Interfaces; +using Enclave.Sdk.Api.Data.Logging; +using Enclave.Sdk.Api.Data.Pagination; +namespace Enclave.Sdk.Api.Clients.Interfaces; + +/// +/// Access and search for logs. +/// public interface ILogsClient { + /// + /// Gets a paginated list of logs which can be searched and interated upon. + /// + /// Which page number do you want to return. + /// How many per page. + /// A paginated response model with links to get the previous, next, first and last pages. + Task> GetLogsAsync(int? pageNumber = null, int? perPage = null); } \ No newline at end of file diff --git a/src/Enclave.Sdk/Clients/Interfaces/IOrganisationClient.cs b/src/Enclave.Sdk/Clients/Interfaces/IOrganisationClient.cs index 519151d..5cff80b 100644 --- a/src/Enclave.Sdk/Clients/Interfaces/IOrganisationClient.cs +++ b/src/Enclave.Sdk/Clients/Interfaces/IOrganisationClient.cs @@ -16,11 +16,6 @@ public interface IOrganisationClient /// AccountOrganisation Organisation { get; } - /// - /// An instance of associated with the current organisaiton. - /// - IAuthorityClient Authority { get; } - /// /// An instance of associated with the current organisaiton. /// @@ -42,9 +37,9 @@ public interface IOrganisationClient IPoliciesClient Policies { get; } /// - /// An instance of associated with the current organisaiton. + /// An instance of associated with the current organisaiton. /// - ISystemsClient Systems { get; } + IEnrolledSystemsClient EnrolledSystems { get; } /// /// An instance of associated with the current organisaiton. @@ -94,9 +89,9 @@ public interface IOrganisationClient /// /// Patch request to update the organisation. - /// Use Builder.cs to help you generate the dictionary. + /// Use . /// - /// An instance of used to setup our patch request. + /// An instance of used to setup our patch request. /// The updated organisation. /// Throws if builder is null. Task UpdateAsync(PatchBuilder builder); diff --git a/src/Enclave.Sdk/Clients/Interfaces/IPoliciesClient.cs b/src/Enclave.Sdk/Clients/Interfaces/IPoliciesClient.cs index b3283e4..5f2d690 100644 --- a/src/Enclave.Sdk/Clients/Interfaces/IPoliciesClient.cs +++ b/src/Enclave.Sdk/Clients/Interfaces/IPoliciesClient.cs @@ -1,5 +1,114 @@ -namespace Enclave.Sdk.Api.Clients.Interfaces; +using Enclave.Sdk.Api.Data; +using Enclave.Sdk.Api.Data.Pagination; +using Enclave.Sdk.Api.Data.PatchModel; +using Enclave.Sdk.Api.Data.Policies; +using Enclave.Sdk.Api.Data.Policies.Enum; +namespace Enclave.Sdk.Api.Clients.Interfaces; + +/// +/// Provides operations to get, create, and manipulate DNS rules. +/// public interface IPoliciesClient { + /// + /// Gets a paginated list of Policies which can be searched and iterated upon. + /// + /// A partial matching search term. + /// Include the disabled Policies in the results. + /// Sort order for the pagination. + /// Which page number do you want to return. + /// How many per page. + /// A paginated response model with links to get the previous, next, first and last pages. + Task> GetPoliciesAsync( + string? searchTerm = null, + bool? includeDisabled = null, + PolicySortOrder? sortOrder = null, + int? pageNumber = null, + int? perPage = null); + + /// + /// Creates a Policy using a Model. + /// + /// The model needed to create a Policy. + /// The created . + Task CreateAsync(PolicyCreate createModel); + + /// + /// Delete multiple Policies. + /// + /// The ids of the Policies you want to delete. + /// The number of deleted Policies. + Task DeletePoliciesAsync(params PolicyId[] policyIds); + + /// + /// Delete multiple Policies. + /// + /// The ids of the Policies you want to delete. + /// The number of deleted Policies. + Task DeletePoliciesAsync(IEnumerable policyIds); + + /// + /// Get a specific Policy. + /// + /// The Id of the Policy to get. + /// The . + Task GetAsync(PolicyId policyId); + + /// + /// Patch request to update a Policy. + /// + /// The Id of the Policy to update. + /// An instance of used to setup our patch request. + /// The updated . + Task UpdateAsync(PolicyId policyId, PatchBuilder builder); + + /// + /// Delete a Policy. + /// + /// The Id of the Policy to delete. + /// The deleted . + Task DeleteAsync(PolicyId policyId); + + /// + /// Enable a Policy. + /// + /// The Id of the Policy to enable. + /// The enabled . + Task EnableAsync(PolicyId policyId); + + /// + /// Disable a Policy. + /// + /// The Id of the Policy to disable. + /// The disabled . + Task DisableAsync(PolicyId policyId); + + /// + /// Enable multiple Policies. + /// + /// The ids of the Policies you want to enable. + /// The number of enabled Policies. + Task EnablePoliciesAsync(params PolicyId[] policyIds); + + /// + /// Enable multiple Policies. + /// + /// The ids of the Policies you want to enable. + /// The number of enabled Policies. + Task EnablePoliciesAsync(IEnumerable policyIds); + + /// + /// Disable multiple Policies. + /// + /// The ids of the Policies you want to disable. + /// The number of disabled Policies. + Task DisablePoliciesAsync(params PolicyId[] policyIds); + + /// + /// Disable multiple Policies. + /// + /// The ids of the Policies you want to disable. + /// The number of disabled Policies. + Task DisablePoliciesAsync(IEnumerable policyIds); } \ No newline at end of file diff --git a/src/Enclave.Sdk/Clients/Interfaces/ISystemsClient.cs b/src/Enclave.Sdk/Clients/Interfaces/ISystemsClient.cs deleted file mode 100644 index b0cf022..0000000 --- a/src/Enclave.Sdk/Clients/Interfaces/ISystemsClient.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace Enclave.Sdk.Api.Clients.Interfaces; - -public interface ISystemsClient -{ -} \ No newline at end of file diff --git a/src/Enclave.Sdk/Clients/Interfaces/ITagsClient.cs b/src/Enclave.Sdk/Clients/Interfaces/ITagsClient.cs index 39647d1..d0e7039 100644 --- a/src/Enclave.Sdk/Clients/Interfaces/ITagsClient.cs +++ b/src/Enclave.Sdk/Clients/Interfaces/ITagsClient.cs @@ -14,7 +14,7 @@ public interface ITagsClient /// A partial matching search term. /// Sort order for the pagination. /// Which page number do you want to return. - /// How many tags per page. + /// How many per page. /// A paginated response model with links to get the previous, next, first and last pages. Task> GetAsync(string? searchTerm = null, TagQuerySortOrder? sortOrder = null, int? pageNumber = null, int? perPage = null); } \ No newline at end of file diff --git a/src/Enclave.Sdk/Clients/Interfaces/IUnapprovedSystemsClient.cs b/src/Enclave.Sdk/Clients/Interfaces/IUnapprovedSystemsClient.cs index aa34fdc..d0535e6 100644 --- a/src/Enclave.Sdk/Clients/Interfaces/IUnapprovedSystemsClient.cs +++ b/src/Enclave.Sdk/Clients/Interfaces/IUnapprovedSystemsClient.cs @@ -1,5 +1,85 @@ -namespace Enclave.Sdk.Api.Clients.Interfaces; +using Enclave.Sdk.Api.Data; +using Enclave.Sdk.Api.Data.Pagination; +using Enclave.Sdk.Api.Data.PatchModel; +using Enclave.Sdk.Api.Data.UnaprrovedSystems; +using Enclave.Sdk.Api.Data.UnaprrovedSystems.Enum; +namespace Enclave.Sdk.Api.Clients.Interfaces; + +/// +/// Provides operations to get, create, and manipulate Unapproved Systems. +/// public interface IUnapprovedSystemsClient { + /// + /// Gets a paginated list of Unapproved Systems which can be searched and interated upon. + /// + /// Filter by systems using the specified Enrolment Key. + /// A partial matching search term. + /// Sort order for the pagination. + /// Which page number do you want to return. + /// How many per page. + /// A paginated response model with links to get the previous, next, first and last pages. + Task> GetSystemsAsync( + int? enrolmentKeyId = null, + string? searchTerm = null, + UnapprovedSystemQuerySortMode? sortOrder = null, + int? pageNumber = null, + int? perPage = null); + + /// + /// Permanetly decline multiple Unapproved Systems. + /// + /// System Ids to decline. + /// The number of systesm declined. + Task DeclineSystems(params SystemId[] systemIds); + + /// + /// Permanetly decline multiple Unapproved Systems. + /// + /// System Ids to decline. + /// The number of systesm declined. + Task DeclineSystems(IEnumerable systemIds); + + /// + /// Get the details of an Unapproved System. + /// + /// The system Id you want to get. + /// A Detailed Unapproved System Model. + Task GetAsync(SystemId systemId); + + /// + /// Patch request to update an Unapproved System. + /// + /// The system Id you want to update. + /// An instance of used to setup our patch request. + /// An updated Detailed Unapproved System model. + Task UpdateAsync(SystemId systemId, PatchBuilder builder); + + /// + /// Decline an Unapproved System. + /// + /// The system Id you want to decline. + /// The declined System. + Task DeclineAsync(SystemId systemId); + + /// + /// Approve a System. + /// + /// The system Id you want to approve. + Task ApproveAsync(SystemId systemId); + + /// + /// Approve multiple Unapproved Systems. + /// + /// System Ids to approve. + /// The number of systesm approved. + Task ApproveSystemsAsync(params SystemId[] systemIds); + + /// + /// Approve multiple Unapproved Systems. + /// + /// System Ids to approve. + /// The number of systesm approved. + Task ApproveSystemsAsync(IEnumerable systemIds); } \ No newline at end of file diff --git a/src/Enclave.Sdk/Clients/LogsClient.cs b/src/Enclave.Sdk/Clients/LogsClient.cs index 53e7df1..6ea546f 100644 --- a/src/Enclave.Sdk/Clients/LogsClient.cs +++ b/src/Enclave.Sdk/Clients/LogsClient.cs @@ -1,14 +1,52 @@ +using System.Net.Http.Json; +using System.Web; using Enclave.Sdk.Api.Clients.Interfaces; +using Enclave.Sdk.Api.Data.Logging; +using Enclave.Sdk.Api.Data.Pagination; namespace Enclave.Sdk.Api.Clients; -public class LogsClient : ClientBase, ILogsClient +/// +internal class LogsClient : ClientBase, ILogsClient { - private string _orgRoute; + private readonly string _orgRoute; + /// + /// Constructor which will be called by when it's created. + /// + /// an instance of httpClient with a baseURL referencing the API. + /// the orgRoute which specifies the orgId. public LogsClient(HttpClient httpClient, string orgRoute) : base(httpClient) { _orgRoute = orgRoute; } + + /// + public async Task> GetLogsAsync(int? pageNumber = null, int? perPage = null) + { + var queryString = BuildQueryString(pageNumber, perPage); + + var model = await HttpClient.GetFromJsonAsync>($"{_orgRoute}/logs?{queryString}"); + + EnsureNotNull(model); + + return model; + } + + private static string? BuildQueryString(int? pageNumber, int? perPage) + { + var queryString = HttpUtility.ParseQueryString(string.Empty); + if (pageNumber is not null) + { + queryString.Add("page", pageNumber.ToString()); + } + + if (perPage is not null) + { + queryString.Add("per_page", perPage.ToString()); + } + + return queryString.ToString(); + } } diff --git a/src/Enclave.Sdk/Clients/OrganisationClient.cs b/src/Enclave.Sdk/Clients/OrganisationClient.cs index d876310..2a10d30 100644 --- a/src/Enclave.Sdk/Clients/OrganisationClient.cs +++ b/src/Enclave.Sdk/Clients/OrganisationClient.cs @@ -8,9 +8,9 @@ namespace Enclave.Sdk.Api.Clients; /// -public class OrganisationClient : ClientBase, IOrganisationClient +internal class OrganisationClient : ClientBase, IOrganisationClient { - private string _orgRoute; + private readonly string _orgRoute; /// /// This constructor is called by when setting up the . @@ -23,34 +23,39 @@ public OrganisationClient(HttpClient httpClient, AccountOrganisation currentOrga { Organisation = currentOrganisation; _orgRoute = $"org/{Organisation.OrgId}"; + + Dns = new DnsClient(httpClient, _orgRoute); + EnrolmentKeys = new EnrolmentKeysClient(httpClient, _orgRoute); + Logs = new LogsClient(httpClient, _orgRoute); + Policies = new PoliciesClient(httpClient, _orgRoute); + EnrolledSystems = new EnrolledSystemsClient(httpClient, _orgRoute); + Tags = new TagsClient(httpClient, _orgRoute); + UnapprovedSystems = new UnapprovedSystemsClient(httpClient, _orgRoute); } /// public AccountOrganisation Organisation { get; } /// - public IAuthorityClient Authority => throw new NotImplementedException(); - - /// - public IDnsClient Dns => throw new NotImplementedException(); + public IDnsClient Dns { get; } /// - public IEnrolmentKeysClient EnrolmentKeys => throw new NotImplementedException(); + public IEnrolmentKeysClient EnrolmentKeys { get; } /// - public ILogsClient Logs => throw new NotImplementedException(); + public ILogsClient Logs { get; } /// - public IPoliciesClient Policies => throw new NotImplementedException(); + public IPoliciesClient Policies { get; } /// - public ISystemsClient Systems => throw new NotImplementedException(); + public IEnrolledSystemsClient EnrolledSystems { get; } /// - public ITagsClient Tags => throw new NotImplementedException(); + public ITagsClient Tags { get; } /// - public IUnapprovedSystemsClient UnapprovedSystems => throw new NotImplementedException(); + public IUnapprovedSystemsClient UnapprovedSystems { get; } /// public async Task GetAsync() @@ -68,10 +73,13 @@ public async Task UpdateAsync(PatchBuilder buil if (builder is null) { throw new ArgumentNullException(nameof(builder)); - } + } + using var encoded = CreateJsonContent(builder.Send()); var result = await HttpClient.PatchAsync(_orgRoute, encoded); + result.EnsureSuccessStatusCode(); + var model = await DeserialiseAsync(result.Content); EnsureNotNull(model); diff --git a/src/Enclave.Sdk/Clients/PoliciesClient.cs b/src/Enclave.Sdk/Clients/PoliciesClient.cs index 0f6aafd..843a37c 100644 --- a/src/Enclave.Sdk/Clients/PoliciesClient.cs +++ b/src/Enclave.Sdk/Clients/PoliciesClient.cs @@ -1,14 +1,243 @@ -using Enclave.Sdk.Api.Clients.Interfaces; +using System.Net.Http.Json; +using System.Web; +using Enclave.Sdk.Api.Clients.Interfaces; +using Enclave.Sdk.Api.Data; +using Enclave.Sdk.Api.Data.Pagination; +using Enclave.Sdk.Api.Data.PatchModel; +using Enclave.Sdk.Api.Data.Policies; +using Enclave.Sdk.Api.Data.Policies.Enum; namespace Enclave.Sdk.Api.Clients; -public class PoliciesClient : ClientBase, IPoliciesClient +/// +internal class PoliciesClient : ClientBase, IPoliciesClient { - private string _orgRoute; + private readonly string _orgRoute; + /// + /// Constructor which will be called by when it's created. + /// It also calls the constructor. + /// + /// an instance of httpClient with a baseURL referencing the API. + /// The organisation API route. public PoliciesClient(HttpClient httpClient, string orgRoute) : base(httpClient) { _orgRoute = orgRoute; } -} + + /// + public async Task> GetPoliciesAsync( + string? searchTerm = null, + bool? includeDisabled = null, + PolicySortOrder? sortOrder = null, + int? pageNumber = null, + int? perPage = null) + { + var queryString = BuildQueryString(searchTerm, includeDisabled, sortOrder, pageNumber, perPage); + + var model = await HttpClient.GetFromJsonAsync>($"{_orgRoute}/policies?{queryString}"); + + EnsureNotNull(model); + + return model; + } + + /// + public async Task CreateAsync(PolicyCreate createModel) + { + if (createModel is null) + { + throw new ArgumentNullException(nameof(createModel)); + } + + var result = await HttpClient.PostAsJsonAsync($"{_orgRoute}/policies", createModel, Constants.JsonSerializerOptions); + + var model = await DeserialiseAsync(result.Content); + + EnsureNotNull(model); + + return model; + } + + /// + public async Task DeletePoliciesAsync(params PolicyId[] policyIds) + { + using var content = CreateJsonContent(new + { + policyIds = policyIds, + }); + + using var request = new HttpRequestMessage + { + Content = content, + Method = HttpMethod.Delete, + RequestUri = new Uri($"{HttpClient.BaseAddress}{_orgRoute}/policies"), + }; + + var result = await HttpClient.SendAsync(request); + + result.EnsureSuccessStatusCode(); + + var model = await DeserialiseAsync(result.Content); + + EnsureNotNull(model); + + return model.PoliciesDeleted; + } + + /// + public async Task DeletePoliciesAsync(IEnumerable policyIds) + { + return await DeletePoliciesAsync(policyIds.ToArray()); + } + + /// + public async Task GetAsync(PolicyId policyId) + { + var model = await HttpClient.GetFromJsonAsync($"{_orgRoute}/policies/{policyId}", Constants.JsonSerializerOptions); + + EnsureNotNull(model); + + return model; + } + + /// + public async Task UpdateAsync(PolicyId policyId, PatchBuilder builder) + { + if (builder is null) + { + throw new ArgumentNullException(nameof(builder)); + } + + using var encoded = CreateJsonContent(builder.Send()); + var result = await HttpClient.PatchAsync($"{_orgRoute}/policies/{policyId}", encoded); + + result.EnsureSuccessStatusCode(); + + var model = await DeserialiseAsync(result.Content); + + EnsureNotNull(model); + + return model; + } + + /// + public async Task DeleteAsync(PolicyId policyId) + { + var result = await HttpClient.DeleteAsync($"{_orgRoute}/policies/{policyId}"); + + result.EnsureSuccessStatusCode(); + + var model = await DeserialiseAsync(result.Content); + + EnsureNotNull(model); + + return model; + } + + /// + public async Task EnableAsync(PolicyId policyId) + { + var result = await HttpClient.PutAsync($"{_orgRoute}/policies/{policyId}/enable", null); + + var model = await DeserialiseAsync(result.Content); + + EnsureNotNull(model); + + return model; + } + + /// + public async Task DisableAsync(PolicyId policyId) + { + var result = await HttpClient.PutAsync($"{_orgRoute}/policies/{policyId}/disable", null); + + var model = await DeserialiseAsync(result.Content); + + EnsureNotNull(model); + + return model; + } + + /// + public async Task EnablePoliciesAsync(params PolicyId[] policyIds) + { + var requestModel = new + { + policyIds = policyIds, + }; + + var result = await HttpClient.PutAsJsonAsync($"{_orgRoute}/policies/enable", requestModel, Constants.JsonSerializerOptions); + + result.EnsureSuccessStatusCode(); + + var model = await DeserialiseAsync(result.Content); + + EnsureNotNull(model); + + return model.PoliciesUpdated; + } + + /// + public async Task EnablePoliciesAsync(IEnumerable policyIds) + { + return await EnablePoliciesAsync(policyIds.ToArray()); + } + + /// + public async Task DisablePoliciesAsync(params PolicyId[] policyIds) + { + var requestModel = new + { + policyIds = policyIds, + }; + + var result = await HttpClient.PutAsJsonAsync($"{_orgRoute}/policies/disable", requestModel, Constants.JsonSerializerOptions); + + result.EnsureSuccessStatusCode(); + + var model = await DeserialiseAsync(result.Content); + + EnsureNotNull(model); + + return model.PoliciesUpdated; + } + + /// + public async Task DisablePoliciesAsync(IEnumerable policyIds) + { + return await DisablePoliciesAsync(policyIds.ToArray()); + } + + private static string? BuildQueryString(string? searchTerm, bool? includeDisabled, PolicySortOrder? sortOrder, int? pageNumber, int? perPage) + { + var queryString = HttpUtility.ParseQueryString(string.Empty); + if (searchTerm is not null) + { + queryString.Add("search", searchTerm); + } + + if (includeDisabled is not null) + { + queryString.Add("include_disabled", includeDisabled.ToString()); + } + + if (sortOrder is not null) + { + queryString.Add("sort", sortOrder.ToString()); + } + + if (pageNumber is not null) + { + queryString.Add("page", pageNumber.ToString()); + } + + if (perPage is not null) + { + queryString.Add("per_page", perPage.ToString()); + } + + return queryString.ToString(); + } +} \ No newline at end of file diff --git a/src/Enclave.Sdk/Clients/SystemsClient.cs b/src/Enclave.Sdk/Clients/SystemsClient.cs deleted file mode 100644 index fb4bc02..0000000 --- a/src/Enclave.Sdk/Clients/SystemsClient.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Enclave.Sdk.Api.Clients.Interfaces; - -namespace Enclave.Sdk.Api.Clients; - -public class SystemsClient : ClientBase, ISystemsClient -{ - private string _orgRoute; - - public SystemsClient(HttpClient httpClient, string orgRoute) - : base(httpClient) - { - _orgRoute = orgRoute; - } -} diff --git a/src/Enclave.Sdk/Clients/TagsClient.cs b/src/Enclave.Sdk/Clients/TagsClient.cs index 43eea63..835a9ff 100644 --- a/src/Enclave.Sdk/Clients/TagsClient.cs +++ b/src/Enclave.Sdk/Clients/TagsClient.cs @@ -1,18 +1,18 @@ +using System.Net.Http.Json; +using System.Web; using Enclave.Sdk.Api.Clients.Interfaces; using Enclave.Sdk.Api.Data.Pagination; using Enclave.Sdk.Api.Data.Tags; -using System.Net.Http.Json; -using System.Web; namespace Enclave.Sdk.Api.Clients; /// -public class TagsClient : ClientBase, ITagsClient +internal class TagsClient : ClientBase, ITagsClient { - private string _orgRoute; + private readonly string _orgRoute; /// - /// Consutructor which will be called by when it's created. + /// Constructor which will be called by when it's created. /// /// an instance of httpClient with a baseURL referencing the API. /// the orgRoute which specifies the orgId. diff --git a/src/Enclave.Sdk/Clients/UnapprovedSystemsClient.cs b/src/Enclave.Sdk/Clients/UnapprovedSystemsClient.cs index 5eebb5f..cd549e4 100644 --- a/src/Enclave.Sdk/Clients/UnapprovedSystemsClient.cs +++ b/src/Enclave.Sdk/Clients/UnapprovedSystemsClient.cs @@ -1,14 +1,190 @@ +using System.Net.Http.Json; +using System.Web; using Enclave.Sdk.Api.Clients.Interfaces; +using Enclave.Sdk.Api.Data; +using Enclave.Sdk.Api.Data.Pagination; +using Enclave.Sdk.Api.Data.PatchModel; +using Enclave.Sdk.Api.Data.UnaprrovedSystems; +using Enclave.Sdk.Api.Data.UnaprrovedSystems.Enum; namespace Enclave.Sdk.Api.Clients; -public class UnapprovedSystemsClient : ClientBase, IUnapprovedSystemsClient +/// +internal class UnapprovedSystemsClient : ClientBase, IUnapprovedSystemsClient { - private string _orgRoute; + private readonly string _orgRoute; + /// + /// This constructor is called by when setting up the . + /// It also calls the constructor. + /// + /// an instance of httpClient with a baseURL referencing the API. + /// The organisation API route. public UnapprovedSystemsClient(HttpClient httpClient, string orgRoute) : base(httpClient) { _orgRoute = orgRoute; } + + /// + public async Task> GetSystemsAsync( + int? enrolmentKeyId = null, + string? searchTerm = null, + UnapprovedSystemQuerySortMode? sortOrder = null, + int? pageNumber = null, + int? perPage = null) + { + var queryString = BuildQueryString(enrolmentKeyId, searchTerm, sortOrder, pageNumber, perPage); + + var model = await HttpClient.GetFromJsonAsync>($"{_orgRoute}/unapproved-systems?{queryString}"); + + EnsureNotNull(model); + + return model; + } + + /// + public async Task DeclineSystems(params SystemId[] systemIds) + { + using var content = CreateJsonContent(new + { + systemIds = systemIds, + }); + + using var request = new HttpRequestMessage + { + Content = content, + Method = HttpMethod.Delete, + RequestUri = new Uri($"{HttpClient.BaseAddress}{_orgRoute}/unapproved-systems"), + }; + + var result = await HttpClient.SendAsync(request); + + result.EnsureSuccessStatusCode(); + + var model = await DeserialiseAsync(result.Content); + + EnsureNotNull(model); + + return model.SystemsDeclined; + } + + /// + public async Task DeclineSystems(IEnumerable systemIds) + { + return await DeclineSystems(systemIds.ToArray()); + } + + /// + public async Task GetAsync(SystemId systemId) + { + var model = await HttpClient.GetFromJsonAsync($"{_orgRoute}/unapproved-systems/{systemId}", Constants.JsonSerializerOptions); + + EnsureNotNull(model); + + return model; + } + + /// + public async Task UpdateAsync(SystemId systemId, PatchBuilder builder) + { + if (builder is null) + { + throw new ArgumentNullException(nameof(builder)); + } + + using var encoded = CreateJsonContent(builder.Send()); + var result = await HttpClient.PatchAsync($"{_orgRoute}/unapproved-systems/{systemId}", encoded); + + result.EnsureSuccessStatusCode(); + + var model = await DeserialiseAsync(result.Content); + + EnsureNotNull(model); + + return model; + } + + /// + public async Task DeclineAsync(SystemId systemId) + { + var result = await HttpClient.DeleteAsync($"{_orgRoute}/unapproved-systems/{systemId}"); + + result.EnsureSuccessStatusCode(); + + var model = await DeserialiseAsync(result.Content); + + EnsureNotNull(model); + + return model; + } + + /// + public async Task ApproveAsync(SystemId systemId) + { + var result = await HttpClient.PutAsync($"{_orgRoute}/unapproved-systems/{systemId}/approve", null); + + result.EnsureSuccessStatusCode(); + } + + /// + public async Task ApproveSystemsAsync(params SystemId[] systemIds) + { + var requestModel = new + { + systemIds = systemIds, + }; + + var result = await HttpClient.PutAsJsonAsync($"{_orgRoute}/unapproved-systems/approve", requestModel, Constants.JsonSerializerOptions); + + result.EnsureSuccessStatusCode(); + + var model = await DeserialiseAsync(result.Content); + + EnsureNotNull(model); + + return model.SystemsApproved; + } + + /// + public async Task ApproveSystemsAsync(IEnumerable systemIds) + { + return await ApproveSystemsAsync(systemIds.ToArray()); + } + + private static string? BuildQueryString( + int? enrolmentKeyId, + string? searchTerm, + UnapprovedSystemQuerySortMode? sortOrder, + int? pageNumber, + int? perPage) + { + var queryString = HttpUtility.ParseQueryString(string.Empty); + if (enrolmentKeyId is not null) + { + queryString.Add("enrolment_key", enrolmentKeyId.ToString()); + } + + if (searchTerm is not null) + { + queryString.Add("search", searchTerm); + } + + if (sortOrder is not null) + { + queryString.Add("sort", sortOrder.ToString()); + } + + if (pageNumber is not null) + { + queryString.Add("page", pageNumber.ToString()); + } + + if (perPage is not null) + { + queryString.Add("per_page", perPage.ToString()); + } + + return queryString.ToString(); + } } \ No newline at end of file diff --git a/src/Enclave.Sdk/Constants.cs b/src/Enclave.Sdk/Constants.cs index b516ecd..5373998 100644 --- a/src/Enclave.Sdk/Constants.cs +++ b/src/Enclave.Sdk/Constants.cs @@ -1,18 +1,23 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Text.Json; -using System.Threading.Tasks; +using System.Text.Json; +using System.Text.Json.Serialization; namespace Enclave.Sdk.Api; internal static class Constants { - public static JsonSerializerOptions JsonSerializerOptions { get; } = new JsonSerializerOptions + public static JsonSerializerOptions JsonSerializerOptions { - PropertyNamingPolicy = JsonNamingPolicy.CamelCase, - }; + get + { + var options = new JsonSerializerOptions + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + }; + + options.Converters.Add(new JsonStringEnumConverter()); + return options; + } + } public const string ApiUrl = "https://api.enclave.io"; } \ No newline at end of file diff --git a/src/Enclave.Sdk/Data/Account/AccountOrganisation.cs b/src/Enclave.Sdk/Data/Account/AccountOrganisation.cs index a0dd9bc..c27e574 100644 --- a/src/Enclave.Sdk/Data/Account/AccountOrganisation.cs +++ b/src/Enclave.Sdk/Data/Account/AccountOrganisation.cs @@ -1,4 +1,6 @@ -namespace Enclave.Sdk.Api.Data.Account; +using Enclave.Sdk.Api.Data.Organisations; + +namespace Enclave.Sdk.Api.Data.Account; /// /// Contains the role an account has within an organisation. @@ -29,5 +31,5 @@ public class AccountOrganisationTopLevel /// /// The set of organisations. /// - public List Orgs { get; init; } = default!; + public IReadOnlyList Orgs { get; init; } = Array.Empty(); } \ No newline at end of file diff --git a/src/Enclave.Sdk/Data/Account/UserOrganisationRole.cs b/src/Enclave.Sdk/Data/Account/UserOrganisationRole.cs index d5af46d..72fcb83 100644 --- a/src/Enclave.Sdk/Data/Account/UserOrganisationRole.cs +++ b/src/Enclave.Sdk/Data/Account/UserOrganisationRole.cs @@ -1,7 +1,17 @@ namespace Enclave.Sdk.Api.Data.Account; +/// +/// The User roles. +/// public enum UserOrganisationRole { + /// + /// The owner of the organisation. + /// Owner, + + /// + /// An Admin of the organisaiton. + /// Admin, } \ No newline at end of file diff --git a/src/Enclave.Sdk/Data/Dns/BulkDnsRecordDeleteResult.cs b/src/Enclave.Sdk/Data/Dns/BulkDnsRecordDeleteResult.cs new file mode 100644 index 0000000..6d494b2 --- /dev/null +++ b/src/Enclave.Sdk/Data/Dns/BulkDnsRecordDeleteResult.cs @@ -0,0 +1,12 @@ +namespace Enclave.Sdk.Api.Data.Dns; + +/// +/// Defines the result of a DNS Record bulk delete. +/// +public class BulkDnsRecordDeleteResult +{ + /// + /// Specifies the number of DNS Records that were successfully deleted. + /// + public int DnsRecordsDeleted { get; init; } +} diff --git a/src/Enclave.Sdk/Data/Dns/DnsRecord.cs b/src/Enclave.Sdk/Data/Dns/DnsRecord.cs new file mode 100644 index 0000000..a07e456 --- /dev/null +++ b/src/Enclave.Sdk/Data/Dns/DnsRecord.cs @@ -0,0 +1,56 @@ +using Enclave.Sdk.Api.Data.EnrolledSystems; +using Enclave.Sdk.Api.Data.SystemManagement; +using Enclave.Sdk.Api.Data.Tags; + +namespace Enclave.Sdk.Api.Data.Dns; + +/// +/// Detailed model representing a DNS record. +/// +public class DnsRecord +{ + /// + /// The ID of the record. + /// + public DnsRecordId Id { get; init; } + + /// + /// The name of the record (without the zone). + /// + public string Name { get; init; } = default!; + + /// + /// The type of DNS record. + /// + public string Type { get; init; } = default!; + + /// + /// The ID of the DNS Zone to which this record belongs. + /// + public DnsZoneId ZoneId { get; init; } + + /// + /// The name of the DNS Zone to which this record belongs. + /// + public string ZoneName { get; init; } = default!; + + /// + /// The fully-qualified domain name of the record, including the zone name. + /// + public string Fqdn { get; init; } = default!; + + /// + /// The set of tags to which this DNS name is applied. + /// + public IReadOnlyList Tags { get; init; } = Array.Empty(); + + /// + /// The set of systems to which this DNS name is applied. + /// + public IReadOnlyList? Systems { get; init; } + + /// + /// Any provided notes for this record. + /// + public string? Notes { get; init; } +} diff --git a/src/Enclave.Sdk/Data/Dns/DnsRecordCreate.cs b/src/Enclave.Sdk/Data/Dns/DnsRecordCreate.cs new file mode 100644 index 0000000..c209f70 --- /dev/null +++ b/src/Enclave.Sdk/Data/Dns/DnsRecordCreate.cs @@ -0,0 +1,40 @@ +using System.ComponentModel; + +namespace Enclave.Sdk.Api.Data.Dns; + +/// +/// Record for creating a DNS model. +/// +public class DnsRecordCreate +{ + /// + /// The name of the record to create. Use '@' for an apex record in the zone. + /// + public string Name { get; set; } = default!; + + /// + /// The ID of the DNS zone this record should be added to. + /// + public DnsZoneId ZoneId { get; set; } + + /// + /// The type of the DNS record (currently only "ENCLAVE" is supported). + /// + [DefaultValue("ENCLAVE")] + public string? Type { get; set; } + + /// + /// The set of tags this name should apply to. All systems with one of these tags will get the name. + /// + public IReadOnlyList? Tags { get; set; } + + /// + /// The set of systems to directly apply this name to. + /// + public IReadOnlyList? Systems { get; set; } + + /// + /// Any notes or additional info for this record. + /// + public string? Notes { get; set; } +} diff --git a/src/Enclave.Sdk/Data/Dns/DnsRecordId.cs b/src/Enclave.Sdk/Data/Dns/DnsRecordId.cs new file mode 100644 index 0000000..12423c4 --- /dev/null +++ b/src/Enclave.Sdk/Data/Dns/DnsRecordId.cs @@ -0,0 +1,11 @@ +using TypedIds; + +namespace Enclave.Sdk.Api.Data.Dns; + +/// +/// An int backed Dns Record Id. +/// +[TypedId(IdBackingType.Int)] +public readonly partial struct DnsRecordId +{ +} diff --git a/src/Enclave.Sdk/Data/Dns/DnsRecordSummary.cs b/src/Enclave.Sdk/Data/Dns/DnsRecordSummary.cs new file mode 100644 index 0000000..8a24104 --- /dev/null +++ b/src/Enclave.Sdk/Data/Dns/DnsRecordSummary.cs @@ -0,0 +1,51 @@ +using Enclave.Sdk.Api.Data.EnrolledSystems; +using Enclave.Sdk.Api.Data.SystemManagement; +using Enclave.Sdk.Api.Data.Tags; + +namespace Enclave.Sdk.Api.Data.Dns; + +/// +/// Model representing a summary of a DNS record. +/// +public class DnsRecordSummary +{ + /// + /// The ID of the record. + /// + public DnsRecordId Id { get; init; } + + /// + /// The name of the record (without the zone). + /// + public string Name { get; init; } = default!; + + /// + /// The type of DNS record. + /// + public string Type { get; init; } = default!; + + /// + /// The ID of the DNS Zone to which this record belongs. + /// + public DnsZoneId ZoneId { get; init; } + + /// + /// The name of the DNS Zone to which this record belongs. + /// + public string ZoneName { get; init; } = default!; + + /// + /// The fully-qualified domain name of the record, including the zone name. + /// + public string Fqdn { get; init; } = default!; + + /// + /// The set of tags to which this DNS name is applied. + /// + public IReadOnlyList Tags { get; init; } = Array.Empty(); + + /// + /// The set of systems to which this DNS name is applied. + /// + public IReadOnlyList? Systems { get; init; } +} diff --git a/src/Enclave.Sdk/Data/Dns/DnsSummary.cs b/src/Enclave.Sdk/Data/Dns/DnsSummary.cs new file mode 100644 index 0000000..bf8ae47 --- /dev/null +++ b/src/Enclave.Sdk/Data/Dns/DnsSummary.cs @@ -0,0 +1,17 @@ +namespace Enclave.Sdk.Api.Data.Dns; + +/// +/// Summary of DNS properties for the organisation. +/// +public class DnsSummary +{ + /// + /// The number of zones in the organisation. + /// + public int ZoneCount { get; init; } + + /// + /// The total number of DNS records in the organisation, across all zones. + /// + public int TotalRecordCount { get; init; } +} diff --git a/src/Enclave.Sdk/Data/Dns/DnsZone.cs b/src/Enclave.Sdk/Data/Dns/DnsZone.cs new file mode 100644 index 0000000..6c8809a --- /dev/null +++ b/src/Enclave.Sdk/Data/Dns/DnsZone.cs @@ -0,0 +1,38 @@ +namespace Enclave.Sdk.Api.Data.Dns; + +/// +/// Detailed model representing a DNS zone. +/// +public class DnsZone +{ + /// + /// The ID of the zone. + /// + public DnsZoneId Id { get; init; } + + /// + /// The name of the zone. + /// + public string Name { get; init; } = default!; + + /// + /// The point in time this zone was created. + /// + public DateTime Created { get; init; } + + /// + /// The number of records in the zone. + /// + public int RecordCount { get; init; } + + /// + /// The record counts broken down by type. + /// + // Public-facing APIs use the display name of the record type rather than the actual enum name. SeeDnsRecordTypeFormatConverter + public IReadOnlyDictionary? RecordTypeCounts { get; init; } + + /// + /// Any provided notes for this zone. + /// s + public string? Notes { get; init; } +} diff --git a/src/Enclave.Sdk/Data/Dns/DnsZoneCreate.cs b/src/Enclave.Sdk/Data/Dns/DnsZoneCreate.cs new file mode 100644 index 0000000..c461ba6 --- /dev/null +++ b/src/Enclave.Sdk/Data/Dns/DnsZoneCreate.cs @@ -0,0 +1,17 @@ +namespace Enclave.Sdk.Api.Data.Dns; + +/// +/// The model for creating a new zone. +/// +public class DnsZoneCreate +{ + /// + /// The name of the zone. + /// + public string Name { get; set; } = default!; + + /// + /// Any notes for the zone. + /// + public string? Notes { get; set; } +} diff --git a/src/Enclave.Sdk/Data/Dns/DnsZoneId.cs b/src/Enclave.Sdk/Data/Dns/DnsZoneId.cs new file mode 100644 index 0000000..19a491a --- /dev/null +++ b/src/Enclave.Sdk/Data/Dns/DnsZoneId.cs @@ -0,0 +1,11 @@ +using TypedIds; + +namespace Enclave.Sdk.Api.Data.Dns; + +/// +/// An int backed Dns Zone Id. +/// +[TypedId(IdBackingType.Int)] +public readonly partial struct DnsZoneId +{ +} diff --git a/src/Enclave.Sdk/Data/Dns/DnsZoneSummary.cs b/src/Enclave.Sdk/Data/Dns/DnsZoneSummary.cs new file mode 100644 index 0000000..d7dca28 --- /dev/null +++ b/src/Enclave.Sdk/Data/Dns/DnsZoneSummary.cs @@ -0,0 +1,33 @@ +namespace Enclave.Sdk.Api.Data.Dns; + +/// +/// Model representing a summary of a DNS record. +/// +public class DnsZoneSummary +{ + /// + /// The ID of the zone. + /// + public DnsZoneId Id { get; init; } + + /// + /// The name of the zone. + /// + public string Name { get; init; } = default!; + + /// + /// The point in time this zone was created. + /// + public DateTime Created { get; init; } + + /// + /// The number of records in the zone. + /// + public int RecordCount { get; init; } + + /// + /// The record counts broken down by type. + /// + // Public-facing APIs use the display name of the record type rather than the actual enum name. SeeDnsRecordTypeFormatConverter + public IReadOnlyDictionary? RecordTypeCounts { get; init; } +} diff --git a/src/Enclave.Sdk/Data/EnrolledSystems/BulkSystemRevokedResult.cs b/src/Enclave.Sdk/Data/EnrolledSystems/BulkSystemRevokedResult.cs new file mode 100644 index 0000000..69ea5b2 --- /dev/null +++ b/src/Enclave.Sdk/Data/EnrolledSystems/BulkSystemRevokedResult.cs @@ -0,0 +1,12 @@ +namespace Enclave.Sdk.Api.Data.EnrolledSystems; + +/// +/// The result of a bulk system revocation. +/// +public class BulkSystemRevokedResult +{ + /// + /// How many systems were actually revoked. + /// + public int SystemsRevoked { get; set; } +} diff --git a/src/Enclave.Sdk/Data/EnrolledSystems/BulkSystemUpdateResult.cs b/src/Enclave.Sdk/Data/EnrolledSystems/BulkSystemUpdateResult.cs new file mode 100644 index 0000000..9bdd241 --- /dev/null +++ b/src/Enclave.Sdk/Data/EnrolledSystems/BulkSystemUpdateResult.cs @@ -0,0 +1,12 @@ +namespace Enclave.Sdk.Api.Data.EnrolledSystems; + +/// +/// Defines the result of a bulk system update. +/// +public class BulkSystemUpdateResult +{ + /// + /// The number of systems actually updated. + /// + public int SystemsUpdated { get; set; } +} diff --git a/src/Enclave.Sdk/Data/EnrolledSystems/EnrolledSystem.cs b/src/Enclave.Sdk/Data/EnrolledSystems/EnrolledSystem.cs new file mode 100644 index 0000000..b9f337b --- /dev/null +++ b/src/Enclave.Sdk/Data/EnrolledSystems/EnrolledSystem.cs @@ -0,0 +1,105 @@ +using Enclave.Sdk.Api.Data.EnrolledSystems.Enum; +using Enclave.Sdk.Api.Data.Tags; + +namespace Enclave.Sdk.Api.Data.EnrolledSystems; + +/// +/// A detailed model representing a single system. +/// +public class EnrolledSystem +{ + /// + /// Unique ID for the System. + /// + public SystemId SystemId { get; init; } = default!; + + /// + /// The configured description of the system. + /// + public string? Description { get; init; } + + /// + /// The type of the system. + /// + public SystemType Type { get; init; } + + /// + /// A value indicating the current state of the system from Enclave's perspective. + /// + public SystemState State { get; init; } + + /// + /// Indicates a UTC timestamp when the system connected to the Enclave SaaS. + /// + public DateTime? ConnectedAt { get; init; } + + /// + /// Indicates a UTC timestamp when Enclave last interacted with the system. + /// + public DateTime? LastSeen { get; init; } + + /// + /// Contains a timestamp indicating when the system was enrolled into the account. + /// + public DateTime EnrolledAt { get; init; } + + /// + /// The ID of the enrolment key used to enrol the system. + /// + public int EnrolmentKeyId { get; init; } + + /// + /// The description of the enrolment key used to enrol the system. + /// + public string? EnrolmentKeyDescription { get; init; } + + /// + /// Whether or not the system is enabled. + /// + public bool IsEnabled { get; init; } + + /// + /// The IP address the system is connected from. + /// + public string? ConnectedFrom { get; init; } + + /// + /// The locally-defined host name of the system. + /// + public string? Hostname { get; init; } + + /// + /// The platform type for this system; possible values are Windows, Linux or Darwin. + /// + public string? PlatformType { get; init; } + + /// + /// The version of the operating system. + /// + public string? OSVersion { get; init; } + + /// + /// The Enclave product version. + /// + public string? EnclaveVersion { get; init; } + + /// + /// The tags assigned to the system. + /// + public IReadOnlyList Tags { get; init; } = Array.Empty(); + + /// + /// The set of DNS entries applied to the system. + /// + public IReadOnlyList Dns { get; init; } = Array.Empty(); + + /// + /// Any additional notes attached to the system. + /// + public string? Notes { get; init; } + + /// + /// Defines the number of minutes this system will be retained after a non-graceful disconnect. + /// + public int? DisconnectedRetentionMinutes { get; init; } +} diff --git a/src/Enclave.Sdk/Data/EnrolledSystems/EnrolledSystemSummary.cs b/src/Enclave.Sdk/Data/EnrolledSystems/EnrolledSystemSummary.cs new file mode 100644 index 0000000..a5fbc19 --- /dev/null +++ b/src/Enclave.Sdk/Data/EnrolledSystems/EnrolledSystemSummary.cs @@ -0,0 +1,95 @@ +using Enclave.Sdk.Api.Data.EnrolledSystems.Enum; +using Enclave.Sdk.Api.Data.Tags; + +namespace Enclave.Sdk.Api.Data.EnrolledSystems; + +/// +/// A basic model representing a single system. +/// +public class EnrolledSystemSummary +{ + /// + /// Unique ID for the System. + /// + public SystemId SystemId { get; init; } = default!; + + /// + /// The configured description of the system. + /// + public string? Description { get; init; } + + /// + /// The type of the system. + /// + public SystemType Type { get; init; } + + /// + /// A value indicating the current state of the system from Enclave's perspective. + /// + public SystemState State { get; init; } + + /// + /// Indicates a UTC timestamp when the system connected to the Enclave SaaS. + /// + public DateTime? ConnectedAt { get; init; } + + /// + /// Indicates a UTC timestamp when Enclave last interacted with the system. + /// + public DateTime? LastSeen { get; init; } + + /// + /// Contains a timestamp indicating when the system was enrolled into the account. + /// + public DateTime EnrolledAt { get; init; } + + /// + /// The ID of the enrolment key used to enrol the system. + /// + public int EnrolmentKeyId { get; init; } + + /// + /// The description of the enrolment key used to enrol the system. + /// + public string EnrolmentKeyDescription { get; init; } = default!; + + /// + /// Whether or not the system is enabled. + /// + public bool IsEnabled { get; init; } + + /// + /// The IP address the system is connected from. + /// + public string? ConnectedFrom { get; init; } + + /// + /// The locally-defined host name of the system. + /// + public string? Hostname { get; init; } + + /// + /// The platform type for this system; possible values are Windows, Linux or Darwin. + /// + public string? PlatformType { get; init; } + + /// + /// The version of the operating system. + /// + public string? OSVersion { get; init; } + + /// + /// The Enclave product version. + /// + public string? EnclaveVersion { get; init; } + + /// + /// The tags assigned to the system. + /// + public IReadOnlyList? Tags { get; init; } + + /// + /// Defines the number of minutes this system will be retained after a non-graceful disconnect. + /// + public int? DisconnectedRetentionMinutes { get; init; } +} diff --git a/src/Enclave.Sdk/Data/EnrolledSystems/Enum/SystemQuerySortMode.cs b/src/Enclave.Sdk/Data/EnrolledSystems/Enum/SystemQuerySortMode.cs new file mode 100644 index 0000000..9864272 --- /dev/null +++ b/src/Enclave.Sdk/Data/EnrolledSystems/Enum/SystemQuerySortMode.cs @@ -0,0 +1,27 @@ +namespace Enclave.Sdk.Api.Data.EnrolledSystems.Enum; + +/// +/// System Query Sort order used when making a System request. +/// +public enum SystemQuerySortMode +{ + /// + /// Sort by recently enrolled. + /// + RecentlyEnrolled, + + /// + /// Sort by recently connected. + /// + RecentlyConnected, + + /// + /// Sort by description. + /// + Description, + + /// + /// Sort by enrolment key. + /// + EnrolmentKeyUsed, +} \ No newline at end of file diff --git a/src/Enclave.Sdk/Data/EnrolledSystems/Enum/SystemState.cs b/src/Enclave.Sdk/Data/EnrolledSystems/Enum/SystemState.cs new file mode 100644 index 0000000..251bb3d --- /dev/null +++ b/src/Enclave.Sdk/Data/EnrolledSystems/Enum/SystemState.cs @@ -0,0 +1,22 @@ +namespace Enclave.Sdk.Api.Data.EnrolledSystems.Enum; + +/// +/// The possible states of an enrolled system. +/// +public enum SystemState +{ + /// + /// The system has been explicitly disabled. + /// + Disabled, + + /// + /// The system is disconnected from the Enclave cloud services. + /// + Disconnected, + + /// + /// The system is connected to Enclave cloud services. + /// + Connected, +} \ No newline at end of file diff --git a/src/Enclave.Sdk/Data/EnrolledSystems/Enum/SystemType.cs b/src/Enclave.Sdk/Data/EnrolledSystems/Enum/SystemType.cs new file mode 100644 index 0000000..bce91a5 --- /dev/null +++ b/src/Enclave.Sdk/Data/EnrolledSystems/Enum/SystemType.cs @@ -0,0 +1,19 @@ +namespace Enclave.Sdk.Api.Data.EnrolledSystems.Enum; + +/// +/// Defines the types of Systems. +/// +public enum SystemType +{ + /// + /// For workstations, laptops, servers, and other systems that are relatively long-lived or manually provisioned. + /// Systems remain in your Enclave Organisation if they stop running. + /// + GeneralPurpose, + + /// + /// For containers, kubernetes pods, and other systems that are temporary, short-lived or automatically provisioned. + /// Systems are automatically removed from your Enclave Organisation when they stop/disconnect. + /// + Ephemeral, +} diff --git a/src/Enclave.Sdk/Data/EnrolledSystems/SystemDnsEntry.cs b/src/Enclave.Sdk/Data/EnrolledSystems/SystemDnsEntry.cs new file mode 100644 index 0000000..c84ff5c --- /dev/null +++ b/src/Enclave.Sdk/Data/EnrolledSystems/SystemDnsEntry.cs @@ -0,0 +1,45 @@ +using Enclave.Sdk.Api.Data.Dns; +using Enclave.Sdk.Api.Data.Tags; + +namespace Enclave.Sdk.Api.Data.EnrolledSystems; + +/// +/// Represents a single system DNS entry. +/// +public class SystemDnsEntry +{ + /// + /// The FQDN (Fully Qualified Domain Name) of the entry. + /// + public string Fqdn { get; init; } = default!; + + /// + /// The zone ID the DNS entry came from. + /// + public DnsZoneId? FromZoneId { get; init; } + + /// + /// The zone Name the DNS entry came from. + /// + public string? FromZoneName { get; init; } + + /// + /// The DNS record ID the DNS entry came from. + /// + public DnsRecordId? FromRecordId { get; init; } + + /// + /// The DNS record name the DNS entry came from. + /// + public string? FromRecordName { get; init; } + + /// + /// Indicates whether the DNS entry was directly assigned to this specific system. + /// + public bool FromDirectAssignment { get; init; } + + /// + /// Contains the set of tags on the system that caused it to be assigned this name. + /// + public IReadOnlyList? Tags { get; init; } +} diff --git a/src/Enclave.Sdk/Data/EnrolmentKeys/BulkKeyActionResult.cs b/src/Enclave.Sdk/Data/EnrolmentKeys/BulkKeyActionResult.cs new file mode 100644 index 0000000..18a64d5 --- /dev/null +++ b/src/Enclave.Sdk/Data/EnrolmentKeys/BulkKeyActionResult.cs @@ -0,0 +1,12 @@ +namespace Enclave.Sdk.Api.Data.EnrolmentKeys; + +/// +/// Model for the result of a bulk enrolment key operation. +/// +public class BulkKeyActionResult +{ + /// + /// Number of keys modified. + /// + public int KeysModified { get; set; } +} diff --git a/src/Enclave.Sdk/Data/EnrolmentKeys/EnrolmentKey.cs b/src/Enclave.Sdk/Data/EnrolmentKeys/EnrolmentKey.cs new file mode 100644 index 0000000..2e84c11 --- /dev/null +++ b/src/Enclave.Sdk/Data/EnrolmentKeys/EnrolmentKey.cs @@ -0,0 +1,94 @@ +using Enclave.Sdk.Api.Data.EnrolmentKeys.Enum; +using Enclave.Sdk.Api.Data.Tags; + +namespace Enclave.Sdk.Api.Data.EnrolmentKeys; + +/// +/// Represents a single Enclave Enrolment Key. +/// +public class EnrolmentKey +{ + /// + /// The ID of the enrolment key. + /// + public EnrolmentKeyId Id { get; init; } + + /// + /// The UTC timestamp when the key was created. + /// + public DateTime Created { get; init; } + + /// + /// The UTC timestamp when the key was last used to enrol a system (null if never used). + /// + public DateTime? LastUsed { get; init; } + + /// + /// The type of key; general purpose (default) or ephemeral (enrolled systems are automatically removed). + /// + public EnrolmentKeyType Type { get; init; } + + /// + /// The approval mode for the key. + /// + public ApprovalMode ApprovalMode { get; init; } + + /// + /// The status of the key. + /// + public EnrolmentKeyStatus Status => IsEnabled ? + UsesRemaining == 0 ? + EnrolmentKeyStatus.NoUsesRemaining : EnrolmentKeyStatus.Enabled + : EnrolmentKeyStatus.Disabled; + + /// + /// The key value that can be used to enrol systems. + /// + public string Key { get; init; } = default!; + + /// + /// The provided description of the key. + /// + public string Description { get; init; } = default!; + + /// + /// Whether or not this key is enabled. + /// + public bool IsEnabled { get; init; } + + /// + /// The number of uses remaining. + /// + public long UsesRemaining { get; init; } + + /// + /// The number of systems enrolled with this key. + /// + public long EnrolledCount { get; init; } + + /// + /// The number of unapproved systems enrolled with this key. + /// + public long UnapprovedCount { get; init; } + + /// + /// The set of tags applied to the key. + /// + public IReadOnlyList? Tags { get; init; } = Array.Empty(); + + /// + /// Defines the number of minutes an ephemeral system enrolled with this key will be retained after a non-graceful disconnect. + /// Only has a value when the type is 'Ephemeral'. + /// + public int? DisconnectedRetentionMinutes { get; set; } + + /// + /// The set of IP constraints for the key. + /// + public IReadOnlyList IpConstraints { get; init; } = Array.Empty(); + + /// + /// Any notes or additional info for this key. + /// + public string? Notes { get; init; } +} \ No newline at end of file diff --git a/src/Enclave.Sdk/Data/EnrolmentKeys/EnrolmentKeyCreate.cs b/src/Enclave.Sdk/Data/EnrolmentKeys/EnrolmentKeyCreate.cs new file mode 100644 index 0000000..b422e7c --- /dev/null +++ b/src/Enclave.Sdk/Data/EnrolmentKeys/EnrolmentKeyCreate.cs @@ -0,0 +1,54 @@ +using System.ComponentModel; +using Enclave.Sdk.Api.Data.EnrolmentKeys.Enum; + +namespace Enclave.Sdk.Api.Data.EnrolmentKeys; + +/// +/// Data required to create a new enrolment key. +/// +public class EnrolmentKeyCreate +{ + /// + /// Defines the type of key being created, general purpose (default) or ephemeral (enrolled systems are automatically removed). + /// + [DefaultValue(EnrolmentKeyType.GeneralPurpose)] + public EnrolmentKeyType Type { get; set; } = EnrolmentKeyType.GeneralPurpose; + + /// + /// The approval mode for the key, manual (default) or automatic. + /// + [DefaultValue(Enum.ApprovalMode.Manual)] + public ApprovalMode? ApprovalMode { get; set; } = Enum.ApprovalMode.Manual; + + /// + /// A description for the key you are creating. + /// + public string Description { get; set; } = default!; + + /// + /// The number of uses to start the key with. A value of -1 indicates no limit on the number of uses. + /// + [DefaultValue(-1)] + public int? UsesRemaining { get; set; } = -1; + + /// + /// The set of IP Address constraints on the key. + /// + public List IpConstraints { get; set; } = new List(); + + /// + /// A set of tags automatically applied to systems enrolled with this key. + /// + public List Tags { get; set; } = new List(); + + /// + /// Defines the number of minutes an ephemeral system enrolled with this key will be retained after a non-graceful disconnect. + /// Only valid when key type is 'Ephemeral'. Default of 15 minutes. + /// + public int? DisconnectedRetentionMinutes { get; set; } + + /// + /// Any additional notes to attach to the key. + /// + public string? Notes { get; set; } +} diff --git a/src/Enclave.Sdk/Data/EnrolmentKeys/EnrolmentKeyId.cs b/src/Enclave.Sdk/Data/EnrolmentKeys/EnrolmentKeyId.cs new file mode 100644 index 0000000..d26a553 --- /dev/null +++ b/src/Enclave.Sdk/Data/EnrolmentKeys/EnrolmentKeyId.cs @@ -0,0 +1,11 @@ +using TypedIds; + +namespace Enclave.Sdk.Api.Data.EnrolmentKeys; + +/// +/// An int backed Enrolment Key Id. +/// +[TypedId(IdBackingType.Int)] +public partial struct EnrolmentKeyId +{ +} diff --git a/src/Enclave.Sdk/Data/EnrolmentKeys/EnrolmentKeyIpConstraint.cs b/src/Enclave.Sdk/Data/EnrolmentKeys/EnrolmentKeyIpConstraint.cs new file mode 100644 index 0000000..1826a08 --- /dev/null +++ b/src/Enclave.Sdk/Data/EnrolmentKeys/EnrolmentKeyIpConstraint.cs @@ -0,0 +1,17 @@ +namespace Enclave.Sdk.Api.Data.EnrolmentKeys; + +/// +/// Model for IP Address constraints. +/// +public class EnrolmentKeyIpConstraint +{ + /// + /// The IP range. + /// + public string Range { get; init; } = default!; + + /// + /// A description for the range. + /// + public string? Description { get; init; } +} \ No newline at end of file diff --git a/src/Enclave.Sdk/Data/EnrolmentKeys/EnrolmentKeyIpConstraintInput.cs b/src/Enclave.Sdk/Data/EnrolmentKeys/EnrolmentKeyIpConstraintInput.cs new file mode 100644 index 0000000..dbf835c --- /dev/null +++ b/src/Enclave.Sdk/Data/EnrolmentKeys/EnrolmentKeyIpConstraintInput.cs @@ -0,0 +1,72 @@ +using System.Net; + +namespace Enclave.Sdk.Api.Data.EnrolmentKeys; + +/// +/// Input model for IP Address constraints. +/// +public class EnrolmentKeyIpConstraintInput +{ + /// + /// Constructor that takes a range as a CIDR notation. + /// + /// The IP range as a CIDR notation. + public EnrolmentKeyIpConstraintInput(string cidrNotation) + { + if (string.IsNullOrWhiteSpace(cidrNotation)) + { + throw new ArgumentNullException(nameof(cidrNotation)); + } + + if (!cidrNotation.Contains('/', StringComparison.Ordinal)) + { + throw new ArgumentException("Incorrect CIDR Format"); + } + + Range = cidrNotation; + } + + /// + /// Constructor that takes an IPAddress object to fill the Range field. + /// + /// The IP Address to use as the Range. + public EnrolmentKeyIpConstraintInput(IPAddress ipAddress) + { + if (ipAddress is null) + { + throw new ArgumentNullException(nameof(ipAddress)); + } + + Range = ipAddress.ToString(); + } + + /// + /// Constructor that takes two IPAddress objects to fill the Range property. + /// + /// The start IP Address. + /// The end IP Address. + public EnrolmentKeyIpConstraintInput(IPAddress startAddress, IPAddress endAddress) + { + if (startAddress is null) + { + throw new ArgumentNullException(nameof(startAddress)); + } + + if (endAddress is null) + { + throw new ArgumentNullException(nameof(endAddress)); + } + + Range = $"{startAddress} - {endAddress}"; + } + + /// + /// The IP range. + /// + public string Range { get; } + + /// + /// A description for the range. + /// + public string? Description { get; set; } +} \ No newline at end of file diff --git a/src/Enclave.Sdk/Data/EnrolmentKeys/EnrolmentKeySummary.cs b/src/Enclave.Sdk/Data/EnrolmentKeys/EnrolmentKeySummary.cs new file mode 100644 index 0000000..7ecaf73 --- /dev/null +++ b/src/Enclave.Sdk/Data/EnrolmentKeys/EnrolmentKeySummary.cs @@ -0,0 +1,84 @@ +using Enclave.Sdk.Api.Data.EnrolmentKeys.Enum; +using Enclave.Sdk.Api.Data.Tags; + +namespace Enclave.Sdk.Api.Data.EnrolmentKeys; + +/// +/// Represents a single Enclave Enrolment Key. +/// +public class EnrolmentKeySummary +{ + /// + /// The ID of the enrolment key. + /// + public EnrolmentKeyId Id { get; init; } + + /// + /// The UTC timestamp when the key was created. + /// + public DateTime Created { get; init; } + + /// + /// The UTC timestamp when the key was last used to enrol a system (null if never used). + /// + public DateTime? LastUsed { get; init; } + + /// + /// The type of key; general purpose (default) or ephemeral (enrolled systems are automatically removed). + /// + public EnrolmentKeyType Type { get; init; } + + /// + /// The approval mode for the key. + /// + public ApprovalMode ApprovalMode { get; init; } + + /// + /// The status of the key. + /// + public EnrolmentKeyStatus Status => IsEnabled ? + UsesRemaining == 0 ? + EnrolmentKeyStatus.NoUsesRemaining : EnrolmentKeyStatus.Enabled + : EnrolmentKeyStatus.Disabled; + + /// + /// The key value that can be used to enrol systems. + /// + public string Key { get; init; } = default!; + + /// + /// The provided description of the key. + /// + public string Description { get; init; } = default!; + + /// + /// Whether or not this key is enabled. + /// + public bool IsEnabled { get; init; } + + /// + /// The number of uses remaining. + /// + public long UsesRemaining { get; init; } + + /// + /// The number of systems enrolled with this key. + /// + public long EnrolledCount { get; init; } + + /// + /// The number of unapproved systems enrolled with this key. + /// + public long UnapprovedCount { get; init; } + + /// + /// The set of tags applied to the key. + /// + public IReadOnlyList Tags { get; init; } = Array.Empty(); + + /// + /// Defines the number of minutes an ephemeral system enrolled with this key will be retained after a non-graceful disconnect. + /// Only has a value when the type is 'Ephemeral'. + /// + public int? DisconnectedRetentionMinutes { get; set; } +} diff --git a/src/Enclave.Sdk/Data/EnrolmentKeys/Enum/ApprovalMode.cs b/src/Enclave.Sdk/Data/EnrolmentKeys/Enum/ApprovalMode.cs new file mode 100644 index 0000000..8c3b64c --- /dev/null +++ b/src/Enclave.Sdk/Data/EnrolmentKeys/Enum/ApprovalMode.cs @@ -0,0 +1,17 @@ +namespace Enclave.Sdk.Api.Data.EnrolmentKeys.Enum; + +/// +/// System Approval Mode. +/// +public enum ApprovalMode +{ + /// + /// Automatically approve systems. + /// + Automatic, + + /// + /// Manually approve systems. + /// + Manual, +} \ No newline at end of file diff --git a/src/Enclave.Sdk/Data/EnrolmentKeys/Enum/EnrolmentKeySortOrder.cs b/src/Enclave.Sdk/Data/EnrolmentKeys/Enum/EnrolmentKeySortOrder.cs new file mode 100644 index 0000000..fa62bef --- /dev/null +++ b/src/Enclave.Sdk/Data/EnrolmentKeys/Enum/EnrolmentKeySortOrder.cs @@ -0,0 +1,27 @@ +namespace Enclave.Sdk.Api.Data.EnrolmentKeys.Enum; + +/// +/// Enrolment Key Sort Order used when making an Enrolment Key request. +/// +public enum EnrolmentKeySortOrder +{ + /// + /// Sort by Description. + /// + Description, + + /// + /// Sort by Last Used. + /// + LastUsed, + + /// + /// Sort By Approval Mode. + /// + ApprovalMode, + + /// + /// Sort by Uses Remaining. + /// + UsesRemaining, +} \ No newline at end of file diff --git a/src/Enclave.Sdk/Data/EnrolmentKeys/Enum/EnrolmentKeyStatus.cs b/src/Enclave.Sdk/Data/EnrolmentKeys/Enum/EnrolmentKeyStatus.cs new file mode 100644 index 0000000..95fe6c9 --- /dev/null +++ b/src/Enclave.Sdk/Data/EnrolmentKeys/Enum/EnrolmentKeyStatus.cs @@ -0,0 +1,22 @@ +namespace Enclave.Sdk.Api.Data.EnrolmentKeys.Enum; + +/// +/// The status of an enrolment key. +/// +public enum EnrolmentKeyStatus +{ + /// + /// The key is disabled, so cannot be used. + /// + Disabled, + + /// + /// The key is enabled, and can be used. + /// + Enabled, + + /// + /// The key is enabled, but has no uses left, so cannot be used. + /// + NoUsesRemaining, +} \ No newline at end of file diff --git a/src/Enclave.Sdk/Data/EnrolmentKeys/Enum/EnrolmentKeyType.cs b/src/Enclave.Sdk/Data/EnrolmentKeys/Enum/EnrolmentKeyType.cs new file mode 100644 index 0000000..35929bc --- /dev/null +++ b/src/Enclave.Sdk/Data/EnrolmentKeys/Enum/EnrolmentKeyType.cs @@ -0,0 +1,19 @@ +namespace Enclave.Sdk.Api.Data.EnrolmentKeys.Enum; + +/// +/// Defines the types of Enrolment Keys. +/// +public enum EnrolmentKeyType +{ + /// + /// For workstations, laptops, servers, and other systems that are relatively long-lived or manually provisioned. + /// Systems remain in your Enclave Organisation if they stop running. + /// + GeneralPurpose, + + /// + /// For containers, kubernetes pods, and other systems that are temporary, short-lived or automatically provisioned. + /// Systems are automatically removed from your Enclave Organisation when they stop/disconnect. + /// + Ephemeral, +} diff --git a/src/Enclave.Sdk/Data/Organisations/Enum/BillingEventLevel.cs b/src/Enclave.Sdk/Data/Logging/Enum/ActivityLogLevel.cs similarity index 52% rename from src/Enclave.Sdk/Data/Organisations/Enum/BillingEventLevel.cs rename to src/Enclave.Sdk/Data/Logging/Enum/ActivityLogLevel.cs index 7b92bd8..11b52d6 100644 --- a/src/Enclave.Sdk/Data/Organisations/Enum/BillingEventLevel.cs +++ b/src/Enclave.Sdk/Data/Logging/Enum/ActivityLogLevel.cs @@ -1,22 +1,22 @@ -namespace Enclave.Sdk.Api.Data.Organisations.Enum; +namespace Enclave.Sdk.Api.Data.Logging.Enum; /// -/// Defines levels for billing events. +/// Log Error Type. /// -public enum BillingEventLevel +public enum ActivityLogLevel { /// - /// Info. + /// Information log level. /// Information, /// - /// Warning. + /// Warning Log. /// Warning, /// - /// Error. + /// Error Log. /// Error, } \ No newline at end of file diff --git a/src/Enclave.Sdk/Data/Logging/LogEntry.cs b/src/Enclave.Sdk/Data/Logging/LogEntry.cs new file mode 100644 index 0000000..e40c6d5 --- /dev/null +++ b/src/Enclave.Sdk/Data/Logging/LogEntry.cs @@ -0,0 +1,34 @@ +using Enclave.Sdk.Api.Data.Logging.Enum; + +namespace Enclave.Sdk.Api.Data.Logging; + +/// +/// Model for a single log entry. +/// +public class LogEntry +{ + /// + /// The UTC timestamp of the log event. + /// + public DateTime TimeStamp { get; set; } + + /// + /// The log level. + /// + public ActivityLogLevel Level { get; set; } + + /// + /// The log message. + /// + public string Message { get; set; } = default!; + + /// + /// The user responsible for the event (if known). + /// + public string? UserName { get; set; } + + /// + /// The IP address from which the action was carried out. + /// + public string? IpAddress { get; set; } +} \ No newline at end of file diff --git a/src/Enclave.Sdk/Data/Organisations/Enum/OrganisationPlan.cs b/src/Enclave.Sdk/Data/Organisations/Enum/OrganisationPlan.cs index 8ed8025..c845afa 100644 --- a/src/Enclave.Sdk/Data/Organisations/Enum/OrganisationPlan.cs +++ b/src/Enclave.Sdk/Data/Organisations/Enum/OrganisationPlan.cs @@ -1,8 +1,22 @@ namespace Enclave.Sdk.Api.Data.Organisations.Enum; +/// +/// An Enum describing the types of plan. +/// public enum OrganisationPlan { + /// + /// Starter Organisation Plan. + /// Starter = 0, + + /// + /// Pro Organisaiton Plan. + /// Pro = 1, + + /// + /// Business Organisaiton Plan. + /// Business = 2, } diff --git a/src/Enclave.Sdk/Data/Organisations/Organisation.cs b/src/Enclave.Sdk/Data/Organisations/Organisation.cs index e329646..16d3bf2 100644 --- a/src/Enclave.Sdk/Data/Organisations/Organisation.cs +++ b/src/Enclave.Sdk/Data/Organisations/Organisation.cs @@ -1,5 +1,4 @@ -using Enclave.Sdk.Api.Data.Account; -using Enclave.Sdk.Api.Data.Organisations.Enum; +using Enclave.Sdk.Api.Data.Organisations.Enum; namespace Enclave.Sdk.Api.Data.Organisations; diff --git a/src/Enclave.Sdk/Data/Organisations/OrganisationBillingEvent.cs b/src/Enclave.Sdk/Data/Organisations/OrganisationBillingEvent.cs deleted file mode 100644 index e54d139..0000000 --- a/src/Enclave.Sdk/Data/Organisations/OrganisationBillingEvent.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Enclave.Sdk.Api.Data.Organisations.Enum; - -namespace Enclave.Sdk.Api.Data.Organisations; - -/// -/// Defines the properties available for a billing event on an organisation. -/// -public class OrganisationBillingEvent -{ - /// - /// The code indicating the billing event. - /// - public string Code { get; init; } = default!; - - /// - /// A human-readable message describing the event. - /// - public string Message { get; init; } = default!; - - /// - /// The event 'level'. - /// - public BillingEventLevel Level { get; init; } -} \ No newline at end of file diff --git a/src/Enclave.Sdk/Data/Account/OrganisationId.cs b/src/Enclave.Sdk/Data/Organisations/OrganisationId.cs similarity index 51% rename from src/Enclave.Sdk/Data/Account/OrganisationId.cs rename to src/Enclave.Sdk/Data/Organisations/OrganisationId.cs index 5907c18..287aa09 100644 --- a/src/Enclave.Sdk/Data/Account/OrganisationId.cs +++ b/src/Enclave.Sdk/Data/Organisations/OrganisationId.cs @@ -1,9 +1,9 @@ using TypedIds; -namespace Enclave.Sdk.Api.Data.Account; +namespace Enclave.Sdk.Api.Data.Organisations; /// -/// OrganisaitonId struct to distinguish between Id types. +/// OrganisationId struct to distinguish between Id types. /// [TypedId] public readonly partial struct OrganisationId diff --git a/src/Enclave.Sdk/Data/Organisations/OrganisationUser.cs b/src/Enclave.Sdk/Data/Organisations/OrganisationUser.cs index 59b7519..03c2f41 100644 --- a/src/Enclave.Sdk/Data/Organisations/OrganisationUser.cs +++ b/src/Enclave.Sdk/Data/Organisations/OrganisationUser.cs @@ -38,7 +38,13 @@ public class OrganisationUser public UserOrganisationRole Role { get; init; } } +/// +/// Top Level model for organisation user requests. +/// public class OrganisationUsersTopLevel { + /// + /// A list of Organisation Users. + /// public IReadOnlyList? Users { get; init; } } \ No newline at end of file diff --git a/src/Enclave.Sdk/Data/Pagination/PaginatedResponseModel.cs b/src/Enclave.Sdk/Data/Pagination/PaginatedResponseModel.cs index 6018bb9..17e9de4 100644 --- a/src/Enclave.Sdk/Data/Pagination/PaginatedResponseModel.cs +++ b/src/Enclave.Sdk/Data/Pagination/PaginatedResponseModel.cs @@ -9,15 +9,15 @@ public class PaginatedResponseModel /// /// Metadata for the paginated data. /// - public PaginationMetadata Metadata { get; init; } + public PaginationMetadata Metadata { get; init; } = default!; /// /// The related links for the current page of data. /// - public PaginationLinks Links { get; init; } + public PaginationLinks Links { get; init; } = default!; /// /// The requested page of items. /// - public IEnumerable Items { get; init; } + public IEnumerable Items { get; init; } = default!; } \ No newline at end of file diff --git a/src/Enclave.Sdk/Data/Pagination/PaginationLinks.cs b/src/Enclave.Sdk/Data/Pagination/PaginationLinks.cs index d72c346..4445eae 100644 --- a/src/Enclave.Sdk/Data/Pagination/PaginationLinks.cs +++ b/src/Enclave.Sdk/Data/Pagination/PaginationLinks.cs @@ -8,7 +8,7 @@ public class PaginationLinks /// /// The first page of data. /// - public Uri First { get; init; } + public Uri? First { get; init; } /// /// The previous page of data (or null if this is the first page). @@ -23,5 +23,5 @@ public class PaginationLinks /// /// The last page of data. /// - public Uri Last { get; init; } + public Uri? Last { get; init; } } \ No newline at end of file diff --git a/src/Enclave.Sdk/Data/PatchBuilder.cs b/src/Enclave.Sdk/Data/PatchBuilder.cs index 531a67e..d6e3d29 100644 --- a/src/Enclave.Sdk/Data/PatchBuilder.cs +++ b/src/Enclave.Sdk/Data/PatchBuilder.cs @@ -1,5 +1,5 @@ -using Enclave.Sdk.Api.Data.PatchModel; -using System.Linq.Expressions; +using System.Linq.Expressions; +using Enclave.Sdk.Api.Data.PatchModel; namespace Enclave.Sdk.Api.Data; diff --git a/src/Enclave.Sdk/Data/PatchModel/SystemPatch.cs b/src/Enclave.Sdk/Data/PatchModel/EnrolledSystemPatch.cs similarity index 85% rename from src/Enclave.Sdk/Data/PatchModel/SystemPatch.cs rename to src/Enclave.Sdk/Data/PatchModel/EnrolledSystemPatch.cs index e309b1a..78d2287 100644 --- a/src/Enclave.Sdk/Data/PatchModel/SystemPatch.cs +++ b/src/Enclave.Sdk/Data/PatchModel/EnrolledSystemPatch.cs @@ -3,7 +3,7 @@ /// /// Defines the modifiable properties of a system. /// -public class SystemPatch : IPatchModel +public class EnrolledSystemPatch : IPatchModel { /// /// The system description. @@ -18,7 +18,7 @@ public class SystemPatch : IPatchModel /// /// The set of tags applied to the system. /// - public string[]? Tags { get; set; } + public IReadOnlyList? Tags { get; set; } /// /// Any notes or additional info for this system. diff --git a/src/Enclave.Sdk/Data/PatchModel/EnrolmentKeyPatch.cs b/src/Enclave.Sdk/Data/PatchModel/EnrolmentKeyPatch.cs new file mode 100644 index 0000000..ecf2476 --- /dev/null +++ b/src/Enclave.Sdk/Data/PatchModel/EnrolmentKeyPatch.cs @@ -0,0 +1,51 @@ +using Enclave.Sdk.Api.Data.EnrolmentKeys; +using Enclave.Sdk.Api.Data.EnrolmentKeys.Enum; + +namespace Enclave.Sdk.Api.Data.PatchModel; + +/// +/// Defines the modifiable properties of an enrolment key. +/// +public class EnrolmentKeyPatchModel : IPatchModel +{ + /// + /// The description for the key. + /// + public string? Description { get; set; } + + /// + /// Whether the key is enabled (meaning it can be used) or disabled. + /// + public bool? IsEnabled { get; set; } + + /// + /// The approval mode for the key, manual (default) or automatic. + /// + public ApprovalMode? ApprovalMode { get; set; } + + /// + /// The number of uses remaining the key should have. A value of -1 indicates no limit on the number of uses. + /// + public int? UsesRemaining { get; set; } + + /// + /// The set of IP Address constraints on the key. + /// + public IReadOnlyList? IpConstraints { get; set; } + + /// + /// A set of tags automatically applied to systems enrolled with this key. + /// + public List Tags { get; set; } = new List(); + + /// + /// Defines the number of minutes an ephemeral system enrolled with this key will be retained after a non-graceful disconnect. + /// Only valid when key type is 'Ephemeral'. + /// + public int? DisconnectedRetentionMinutes { get; set; } + + /// + /// Any notes or additional info for this key. + /// + public string? Notes { get; set; } +} diff --git a/src/Enclave.Sdk/Data/PatchModel/IPatchModel.cs b/src/Enclave.Sdk/Data/PatchModel/IPatchModel.cs index 96e0059..2b3f768 100644 --- a/src/Enclave.Sdk/Data/PatchModel/IPatchModel.cs +++ b/src/Enclave.Sdk/Data/PatchModel/IPatchModel.cs @@ -1,5 +1,9 @@ namespace Enclave.Sdk.Api.Data.PatchModel; +/// +/// An interface for use with a PatchBuilder. +/// +[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1040:Avoid empty interfaces", Justification = "Useful for using PatchBuilder")] public interface IPatchModel { } \ No newline at end of file diff --git a/src/Enclave.Sdk/Data/PatchModel/PolicyPatch.cs b/src/Enclave.Sdk/Data/PatchModel/PolicyPatch.cs index 4cb526a..53479d8 100644 --- a/src/Enclave.Sdk/Data/PatchModel/PolicyPatch.cs +++ b/src/Enclave.Sdk/Data/PatchModel/PolicyPatch.cs @@ -1,4 +1,4 @@ -using Enclave.Sdk.Api.Data.Policy; +using Enclave.Sdk.Api.Data.Policies; namespace Enclave.Sdk.Api.Data.PatchModel; @@ -20,17 +20,17 @@ public class PolicyPatch : IPatchModel /// /// A set of sender tags. /// - public string[]? SenderTags { get; set; } + public IReadOnlyList? SenderTags { get; set; } /// /// A set of receiver tags. /// - public string[]? ReceiverTags { get; set; } + public IReadOnlyList? ReceiverTags { get; set; } /// /// The set of ACLs for the policy. /// - public PolicyAclModel[]? Acls { get; set; } + public IReadOnlyList? Acls { get; set; } /// /// Notes for the policy. diff --git a/src/Enclave.Sdk/Data/PatchModel/UnapprovedSystemPatch.cs b/src/Enclave.Sdk/Data/PatchModel/UnapprovedSystemPatch.cs new file mode 100644 index 0000000..6d5c829 --- /dev/null +++ b/src/Enclave.Sdk/Data/PatchModel/UnapprovedSystemPatch.cs @@ -0,0 +1,22 @@ +namespace Enclave.Sdk.Api.Data.PatchModel; + +/// +/// Patch model containing properties that can be updated. +/// +public class UnapprovedSystemPatch : IPatchModel +{ + /// + /// The system description. + /// + public string? Description { get; set; } + + /// + /// The set of tags applied to the system. + /// + public IReadOnlyList? Tags { get; set; } + + /// + /// Any notes or additional info for this system. + /// + public string? Notes { get; set; } +} diff --git a/src/Enclave.Sdk/Data/Policies/BulkPolicyDeleteResult.cs b/src/Enclave.Sdk/Data/Policies/BulkPolicyDeleteResult.cs new file mode 100644 index 0000000..89691a3 --- /dev/null +++ b/src/Enclave.Sdk/Data/Policies/BulkPolicyDeleteResult.cs @@ -0,0 +1,12 @@ +namespace Enclave.Sdk.Api.Data.Policies; + +/// +/// Model for the result of a policy bulk delete operation. +/// +public class BulkPolicyDeleteResult +{ + /// + /// The number of policies successfully deleted. + /// + public int PoliciesDeleted { get; init; } +} diff --git a/src/Enclave.Sdk/Data/Policies/BulkPolicyUpdateResult.cs b/src/Enclave.Sdk/Data/Policies/BulkPolicyUpdateResult.cs new file mode 100644 index 0000000..024991a --- /dev/null +++ b/src/Enclave.Sdk/Data/Policies/BulkPolicyUpdateResult.cs @@ -0,0 +1,12 @@ +namespace Enclave.Sdk.Api.Data.Policies; + +/// +/// Defines the result of a bulk policy update. +/// +public class BulkPolicyUpdateResult +{ + /// + /// The number of policies successfully updated. + /// + public int PoliciesUpdated { get; init; } +} diff --git a/src/Enclave.Sdk/Data/Policies/Enum/PolicyAclProtocol.cs b/src/Enclave.Sdk/Data/Policies/Enum/PolicyAclProtocol.cs new file mode 100644 index 0000000..a4d7bed --- /dev/null +++ b/src/Enclave.Sdk/Data/Policies/Enum/PolicyAclProtocol.cs @@ -0,0 +1,27 @@ +namespace Enclave.Sdk.Api.Data.Policies.Enum; + +/// +/// Defines the known protocols enforced in policy ACLs. +/// +public enum PolicyAclProtocol +{ + /// + /// Any Protocol. + /// + Any, + + /// + /// Tcp. + /// + Tcp, + + /// + /// Udp. + /// + Udp, + + /// + /// Icmp. + /// + Icmp, +} diff --git a/src/Enclave.Sdk/Data/Policies/Enum/PolicySortOrder.cs b/src/Enclave.Sdk/Data/Policies/Enum/PolicySortOrder.cs new file mode 100644 index 0000000..c46565d --- /dev/null +++ b/src/Enclave.Sdk/Data/Policies/Enum/PolicySortOrder.cs @@ -0,0 +1,17 @@ +namespace Enclave.Sdk.Api.Data.Policies.Enum; + +/// +/// The sort order when making a Policy Request. +/// +public enum PolicySortOrder +{ + /// + /// Sort by description. + /// + Description, + + /// + /// Sort by recently created. + /// + RecentlyCreated, +} \ No newline at end of file diff --git a/src/Enclave.Sdk/Data/Policies/Enum/PolicyState.cs b/src/Enclave.Sdk/Data/Policies/Enum/PolicyState.cs new file mode 100644 index 0000000..dc83d13 --- /dev/null +++ b/src/Enclave.Sdk/Data/Policies/Enum/PolicyState.cs @@ -0,0 +1,22 @@ +namespace Enclave.Sdk.Api.Data.Policies.Enum; + +/// +/// Defines the possible states of policies. +/// +public enum PolicyState +{ + /// + /// Policy is disabled. + /// + Disabled, + + /// + /// Policy is active. + /// + Active, + + /// + /// Policy is enabled, but no traffic will flow, due to the lack of ACLs. + /// + InactiveNoAcls, +} \ No newline at end of file diff --git a/src/Enclave.Sdk/Data/Policies/Policy.cs b/src/Enclave.Sdk/Data/Policies/Policy.cs new file mode 100644 index 0000000..d1bc32f --- /dev/null +++ b/src/Enclave.Sdk/Data/Policies/Policy.cs @@ -0,0 +1,55 @@ +using Enclave.Sdk.Api.Data.Policies.Enum; +using Enclave.Sdk.Api.Data.Tags; + +namespace Enclave.Sdk.Api.Data.Policies; + +/// +/// Represents a single policy. +/// +public class Policy +{ + /// + /// The ID of the policy. + /// + public PolicyId Id { get; init; } + + /// + /// The UTC timestamp when the policy was created. + /// + public DateTime Created { get; init; } + + /// + /// The provided description of the policy. + /// + public string Description { get; init; } = default!; + + /// + /// Whether or not this policy is enabled. + /// + public bool IsEnabled { get; init; } + + /// + /// Indicates the state of the policy. + /// + public PolicyState State { get; init; } + + /// + /// The sender-side tags. + /// + public IReadOnlyList SenderTags { get; init; } = Array.Empty(); + + /// + /// The receiver-side tags. + /// + public IReadOnlyList ReceiverTags { get; init; } = Array.Empty(); + + /// + /// Access control lists. + /// + public IReadOnlyList Acls { get; init; } = Array.Empty(); + + /// + /// Optional notes for the policy. + /// + public string? Notes { get; init; } +} diff --git a/src/Enclave.Sdk/Data/Policy/PolicyAclModel.cs b/src/Enclave.Sdk/Data/Policies/PolicyAcl.cs similarity index 55% rename from src/Enclave.Sdk/Data/Policy/PolicyAclModel.cs rename to src/Enclave.Sdk/Data/Policies/PolicyAcl.cs index 95ec7e5..82423d1 100644 --- a/src/Enclave.Sdk/Data/Policy/PolicyAclModel.cs +++ b/src/Enclave.Sdk/Data/Policies/PolicyAcl.cs @@ -1,22 +1,24 @@ -namespace Enclave.Sdk.Api.Data.Policy; +using Enclave.Sdk.Api.Data.Policies.Enum; + +namespace Enclave.Sdk.Api.Data.Policies; /// /// Model representing a single ACL entry for a policy. /// -public class PolicyAclModel +public class PolicyAcl { /// /// The protocol. /// - public PolicyAclProtocol Protocol { get; set; } + public PolicyAclProtocol Protocol { get; init; } /// /// The port range (or single port) for the ACL. /// - public string? Ports { get; set; } + public string? Ports { get; init; } /// /// An optional description. /// - public string? Description { get; set; } + public string? Description { get; init; } } diff --git a/src/Enclave.Sdk/Data/Policies/PolicyCreate.cs b/src/Enclave.Sdk/Data/Policies/PolicyCreate.cs new file mode 100644 index 0000000..bf5f99b --- /dev/null +++ b/src/Enclave.Sdk/Data/Policies/PolicyCreate.cs @@ -0,0 +1,40 @@ +using System.ComponentModel; + +namespace Enclave.Sdk.Api.Data.Policies; + +/// +/// Data required to create a new policy. +/// +public class PolicyCreate +{ + /// + /// A description for the policy you are creating. + /// + public string Description { get; set; } = default!; + + /// + /// Whether or not the policy is initially enabled. + /// + [DefaultValue(true)] + public bool IsEnabled { get; set; } = true; + + /// + /// A set of sender tags. + /// + public List SenderTags { get; set; } = new List(); + + /// + /// A set of receiver tags. + /// + public List ReceiverTags { get; set; } = new List(); + + /// + /// The set of ACLs for the policy. + /// + public List Acls { get; set; } = new List(); + + /// + /// Optional notes for the policy. + /// + public string? Notes { get; set; } +} diff --git a/src/Enclave.Sdk/Data/Policies/PolicyId.cs b/src/Enclave.Sdk/Data/Policies/PolicyId.cs new file mode 100644 index 0000000..6f8e191 --- /dev/null +++ b/src/Enclave.Sdk/Data/Policies/PolicyId.cs @@ -0,0 +1,11 @@ +using TypedIds; + +namespace Enclave.Sdk.Api.Data.Policies; + +/// +/// An int backed Policy Id. +/// +[TypedId(IdBackingType.Int)] +public partial struct PolicyId +{ +} diff --git a/src/Enclave.Sdk/Data/Policy/PolicyAclProtocol.cs b/src/Enclave.Sdk/Data/Policy/PolicyAclProtocol.cs deleted file mode 100644 index aeba45d..0000000 --- a/src/Enclave.Sdk/Data/Policy/PolicyAclProtocol.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Enclave.Sdk.Api.Data.Policy; - -public enum PolicyAclProtocol -{ - Any, - Tcp, - Udp, - Icmp -} diff --git a/src/Enclave.Sdk/Data/SystemId.cs b/src/Enclave.Sdk/Data/SystemId.cs new file mode 100644 index 0000000..4156752 --- /dev/null +++ b/src/Enclave.Sdk/Data/SystemId.cs @@ -0,0 +1,11 @@ +using TypedIds; + +namespace Enclave.Sdk.Api.Data; + +/// +/// A String backed System Id. +/// +[TypedId(IdBackingType.String)] +public partial struct SystemId +{ +} diff --git a/src/Enclave.Sdk/Data/SystemManagement/SystemReference.cs b/src/Enclave.Sdk/Data/SystemManagement/SystemReference.cs new file mode 100644 index 0000000..fa24d19 --- /dev/null +++ b/src/Enclave.Sdk/Data/SystemManagement/SystemReference.cs @@ -0,0 +1,39 @@ +using Enclave.Sdk.Api.Data.EnrolledSystems.Enum; + +namespace Enclave.Sdk.Api.Data.SystemManagement; + +/// +/// Defines a system reference model. +/// +public class SystemReference +{ + /// + /// Contains the last connected IP of the system. + /// + public string? ConnectedFrom { get; init; } + + /// + /// The System ID. + /// + public string Id { get; init; } = default!; + + /// + /// The local hostname of the system (if known). + /// + public string? MachineName { get; init; } + + /// + /// The set name of the system (if one was provided). + /// + public string? Name { get; init; } + + /// + /// The System platform type (if known). + /// + public string? PlatformType { get; init; } + + /// + /// The state of the system. + /// + public SystemState State { get; init; } +} diff --git a/src/Enclave.Sdk/Data/Tags/TagQuerySortOrder.cs b/src/Enclave.Sdk/Data/Tags/TagQuerySortOrder.cs index d44057f..13c6d99 100644 --- a/src/Enclave.Sdk/Data/Tags/TagQuerySortOrder.cs +++ b/src/Enclave.Sdk/Data/Tags/TagQuerySortOrder.cs @@ -1,8 +1,22 @@ namespace Enclave.Sdk.Api.Data.Tags; +/// +/// The sort order used for a Tag Query. +/// public enum TagQuerySortOrder { + /// + /// Sort Alphabetically. + /// Alphabetical, + + /// + /// Sort by recently used Tags. + /// RecentlyUsed, + + /// + /// Sort by number of Referenced Systems. + /// ReferencedSystems, } \ No newline at end of file diff --git a/src/Enclave.Sdk/Data/Tags/TagReference.cs b/src/Enclave.Sdk/Data/Tags/TagReference.cs new file mode 100644 index 0000000..fe5038d --- /dev/null +++ b/src/Enclave.Sdk/Data/Tags/TagReference.cs @@ -0,0 +1,12 @@ +namespace Enclave.Sdk.Api.Data.Tags; + +/// +/// A referenced tag. +/// +public class TagReference +{ + /// + /// The tag name. + /// + public string Tag { get; init; } = default!; +} diff --git a/src/Enclave.Sdk/Data/UnaprrovedSystems/BulkUnapprovedSystemApproveResult.cs b/src/Enclave.Sdk/Data/UnaprrovedSystems/BulkUnapprovedSystemApproveResult.cs new file mode 100644 index 0000000..c7fee7d --- /dev/null +++ b/src/Enclave.Sdk/Data/UnaprrovedSystems/BulkUnapprovedSystemApproveResult.cs @@ -0,0 +1,12 @@ +namespace Enclave.Sdk.Api.Data.UnaprrovedSystems; + +/// +/// The result of a bulk unapproved system approve operation. +/// +public class BulkUnapprovedSystemApproveResult +{ + /// + /// The number of systems approved. + /// + public int SystemsApproved { get; init; } +} diff --git a/src/Enclave.Sdk/Data/UnaprrovedSystems/BulkUnapprovedSystemDeclineResult.cs b/src/Enclave.Sdk/Data/UnaprrovedSystems/BulkUnapprovedSystemDeclineResult.cs new file mode 100644 index 0000000..106bdd3 --- /dev/null +++ b/src/Enclave.Sdk/Data/UnaprrovedSystems/BulkUnapprovedSystemDeclineResult.cs @@ -0,0 +1,12 @@ +namespace Enclave.Sdk.Api.Data.UnaprrovedSystems; + + /// + /// The result of a bulk unapproved system decline operation. + /// +public class BulkUnapprovedSystemDeclineResult +{ + /// + /// The number of systems declined. + /// + public int SystemsDeclined { get; init; } +} diff --git a/src/Enclave.Sdk/Data/UnaprrovedSystems/Enum/SystemType.cs b/src/Enclave.Sdk/Data/UnaprrovedSystems/Enum/SystemType.cs new file mode 100644 index 0000000..a69868d --- /dev/null +++ b/src/Enclave.Sdk/Data/UnaprrovedSystems/Enum/SystemType.cs @@ -0,0 +1,19 @@ +namespace Enclave.Sdk.Api.Data.UnaprrovedSystems.Enum; + +/// +/// Defines the types of Enrolment Keys. +/// +public enum SystemType +{ + /// + /// For workstations, laptops, servers, and other systems that are relatively long-lived or manually provisioned. + /// Systems remain in your Enclave Organisation if they stop running. + /// + GeneralPurpose, + + /// + /// For containers, kubernetes pods, and other systems that are temporary, short-lived or automatically provisioned. + /// Systems are automatically removed from your Enclave Organisation when they stop/disconnect. + /// + Ephemeral, +} diff --git a/src/Enclave.Sdk/Data/UnaprrovedSystems/Enum/UnapprovedSystemQuerySortMode.cs b/src/Enclave.Sdk/Data/UnaprrovedSystems/Enum/UnapprovedSystemQuerySortMode.cs new file mode 100644 index 0000000..1453ff3 --- /dev/null +++ b/src/Enclave.Sdk/Data/UnaprrovedSystems/Enum/UnapprovedSystemQuerySortMode.cs @@ -0,0 +1,22 @@ +namespace Enclave.Sdk.Api.Data.UnaprrovedSystems.Enum; + +/// +/// Sort modes for unapproved systems. +/// +public enum UnapprovedSystemQuerySortMode +{ + /// + /// Sort by the when the system was enrolled (most recent first). + /// + RecentlyEnrolled, + + /// + /// Sort by the system description. + /// + Description, + + /// + /// Sort by the name of enrolment key used. + /// + EnrolmentKeyUsed, +} \ No newline at end of file diff --git a/src/Enclave.Sdk/Data/UnaprrovedSystems/UnapprovedSystem.cs b/src/Enclave.Sdk/Data/UnaprrovedSystems/UnapprovedSystem.cs new file mode 100644 index 0000000..5a4aa06 --- /dev/null +++ b/src/Enclave.Sdk/Data/UnaprrovedSystems/UnapprovedSystem.cs @@ -0,0 +1,80 @@ +using Enclave.Sdk.Api.Data.Tags; +using Enclave.Sdk.Api.Data.UnaprrovedSystems.Enum; + +namespace Enclave.Sdk.Api.Data.UnaprrovedSystems; + +/// +/// Model representing an unapproved system. +/// +public class UnapprovedSystem +{ + /// + /// The system ID. + /// + public SystemId SystemId { get; init; } = default!; + + /// + /// The system type. + /// + public SystemType Type { get; init; } + + /// + /// The system description (if one has been provided). + /// + public string? Description { get; init; } + + /// + /// The IP address the system was enrolled from. + /// + public string EnrolledFrom { get; init; } = default!; + + /// + /// The UTC timestamp when the system was enrolled. + /// + public DateTime EnrolledAt { get; init; } + + /// + /// The set of tags assigned to the system (either manually or automatically from the key). + /// + public IReadOnlyList Tags { get; init; } = Array.Empty(); + + /// + /// The ID of the enrolment key used to enrol the system. + /// + public int EnrolmentKeyId { get; init; } + + /// + /// The description of the enrolment key used to enrol the system. + /// + public string EnrolmentKeyDescription { get; init; } = default!; + + /// + /// Any additional notes for the system. + /// + public string? Notes { get; init; } + + /// + /// The locally-defined host name of the system. + /// + public string? Hostname { get; init; } + + /// + /// The platform type for this system; possible values are Windows, Linux or MacOSX. + /// + public string? PlatformType { get; init; } + + /// + /// The version of the operating system. + /// + public string? OSVersion { get; init; } + + /// + /// The Enclave product version. + /// + public string? EnclaveVersion { get; init; } + + /// + /// The IP address the system is connected from. + /// + public string? ConnectedFrom { get; init; } +} diff --git a/src/Enclave.Sdk/Data/UnaprrovedSystems/UnapprovedSystemSummary.cs b/src/Enclave.Sdk/Data/UnaprrovedSystems/UnapprovedSystemSummary.cs new file mode 100644 index 0000000..148bd73 --- /dev/null +++ b/src/Enclave.Sdk/Data/UnaprrovedSystems/UnapprovedSystemSummary.cs @@ -0,0 +1,75 @@ +using Enclave.Sdk.Api.Data.Tags; +using Enclave.Sdk.Api.Data.UnaprrovedSystems.Enum; + +namespace Enclave.Sdk.Api.Data.UnaprrovedSystems; + +/// +/// Model representing an unapproved system. +/// +public class UnapprovedSystemSummary +{ + /// + /// The system ID. + /// + public SystemId SystemId { get; init; } = default!; + + /// + /// The system type. + /// + public SystemType Type { get; init; } + + /// + /// The system description (if one has been provided). + /// + public string? Description { get; init; } + + /// + /// The IP address the system was enrolled from. + /// + public string EnrolledFrom { get; init; } = default!; + + /// + /// The UTC timestamp when the system was enrolled. + /// + public DateTime EnrolledAt { get; init; } + + /// + /// The set of tags assigned to the system (either manually or automatically from the key). + /// + public IReadOnlyList Tags { get; init; } = Array.Empty(); + + /// + /// The ID of the enrolment key used to enrol the system. + /// + public int EnrolmentKeyId { get; init; } + + /// + /// The description of the enrolment key used to enrol the system. + /// + public string? EnrolmentKeyDescription { get; init; } + + /// + /// The locally-defined host name of the system. + /// + public string? Hostname { get; init; } + + /// + /// The platform type for this system; possible values are Windows, Linux or MacOSX. + /// + public string? PlatformType { get; init; } + + /// + /// The version of the operating system. + /// + public string? OSVersion { get; init; } + + /// + /// The Enclave product version. + /// + public string? EnclaveVersion { get; init; } + + /// + /// The IP address the system is connected from. + /// + public string? ConnectedFrom { get; init; } +} diff --git a/src/Enclave.Sdk/Enclave.Sdk.Api.csproj b/src/Enclave.Sdk/Enclave.Sdk.Api.csproj index 3e9595c..cfeb246 100644 --- a/src/Enclave.Sdk/Enclave.Sdk.Api.csproj +++ b/src/Enclave.Sdk/Enclave.Sdk.Api.csproj @@ -17,8 +17,12 @@ - + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + diff --git a/src/Enclave.Sdk/EnclaveClient.cs b/src/Enclave.Sdk/EnclaveClient.cs index f4972b0..0ad177a 100644 --- a/src/Enclave.Sdk/EnclaveClient.cs +++ b/src/Enclave.Sdk/EnclaveClient.cs @@ -4,7 +4,6 @@ using System.Text.Json; using Enclave.Sdk.Api.Clients; using Enclave.Sdk.Api.Clients.Interfaces; -using Enclave.Sdk.Api.Data; using Enclave.Sdk.Api.Data.Account; using Enclave.Sdk.Api.Handlers; @@ -64,7 +63,7 @@ public EnclaveClient(EnclaveClientOptions options) /// /// List of organisation containing the OrgId and Name and the users role in that organisation. /// throws when the Api returns a null response. - public async Task> GetOrganisationsAsync() + public async Task> GetOrganisationsAsync() { var organisations = await _httpClient.GetFromJsonAsync("/account/orgs", Constants.JsonSerializerOptions); @@ -86,7 +85,7 @@ public IOrganisationClient CreateOrganisationClient(AccountOrganisation organisa return new OrganisationClient(_httpClient, organisation); } - private EnclaveClientOptions? GetSettingsFile() + private static EnclaveClientOptions? GetSettingsFile() { var userProfile = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); @@ -105,6 +104,7 @@ public IOrganisationClient CreateOrganisationClient(AccountOrganisation organisa } } + [System.Diagnostics.CodeAnalysis.SuppressMessage("Reliability", "CA2000:Dispose objects before losing scope", Justification = "Needed for lifecycle of consumer")] private static HttpClient SetupHttpClient(EnclaveClientOptions options) { var httpClient = new HttpClient(new ProblemDetailsHttpMessageHandler()) diff --git a/src/Enclave.Sdk/EnclaveClientOptions.cs b/src/Enclave.Sdk/EnclaveClientOptions.cs index 54c7d55..b1d48fd 100644 --- a/src/Enclave.Sdk/EnclaveClientOptions.cs +++ b/src/Enclave.Sdk/EnclaveClientOptions.cs @@ -1,6 +1,4 @@ -using System.Text.Json.Serialization; - -namespace Enclave.Sdk.Api; +namespace Enclave.Sdk.Api; /// /// A representation of options used when creating . diff --git a/src/Enclave.Sdk/Exceptions/ProblemDetails.cs b/src/Enclave.Sdk/Exceptions/ProblemDetails.cs index 26f044a..8c8edce 100644 --- a/src/Enclave.Sdk/Exceptions/ProblemDetails.cs +++ b/src/Enclave.Sdk/Exceptions/ProblemDetails.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Text.Json.Serialization; -using System.Threading.Tasks; +using System.Text.Json.Serialization; namespace Enclave.Sdk.Api.Exceptions; diff --git a/src/Enclave.Sdk/Exceptions/ProblemDetailsException.cs b/src/Enclave.Sdk/Exceptions/ProblemDetailsException.cs index af74bfc..676c931 100644 --- a/src/Enclave.Sdk/Exceptions/ProblemDetailsException.cs +++ b/src/Enclave.Sdk/Exceptions/ProblemDetailsException.cs @@ -1,15 +1,29 @@ namespace Enclave.Sdk.Api.Exceptions; -internal class ProblemDetailsException : Exception +/// +/// An Exception to display the details of an Http Problem Details. +/// +public class ProblemDetailsException : Exception { + /// + /// The Problem Details. + /// public ProblemDetails ProblemDetails { get; } + /// + /// The HttpResponse message. + /// public HttpResponseMessage Response { get; } + /// + /// Constructor for setting up the problem details exception. + /// + /// The problem details from the HTTP response. + /// The HTTP Response message. public ProblemDetailsException(ProblemDetails problemDetails, HttpResponseMessage response) - : base(problemDetails.Title) + : base(problemDetails?.Title) { - ProblemDetails = problemDetails; + ProblemDetails = problemDetails!; Response = response; } } \ No newline at end of file diff --git a/src/Enclave.Sdk/Exceptions/ProblemDetailsJsonConverter.cs b/src/Enclave.Sdk/Exceptions/ProblemDetailsJsonConverter.cs index 380c8ae..2fea2e4 100644 --- a/src/Enclave.Sdk/Exceptions/ProblemDetailsJsonConverter.cs +++ b/src/Enclave.Sdk/Exceptions/ProblemDetailsJsonConverter.cs @@ -1,11 +1,6 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Text; +using System.Diagnostics.CodeAnalysis; using System.Text.Json; using System.Text.Json.Serialization; -using System.Threading.Tasks; namespace Enclave.Sdk.Api.Exceptions; diff --git a/src/Enclave.Sdk/Handlers/ProblemDetailsHttpMessageHandler.cs b/src/Enclave.Sdk/Handlers/ProblemDetailsHttpMessageHandler.cs index 5bbedfb..b063236 100644 --- a/src/Enclave.Sdk/Handlers/ProblemDetailsHttpMessageHandler.cs +++ b/src/Enclave.Sdk/Handlers/ProblemDetailsHttpMessageHandler.cs @@ -7,7 +7,9 @@ internal sealed class ProblemDetailsHttpMessageHandler : DelegatingHandler { public ProblemDetailsHttpMessageHandler() #pragma warning disable CA2000 // Dispose objects before losing scope - : base(new HttpClientHandler()) { } + : base(new HttpClientHandler()) + { + } #pragma warning restore CA2000 // Dispose objects before losing scope protected override async Task SendAsync(HttpRequestMessage request, CancellationToken ct) diff --git a/tests/Enclave.Sdk.Api.Tests/Clients/DnsClientTests.cs b/tests/Enclave.Sdk.Api.Tests/Clients/DnsClientTests.cs new file mode 100644 index 0000000..86ae32d --- /dev/null +++ b/tests/Enclave.Sdk.Api.Tests/Clients/DnsClientTests.cs @@ -0,0 +1,559 @@ +using Enclave.Sdk.Api.Clients; +using Enclave.Sdk.Api.Data; +using Enclave.Sdk.Api.Data.Dns; +using Enclave.Sdk.Api.Data.Organisations; +using Enclave.Sdk.Api.Data.Pagination; +using Enclave.Sdk.Api.Data.PatchModel; +using FluentAssertions; +using NUnit.Framework; +using System.Text.Json; +using WireMock.FluentAssertions; +using WireMock.RequestBuilders; +using WireMock.ResponseBuilders; +using WireMock.Server; + +namespace Enclave.Sdk.Api.Tests.Clients; + +public class DnsClientTests +{ + private DnsClient _dnsClient; + private WireMockServer _server; + private string _orgRoute; + private JsonSerializerOptions _serializerOptions = new() + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + }; + + [SetUp] + public void Setup() + { + _server = WireMockServer.Start(); + + var httpClient = new HttpClient + { + BaseAddress = new Uri(_server.Urls[0]), + }; + + var organisationId = OrganisationId.New(); + _orgRoute = $"/org/{organisationId}"; + + _dnsClient = new DnsClient(httpClient, $"org/{organisationId}"); + } + + [Test] + public async Task Should_return_a_Dns_summary_when_calling_GetPropertiesSummaryAsync() + { + // Arrange + var response = new DnsSummary + { + TotalRecordCount = 12, + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/dns").UsingGet()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + // Act + var result = await _dnsClient.GetPropertiesSummaryAsync(); + + // Assert + result.TotalRecordCount.Should().Be(response.TotalRecordCount); + } + + [Test] + public async Task Should_return_a_paginated_response_model_when_calling_GetZonesAsync() + { + // Arrange + var response = new PaginatedResponseModel + { + Items = new List + { + new DnsZoneSummary { Name = "test"} + }, + Links = new PaginationLinks(), + Metadata = new PaginationMetadata(), + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/dns/zones").UsingGet()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + // Act + var result = await _dnsClient.GetZonesAsync(); + + // Assert + result.Should().NotBeNull(); + } + + [Test] + public async Task Should_make_a_call_to_api_with_page_quertString_when_calling_GetZonesAsync() + { + // Arrange + var response = new PaginatedResponseModel + { + Items = new List + { + new DnsZoneSummary { Name = "test"} + }, + Links = new PaginationLinks(), + Metadata = new PaginationMetadata(), + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/dns/zones").UsingGet()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + var page = 12; + + // Act + await _dnsClient.GetZonesAsync(pageNumber: page); + + // Assert + _server.Should().HaveReceivedACall().AtAbsoluteUrl($"{_server.Urls[0]}{_orgRoute}/dns/zones?page={page}"); + } + + [Test] + public async Task Should_make_a_call_to_api_with_per_page_quertString_when_calling_GetZonesAsync() + { + // Arrange + var response = new PaginatedResponseModel + { + Items = new List + { + new DnsZoneSummary { Name = "test"} + }, + Links = new PaginationLinks(), + Metadata = new PaginationMetadata(), + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/dns/zones").UsingGet()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + var page = 12; + + // Act + await _dnsClient.GetZonesAsync(perPage: page); + + // Assert + _server.Should().HaveReceivedACall().AtAbsoluteUrl($"{_server.Urls[0]}{_orgRoute}/dns/zones?per_page={page}"); + } + + [Test] + public async Task Should_return_a_full_dns_zone_when_calling_CreateZoneAsync() + { + // Arrange + var response = new DnsZone + { + Id = DnsZoneId.FromInt(123), + Name = "test", + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/dns/zones").UsingPost()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + // Act + var result = await _dnsClient.CreateZoneAsync(new DnsZoneCreate()); + + // Assert + result.Should().NotBeNull(); + result.Name.Should().Be(response.Name); + } + + [Test] + public async Task Should_return_a_full_dns_zone_when_calling_GetZoneAsync() + { + // Arrange + var response = new DnsZone + { + Id = DnsZoneId.FromInt(123), + Name = "test", + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/dns/zones/{123}").UsingGet()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + // Act + var result = await _dnsClient.GetZoneAsync(DnsZoneId.FromInt(123)); + + // Assert + result.Should().NotBeNull(); + result.Name.Should().Be(response.Name); + } + + [Test] + public async Task Should_return_a_full_dns_zone_when_calling_UpdateZoneAsync() + { + // Arrange + var response = new DnsZone + { + Id = DnsZoneId.FromInt(123), + Name = "New Name", + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/dns/zones/{123}").UsingPatch()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + var builder = new PatchBuilder().Set(d => d.Name, "New Name"); + + // Act + var result = await _dnsClient.UpdateZoneAsync(DnsZoneId.FromInt(123), builder); + + // Assert + result.Should().NotBeNull(); + result.Name.Should().Be(response.Name); + } + + [Test] + public async Task Should_return_a_full_dns_zone_when_calling_DeleteZoneAsync() + { + // Arrange + var response = new DnsZone + { + Id = DnsZoneId.FromInt(123), + Name = "New Name", + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/dns/zones/{123}").UsingDelete()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + // Act + var result = await _dnsClient.DeleteZoneAsync(DnsZoneId.FromInt(123)); + + // Assert + result.Should().NotBeNull(); + result.Name.Should().Be(response.Name); + } + + [Test] + public async Task Should_return_a_paginated_response_model_when_calling_GetRecordsAsync() + { + // Arrange + var response = new PaginatedResponseModel + { + Items = new List + { + new DnsRecordSummary { Name = "test"} + }, + Links = new PaginationLinks(), + Metadata = new PaginationMetadata(), + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/dns/records").UsingGet()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + // Act + var result = await _dnsClient.GetRecordsAsync(); + + // Assert + result.Should().NotBeNull(); + } + + [Test] + public async Task Should_make_a_call_to_api_with_zoneId_quertString_when_calling_GetRecordsAsync() + { + // Arrange + var response = new PaginatedResponseModel + { + Items = new List + { + new DnsRecordSummary { Name = "test"} + }, + Links = new PaginationLinks(), + Metadata = new PaginationMetadata(), + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/dns/records").UsingGet()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + var zonedId = DnsZoneId.FromInt(12); + + // Act + await _dnsClient.GetRecordsAsync(dnsZoneId: zonedId); + + // Assert + _server.Should().HaveReceivedACall().AtAbsoluteUrl($"{_server.Urls[0]}{_orgRoute}/dns/records?zoneId={zonedId}"); + } + + [Test] + public async Task Should_make_a_call_to_api_with_hostname_quertString_when_calling_GetRecordsAsync() + { + // Arrange + var response = new PaginatedResponseModel + { + Items = new List + { + new DnsRecordSummary { Name = "test"} + }, + Links = new PaginationLinks(), + Metadata = new PaginationMetadata(), + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/dns/records").UsingGet()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + var hostname = "name"; + + // Act + await _dnsClient.GetRecordsAsync(hostname: hostname); + + // Assert + _server.Should().HaveReceivedACall().AtAbsoluteUrl($"{_server.Urls[0]}{_orgRoute}/dns/records?hostname={hostname}"); + } + + + [Test] + public async Task Should_make_a_call_to_api_with_page_quertString_when_calling_GetRecordsAsync() + { + // Arrange + var response = new PaginatedResponseModel + { + Items = new List + { + new DnsRecordSummary { Name = "test"} + }, + Links = new PaginationLinks(), + Metadata = new PaginationMetadata(), + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/dns/records").UsingGet()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + var page = 12; + + // Act + await _dnsClient.GetRecordsAsync(pageNumber: page); + + // Assert + _server.Should().HaveReceivedACall().AtAbsoluteUrl($"{_server.Urls[0]}{_orgRoute}/dns/records?page={page}"); + } + + [Test] + public async Task Should_make_a_call_to_api_with_per_page_quertString_when_calling_GetRecordsAsync() + { + // Arrange + var response = new PaginatedResponseModel + { + Items = new List + { + new DnsRecordSummary { Name = "test"} + }, + Links = new PaginationLinks(), + Metadata = new PaginationMetadata(), + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/dns/records").UsingGet()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + var page = 12; + + // Act + await _dnsClient.GetRecordsAsync(perPage: page); + + // Assert + _server.Should().HaveReceivedACall().AtAbsoluteUrl($"{_server.Urls[0]}{_orgRoute}/dns/records?per_page={page}"); + } + + [Test] + public async Task Should_return_a_full_dns_record_model_when_calling_CreateRecordAsync() + { + // Arrange + var response = new DnsRecord + { + Id = DnsRecordId.FromInt(123), + Name = "Name", + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/dns/records").UsingPost()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + var createModel = new DnsRecordCreate + { + Name = "Name", + }; + + // Act + var result = await _dnsClient.CreateRecordAsync(createModel); + + // Assert + result.Should().NotBeNull(); + result.Id.Should().Be(response.Id); + } + + [Test] + public async Task Should_return_number_of_deleted_records_when_calling_DeleteRecordsAsync() + { + // Arrange + var response = new BulkDnsRecordDeleteResult + { + DnsRecordsDeleted = 3, + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/dns/records").UsingDelete()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + var records = new DnsRecordId[] { DnsRecordId.FromInt(1), DnsRecordId.FromInt(2), DnsRecordId.FromInt(3) }; + + + // Act + var result = await _dnsClient.DeleteRecordsAsync(records); + + // Assert + result.Should().Be(3); + } + + [Test] + public async Task Should_return_full_dns_record_when_calling_GetRecordAsync() + { + // Arrange + var id = DnsRecordId.FromInt(123); + var response = new DnsRecord + { + Id = id, + Name = "Name", + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/dns/records/{id}").UsingGet()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + // Act + var result = await _dnsClient.GetRecordAsync(id); + + // Assert + result.Should().NotBeNull(); + result.Id.Should().Be(id); + } + + [Test] + public async Task Should_return_full_dns_record_when_calling_UpdateRecordAsync() + { + // Arrange + var id = DnsRecordId.FromInt(123); + var response = new DnsRecord + { + Id = id, + Name = "New Name", + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/dns/records/{id}").UsingPatch()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + var builder = new PatchBuilder().Set(d => d.Name, "New Name"); + + // Act + var result = await _dnsClient.UpdateRecordAsync(id, builder); + + // Assert + result.Should().NotBeNull(); + result.Name.Should().Be(response.Name); + } + + + [Test] + public async Task Should_return_full_dns_record_when_calling_DeleteRecord() + { + // Arrange + var id = DnsRecordId.FromInt(123); + var response = new DnsRecord + { + Id = id, + Name = "Name", + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/dns/records/{id}").UsingDelete()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + // Act + var result = await _dnsClient.DeleteRecordAsync(id); + + // Assert + result.Should().NotBeNull(); + result.Id.Should().Be(id); + } +} diff --git a/tests/Enclave.Sdk.Api.Tests/Clients/EnrolledSystemClientTests.cs b/tests/Enclave.Sdk.Api.Tests/Clients/EnrolledSystemClientTests.cs new file mode 100644 index 0000000..a5610a2 --- /dev/null +++ b/tests/Enclave.Sdk.Api.Tests/Clients/EnrolledSystemClientTests.cs @@ -0,0 +1,470 @@ +using Enclave.Sdk.Api.Clients; +using Enclave.Sdk.Api.Data.Account; +using Enclave.Sdk.Api.Data.EnrolledSystems; +using Enclave.Sdk.Api.Data.Pagination; +using FluentAssertions; +using NUnit.Framework; +using System.Text.Json; +using WireMock.RequestBuilders; +using WireMock.ResponseBuilders; +using WireMock.Server; +using WireMock.FluentAssertions; +using Enclave.Sdk.Api.Data.EnrolledSystems.Enum; +using Enclave.Sdk.Api.Data.Organisations; +using Enclave.Sdk.Api.Data; +using Enclave.Sdk.Api.Data.PatchModel; + +namespace Enclave.Sdk.Api.Tests.Clients; + +public class EnrolledSystemClientTests +{ + private EnrolledSystemsClient _enrolledSystemsClient; + private WireMockServer _server; + private string _orgRoute; + private JsonSerializerOptions _serializerOptions = new() + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + }; + + [SetUp] + public void Setup() + { + _server = WireMockServer.Start(); + + var httpClient = new HttpClient + { + BaseAddress = new Uri(_server.Urls[0]), + }; + + var organisationId = OrganisationId.New(); + _orgRoute = $"/org/{organisationId}"; + + _enrolledSystemsClient = new EnrolledSystemsClient(httpClient, $"org/{organisationId}"); + } + + [Test] + public async Task Should_return_a_paginated_response_model_when_calling_GetSystemsAsync() + { + // Arrange + var response = new PaginatedResponseModel + { + Items = new List + { + new EnrolledSystemSummary { Description = "test"} + }, + Links = new PaginationLinks(), + Metadata = new PaginationMetadata(), + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/systems").UsingGet()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + // Act + var result = await _enrolledSystemsClient.GetSystemsAsync(); + + // Assert + result.Should().NotBeNull(); + } + + [Test] + public async Task Should_make_a_call_to_api_with_enrolment_key_quertString_when_calling_GetSystemsAsync() + { + // Arrange + var response = new PaginatedResponseModel + { + Items = new List + { + new EnrolledSystemSummary { Description = "test"} + }, + Links = new PaginationLinks(), + Metadata = new PaginationMetadata(), + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/systems").UsingGet()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + var enrolmentKeyId = 123; + + // Act + await _enrolledSystemsClient.GetSystemsAsync(enrolmentKeyId: enrolmentKeyId); + + // Assert + _server.Should().HaveReceivedACall().AtAbsoluteUrl($"{_server.Urls[0]}{_orgRoute}/systems?enrolment_key={enrolmentKeyId}"); + } + + [Test] + public async Task Should_make_a_call_to_api_with_search_quertString_when_calling_GetSystemsAsync() + { + // Arrange + var response = new PaginatedResponseModel + { + Items = new List + { + new EnrolledSystemSummary { Description = "test"} + }, + Links = new PaginationLinks(), + Metadata = new PaginationMetadata(), + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/systems").UsingGet()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + var searchTerm = "term"; + + // Act + await _enrolledSystemsClient.GetSystemsAsync(searchTerm: searchTerm); + + // Assert + _server.Should().HaveReceivedACall().AtAbsoluteUrl($"{_server.Urls[0]}{_orgRoute}/systems?search={searchTerm}"); + } + + [Test] + public async Task Should_make_a_call_to_api_with_include_disabled_quertString_when_calling_GetSystemsAsync() + { + // Arrange + var response = new PaginatedResponseModel + { + Items = new List + { + new EnrolledSystemSummary { Description = "test"} + }, + Links = new PaginationLinks(), + Metadata = new PaginationMetadata(), + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/systems").UsingGet()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + var includeDisabled = true; + + // Act + await _enrolledSystemsClient.GetSystemsAsync(includeDisabled: includeDisabled); + + // Assert + _server.Should().HaveReceivedACall().AtAbsoluteUrl($"{_server.Urls[0]}{_orgRoute}/systems?include_disabled={includeDisabled}"); + } + + [Test] + public async Task Should_make_a_call_to_api_with_sort_quertString_when_calling_GetSystemsAsync() + { + // Arrange + var response = new PaginatedResponseModel + { + Items = new List + { + new EnrolledSystemSummary { Description = "test"} + }, + Links = new PaginationLinks(), + Metadata = new PaginationMetadata(), + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/systems").UsingGet()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + var sortOrder = SystemQuerySortMode.RecentlyConnected; + + // Act + await _enrolledSystemsClient.GetSystemsAsync(sortOrder: sortOrder); + + // Assert + _server.Should().HaveReceivedACall().AtAbsoluteUrl($"{_server.Urls[0]}{_orgRoute}/systems?sort={sortOrder}"); + } + + [Test] + public async Task Should_make_a_call_to_api_with_dns_quertString_when_calling_GetSystemsAsync() + { + // Arrange + var response = new PaginatedResponseModel + { + Items = new List + { + new EnrolledSystemSummary { Description = "test"} + }, + Links = new PaginationLinks(), + Metadata = new PaginationMetadata(), + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/systems").UsingGet()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + var dnsName = "test.dns"; + + // Act + await _enrolledSystemsClient.GetSystemsAsync(dnsName: dnsName); + + // Assert + _server.Should().HaveReceivedACall().AtAbsoluteUrl($"{_server.Urls[0]}{_orgRoute}/systems?dns={dnsName}"); + } + + [Test] + public async Task Should_make_a_call_to_api_with_page_quertString_when_calling_GetSystemsAsync() + { + // Arrange + var response = new PaginatedResponseModel + { + Items = new List + { + new EnrolledSystemSummary { Description = "test"} + }, + Links = new PaginationLinks(), + Metadata = new PaginationMetadata(), + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/systems").UsingGet()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + var page = 12; + + // Act + await _enrolledSystemsClient.GetSystemsAsync(pageNumber: page); + + // Assert + _server.Should().HaveReceivedACall().AtAbsoluteUrl($"{_server.Urls[0]}{_orgRoute}/systems?page={page}"); + } + + [Test] + public async Task Should_make_a_call_to_api_with_per_page_quertString_when_calling_GetSystemsAsync() + { + // Arrange + var response = new PaginatedResponseModel + { + Items = new List + { + new EnrolledSystemSummary { Description = "test"} + }, + Links = new PaginationLinks(), + Metadata = new PaginationMetadata(), + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/systems").UsingGet()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + var page = 12; + + // Act + await _enrolledSystemsClient.GetSystemsAsync(perPage: page); + + // Assert + _server.Should().HaveReceivedACall().AtAbsoluteUrl($"{_server.Urls[0]}{_orgRoute}/systems?per_page={page}"); + } + + [Test] + public async Task Should_return_number_of_revoked_systems_when_calling_RevokeSystemsAsync() + { + // Arrange + var response = new BulkSystemRevokedResult + { + SystemsRevoked = 2, + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/systems").UsingDelete()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + // Act + var result = await _enrolledSystemsClient.RevokeSystemsAsync(SystemId.FromString("asdf"), SystemId.FromString("asdf3")); + + // Assert + result.Should().Be(2); + } + + [Test] + public async Task Should_return_the_updated_system_when_calling_UpdateAsync() + { + // Arrange + var response = new EnrolledSystem + { + SystemId = SystemId.FromString("system1"), + Description = "new description", + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/systems/{response.SystemId}").UsingPatch()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + var builder = new PatchBuilder().Set(e => e.Description, "new description"); + + // Act + var result = await _enrolledSystemsClient.UpdateAsync(response.SystemId, builder); + + // Assert + result.Should().NotBeNull(); + result.Description.Should().Be(response.Description); + } + + [Test] + public async Task Should_return_the_revoked_system_when_calling_RevokeAsync() + { + // Arrange + var response = new EnrolledSystem + { + SystemId = SystemId.FromString("system1"), + Description = "description", + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/systems/{response.SystemId}").UsingDelete()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + // Act + var result = await _enrolledSystemsClient.RevokeAsync(response.SystemId); + + // Assert + result.Should().NotBeNull(); + result.SystemId.Should().Be(response.SystemId); + } + + [Test] + public async Task Should_return_the_enabled_system_when_calling_EnableAsync() + { + // Arrange + var response = new EnrolledSystem + { + SystemId = SystemId.FromString("system1"), + Description = "description", + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/systems/{response.SystemId}/enable").UsingPut()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + // Act + var result = await _enrolledSystemsClient.EnableAsync(response.SystemId); + + // Assert + result.Should().NotBeNull(); + result.SystemId.Should().Be(response.SystemId); + } + + [Test] + public async Task Should_return_the_disabled_system_when_calling_DisableAsync() + { + // Arrange + var response = new EnrolledSystem + { + SystemId = SystemId.FromString("system1"), + Description = "description", + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/systems/{response.SystemId}/disable").UsingPut()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + // Act + var result = await _enrolledSystemsClient.DisableAsync(response.SystemId); + + // Assert + result.Should().NotBeNull(); + result.SystemId.Should().Be(response.SystemId); + } + + [Test] + public async Task Should_return_number_of_enabled_systems_when_calling_BulkEnableAsync() + { + // Arrange + var response = new BulkSystemUpdateResult + { + SystemsUpdated = 2, + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/systems/enable").UsingPut()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + // Act + var result = await _enrolledSystemsClient.BulkEnableAsync(SystemId.FromString("asdf"), SystemId.FromString("asdf3")); + + // Assert + result.Should().Be(2); + } + + [Test] + public async Task Should_return_number_of_disabled_systems_when_calling_BulkDisableAsync() + { + // Arrange + var response = new BulkSystemUpdateResult + { + SystemsUpdated = 2, + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/systems/disable").UsingPut()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + // Act + var result = await _enrolledSystemsClient.BulkDisableAsync(SystemId.FromString("asdf"), SystemId.FromString("asdf3")); + + // Assert + result.Should().Be(2); + } + + +} diff --git a/tests/Enclave.Sdk.Api.Tests/Clients/EnrolmentKeyClientTests.cs b/tests/Enclave.Sdk.Api.Tests/Clients/EnrolmentKeyClientTests.cs new file mode 100644 index 0000000..b830ed9 --- /dev/null +++ b/tests/Enclave.Sdk.Api.Tests/Clients/EnrolmentKeyClientTests.cs @@ -0,0 +1,392 @@ +using Enclave.Sdk.Api.Clients; +using Enclave.Sdk.Api.Data; +using Enclave.Sdk.Api.Data.EnrolmentKeys; +using Enclave.Sdk.Api.Data.EnrolmentKeys.Enum; +using Enclave.Sdk.Api.Data.Organisations; +using Enclave.Sdk.Api.Data.Pagination; +using Enclave.Sdk.Api.Data.PatchModel; +using FluentAssertions; +using NUnit.Framework; +using System.Text.Json; +using WireMock.FluentAssertions; +using WireMock.RequestBuilders; +using WireMock.ResponseBuilders; +using WireMock.Server; + +namespace Enclave.Sdk.Api.Tests.Clients; + +public class EnrolmentKeyClientTests +{ + private EnrolmentKeysClient _enrolmentKeysClient; + private WireMockServer _server; + private string _orgRoute; + private JsonSerializerOptions _serializerOptions = new() + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + }; + + [SetUp] + public void Setup() + { + _server = WireMockServer.Start(); + + var httpClient = new HttpClient + { + BaseAddress = new Uri(_server.Urls[0]), + }; + + + var organisationId = OrganisationId.New(); + _orgRoute = $"/org/{organisationId}"; + + _enrolmentKeysClient = new EnrolmentKeysClient(httpClient, $"org/{organisationId}"); + } + + [Test] + public async Task Should_return_paginated_response_when_calling_GetEnrolmentKeysAsync() + { + // Arrange + var response = new PaginatedResponseModel() + { + Items = new List + { + new EnrolmentKeySummary(), + }, + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/enrolment-keys").UsingGet()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + // Act + var result = await _enrolmentKeysClient.GetEnrolmentKeysAsync(); + + // Assert + result.Should().NotBe(null); + result.Items.Should().HaveCount(1); + + } + + [Test] + public async Task Should_make_call_to_api_with_search_queryString_when_calling_GetEnrolmentKeysAsync() + { + // Arrange + var response = new PaginatedResponseModel() + { + Items = new List + { + new EnrolmentKeySummary(), + }, + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/enrolment-keys").UsingGet()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + var searchTerm = "term"; + + // Act + await _enrolmentKeysClient.GetEnrolmentKeysAsync(searchTerm: searchTerm); + + _server.Should().HaveReceivedACall().AtAbsoluteUrl($"{_server.Urls[0]}{_orgRoute}/enrolment-keys?search={searchTerm}"); + } + + + [Test] + public async Task Should_make_call_to_api_with_include_disabled_queryString_when_calling_GetEnrolmentKeysAsync() + { + // Arrange + var response = new PaginatedResponseModel() + { + Items = new List + { + new EnrolmentKeySummary(), + }, + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/enrolment-keys").UsingGet()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + var includeDisabled = true; + + // Act + await _enrolmentKeysClient.GetEnrolmentKeysAsync(includeDisabled: includeDisabled); + + // Assert + _server.Should().HaveReceivedACall().AtAbsoluteUrl($"{_server.Urls[0]}{_orgRoute}/enrolment-keys?include_disabled={includeDisabled}"); + } + + [Test] + public async Task Should_make_call_to_api_with_sort_queryString_when_calling_GetEnrolmentKeysAsync() + { + // Arrange + var response = new PaginatedResponseModel() + { + Items = new List + { + new EnrolmentKeySummary(), + }, + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/enrolment-keys").UsingGet()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + var sort = EnrolmentKeySortOrder.UsesRemaining; + + // Act + await _enrolmentKeysClient.GetEnrolmentKeysAsync(sortOrder: sort); + + // Assert + _server.Should().HaveReceivedACall().AtAbsoluteUrl($"{_server.Urls[0]}{_orgRoute}/enrolment-keys?sort={sort}"); + } + + [Test] + public async Task Should_make_call_to_api_with_page_queryString_when_calling_GetEnrolmentKeysAsync() + { + // Arrange + var response = new PaginatedResponseModel() + { + Items = new List + { + new EnrolmentKeySummary(), + }, + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/enrolment-keys").UsingGet()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + var page = 2; + + // Act + await _enrolmentKeysClient.GetEnrolmentKeysAsync(pageNumber: page); + + // Assert + _server.Should().HaveReceivedACall().AtAbsoluteUrl($"{_server.Urls[0]}{_orgRoute}/enrolment-keys?page={page}"); + } + + [Test] + public async Task Should_make_call_to_api_with_per_page_queryString_when_calling_GetEnrolmentKeysAsync() + { + // Arrange + var response = new PaginatedResponseModel() + { + Items = new List + { + new EnrolmentKeySummary(), + }, + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/enrolment-keys").UsingGet()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + var perPage = 2; + + // Act + await _enrolmentKeysClient.GetEnrolmentKeysAsync(perPage: perPage); + + // Assert + _server.Should().HaveReceivedACall().AtAbsoluteUrl($"{_server.Urls[0]}{_orgRoute}/enrolment-keys?per_page={perPage}"); + } + + [Test] + public async Task Should_return_a_full_enrolment_key_model_when_calling_CreateAsync() + { + // Arrange + var response = new EnrolmentKey(); + + var createModel = new EnrolmentKeyCreate(); + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/enrolment-keys").UsingPost()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + + + // Act + var result = await _enrolmentKeysClient.CreateAsync(createModel); + + // Assert + result.Should().NotBeNull(); + } + + [Test] + public async Task Should_return_a_full_enrolment_key_model_when_calling_GetAsync() + { + // Arrange + var response = new EnrolmentKey(); + + var enrolmentKeyId = EnrolmentKeyId.FromInt(12); + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/enrolment-keys/{enrolmentKeyId}").UsingGet()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + + + // Act + var result = await _enrolmentKeysClient.GetAsync(enrolmentKeyId); + + // Assert + result.Should().NotBeNull(); + } + + [Test] + public async Task Should_return_a_full_enrolment_key_model_when_calling_UpdateAsync() + { + // Arrange + var response = new EnrolmentKey(); + + var enrolmentKeyId = EnrolmentKeyId.FromInt(12); + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/enrolment-keys/{enrolmentKeyId}").UsingPatch()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + var patchBuilder = new PatchBuilder().Set(e => e.Description, "New Value"); + + + // Act + var result = await _enrolmentKeysClient.UpdateAsync(enrolmentKeyId, patchBuilder); + + // Assert + result.Should().NotBeNull(); + } + + [Test] + public async Task Should_return_a_full_enrolment_key_model_when_calling_EnableAsync() + { + // Arrange + var response = new EnrolmentKey(); + + var enrolmentKeyId = EnrolmentKeyId.FromInt(12); + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/enrolment-keys/{enrolmentKeyId}/enable").UsingPut()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + // Act + var result = await _enrolmentKeysClient.EnableAsync(enrolmentKeyId); + + // Assert + result.Should().NotBeNull(); + } + + [Test] + public async Task Should_return_a_full_enrolment_key_model_when_calling_DisableAsync() + { + // Arrange + var response = new EnrolmentKey(); + + var enrolmentKeyId = EnrolmentKeyId.FromInt(12); + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/enrolment-keys/{enrolmentKeyId}/disable").UsingPut()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + // Act + var result = await _enrolmentKeysClient.DisableAsync(enrolmentKeyId); + + // Assert + result.Should().NotBeNull(); + } + + [Test] + public async Task Should_return_number_of_keys_modified_when_calling_BulkEnableAsync() + { + // Arrange + var response = new BulkKeyActionResult() + { + KeysModified = 2, + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/enrolment-keys/enable").UsingPut()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + var keys = new EnrolmentKeyId[] { EnrolmentKeyId.FromInt(1), EnrolmentKeyId.FromInt(2) }; + + // Act + var result = await _enrolmentKeysClient.BulkEnableAsync(keys); + + // Assert + result.Should().Be(keys.Length); + } + + [Test] + public async Task Should_return_number_of_keys_modified_when_calling_BulkDisableAsync() + { + // Arrange + var response = new BulkKeyActionResult() + { + KeysModified = 2, + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/enrolment-keys/disable").UsingPut()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + var keys = new EnrolmentKeyId[] { EnrolmentKeyId.FromInt(1), EnrolmentKeyId.FromInt(2) }; + + // Act + var result = await _enrolmentKeysClient.BulkDisableAsync(keys); + + // Assert + result.Should().Be(keys.Length); + } +} diff --git a/tests/Enclave.Sdk.Api.Tests/Clients/LogsClientTests.cs b/tests/Enclave.Sdk.Api.Tests/Clients/LogsClientTests.cs new file mode 100644 index 0000000..50f3344 --- /dev/null +++ b/tests/Enclave.Sdk.Api.Tests/Clients/LogsClientTests.cs @@ -0,0 +1,125 @@ +using Enclave.Sdk.Api.Clients; +using Enclave.Sdk.Api.Data.Logging; +using Enclave.Sdk.Api.Data.Organisations; +using Enclave.Sdk.Api.Data.Pagination; +using NUnit.Framework; +using System.Text.Json; +using WireMock.RequestBuilders; +using WireMock.ResponseBuilders; +using WireMock.Server; +using WireMock.FluentAssertions; +using FluentAssertions; + +namespace Enclave.Sdk.Api.Tests.Clients; + +public class LogsClientTests +{ + private LogsClient _logsClient; + private WireMockServer _server; + private string _orgRoute; + private JsonSerializerOptions _serializerOptions = new JsonSerializerOptions + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + }; + + [SetUp] + public void Setup() + { + _server = WireMockServer.Start(); + + var httpClient = new HttpClient + { + BaseAddress = new Uri(_server.Urls[0]), + }; + + var organisationId = OrganisationId.New(); + _orgRoute = $"/org/{organisationId}"; + + + _logsClient = new LogsClient(httpClient, $"org/{organisationId}"); + } + + [Test] + public async Task Should_return_a_list_of_logs_in_pagination_format() + { + // Arrange + var responseModel = new PaginatedResponseModel + { + Items = new List + { + new LogEntry { }, + new LogEntry { }, + }, + Links = new PaginationLinks(), + Metadata = new PaginationMetadata(), + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/logs").UsingGet()) + .RespondWith( + Response.Create() + .WithStatusCode(200) + .WithBody(JsonSerializer.Serialize(responseModel, _serializerOptions))); + + // Act + var result = await _logsClient.GetLogsAsync(); + + // Assert + result.Should().NotBeNull(); + result.Items.Should().NotBeNull(); + } + + [Test] + public async Task Should_make_call_to_api_with_page_queryString() + { + // Arrange + var pageNumber = 1; + + var responseModel = new PaginatedResponseModel + { + Items = new List(), + Links = new PaginationLinks(), + Metadata = new PaginationMetadata(), + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/logs").UsingGet()) + .RespondWith( + Response.Create() + .WithStatusCode(200) + .WithBody(JsonSerializer.Serialize(responseModel, _serializerOptions))); + + // Act + var result = await _logsClient.GetLogsAsync(pageNumber: pageNumber); + + // Assert + _server.Should().HaveReceivedACall().AtAbsoluteUrl($"{_server.Urls[0]}{_orgRoute}/logs?page={pageNumber}"); + } + + [Test] + public async Task Should_make_call_to_api_with_per_page_queryString() + { + // Arrange + var perPage = 1; + + var responseModel = new PaginatedResponseModel + { + Items = new List(), + Links = new PaginationLinks(), + Metadata = new PaginationMetadata(), + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/logs").UsingGet()) + .RespondWith( + Response.Create() + .WithStatusCode(200) + .WithBody(JsonSerializer.Serialize(responseModel, _serializerOptions))); + + // Act + var result = await _logsClient.GetLogsAsync(perPage: perPage); + + // Assert + _server.Should().HaveReceivedACall().AtAbsoluteUrl($"{_server.Urls[0]}{_orgRoute}/logs?per_page={perPage}"); + } +} diff --git a/tests/Enclave.Sdk.Api.Tests/Clients/PoliciesClientTests.cs b/tests/Enclave.Sdk.Api.Tests/Clients/PoliciesClientTests.cs new file mode 100644 index 0000000..69533c2 --- /dev/null +++ b/tests/Enclave.Sdk.Api.Tests/Clients/PoliciesClientTests.cs @@ -0,0 +1,464 @@ +using Enclave.Sdk.Api.Clients; +using Enclave.Sdk.Api.Data.Pagination; +using Enclave.Sdk.Api.Data.Policies; +using FluentAssertions; +using NUnit.Framework; +using System.Text.Json; +using WireMock.RequestBuilders; +using WireMock.ResponseBuilders; +using WireMock.Server; +using WireMock.FluentAssertions; +using Enclave.Sdk.Api.Data; +using Enclave.Sdk.Api.Data.PatchModel; +using Enclave.Sdk.Api.Data.Policies.Enum; +using Enclave.Sdk.Api.Data.Organisations; + +namespace Enclave.Sdk.Api.Tests.Clients; + +public class PoliciesClientTests +{ + private PoliciesClient _policiesClient; + private WireMockServer _server; + private string _orgRoute; + private JsonSerializerOptions _serializerOptions = new() + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + }; + + [SetUp] + public void Setup() + { + _server = WireMockServer.Start(); + + var httpClient = new HttpClient + { + BaseAddress = new Uri(_server.Urls[0]), + }; + + var organisationId = OrganisationId.New(); + _orgRoute = $"/org/{organisationId}"; + + _policiesClient = new PoliciesClient(httpClient, $"org/{organisationId}"); + } + + [Test] + public async Task Should_return_a_paginated_response_model_when_calling_GetPoliciesAsync() + { + // Arrange + var response = new PaginatedResponseModel + { + Items = new List + { + new Policy { Description = "test"} + }, + Links = new PaginationLinks(), + Metadata = new PaginationMetadata(), + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/policies").UsingGet()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + // Act + var result = await _policiesClient.GetPoliciesAsync(); + + // Assert + result.Should().NotBeNull(); + } + + [Test] + public async Task Should_make_a_call_to_api_with_include_disabled_quertString_when_calling_GetPoliciesAsync() + { + // Arrange + var response = new PaginatedResponseModel + { + Items = new List + { + new Policy { Description = "test"} + }, + Links = new PaginationLinks(), + Metadata = new PaginationMetadata(), + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/policies").UsingGet()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + var includeDisabled = true; + + // Act + await _policiesClient.GetPoliciesAsync(includeDisabled: includeDisabled); + + // Assert + _server.Should().HaveReceivedACall().AtAbsoluteUrl($"{_server.Urls[0]}{_orgRoute}/policies?include_disabled={includeDisabled}"); + } + + [Test] + public async Task Should_make_a_call_to_api_with_search_quertString_when_calling_GetPoliciesAsync() + { + // Arrange + var response = new PaginatedResponseModel + { + Items = new List + { + new Policy { Description = "test"} + }, + Links = new PaginationLinks(), + Metadata = new PaginationMetadata(), + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/policies").UsingGet()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + var searchTerm = "term"; + + // Act + await _policiesClient.GetPoliciesAsync(searchTerm: searchTerm); + + // Assert + _server.Should().HaveReceivedACall().AtAbsoluteUrl($"{_server.Urls[0]}{_orgRoute}/policies?search={searchTerm}"); + } + + + [Test] + public async Task Should_make_a_call_to_api_with_sort_quertString_when_calling_GetPoliciesAsync() + { + // Arrange + var response = new PaginatedResponseModel + { + Items = new List + { + new Policy { Description = "test"} + }, + Links = new PaginationLinks(), + Metadata = new PaginationMetadata(), + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/policies").UsingGet()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + var sortOrder = PolicySortOrder.RecentlyCreated; + + // Act + await _policiesClient.GetPoliciesAsync(sortOrder: sortOrder); + + // Assert + _server.Should().HaveReceivedACall().AtAbsoluteUrl($"{_server.Urls[0]}{_orgRoute}/policies?sort={sortOrder}"); + } + + [Test] + public async Task Should_make_a_call_to_api_with_page_quertString_when_calling_GetPoliciesAsync() + { + // Arrange + var response = new PaginatedResponseModel + { + Items = new List + { + new Policy { Description = "test"} + }, + Links = new PaginationLinks(), + Metadata = new PaginationMetadata(), + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/policies").UsingGet()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + var page = 12; + + // Act + await _policiesClient.GetPoliciesAsync(pageNumber: page); + + // Assert + _server.Should().HaveReceivedACall().AtAbsoluteUrl($"{_server.Urls[0]}{_orgRoute}/policies?page={page}"); + } + + [Test] + public async Task Should_make_a_call_to_api_with_per_page_quertString_when_calling_GetPoliciesAsync() + { + // Arrange + var response = new PaginatedResponseModel + { + Items = new List + { + new Policy { Description = "test"} + }, + Links = new PaginationLinks(), + Metadata = new PaginationMetadata(), + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/policies").UsingGet()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + var page = 12; + + // Act + await _policiesClient.GetPoliciesAsync(perPage: page); + + // Assert + _server.Should().HaveReceivedACall().AtAbsoluteUrl($"{_server.Urls[0]}{_orgRoute}/policies?per_page={page}"); + } + + [Test] + public async Task Should_return_policy_model_when_calling_CreateAsync() + { + // Arrange + var response = new Policy + { + Description = "test", + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/policies").UsingPost()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + var createModel = new PolicyCreate + { + Description = "test", + }; + + // Act + var result = await _policiesClient.CreateAsync(createModel); + + // Assert + result.Should().NotBeNull(); + result.Description.Should().Be(response.Description); + } + + [Test] + public async Task Should_return_number_of_deleted_policies_when_calling_DeletePoliciesAsync() + { + // Arrange + var response = new BulkPolicyDeleteResult + { + PoliciesDeleted = 3, + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/policies").UsingDelete()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + // Act + var result = await _policiesClient.DeletePoliciesAsync(PolicyId.FromInt(2), PolicyId.FromInt(3), PolicyId.FromInt(5)); + + // Assert + result.Should().Be(3); + } + + [Test] + public async Task Should_return_policy_model_when_calling_GetAsync() + { + // Arrange + var response = new Policy + { + Id = PolicyId.FromInt(12), + Description = "test", + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/policies/{response.Id}").UsingGet()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + // Act + var result = await _policiesClient.GetAsync(PolicyId.FromInt(12)); + + // Assert + result.Should().NotBeNull(); + result.Description.Should().Be(response.Description); + } + + [Test] + public async Task Should_return_policy_model_when_calling_UpdateAsync() + { + // Arrange + var response = new Policy + { + Id = PolicyId.FromInt(12), + Description = "test", + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/policies/{response.Id}").UsingPatch()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + var builder = new PatchBuilder().Set(p => p.IsEnabled, false); + + // Act + var result = await _policiesClient.UpdateAsync(PolicyId.FromInt(12), builder); + + // Assert + result.Should().NotBeNull(); + result.Description.Should().Be(response.Description); + } + + [Test] + public async Task Should_return_policy_model_when_calling_DeleteAsync() + { + // Arrange + var response = new Policy + { + Id = PolicyId.FromInt(12), + Description = "test", + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/policies/{response.Id}").UsingDelete()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + // Act + var result = await _policiesClient.DeleteAsync(PolicyId.FromInt(12)); + + // Assert + result.Should().NotBeNull(); + result.Description.Should().Be(response.Description); + } + + [Test] + public async Task Should_return_policy_model_when_calling_EnableAsync() + { + // Arrange + var response = new Policy + { + Id = PolicyId.FromInt(12), + Description = "test", + IsEnabled = true, + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/policies/{response.Id}/enable").UsingPut()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + // Act + var result = await _policiesClient.EnableAsync(PolicyId.FromInt(12)); + + // Assert + result.Should().NotBeNull(); + result.IsEnabled.Should().Be(response.IsEnabled); + } + + [Test] + public async Task Should_return_policy_model_when_calling_DisableAsync() + { + // Arrange + var response = new Policy + { + Id = PolicyId.FromInt(12), + Description = "test", + IsEnabled = false, + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/policies/{response.Id}/disable").UsingPut()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + // Act + var result = await _policiesClient.DisableAsync(PolicyId.FromInt(12)); + + // Assert + result.Should().NotBeNull(); + result.IsEnabled.Should().Be(response.IsEnabled); + } + + [Test] + public async Task Should_return_number_of_deleted_policies_when_calling_EnablePoliciesAsync() + { + // Arrange + var response = new BulkPolicyUpdateResult + { + PoliciesUpdated = 3, + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/policies/enable").UsingPut()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + // Act + var result = await _policiesClient.EnablePoliciesAsync(PolicyId.FromInt(2), PolicyId.FromInt(3), PolicyId.FromInt(5)); + + // Assert + result.Should().Be(3); + } + + [Test] + public async Task Should_return_number_of_deleted_policies_when_calling_DisablePoliciesAsync() + { + // Arrange + var response = new BulkPolicyUpdateResult + { + PoliciesUpdated = 3, + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/policies/disable").UsingPut()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + // Act + var result = await _policiesClient.DisablePoliciesAsync(PolicyId.FromInt(2), PolicyId.FromInt(3), PolicyId.FromInt(5)); + + // Assert + result.Should().Be(3); + } +} diff --git a/tests/Enclave.Sdk.Api.Tests/Clients/TagClientTests.cs b/tests/Enclave.Sdk.Api.Tests/Clients/TagClientTests.cs index 5675c01..8366a33 100644 --- a/tests/Enclave.Sdk.Api.Tests/Clients/TagClientTests.cs +++ b/tests/Enclave.Sdk.Api.Tests/Clients/TagClientTests.cs @@ -1,5 +1,6 @@ using System.Text.Json; using Enclave.Sdk.Api.Clients; +using Enclave.Sdk.Api.Data.Organisations; using Enclave.Sdk.Api.Data.Pagination; using Enclave.Sdk.Api.Data.Tags; using FluentAssertions; @@ -31,11 +32,11 @@ public void Setup() BaseAddress = new Uri(_server.Urls[0]), }; - var orgId = "testId"; + var organisationId = OrganisationId.New(); + _orgRoute = $"/org/{organisationId}"; - _orgRoute = $"/org/{orgId}"; - _tagClient = new TagsClient(httpClient, _orgRoute); + _tagClient = new TagsClient(httpClient, $"org/{organisationId}"); } [Test] diff --git a/tests/Enclave.Sdk.Api.Tests/Clients/UnapprovedSystemsClientTests.cs b/tests/Enclave.Sdk.Api.Tests/Clients/UnapprovedSystemsClientTests.cs new file mode 100644 index 0000000..df2cc8b --- /dev/null +++ b/tests/Enclave.Sdk.Api.Tests/Clients/UnapprovedSystemsClientTests.cs @@ -0,0 +1,371 @@ +using Enclave.Sdk.Api.Clients; +using Enclave.Sdk.Api.Data.Pagination; +using Enclave.Sdk.Api.Data.UnaprrovedSystems; +using FluentAssertions; +using NUnit.Framework; +using System.Text.Json; +using WireMock.Server; +using WireMock.FluentAssertions; +using WireMock.RequestBuilders; +using WireMock.ResponseBuilders; +using Enclave.Sdk.Api.Data; +using Enclave.Sdk.Api.Data.PatchModel; +using Enclave.Sdk.Api.Data.UnaprrovedSystems.Enum; +using Enclave.Sdk.Api.Data.Organisations; + +namespace Enclave.Sdk.Api.Tests.Clients; + +public class UnapprovedSystemsClientTests +{ + private UnapprovedSystemsClient _unapprovedSystemsClient; + private WireMockServer _server; + private string _orgRoute; + private JsonSerializerOptions _serializerOptions = new() + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + }; + + public UnapprovedSystemsClientTests() + { + _server = WireMockServer.Start(); + + var httpClient = new HttpClient + { + BaseAddress = new Uri(_server.Urls[0]), + }; + + var organisationId = OrganisationId.New(); + _orgRoute = $"/org/{organisationId}"; + + _unapprovedSystemsClient = new UnapprovedSystemsClient(httpClient, $"org/{organisationId}"); + } + + [Test] + public async Task Should_return_a_paginated_response_model_when_calling_GetSystemsAsync() + { + // Arrange + var response = new PaginatedResponseModel + { + Items = new List + { + new UnapprovedSystemSummary { Description = "test"} + }, + Links = new PaginationLinks(), + Metadata = new PaginationMetadata(), + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/unapproved-systems").UsingGet()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + // Act + var result = await _unapprovedSystemsClient.GetSystemsAsync(); + + // Assert + result.Should().NotBeNull(); + } + + [Test] + public async Task Should_make_a_call_to_api_with_enrolment_key_quertString_when_calling_GetSystemsAsync() + { + // Arrange + var response = new PaginatedResponseModel + { + Items = new List + { + new UnapprovedSystemSummary { Description = "test"} + }, + Links = new PaginationLinks(), + Metadata = new PaginationMetadata(), + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/unapproved-systems").UsingGet()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + var enrolmentKeyId = 12; + + // Act + await _unapprovedSystemsClient.GetSystemsAsync(enrolmentKeyId: enrolmentKeyId); + + // Assert + _server.Should().HaveReceivedACall().AtAbsoluteUrl($"{_server.Urls[0]}{_orgRoute}/unapproved-systems?enrolment_key={enrolmentKeyId}"); + } + + [Test] + public async Task Should_make_a_call_to_api_with_search_quertString_when_calling_GetSystemsAsync() + { + // Arrange + var response = new PaginatedResponseModel + { + Items = new List + { + new UnapprovedSystemSummary { Description = "test"} + }, + Links = new PaginationLinks(), + Metadata = new PaginationMetadata(), + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/unapproved-systems").UsingGet()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + var searchTerm = "term"; + + // Act + await _unapprovedSystemsClient.GetSystemsAsync(searchTerm: searchTerm); + + // Assert + _server.Should().HaveReceivedACall().AtAbsoluteUrl($"{_server.Urls[0]}{_orgRoute}/unapproved-systems?search={searchTerm}"); + } + + + [Test] + public async Task Should_make_a_call_to_api_with_sort_quertString_when_calling_GetSystemsAsync() + { + // Arrange + var response = new PaginatedResponseModel + { + Items = new List + { + new UnapprovedSystemSummary { Description = "test"} + }, + Links = new PaginationLinks(), + Metadata = new PaginationMetadata(), + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/unapproved-systems").UsingGet()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + var sortOrder = UnapprovedSystemQuerySortMode.RecentlyEnrolled; + + // Act + await _unapprovedSystemsClient.GetSystemsAsync(sortOrder: sortOrder); + + // Assert + _server.Should().HaveReceivedACall().AtAbsoluteUrl($"{_server.Urls[0]}{_orgRoute}/unapproved-systems?sort={sortOrder}"); + } + + [Test] + public async Task Should_make_a_call_to_api_with_page_quertString_when_calling_GetSystemsAsync() + { + // Arrange + var response = new PaginatedResponseModel + { + Items = new List + { + new UnapprovedSystemSummary { Description = "test"} + }, + Links = new PaginationLinks(), + Metadata = new PaginationMetadata(), + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/unapproved-systems").UsingGet()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + var page = 12; + + // Act + await _unapprovedSystemsClient.GetSystemsAsync(pageNumber: page); + + // Assert + _server.Should().HaveReceivedACall().AtAbsoluteUrl($"{_server.Urls[0]}{_orgRoute}/unapproved-systems?page={page}"); + } + + [Test] + public async Task Should_make_a_call_to_api_with_per_page_quertString_when_calling_GetSystemsAsync() + { + // Arrange + var response = new PaginatedResponseModel + { + Items = new List + { + new UnapprovedSystemSummary { Description = "test"} + }, + Links = new PaginationLinks(), + Metadata = new PaginationMetadata(), + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/unapproved-systems").UsingGet()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + var page = 12; + + // Act + await _unapprovedSystemsClient.GetSystemsAsync(perPage: page); + + // Assert + _server.Should().HaveReceivedACall().AtAbsoluteUrl($"{_server.Urls[0]}{_orgRoute}/unapproved-systems?per_page={page}"); + } + + [Test] + public async Task Should_return_number_of_declined_systems_when_calling_DeclineSystems() + { + var response = new BulkUnapprovedSystemDeclineResult + { + SystemsDeclined = 2, + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/unapproved-systems").UsingDelete()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + // Act + var result = await _unapprovedSystemsClient.DeclineSystems(SystemId.FromString("system1"), SystemId.FromString("system2")); + + // Assert + result.Should().Be(2); + } + + [Test] + public async Task Should_return_unapproved_system_detail_model_when_calling_GetAsync() + { + // Arrange + var response = new UnapprovedSystem + { + SystemId = SystemId.FromString("newId"), + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/unapproved-systems/{response.SystemId}").UsingGet()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + // Act + var result = await _unapprovedSystemsClient.GetAsync(SystemId.FromString("newId")); + + // Assert + result.Should().NotBeNull(); + result.SystemId.Should().Be(response.SystemId); + } + + [Test] + public async Task Should_return_unapproved_system_detail_model_when_calling_UpdateAsync() + { + // Arrange + var response = new UnapprovedSystem + { + SystemId = SystemId.FromString("newId"), + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/unapproved-systems/{response.SystemId}").UsingPatch()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + var builder = new PatchBuilder().Set(u => u.Description, "New System"); + + // Act + var result = await _unapprovedSystemsClient.UpdateAsync(SystemId.FromString("newId"), builder); + + // Assert + result.Should().NotBeNull(); + result.SystemId.Should().Be(response.SystemId); + } + + [Test] + public async Task Should_return_unapproved_system_detail_model_when_calling_DeclineAsync() + { + // Arrange + var response = new UnapprovedSystem + { + SystemId = SystemId.FromString("newId"), + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/unapproved-systems/{response.SystemId}").UsingDelete()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + // Act + var result = await _unapprovedSystemsClient.DeclineAsync(SystemId.FromString("newId")); + + // Assert + result.Should().NotBeNull(); + result.SystemId.Should().Be(response.SystemId); + } + + [Test] + public async Task Should_not_throw_an_error_when_calling_ApproveAsync() + { + // Arrange + var response = new UnapprovedSystem + { + SystemId = SystemId.FromString("newId"), + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/unapproved-systems/{response.SystemId}/approve").UsingPut()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + // Act + await _unapprovedSystemsClient.ApproveAsync(SystemId.FromString("newId")); + } + + [Test] + public async Task Should_return_number_of_approved_systems_when_calling_ApproveSystemsAsync() + { + var response = new BulkUnapprovedSystemApproveResult + { + SystemsApproved = 2, + }; + + _server + .Given(Request.Create().WithPath($"{_orgRoute}/unapproved-systems/approve").UsingPut()) + .RespondWith( + Response.Create() + .WithSuccess() + .WithHeader("Content-Type", "application/json") + .WithBody(JsonSerializer.Serialize(response, _serializerOptions))); + + // Act + var result = await _unapprovedSystemsClient.ApproveSystemsAsync(SystemId.FromString("system1") , SystemId.FromString("system2")); + + // Assert + result.Should().Be(2); + } +} \ No newline at end of file diff --git a/tests/Enclave.Sdk.Api.Tests/EnclaveClientTests.cs b/tests/Enclave.Sdk.Api.Tests/EnclaveClientTests.cs index 0091ac5..bdd1995 100644 --- a/tests/Enclave.Sdk.Api.Tests/EnclaveClientTests.cs +++ b/tests/Enclave.Sdk.Api.Tests/EnclaveClientTests.cs @@ -1,6 +1,6 @@ using System.Text.Json; -using Enclave.Sdk.Api.Data; using Enclave.Sdk.Api.Data.Account; +using Enclave.Sdk.Api.Data.Organisations; using FluentAssertions; using NUnit.Framework; using WireMock.RequestBuilders;