diff --git a/source/Octopus.Client.Tests/Conventions/AccountTypeConventions.cs b/source/Octopus.Client.Tests/Conventions/AccountTypeConventions.cs new file mode 100644 index 000000000..4f4759899 --- /dev/null +++ b/source/Octopus.Client.Tests/Conventions/AccountTypeConventions.cs @@ -0,0 +1,41 @@ +using System; +using System.Linq; +using System.Reflection; +using NUnit.Framework; +using Octopus.Client.Extensions; +using Octopus.Client.Model.Accounts; + +namespace Octopus.Client.Tests.Conventions +{ + [TestFixture] + public class AccountTypeConventions + { + private static readonly TypeInfo[] ExportedTypes = typeof(AccountResource).GetTypeInfo().Assembly.GetExportedTypes().Select(t => t.GetTypeInfo()).ToArray(); + + [Test] + public void AllAccountResourceTypeCanBeMappedToAnAccountType() + { + var derivedAccountTypes = ExportedTypes + .Where(t => !t.IsAbstract) + .Where(t => typeof(AccountResource).IsAssignableFrom(t)) + .ToArray(); + + var typesThatCannotBeMapped = derivedAccountTypes.Where(t => + { + try + { + t.DetermineAccountType(); + return false; + } + catch (ArgumentException) + { + return true; + } + }) + .ToArray(); + + if (typesThatCannotBeMapped.Any()) + Assert.Fail($"The following AccountResource types cannot be mapped to an AccountType: " + typesThatCannotBeMapped.CommaSeperate()); + } + } +} \ No newline at end of file diff --git a/source/Octopus.Client.Tests/Conventions/ClientConventions.cs b/source/Octopus.Client.Tests/Conventions/ClientConventions.cs index d68d9950e..3e4ab4e09 100644 --- a/source/Octopus.Client.Tests/Conventions/ClientConventions.cs +++ b/source/Octopus.Client.Tests/Conventions/ClientConventions.cs @@ -25,9 +25,9 @@ public class ClientConventions private static readonly TypeInfo[] RepositoryInterfaceTypes = ExportedTypes .Where(t => t.IsInterface && t.Name.EndsWith("Repository")) - .Where(t => t.AsType() != typeof(IOctopusAsyncRepository)) + .Where(t => t.AsType() != typeof(IOctopusAsyncRepository) && t.AsType() != typeof(IResourceRepository)) #if SYNC_CLIENT - .Where(t => t.AsType() != typeof(IOctopusRepository)) + .Where(t => t.AsType() != typeof(IOctopusRepository) && t.AsType() != typeof(Sync.IResourceRepository)) #endif .ToArray(); diff --git a/source/Octopus.Client.Tests/PublicSurfaceAreaFixture.ThePublicSurfaceAreaShouldNotRegress..NETCore.approved.txt b/source/Octopus.Client.Tests/PublicSurfaceAreaFixture.ThePublicSurfaceAreaShouldNotRegress..NETCore.approved.txt index 88090395d..28f1827b5 100644 --- a/source/Octopus.Client.Tests/PublicSurfaceAreaFixture.ThePublicSurfaceAreaShouldNotRegress..NETCore.approved.txt +++ b/source/Octopus.Client.Tests/PublicSurfaceAreaFixture.ThePublicSurfaceAreaShouldNotRegress..NETCore.approved.txt @@ -243,6 +243,64 @@ Octopus.Client } Octopus.Client.Editors.Async { + class AccountEditor`2 + Octopus.Client.Editors.Async.IResourceEditor + Octopus.Client.Editors.Async.IResourceBuilder + { + .ctor(Octopus.Client.Repositories.Async.IAccountRepository) + Octopus.Client.Editors.Async.TAccountResource Instance { get; } + Task CreateOrModify(String) + Octopus.Client.Editors.Async.TAccountEditor Customize(Action) + Task FindByName(String) + Task Save() + Task Usages() + } + class AmazonWebServicesAccountEditor + Octopus.Client.Editors.Async.IResourceEditor + Octopus.Client.Editors.Async.IResourceBuilder + Octopus.Client.Editors.Async.AccountEditor + { + .ctor(Octopus.Client.Repositories.Async.IAccountRepository) + } + class AmazonWebServicesRoleAccountEditor + Octopus.Client.Editors.Async.IResourceEditor + Octopus.Client.Editors.Async.IResourceBuilder + Octopus.Client.Editors.Async.AccountEditor + { + .ctor(Octopus.Client.Repositories.Async.IAccountRepository) + } + class AzureServicePrincipalAccountEditor + Octopus.Client.Editors.Async.IResourceEditor + Octopus.Client.Editors.Async.IResourceBuilder + Octopus.Client.Editors.Async.AccountEditor + { + .ctor(Octopus.Client.Repositories.Async.IAccountRepository) + Task> ResourceGroups() + Task> StorageAccounts() + Task> WebSites() + } + class AzureSubscriptionAccountEditor + Octopus.Client.Editors.Async.IResourceEditor + Octopus.Client.Editors.Async.IResourceBuilder + Octopus.Client.Editors.Async.AccountEditor + { + .ctor(Octopus.Client.Repositories.Async.IAccountRepository) + Task> CloudServices(Octopus.Client.Model.Accounts.AzureSubscriptionAccountResource) + Task> StorageAccounts(Octopus.Client.Model.Accounts.AzureSubscriptionAccountResource) + Task> WebSites(Octopus.Client.Model.Accounts.AzureSubscriptionAccountResource) + } + class CertificateEditor + Octopus.Client.Editors.Async.IResourceEditor + Octopus.Client.Editors.Async.IResourceBuilder + { + .ctor(Octopus.Client.Repositories.Async.ICertificateRepository) + Octopus.Client.Model.CertificateResource Instance { get; } + Task Create(String, String) + Octopus.Client.Editors.Async.CertificateEditor Customize(Action) + Task FindByName(String) + Task Save() + Task Usages() + } class ChannelEditor Octopus.Client.Editors.Async.IResourceEditor Octopus.Client.Editors.Async.IResourceBuilder @@ -390,6 +448,13 @@ Octopus.Client.Editors.Async Task Delete(String) Task SaveAll() } + class SshKeyPairAccountEditor + Octopus.Client.Editors.Async.IResourceEditor + Octopus.Client.Editors.Async.IResourceBuilder + Octopus.Client.Editors.Async.AccountEditor + { + .ctor(Octopus.Client.Repositories.Async.IAccountRepository) + } class SubscriptionEditor Octopus.Client.Editors.Async.IResourceEditor Octopus.Client.Editors.Async.IResourceBuilder @@ -440,6 +505,13 @@ Octopus.Client.Editors.Async Task Load() Task Save() } + class UsernamePasswordAccountEditor + Octopus.Client.Editors.Async.IResourceEditor + Octopus.Client.Editors.Async.IResourceBuilder + Octopus.Client.Editors.Async.AccountEditor + { + .ctor(Octopus.Client.Repositories.Async.IAccountRepository) + } class VariableSetEditor Octopus.Client.Editors.Async.IResourceEditor Octopus.Client.Editors.Async.IResourceBuilder @@ -631,6 +703,7 @@ Octopus.Client.Extensions } abstract class TypeExtensions { + static Octopus.Client.Model.Accounts.AccountType DetermineAccountType(Type) static Object GetDefault(Type) } abstract class UriExtensions @@ -1160,6 +1233,13 @@ Octopus.Client.Model String Thumbprint { get; } Int32 Version { get; } } + class CertificateUsageResource + { + .ctor() + ICollection LibraryVariableSetUsages { get; set; } + ICollection ProjectUsages { get; set; } + ICollection TenantUsages { get; set; } + } class ChannelResource Octopus.Client.Extensibility.IResource Octopus.Client.Model.IAuditedResource @@ -3314,6 +3394,7 @@ Octopus.Client.Model Sensitive = 1 Certificate = 2 AmazonWebServicesAccount = 3 + AzureAccount = 4 } class VersioningStrategyResource { @@ -3392,6 +3473,12 @@ Octopus.Client.Model.Accounts .ctor() Octopus.Client.Model.Accounts.AccountType AccountType { get; } } + class AzureCloudService + { + .ctor() + String Location { get; set; } + String Name { get; set; } + } class AzureServicePrincipalAccountResource Octopus.Client.Extensibility.IResource Octopus.Client.Model.IAuditedResource @@ -3407,6 +3494,24 @@ Octopus.Client.Model.Accounts String ResourceManagementEndpointBaseUri { get; set; } String SubscriptionNumber { get; set; } String TenantId { get; set; } + class ResourceGroup + { + .ctor() + String Id { get; set; } + String Name { get; set; } + } + class WebSite + { + .ctor() + String Name { get; } + String ResourceGroup { get; set; } + } + } + class AzureStorageAccount + { + .ctor() + String Location { get; set; } + String Name { get; set; } } class AzureSubscriptionAccountResource Octopus.Client.Extensibility.IResource @@ -3421,6 +3526,19 @@ Octopus.Client.Model.Accounts String CertificateThumbprint { get; set; } String ServiceManagementEndpointBaseUri { get; set; } String SubscriptionNumber { get; set; } + class WebSite + { + .ctor() + String Name { get; } + String ResourceGroup { get; set; } + String WebSpace { get; set; } + } + } + class ResourceGroup + { + .ctor() + String Id { get; set; } + String Name { get; set; } } class SshKeyPairAccountResource Octopus.Client.Extensibility.IResource @@ -3451,6 +3569,82 @@ Octopus.Client.Model.Accounts Octopus.Client.Model.SensitiveValue Password { get; set; } String Username { get; set; } } + class WebSite + { + .ctor() + String Name { get; } + String ResourceGroup { get; set; } + } + class WebSite + { + .ctor() + String Name { get; } + String ResourceGroup { get; set; } + String WebSpace { get; set; } + } +} +Octopus.Client.Model.Accounts.Usages +{ + class AccountUsageResource + Octopus.Client.Extensibility.IResource + Octopus.Client.Model.IAuditedResource + Octopus.Client.Model.Resource + { + .ctor() + ICollection DeploymentProcesses { get; set; } + ICollection LibraryVariableSets { get; set; } + ICollection ProjectVariableSets { get; set; } + ICollection Releases { get; set; } + ICollection Targets { get; set; } + } + class LibraryVariableSetUsageEntry + { + .ctor() + String LibraryVariableSetId { get; set; } + String LibraryVariableSetName { get; set; } + } + class ProjectVariableSetUsage + { + .ctor() + Boolean IsCurrentlyBeingUsedInProject { get; set; } + String ProjectId { get; set; } + String ProjectName { get; set; } + String ProjectSlug { get; set; } + ICollection Releases { get; set; } + } + class ReleaseUsage + { + .ctor() + String ProjectId { get; set; } + String ProjectName { get; set; } + ICollection Releases { get; set; } + } + class ReleaseUsageEntry + { + .ctor() + String ReleaseId { get; set; } + String ReleaseVersion { get; set; } + } + class StepUsage + { + .ctor() + String ProjectId { get; set; } + String ProjectName { get; set; } + String ProjectSlug { get; set; } + ICollection Steps { get; set; } + } + class StepUsageEntry + { + .ctor() + String StepId { get; set; } + String StepName { get; set; } + } + class TargetUsageEntry + { + .ctor() + String TargetId { get; set; } + String TargetName { get; set; } + } } Octopus.Client.Model.DeploymentProcess { @@ -3516,6 +3710,7 @@ Octopus.Client.Model.Endpoints .ctor() String AccountId { get; set; } Octopus.Client.Model.CommunicationStyle CommunicationStyle { get; } + String ResourceGroupName { get; set; } String WebAppName { get; set; } String WebSpaceName { get; set; } } @@ -4029,6 +4224,7 @@ Octopus.Client.Operations Octopus.Client.Repositories.Async { interface IAccountRepository + Octopus.Client.Repositories.Async.IResourceRepository Octopus.Client.Repositories.Async.ICreate Octopus.Client.Repositories.Async.IModify Octopus.Client.Repositories.Async.IDelete @@ -4036,6 +4232,17 @@ Octopus.Client.Repositories.Async Octopus.Client.Repositories.Async.IFindByName Octopus.Client.Repositories.Async.IPaginate { + Octopus.Client.Model.Accounts.AccountType DetermineAccountType() + Task> FindAllOfType(Object) + Task FindByNameOfType(String) + Task> FindByNamesOfType(IEnumerable) + Task> FindManyOfType(Func, Object) + Task FindOneOfType(Func, Object) + Task GetAccountUsage(String) + Task GetOfType(String) + Task> GetOfType(String[]) + Task PaginateOfType(Func, Boolean>, Object) + Task RefreshOfType(Octopus.Client.Repositories.Async.TAccount) } interface IActionTemplateRepository Octopus.Client.Repositories.Async.ICreate @@ -4083,6 +4290,7 @@ Octopus.Client.Repositories.Async Task GetOctopusCertificate() } interface ICertificateRepository + Octopus.Client.Repositories.Async.IResourceRepository Octopus.Client.Repositories.Async.IGet Octopus.Client.Repositories.Async.IFindByName Octopus.Client.Repositories.Async.IPaginate @@ -4364,6 +4572,10 @@ Octopus.Client.Repositories.Async Task Modify(Octopus.Client.Model.ReleaseResource, Boolean) Task SnapshotVariables(Octopus.Client.Model.ReleaseResource) } + interface IResourceRepository + { + Octopus.Client.IOctopusAsyncClient Client { get; } + } interface IRetentionPolicyRepository { Task ApplyNow() diff --git a/source/Octopus.Client.Tests/PublicSurfaceAreaFixture.ThePublicSurfaceAreaShouldNotRegress..NETFramework.approved.txt b/source/Octopus.Client.Tests/PublicSurfaceAreaFixture.ThePublicSurfaceAreaShouldNotRegress..NETFramework.approved.txt index a2f0da94b..59c3e5fc6 100644 --- a/source/Octopus.Client.Tests/PublicSurfaceAreaFixture.ThePublicSurfaceAreaShouldNotRegress..NETFramework.approved.txt +++ b/source/Octopus.Client.Tests/PublicSurfaceAreaFixture.ThePublicSurfaceAreaShouldNotRegress..NETFramework.approved.txt @@ -406,6 +406,64 @@ Octopus.Client } Octopus.Client.Editors { + class AccountEditor`2 + Octopus.Client.Editors.IResourceEditor + Octopus.Client.Editors.IResourceBuilder + { + .ctor(Octopus.Client.Repositories.IAccountRepository) + Octopus.Client.Editors.TAccountResource Instance { get; } + Octopus.Client.Editors.TAccountEditor CreateOrModify(String) + Octopus.Client.Editors.TAccountEditor Customize(Action) + Octopus.Client.Editors.TAccountEditor FindByName(String) + Octopus.Client.Editors.TAccountEditor Save() + Octopus.Client.Model.Accounts.Usages.AccountUsageResource Usages() + } + class AmazonWebServicesAccountEditor + Octopus.Client.Editors.IResourceEditor + Octopus.Client.Editors.IResourceBuilder + Octopus.Client.Editors.AccountEditor + { + .ctor(Octopus.Client.Repositories.IAccountRepository) + } + class AmazonWebServicesRoleAccountEditor + Octopus.Client.Editors.IResourceEditor + Octopus.Client.Editors.IResourceBuilder + Octopus.Client.Editors.AccountEditor + { + .ctor(Octopus.Client.Repositories.IAccountRepository) + } + class AzureServicePrincipalAccountEditor + Octopus.Client.Editors.IResourceEditor + Octopus.Client.Editors.IResourceBuilder + Octopus.Client.Editors.AccountEditor + { + .ctor(Octopus.Client.Repositories.IAccountRepository) + List ResourceGroups() + List StorageAccounts() + List WebSites() + } + class AzureSubscriptionAccountEditor + Octopus.Client.Editors.IResourceEditor + Octopus.Client.Editors.IResourceBuilder + Octopus.Client.Editors.AccountEditor + { + .ctor(Octopus.Client.Repositories.IAccountRepository) + List CloudServices(Octopus.Client.Model.Accounts.AzureSubscriptionAccountResource) + List StorageAccounts(Octopus.Client.Model.Accounts.AzureSubscriptionAccountResource) + List WebSites(Octopus.Client.Model.Accounts.AzureSubscriptionAccountResource) + } + class CertificateEditor + Octopus.Client.Editors.IResourceEditor + Octopus.Client.Editors.IResourceBuilder + { + .ctor(Octopus.Client.Repositories.ICertificateRepository) + Octopus.Client.Model.CertificateResource Instance { get; } + Octopus.Client.Editors.CertificateEditor Create(String, String) + Octopus.Client.Editors.CertificateEditor Customize(Action) + Octopus.Client.Editors.CertificateEditor FindByName(String) + Octopus.Client.Editors.CertificateEditor Save() + Octopus.Client.Model.CertificateUsageResource Usages() + } class ChannelEditor Octopus.Client.Editors.IResourceEditor Octopus.Client.Editors.IResourceBuilder @@ -553,6 +611,13 @@ Octopus.Client.Editors Octopus.Client.Editors.ProjectTriggersEditor Delete(String) Octopus.Client.Editors.ProjectTriggersEditor SaveAll() } + class SshKeyPairAccountEditor + Octopus.Client.Editors.IResourceEditor + Octopus.Client.Editors.IResourceBuilder + Octopus.Client.Editors.AccountEditor + { + .ctor(Octopus.Client.Repositories.IAccountRepository) + } class SubscriptionEditor Octopus.Client.Editors.IResourceEditor Octopus.Client.Editors.IResourceBuilder @@ -603,6 +668,13 @@ Octopus.Client.Editors Octopus.Client.Editors.TenantVariablesEditor Load() Octopus.Client.Editors.TenantVariablesEditor Save() } + class UsernamePasswordAccountEditor + Octopus.Client.Editors.IResourceEditor + Octopus.Client.Editors.IResourceBuilder + Octopus.Client.Editors.AccountEditor + { + .ctor(Octopus.Client.Repositories.IAccountRepository) + } class VariableSetEditor Octopus.Client.Editors.IResourceEditor Octopus.Client.Editors.IResourceBuilder @@ -635,6 +707,64 @@ Octopus.Client.Editors } Octopus.Client.Editors.Async { + class AccountEditor`2 + Octopus.Client.Editors.Async.IResourceEditor + Octopus.Client.Editors.Async.IResourceBuilder + { + .ctor(Octopus.Client.Repositories.Async.IAccountRepository) + Octopus.Client.Editors.Async.TAccountResource Instance { get; } + Task CreateOrModify(String) + Octopus.Client.Editors.Async.TAccountEditor Customize(Action) + Task FindByName(String) + Task Save() + Task Usages() + } + class AmazonWebServicesAccountEditor + Octopus.Client.Editors.Async.IResourceEditor + Octopus.Client.Editors.Async.IResourceBuilder + Octopus.Client.Editors.Async.AccountEditor + { + .ctor(Octopus.Client.Repositories.Async.IAccountRepository) + } + class AmazonWebServicesRoleAccountEditor + Octopus.Client.Editors.Async.IResourceEditor + Octopus.Client.Editors.Async.IResourceBuilder + Octopus.Client.Editors.Async.AccountEditor + { + .ctor(Octopus.Client.Repositories.Async.IAccountRepository) + } + class AzureServicePrincipalAccountEditor + Octopus.Client.Editors.Async.IResourceEditor + Octopus.Client.Editors.Async.IResourceBuilder + Octopus.Client.Editors.Async.AccountEditor + { + .ctor(Octopus.Client.Repositories.Async.IAccountRepository) + Task> ResourceGroups() + Task> StorageAccounts() + Task> WebSites() + } + class AzureSubscriptionAccountEditor + Octopus.Client.Editors.Async.IResourceEditor + Octopus.Client.Editors.Async.IResourceBuilder + Octopus.Client.Editors.Async.AccountEditor + { + .ctor(Octopus.Client.Repositories.Async.IAccountRepository) + Task> CloudServices(Octopus.Client.Model.Accounts.AzureSubscriptionAccountResource) + Task> StorageAccounts(Octopus.Client.Model.Accounts.AzureSubscriptionAccountResource) + Task> WebSites(Octopus.Client.Model.Accounts.AzureSubscriptionAccountResource) + } + class CertificateEditor + Octopus.Client.Editors.Async.IResourceEditor + Octopus.Client.Editors.Async.IResourceBuilder + { + .ctor(Octopus.Client.Repositories.Async.ICertificateRepository) + Octopus.Client.Model.CertificateResource Instance { get; } + Task Create(String, String) + Octopus.Client.Editors.Async.CertificateEditor Customize(Action) + Task FindByName(String) + Task Save() + Task Usages() + } class ChannelEditor Octopus.Client.Editors.Async.IResourceEditor Octopus.Client.Editors.Async.IResourceBuilder @@ -782,6 +912,13 @@ Octopus.Client.Editors.Async Task Delete(String) Task SaveAll() } + class SshKeyPairAccountEditor + Octopus.Client.Editors.Async.IResourceEditor + Octopus.Client.Editors.Async.IResourceBuilder + Octopus.Client.Editors.Async.AccountEditor + { + .ctor(Octopus.Client.Repositories.Async.IAccountRepository) + } class SubscriptionEditor Octopus.Client.Editors.Async.IResourceEditor Octopus.Client.Editors.Async.IResourceBuilder @@ -832,6 +969,13 @@ Octopus.Client.Editors.Async Task Load() Task Save() } + class UsernamePasswordAccountEditor + Octopus.Client.Editors.Async.IResourceEditor + Octopus.Client.Editors.Async.IResourceBuilder + Octopus.Client.Editors.Async.AccountEditor + { + .ctor(Octopus.Client.Repositories.Async.IAccountRepository) + } class VariableSetEditor Octopus.Client.Editors.Async.IResourceEditor Octopus.Client.Editors.Async.IResourceBuilder @@ -1035,6 +1179,7 @@ Octopus.Client.Extensions } abstract class TypeExtensions { + static Octopus.Client.Model.Accounts.AccountType DetermineAccountType(Type) static Object GetDefault(Type) } abstract class UriExtensions @@ -1565,6 +1710,13 @@ Octopus.Client.Model String Thumbprint { get; } Int32 Version { get; } } + class CertificateUsageResource + { + .ctor() + ICollection LibraryVariableSetUsages { get; set; } + ICollection ProjectUsages { get; set; } + ICollection TenantUsages { get; set; } + } class ChannelResource Octopus.Client.Extensibility.IResource Octopus.Client.Model.IAuditedResource @@ -3727,6 +3879,7 @@ Octopus.Client.Model Sensitive = 1 Certificate = 2 AmazonWebServicesAccount = 3 + AzureAccount = 4 } class VersioningStrategyResource { @@ -3805,6 +3958,12 @@ Octopus.Client.Model.Accounts .ctor() Octopus.Client.Model.Accounts.AccountType AccountType { get; } } + class AzureCloudService + { + .ctor() + String Location { get; set; } + String Name { get; set; } + } class AzureServicePrincipalAccountResource Octopus.Client.Extensibility.IResource Octopus.Client.Model.IAuditedResource @@ -3820,6 +3979,24 @@ Octopus.Client.Model.Accounts String ResourceManagementEndpointBaseUri { get; set; } String SubscriptionNumber { get; set; } String TenantId { get; set; } + class ResourceGroup + { + .ctor() + String Id { get; set; } + String Name { get; set; } + } + class WebSite + { + .ctor() + String Name { get; } + String ResourceGroup { get; set; } + } + } + class AzureStorageAccount + { + .ctor() + String Location { get; set; } + String Name { get; set; } } class AzureSubscriptionAccountResource Octopus.Client.Extensibility.IResource @@ -3834,6 +4011,19 @@ Octopus.Client.Model.Accounts String CertificateThumbprint { get; set; } String ServiceManagementEndpointBaseUri { get; set; } String SubscriptionNumber { get; set; } + class WebSite + { + .ctor() + String Name { get; } + String ResourceGroup { get; set; } + String WebSpace { get; set; } + } + } + class ResourceGroup + { + .ctor() + String Id { get; set; } + String Name { get; set; } } class SshKeyPairAccountResource Octopus.Client.Extensibility.IResource @@ -3865,6 +4055,82 @@ Octopus.Client.Model.Accounts Octopus.Client.Model.SensitiveValue Password { get; set; } String Username { get; set; } } + class WebSite + { + .ctor() + String Name { get; } + String ResourceGroup { get; set; } + } + class WebSite + { + .ctor() + String Name { get; } + String ResourceGroup { get; set; } + String WebSpace { get; set; } + } +} +Octopus.Client.Model.Accounts.Usages +{ + class AccountUsageResource + Octopus.Client.Extensibility.IResource + Octopus.Client.Model.IAuditedResource + Octopus.Client.Model.Resource + { + .ctor() + ICollection DeploymentProcesses { get; set; } + ICollection LibraryVariableSets { get; set; } + ICollection ProjectVariableSets { get; set; } + ICollection Releases { get; set; } + ICollection Targets { get; set; } + } + class LibraryVariableSetUsageEntry + { + .ctor() + String LibraryVariableSetId { get; set; } + String LibraryVariableSetName { get; set; } + } + class ProjectVariableSetUsage + { + .ctor() + Boolean IsCurrentlyBeingUsedInProject { get; set; } + String ProjectId { get; set; } + String ProjectName { get; set; } + String ProjectSlug { get; set; } + ICollection Releases { get; set; } + } + class ReleaseUsage + { + .ctor() + String ProjectId { get; set; } + String ProjectName { get; set; } + ICollection Releases { get; set; } + } + class ReleaseUsageEntry + { + .ctor() + String ReleaseId { get; set; } + String ReleaseVersion { get; set; } + } + class StepUsage + { + .ctor() + String ProjectId { get; set; } + String ProjectName { get; set; } + String ProjectSlug { get; set; } + ICollection Steps { get; set; } + } + class StepUsageEntry + { + .ctor() + String StepId { get; set; } + String StepName { get; set; } + } + class TargetUsageEntry + { + .ctor() + String TargetId { get; set; } + String TargetName { get; set; } + } } Octopus.Client.Model.DeploymentProcess { @@ -3930,6 +4196,7 @@ Octopus.Client.Model.Endpoints .ctor() String AccountId { get; set; } Octopus.Client.Model.CommunicationStyle CommunicationStyle { get; } + String ResourceGroupName { get; set; } String WebAppName { get; set; } String WebSpaceName { get; set; } } @@ -4449,6 +4716,7 @@ Octopus.Client.Operations Octopus.Client.Repositories { interface IAccountRepository + Octopus.Client.Repositories.IResourceRepository Octopus.Client.Repositories.ICreate Octopus.Client.Repositories.IModify Octopus.Client.Repositories.IDelete @@ -4456,6 +4724,17 @@ Octopus.Client.Repositories Octopus.Client.Repositories.IFindByName Octopus.Client.Repositories.IPaginate { + Octopus.Client.Model.Accounts.AccountType DetermineAccountType() + List FindAllOfType(Object) + Octopus.Client.Repositories.TAccount FindByNameOfType(String) + List FindByNamesOfType(IEnumerable) + List FindManyOfType(Func, Object) + Octopus.Client.Repositories.TAccount FindOneOfType(Func, Object) + Octopus.Client.Model.Accounts.Usages.AccountUsageResource GetAccountUsage(String) + Octopus.Client.Repositories.TAccount GetOfType(String) + List GetOfType(String[]) + void PaginateOfType(Func, Boolean>, Object) + Octopus.Client.Repositories.TAccount RefreshOfType(Octopus.Client.Repositories.TAccount) } interface IActionTemplateRepository Octopus.Client.Repositories.ICreate @@ -4503,6 +4782,7 @@ Octopus.Client.Repositories Octopus.Client.Model.CertificateConfigurationResource GetOctopusCertificate() } interface ICertificateRepository + Octopus.Client.Repositories.IResourceRepository Octopus.Client.Repositories.IGet Octopus.Client.Repositories.IFindByName Octopus.Client.Repositories.IPaginate @@ -4784,6 +5064,10 @@ Octopus.Client.Repositories Octopus.Client.Model.ReleaseResource Modify(Octopus.Client.Model.ReleaseResource, Boolean) Octopus.Client.Model.ReleaseResource SnapshotVariables(Octopus.Client.Model.ReleaseResource) } + interface IResourceRepository + { + Octopus.Client.IOctopusClient Client { get; } + } interface IRetentionPolicyRepository { Octopus.Client.Model.TaskResource ApplyNow() @@ -4917,6 +5201,7 @@ Octopus.Client.Repositories Octopus.Client.Repositories.Async { interface IAccountRepository + Octopus.Client.Repositories.Async.IResourceRepository Octopus.Client.Repositories.Async.ICreate Octopus.Client.Repositories.Async.IModify Octopus.Client.Repositories.Async.IDelete @@ -4924,6 +5209,17 @@ Octopus.Client.Repositories.Async Octopus.Client.Repositories.Async.IFindByName Octopus.Client.Repositories.Async.IPaginate { + Octopus.Client.Model.Accounts.AccountType DetermineAccountType() + Task> FindAllOfType(Object) + Task FindByNameOfType(String) + Task> FindByNamesOfType(IEnumerable) + Task> FindManyOfType(Func, Object) + Task FindOneOfType(Func, Object) + Task GetAccountUsage(String) + Task GetOfType(String) + Task> GetOfType(String[]) + Task PaginateOfType(Func, Boolean>, Object) + Task RefreshOfType(Octopus.Client.Repositories.Async.TAccount) } interface IActionTemplateRepository Octopus.Client.Repositories.Async.ICreate @@ -4971,6 +5267,7 @@ Octopus.Client.Repositories.Async Task GetOctopusCertificate() } interface ICertificateRepository + Octopus.Client.Repositories.Async.IResourceRepository Octopus.Client.Repositories.Async.IGet Octopus.Client.Repositories.Async.IFindByName Octopus.Client.Repositories.Async.IPaginate @@ -5252,6 +5549,10 @@ Octopus.Client.Repositories.Async Task Modify(Octopus.Client.Model.ReleaseResource, Boolean) Task SnapshotVariables(Octopus.Client.Model.ReleaseResource) } + interface IResourceRepository + { + Octopus.Client.IOctopusAsyncClient Client { get; } + } interface IRetentionPolicyRepository { Task ApplyNow() diff --git a/source/Octopus.Client/Editors/AccountEditor.cs b/source/Octopus.Client/Editors/AccountEditor.cs new file mode 100644 index 000000000..0bdd1c31c --- /dev/null +++ b/source/Octopus.Client/Editors/AccountEditor.cs @@ -0,0 +1,82 @@ +using System; +using Octopus.Client.Model.Accounts; +using Octopus.Client.Model.Accounts.Usages; +using Octopus.Client.Repositories; + +namespace Octopus.Client.Editors +{ + public class AccountEditor : IResourceEditor + where TAccountResource : AccountResource, new() + where TAccountEditor : AccountEditor + { + protected readonly IAccountRepository Repository; + + public AccountEditor(IAccountRepository repository) + { + Repository = repository; + } + + public TAccountResource Instance { get; private set; } + + public TAccountEditor CreateOrModify(string name) + { + var existing = Repository.FindByName(name); + if (existing == null) + { + Instance = (TAccountResource) Repository.Create(new TAccountResource + { + Name = name + }); + } + else + { + if (!(existing is TAccountResource)) + { + throw new ArgumentException( + $"An account with that name exists but is not of type {typeof(TAccountResource).Name}"); + } + + Instance = (TAccountResource) existing; + } + + return (TAccountEditor) this; + } + + public TAccountEditor FindByName(string name) + { + var existing = Repository.FindByName(name); + if (existing == null) + { + throw new ArgumentException($"An account with the name {name} could not be found"); + } + else + { + if (!(existing is TAccountResource)) + { + throw new ArgumentException($"An account with that name exists but is not of type {typeof(TAccountResource).Name}"); + } + + Instance = (TAccountResource)existing; + } + + return (TAccountEditor)this; + } + + public virtual TAccountEditor Customize(Action customize) + { + customize?.Invoke(Instance); + return (TAccountEditor)this; + } + + public virtual TAccountEditor Save() + { + Instance = (TAccountResource)Repository.Modify(Instance); + return (TAccountEditor)this; + } + + public AccountUsageResource Usages() + { + return Repository.Client.Get(Instance.Link("Usages")); + } + } +} \ No newline at end of file diff --git a/source/Octopus.Client/Editors/AmazonWebServicesAccountEditor.cs b/source/Octopus.Client/Editors/AmazonWebServicesAccountEditor.cs new file mode 100644 index 000000000..a2849cac9 --- /dev/null +++ b/source/Octopus.Client/Editors/AmazonWebServicesAccountEditor.cs @@ -0,0 +1,12 @@ +using Octopus.Client.Model.Accounts; +using Octopus.Client.Repositories; + +namespace Octopus.Client.Editors +{ + public class AmazonWebServicesAccountEditor : AccountEditor + { + public AmazonWebServicesAccountEditor(IAccountRepository repository) : base(repository) + { + } + } +} \ No newline at end of file diff --git a/source/Octopus.Client/Editors/AmazonWebServicesRoleAccountEditor.cs b/source/Octopus.Client/Editors/AmazonWebServicesRoleAccountEditor.cs new file mode 100644 index 000000000..a6152693c --- /dev/null +++ b/source/Octopus.Client/Editors/AmazonWebServicesRoleAccountEditor.cs @@ -0,0 +1,12 @@ +using Octopus.Client.Model.Accounts; +using Octopus.Client.Repositories; + +namespace Octopus.Client.Editors +{ + public class AmazonWebServicesRoleAccountEditor : AccountEditor + { + public AmazonWebServicesRoleAccountEditor(IAccountRepository repository) : base(repository) + { + } + } +} \ No newline at end of file diff --git a/source/Octopus.Client/Editors/Async/AccountEditor.cs b/source/Octopus.Client/Editors/Async/AccountEditor.cs new file mode 100644 index 000000000..4f73ba521 --- /dev/null +++ b/source/Octopus.Client/Editors/Async/AccountEditor.cs @@ -0,0 +1,82 @@ +using System; +using System.Threading.Tasks; +using Octopus.Client.Model.Accounts; +using Octopus.Client.Model.Accounts.Usages; +using Octopus.Client.Repositories.Async; + +namespace Octopus.Client.Editors.Async +{ + public class AccountEditor : IResourceEditor + where TAccountResource : AccountResource, new() + where TAccountEditor : AccountEditor + { + protected readonly IAccountRepository Repository; + + public AccountEditor(IAccountRepository repository) + { + Repository = repository; + } + + public TAccountResource Instance { get; private set; } + + public async Task CreateOrModify(string name) + { + var existing = await Repository.FindByName(name); + if (existing == null) + { + Instance = (TAccountResource)await Repository.Create(new TAccountResource + { + Name = name + }); + } + else + { + if (!(existing is TAccountResource)) + { + throw new ArgumentException($"An account with that name exists but is not of type {typeof(TAccountResource).Name}"); + } + + Instance = (TAccountResource)existing; + } + + return (TAccountEditor)this; + } + + public async Task FindByName(string name) + { + var existing = await Repository.FindByName(name); + if (existing == null) + { + throw new ArgumentException($"An account with the name {name} could not be found"); + } + else + { + if (!(existing is TAccountResource)) + { + throw new ArgumentException($"An account with that name exists but is not of type {typeof(TAccountResource).Name}"); + } + + Instance = (TAccountResource)existing; + } + + return (TAccountEditor)this; + } + + public virtual TAccountEditor Customize(Action customize) + { + customize?.Invoke(Instance); + return (TAccountEditor)this; + } + + public virtual async Task Save() + { + Instance = (TAccountResource)await Repository.Modify(Instance).ConfigureAwait(false); + return (TAccountEditor)this; + } + + public Task Usages() + { + return Repository.Client.Get(Instance.Link("Usages")); + } + } +} \ No newline at end of file diff --git a/source/Octopus.Client/Editors/Async/AmazonWebServicesAccountEditor.cs b/source/Octopus.Client/Editors/Async/AmazonWebServicesAccountEditor.cs new file mode 100644 index 000000000..82f01ffe2 --- /dev/null +++ b/source/Octopus.Client/Editors/Async/AmazonWebServicesAccountEditor.cs @@ -0,0 +1,12 @@ +using Octopus.Client.Model.Accounts; +using Octopus.Client.Repositories.Async; + +namespace Octopus.Client.Editors.Async +{ + public class AmazonWebServicesAccountEditor : AccountEditor + { + public AmazonWebServicesAccountEditor(IAccountRepository repository) : base(repository) + { + } + } +} \ No newline at end of file diff --git a/source/Octopus.Client/Editors/Async/AmazonWebServicesRoleAccountEditor.cs b/source/Octopus.Client/Editors/Async/AmazonWebServicesRoleAccountEditor.cs new file mode 100644 index 000000000..9f5215e82 --- /dev/null +++ b/source/Octopus.Client/Editors/Async/AmazonWebServicesRoleAccountEditor.cs @@ -0,0 +1,12 @@ +using Octopus.Client.Model.Accounts; +using Octopus.Client.Repositories.Async; + +namespace Octopus.Client.Editors.Async +{ + public class AmazonWebServicesRoleAccountEditor : AccountEditor + { + public AmazonWebServicesRoleAccountEditor(IAccountRepository repository) : base(repository) + { + } + } +} \ No newline at end of file diff --git a/source/Octopus.Client/Editors/Async/AzureServicePrincipalAccountEditor.cs b/source/Octopus.Client/Editors/Async/AzureServicePrincipalAccountEditor.cs new file mode 100644 index 000000000..b0381794d --- /dev/null +++ b/source/Octopus.Client/Editors/Async/AzureServicePrincipalAccountEditor.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Octopus.Client.Model.Accounts; +using Octopus.Client.Repositories.Async; + +namespace Octopus.Client.Editors.Async +{ + public class AzureServicePrincipalAccountEditor : AccountEditor + { + public AzureServicePrincipalAccountEditor(IAccountRepository repository) : base(repository) + { + } + + public Task> ResourceGroups() + { + return Repository.Client.Get>(Instance.Link("ResourceGroups")); + } + + public Task> WebSites() + { + return Repository.Client.Get>(Instance.Link("WebSites")); + } + + public Task> StorageAccounts() + { + return Repository.Client.Get>(Instance.Link("StorageAccounts")); + } + } +} \ No newline at end of file diff --git a/source/Octopus.Client/Editors/Async/AzureSubscriptionAccountEditor.cs b/source/Octopus.Client/Editors/Async/AzureSubscriptionAccountEditor.cs new file mode 100644 index 000000000..76929b866 --- /dev/null +++ b/source/Octopus.Client/Editors/Async/AzureSubscriptionAccountEditor.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Octopus.Client.Model.Accounts; +using Octopus.Client.Repositories.Async; + +namespace Octopus.Client.Editors.Async +{ + public class AzureSubscriptionAccountEditor : AccountEditor + { + public AzureSubscriptionAccountEditor(IAccountRepository repository) : base(repository) + { + } + + public Task> CloudServices(AzureSubscriptionAccountResource account) + { + return Repository.Client.Get>(account.Link("CloudServices")); + } + + public Task> StorageAccounts(AzureSubscriptionAccountResource account) + { + return Repository.Client.Get>(account.Link("StorageAccounts")); + } + + public Task> WebSites(AzureSubscriptionAccountResource account) + { + return Repository.Client.Get>(account.Link("WebSites")); + } + } +} \ No newline at end of file diff --git a/source/Octopus.Client/Editors/Async/CertificateEditor.cs b/source/Octopus.Client/Editors/Async/CertificateEditor.cs new file mode 100644 index 000000000..c6f192212 --- /dev/null +++ b/source/Octopus.Client/Editors/Async/CertificateEditor.cs @@ -0,0 +1,64 @@ +using System; +using System.Threading.Tasks; +using Octopus.Client.Model; +using Octopus.Client.Repositories.Async; + +namespace Octopus.Client.Editors.Async +{ + public class CertificateEditor : IResourceEditor + { + private readonly ICertificateRepository repository; + + public CertificateEditor(ICertificateRepository repository) + { + this.repository = repository; + } + + public CertificateResource Instance { get; private set; } + + public async Task Create(string name, string certificateData) + { + var existing = repository.FindByName(name); + if (existing != null) + { + throw new ArgumentException($"A certificate with the name {name} already exists"); + } + + Instance = await repository.Create(new CertificateResource(name, certificateData)); + + return this; + } + + public async Task FindByName(string name) + { + var existing = await repository.FindByName(name); + if (existing == null) + { + throw new ArgumentException($"A certificate with the name {name} could not be found"); + } + else + { + Instance = existing; + } + + return this; + } + + public CertificateEditor Customize(Action customize) + { + customize?.Invoke(Instance); + return this; + } + + public async Task Save() + { + Instance = await repository.Modify(Instance); + return this; + } + + public async Task Usages() + { + return await repository.Client.Get(Instance.Link("Usages")); + } + } +} \ No newline at end of file diff --git a/source/Octopus.Client/Editors/Async/SshKeyPairAccountEditor.cs b/source/Octopus.Client/Editors/Async/SshKeyPairAccountEditor.cs new file mode 100644 index 000000000..479bb3cba --- /dev/null +++ b/source/Octopus.Client/Editors/Async/SshKeyPairAccountEditor.cs @@ -0,0 +1,12 @@ +using Octopus.Client.Model.Accounts; +using Octopus.Client.Repositories.Async; + +namespace Octopus.Client.Editors.Async +{ + public class SshKeyPairAccountEditor : AccountEditor + { + public SshKeyPairAccountEditor(IAccountRepository repository) : base(repository) + { + } + } +} \ No newline at end of file diff --git a/source/Octopus.Client/Editors/Async/UsernamePasswordAccountEditor.cs b/source/Octopus.Client/Editors/Async/UsernamePasswordAccountEditor.cs new file mode 100644 index 000000000..c513cb479 --- /dev/null +++ b/source/Octopus.Client/Editors/Async/UsernamePasswordAccountEditor.cs @@ -0,0 +1,12 @@ +using Octopus.Client.Model.Accounts; +using Octopus.Client.Repositories.Async; + +namespace Octopus.Client.Editors.Async +{ + public class UsernamePasswordAccountEditor : AccountEditor + { + public UsernamePasswordAccountEditor(IAccountRepository repository) : base(repository) + { + } + } +} \ No newline at end of file diff --git a/source/Octopus.Client/Editors/AzureServicePrincipalAccountEditor.cs b/source/Octopus.Client/Editors/AzureServicePrincipalAccountEditor.cs new file mode 100644 index 000000000..511ec4f60 --- /dev/null +++ b/source/Octopus.Client/Editors/AzureServicePrincipalAccountEditor.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using Octopus.Client.Model.Accounts; +using Octopus.Client.Repositories; + +namespace Octopus.Client.Editors +{ + public class AzureServicePrincipalAccountEditor : AccountEditor + { + public AzureServicePrincipalAccountEditor(IAccountRepository repository) : base(repository) + { + } + + public List ResourceGroups() + { + return Repository.Client.Get>(Instance.Link("ResourceGroups")); + } + + public List WebSites() + { + return Repository.Client.Get>(Instance.Link("WebSites")); + } + + public List StorageAccounts() + { + return Repository.Client.Get>(Instance.Link("StorageAccounts")); + } + } +} \ No newline at end of file diff --git a/source/Octopus.Client/Editors/AzureSubscriptionAccountEditor.cs b/source/Octopus.Client/Editors/AzureSubscriptionAccountEditor.cs new file mode 100644 index 000000000..a3dc3562d --- /dev/null +++ b/source/Octopus.Client/Editors/AzureSubscriptionAccountEditor.cs @@ -0,0 +1,28 @@ +using System.Collections.Generic; +using Octopus.Client.Model.Accounts; +using Octopus.Client.Repositories; + +namespace Octopus.Client.Editors +{ + public class AzureSubscriptionAccountEditor : AccountEditor + { + public AzureSubscriptionAccountEditor(IAccountRepository repository) : base(repository) + { + } + + public List CloudServices(AzureSubscriptionAccountResource account) + { + return Repository.Client.Get>(account.Link("CloudServices")); + } + + public List StorageAccounts(AzureSubscriptionAccountResource account) + { + return Repository.Client.Get>(account.Link("StorageAccounts")); + } + + public List WebSites(AzureSubscriptionAccountResource account) + { + return Repository.Client.Get>(account.Link("WebSites")); + } + } +} \ No newline at end of file diff --git a/source/Octopus.Client/Editors/CertificateEditor.cs b/source/Octopus.Client/Editors/CertificateEditor.cs new file mode 100644 index 000000000..c4e540734 --- /dev/null +++ b/source/Octopus.Client/Editors/CertificateEditor.cs @@ -0,0 +1,63 @@ +using System; +using Octopus.Client.Model; +using Octopus.Client.Repositories; + +namespace Octopus.Client.Editors +{ + public class CertificateEditor : IResourceEditor + { + private readonly ICertificateRepository repository; + + public CertificateEditor(ICertificateRepository repository) + { + this.repository = repository; + } + + public CertificateResource Instance { get; private set; } + + public CertificateEditor Create(string name, string certificateData) + { + var existing = repository.FindByName(name); + if (existing != null) + { + throw new ArgumentException($"A certificate with the name {name} already exists"); + } + + Instance = repository.Create(new CertificateResource(name, certificateData)); + + return this; + } + + public CertificateEditor FindByName(string name) + { + var existing = repository.FindByName(name); + if (existing == null) + { + throw new ArgumentException($"A certificate with the name {name} could not be found"); + } + else + { + Instance = existing; + } + + return this; + } + + public CertificateEditor Customize(Action customize) + { + customize?.Invoke(Instance); + return this; + } + + public CertificateEditor Save() + { + Instance = repository.Modify(Instance); + return this; + } + + public CertificateUsageResource Usages() + { + return repository.Client.Get(Instance.Link("Usages")); + } + } +} \ No newline at end of file diff --git a/source/Octopus.Client/Editors/SshKeyPairAccountEditor.cs b/source/Octopus.Client/Editors/SshKeyPairAccountEditor.cs new file mode 100644 index 000000000..3d3b0d686 --- /dev/null +++ b/source/Octopus.Client/Editors/SshKeyPairAccountEditor.cs @@ -0,0 +1,12 @@ +using Octopus.Client.Model.Accounts; +using Octopus.Client.Repositories; + +namespace Octopus.Client.Editors +{ + public class SshKeyPairAccountEditor : AccountEditor + { + public SshKeyPairAccountEditor(IAccountRepository repository) : base(repository) + { + } + } +} \ No newline at end of file diff --git a/source/Octopus.Client/Editors/UsernamePasswordAccountEditor.cs b/source/Octopus.Client/Editors/UsernamePasswordAccountEditor.cs new file mode 100644 index 000000000..45a865750 --- /dev/null +++ b/source/Octopus.Client/Editors/UsernamePasswordAccountEditor.cs @@ -0,0 +1,12 @@ +using Octopus.Client.Model.Accounts; +using Octopus.Client.Repositories; + +namespace Octopus.Client.Editors +{ + public class UsernamePasswordAccountEditor : AccountEditor + { + public UsernamePasswordAccountEditor(IAccountRepository repository) : base(repository) + { + } + } +} \ No newline at end of file diff --git a/source/Octopus.Client/Extensions/TypeExtensions.cs b/source/Octopus.Client/Extensions/TypeExtensions.cs index a7ed26e0a..49b6b7970 100644 --- a/source/Octopus.Client/Extensions/TypeExtensions.cs +++ b/source/Octopus.Client/Extensions/TypeExtensions.cs @@ -1,10 +1,32 @@ using System; using System.Reflection; +using Octopus.Client.Model.Accounts; namespace Octopus.Client.Extensions { public static class TypeExtensions { public static object GetDefault(this Type t) => t.GetTypeInfo().IsValueType ? Activator.CreateInstance(t) : null; + + public static AccountType DetermineAccountType(this Type type) + { + var accountType = AccountType.None; + + if (type == typeof(UsernamePasswordAccountResource)) + accountType = AccountType.UsernamePassword; + else if (type == typeof(SshKeyPairAccountResource)) + accountType = AccountType.SshKeyPair; + else if (type == typeof(AzureServicePrincipalAccountResource)) + accountType = AccountType.AzureServicePrincipal; + else if (type == typeof(AzureSubscriptionAccountResource)) + accountType = AccountType.AzureSubscription; + else if (type == typeof(AmazonWebServicesAccountResource)) + accountType = AccountType.AmazonWebServicesAccount; + else if (type == typeof(AmazonWebServicesRoleAccountResource)) + accountType = AccountType.AmazonWebServicesRoleAccount; + else + throw new ArgumentException($"Account type {type} is not supported"); + return accountType; + } } } \ No newline at end of file diff --git a/source/Octopus.Client/Model/Accounts/AccountResource.cs b/source/Octopus.Client/Model/Accounts/AccountResource.cs index a1282a959..06affe960 100644 --- a/source/Octopus.Client/Model/Accounts/AccountResource.cs +++ b/source/Octopus.Client/Model/Accounts/AccountResource.cs @@ -1,9 +1,11 @@ -using System.Linq; +using System.Diagnostics; +using System.Linq; using Octopus.Client.Extensibility; using Octopus.Client.Extensibility.Attributes; namespace Octopus.Client.Model.Accounts { + [DebuggerDisplay("Name = {Name}")] public abstract class AccountResource : Resource, INamedResource { protected AccountResource() diff --git a/source/Octopus.Client/Model/Accounts/AzureCloudService.cs b/source/Octopus.Client/Model/Accounts/AzureCloudService.cs new file mode 100644 index 000000000..ac1baa8c9 --- /dev/null +++ b/source/Octopus.Client/Model/Accounts/AzureCloudService.cs @@ -0,0 +1,8 @@ +namespace Octopus.Client.Model.Accounts +{ + public class AzureCloudService + { + public string Name { get; set; } + public string Location { get; set; } + } +} \ No newline at end of file diff --git a/source/Octopus.Client/Model/Accounts/AzureServicePrincipalAccountResource.cs b/source/Octopus.Client/Model/Accounts/AzureServicePrincipalAccountResource.cs index a2b28b521..67c394b23 100644 --- a/source/Octopus.Client/Model/Accounts/AzureServicePrincipalAccountResource.cs +++ b/source/Octopus.Client/Model/Accounts/AzureServicePrincipalAccountResource.cs @@ -37,5 +37,17 @@ public class AzureServicePrincipalAccountResource : AccountResource [Trim] [Writeable] public string ActiveDirectoryEndpointBaseUri { get; set; } + + public class WebSite + { + public string Name { get; } + public string ResourceGroup { get; set; } + } + + public class ResourceGroup + { + public string Id { get; set; } + public string Name { get; set; } + } } } \ No newline at end of file diff --git a/source/Octopus.Client/Model/Accounts/AzureStorageAccount.cs b/source/Octopus.Client/Model/Accounts/AzureStorageAccount.cs new file mode 100644 index 000000000..54d5baf90 --- /dev/null +++ b/source/Octopus.Client/Model/Accounts/AzureStorageAccount.cs @@ -0,0 +1,8 @@ +namespace Octopus.Client.Model.Accounts +{ + public class AzureStorageAccount + { + public string Name { get; set; } + public string Location { get; set; } + } +} \ No newline at end of file diff --git a/source/Octopus.Client/Model/Accounts/AzureSubscriptionAccountResource.cs b/source/Octopus.Client/Model/Accounts/AzureSubscriptionAccountResource.cs index f2563bd16..7614c66a0 100644 --- a/source/Octopus.Client/Model/Accounts/AzureSubscriptionAccountResource.cs +++ b/source/Octopus.Client/Model/Accounts/AzureSubscriptionAccountResource.cs @@ -36,5 +36,12 @@ public override AccountType AccountType [Trim] [Writeable] public string ServiceManagementEndpointBaseUri { get; set; } + + public class WebSite + { + public string Name { get; } + public string WebSpace { get; set; } + public string ResourceGroup { get; set; } + } } } \ No newline at end of file diff --git a/source/Octopus.Client/Model/Accounts/Usages/AccountUsageResource.cs b/source/Octopus.Client/Model/Accounts/Usages/AccountUsageResource.cs new file mode 100644 index 000000000..361427b0d --- /dev/null +++ b/source/Octopus.Client/Model/Accounts/Usages/AccountUsageResource.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections.Generic; + +namespace Octopus.Client.Model.Accounts.Usages +{ + public class AccountUsageResource : Resource + { + public AccountUsageResource() + { + Targets = new List(); + DeploymentProcesses = new List(); + Releases = new List(); + ProjectVariableSets = new List(); + LibraryVariableSets = new List(); + } + + public ICollection Targets { get; set; } + public ICollection DeploymentProcesses { get; set; } + public ICollection Releases { get; set; } + public ICollection ProjectVariableSets { get; set; } + public ICollection LibraryVariableSets { get; set; } + } + + public class TargetUsageEntry + { + public string TargetName { get; set; } + public string TargetId { get; set; } + } + + public class ProjectVariableSetUsage + { + public ProjectVariableSetUsage() + { + Releases = new List(); + } + + public string ProjectSlug { get; set; } + public string ProjectName { get; set; } + public string ProjectId { get; set; } + public ICollection Releases { get; set; } + public bool IsCurrentlyBeingUsedInProject { get; set; } + } + + public class LibraryVariableSetUsageEntry + { + public string LibraryVariableSetId { get; set; } + public string LibraryVariableSetName { get; set; } + } + + public class ReleaseUsage + { + public ReleaseUsage() + { + Releases = new List(); + } + + public string ProjectName { get; set; } + public string ProjectId { get; set; } + public ICollection Releases { get; set; } + } + + public class ReleaseUsageEntry + { + public string ReleaseId { get; set; } + public string ReleaseVersion { get; set; } + } + + public class StepUsage + { + public StepUsage() + { + Steps = new List(); + } + + public string ProjectName { get; set; } + public string ProjectSlug { get; set; } + public string ProjectId { get; set; } + + public ICollection Steps { get; set; } + } + + public class StepUsageEntry + { + public string StepName { get; set; } + public string StepId { get; set; } + } +} diff --git a/source/Octopus.Client/Model/CertificateResource.cs b/source/Octopus.Client/Model/CertificateResource.cs index b1b7d8928..a2a867a43 100644 --- a/source/Octopus.Client/Model/CertificateResource.cs +++ b/source/Octopus.Client/Model/CertificateResource.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics; using System.Linq; using Octopus.Client.Extensibility.Attributes; using Newtonsoft.Json; @@ -6,6 +7,7 @@ namespace Octopus.Client.Model { + [DebuggerDisplay("Name = {Name}")] public class CertificateResource : Resource, INamedResource { [JsonConstructor] diff --git a/source/Octopus.Client/Model/CertificateUsageResource.cs b/source/Octopus.Client/Model/CertificateUsageResource.cs new file mode 100644 index 000000000..64a687d64 --- /dev/null +++ b/source/Octopus.Client/Model/CertificateUsageResource.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; + +namespace Octopus.Client.Model +{ + public class CertificateUsageResource + { + public ICollection ProjectUsages { get; set; } + + public ICollection LibraryVariableSetUsages { get; set; } + + public ICollection TenantUsages { get; set; } + } +} \ No newline at end of file diff --git a/source/Octopus.Client/Model/Endpoints/AzureWebAppEndpointResource.cs b/source/Octopus.Client/Model/Endpoints/AzureWebAppEndpointResource.cs index 07372ab21..85214c260 100644 --- a/source/Octopus.Client/Model/Endpoints/AzureWebAppEndpointResource.cs +++ b/source/Octopus.Client/Model/Endpoints/AzureWebAppEndpointResource.cs @@ -19,6 +19,10 @@ public override CommunicationStyle CommunicationStyle [Writeable] public string WebSpaceName { get; set; } + [Trim] + [Writeable] + public string ResourceGroupName { get; set; } + [Trim] [Writeable] public string WebAppName { get; set; } diff --git a/source/Octopus.Client/Model/VariableType.cs b/source/Octopus.Client/Model/VariableType.cs index d748b331f..e9692752e 100644 --- a/source/Octopus.Client/Model/VariableType.cs +++ b/source/Octopus.Client/Model/VariableType.cs @@ -5,6 +5,7 @@ public enum VariableType String, Sensitive, Certificate, - AmazonWebServicesAccount + AmazonWebServicesAccount, + AzureAccount } } \ No newline at end of file diff --git a/source/Octopus.Client/Repositories/Async/AccountRepository.cs b/source/Octopus.Client/Repositories/Async/AccountRepository.cs index 4ec7123a2..023af890a 100644 --- a/source/Octopus.Client/Repositories/Async/AccountRepository.cs +++ b/source/Octopus.Client/Repositories/Async/AccountRepository.cs @@ -1,10 +1,32 @@ using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Octopus.Client.Extensibility; +using Octopus.Client.Extensions; +using Octopus.Client.Model; using Octopus.Client.Model.Accounts; +using Octopus.Client.Model.Accounts.Usages; namespace Octopus.Client.Repositories.Async { - public interface IAccountRepository : ICreate, IModify, IDelete, IGet, IFindByName + public interface IAccountRepository : IResourceRepository, ICreate, IModify, IDelete, IGet, IFindByName { + AccountType DetermineAccountType() where TAccount : AccountResource; + + Task GetOfType(string idOrHref) where TAccount : AccountResource; + Task> GetOfType(params string[] ids) where TAccount : AccountResource; + Task RefreshOfType(TAccount resource) where TAccount : AccountResource; + + Task FindByNameOfType(string name) where TAccount : AccountResource; + Task> FindByNamesOfType(IEnumerable names) where TAccount : AccountResource; + + Task PaginateOfType(Func, bool> getNextPage, object pathParameters = null) where TAccount : AccountResource; + Task FindOneOfType(Func search, object pathParameters = null) where TAccount : AccountResource; + Task> FindManyOfType(Func search, object pathParameters = null) where TAccount : AccountResource; + Task> FindAllOfType(object pathParameters = null) where TAccount : AccountResource; + + Task GetAccountUsage(string id); } class AccountRepository : BasicRepository, IAccountRepository @@ -13,5 +35,100 @@ public AccountRepository(IOctopusAsyncClient client) : base(client, "Accounts") { } + + public async Task GetOfType(string idOrHref) where TAccount : AccountResource + { + var account = await base.Get(idOrHref); + return account as TAccount; + } + + public async Task> GetOfType(params string[] ids) where TAccount : AccountResource + { + var accounts = await base.Get(ids); + return accounts as List; + } + + public async Task RefreshOfType(TAccount resource) where TAccount : AccountResource + { + var account = await base.Refresh(resource); + return account as TAccount; + } + + public Task FindByNameOfType(string name) where TAccount : AccountResource + { + var accountType = DetermineAccountType(); + name = (name ?? string.Empty).Trim(); + + return FindOneOfType(r => + { + if (r is INamedResource named) + return string.Equals((named.Name ?? string.Empty).Trim(), name, StringComparison.OrdinalIgnoreCase); + return false; + }, pathParameters: new {accountType, name}); + } + + public Task> FindByNamesOfType(IEnumerable names) where TAccount : AccountResource + { + var nameSet = new HashSet((names ?? new string[0]).Select(n => (n ?? string.Empty).Trim()), StringComparer.OrdinalIgnoreCase); + return FindManyOfType(r => + { + if (r is INamedResource named) + return nameSet.Contains((named.Name ?? string.Empty).Trim()); + return false; + }, pathParameters: DetermineAccountType()); + } + + object PathParametersOfType(object pathParameters) where TAccount : AccountResource + { + if (pathParameters != null) + return pathParameters; + var accountType = DetermineAccountType(); + return new {accountType}; + } + + public Task PaginateOfType(Func, bool> getNextPage, object pathParameters = null) where TAccount : AccountResource + { + return Client.Paginate(Client.RootDocument.Link(CollectionLinkName), PathParametersOfType(pathParameters), getNextPage); + } + + public async Task FindOneOfType(Func search, object pathParameters = null) where TAccount : AccountResource + { + TAccount resource = null; + await PaginateOfType(page => + { + resource = page.Items.FirstOrDefault(search); + return resource == null; + }, pathParameters: PathParametersOfType(pathParameters)) + .ConfigureAwait(false); + return resource; + } + + public async Task> FindManyOfType(Func search, object pathParameters = null) where TAccount : AccountResource + { + var resources = new List(); + await PaginateOfType(page => + { + resources.AddRange(page.Items.Where(search)); + return true; + }, pathParameters: PathParametersOfType(pathParameters)) + .ConfigureAwait(false); + return resources; + } + + public Task> FindAllOfType(object pathParameters = null) where TAccount : AccountResource + { + return FindManyOfType(x => true, PathParametersOfType(pathParameters)); + } + + public async Task GetAccountUsage(string id) + { + var account = await Client.Get(id); + return await Client.Get(account.Link("Usages")); + } + + public AccountType DetermineAccountType() where TAccount : AccountResource + { + return typeof(TAccount).DetermineAccountType(); + } } } diff --git a/source/Octopus.Client/Repositories/Async/CertificateRepository.cs b/source/Octopus.Client/Repositories/Async/CertificateRepository.cs index c74be6179..73be3e204 100644 --- a/source/Octopus.Client/Repositories/Async/CertificateRepository.cs +++ b/source/Octopus.Client/Repositories/Async/CertificateRepository.cs @@ -4,7 +4,7 @@ namespace Octopus.Client.Repositories.Async { - public interface ICertificateRepository : IGet, IFindByName, ICreate, IModify, IDelete + public interface ICertificateRepository : IResourceRepository, IGet, IFindByName, ICreate, IModify, IDelete { /// /// Exports the certificate data. diff --git a/source/Octopus.Client/Repositories/Async/IResourceRepository.cs b/source/Octopus.Client/Repositories/Async/IResourceRepository.cs new file mode 100644 index 000000000..7bbd2aaae --- /dev/null +++ b/source/Octopus.Client/Repositories/Async/IResourceRepository.cs @@ -0,0 +1,7 @@ +namespace Octopus.Client.Repositories.Async +{ + public interface IResourceRepository + { + IOctopusAsyncClient Client { get; } + } +} \ No newline at end of file diff --git a/source/Octopus.Client/Repositories/CertificateRepository.cs b/source/Octopus.Client/Repositories/CertificateRepository.cs index d893840b2..149944131 100644 --- a/source/Octopus.Client/Repositories/CertificateRepository.cs +++ b/source/Octopus.Client/Repositories/CertificateRepository.cs @@ -4,7 +4,7 @@ namespace Octopus.Client.Repositories { - public interface ICertificateRepository : IGet, IFindByName, ICreate, IModify, IDelete + public interface ICertificateRepository : IResourceRepository, IGet, IFindByName, ICreate, IModify, IDelete { /// /// Exports the certificate data. diff --git a/source/Octopus.Client/Repositories/IAccountRepository.cs b/source/Octopus.Client/Repositories/IAccountRepository.cs index 36fa8441f..7e893c070 100644 --- a/source/Octopus.Client/Repositories/IAccountRepository.cs +++ b/source/Octopus.Client/Repositories/IAccountRepository.cs @@ -1,10 +1,31 @@ using System; +using System.Collections.Generic; +using System.Linq; +using Octopus.Client.Extensibility; +using Octopus.Client.Extensions; +using Octopus.Client.Model; using Octopus.Client.Model.Accounts; +using Octopus.Client.Model.Accounts.Usages; namespace Octopus.Client.Repositories { - public interface IAccountRepository : ICreate, IModify, IDelete, IGet, IFindByName + public interface IAccountRepository : IResourceRepository, ICreate, IModify, IDelete, IGet, IFindByName { + AccountType DetermineAccountType() where TAccount : AccountResource; + + TAccount GetOfType(string idOrHref) where TAccount : AccountResource; + List GetOfType(params string[] ids) where TAccount : AccountResource; + TAccount RefreshOfType(TAccount resource) where TAccount : AccountResource; + + TAccount FindByNameOfType(string name) where TAccount : AccountResource; + List FindByNamesOfType(IEnumerable names) where TAccount : AccountResource; + + void PaginateOfType(Func, bool> getNextPage, object pathParameters = null) where TAccount : AccountResource; + TAccount FindOneOfType(Func search, object pathParameters = null) where TAccount : AccountResource; + List FindManyOfType(Func search, object pathParameters = null) where TAccount : AccountResource; + List FindAllOfType(object pathParameters = null) where TAccount : AccountResource; + + AccountUsageResource GetAccountUsage(string id); } class AccountRepository : BasicRepository, IAccountRepository @@ -13,5 +34,97 @@ public AccountRepository(IOctopusClient client) : base(client, "Accounts") { } + public TAccount GetOfType(string idOrHref) where TAccount : AccountResource + { + var account = base.Get(idOrHref); + return account as TAccount; + } + + public List GetOfType(params string[] ids) where TAccount : AccountResource + { + var accounts = base.Get(ids); + return accounts as List; + } + + public TAccount RefreshOfType(TAccount resource) where TAccount : AccountResource + { + var account = base.Refresh(resource); + return account as TAccount; + } + + public TAccount FindByNameOfType(string name) where TAccount : AccountResource + { + var accountType = DetermineAccountType(); + name = (name ?? string.Empty).Trim(); + + return FindOneOfType(r => + { + if (r is INamedResource named) + return string.Equals((named.Name ?? string.Empty).Trim(), name, StringComparison.OrdinalIgnoreCase); + return false; + }, pathParameters: new {accountType, name}); + } + + public List FindByNamesOfType(IEnumerable names) where TAccount : AccountResource + { + var nameSet = new HashSet((names ?? new string[0]).Select(n => (n ?? string.Empty).Trim()), StringComparer.OrdinalIgnoreCase); + return FindManyOfType(r => + { + if (r is INamedResource named) + return nameSet.Contains((named.Name ?? string.Empty).Trim()); + return false; + }, pathParameters: DetermineAccountType()); + } + + object PathParametersOfType(object pathParameters) where TAccount : AccountResource + { + if (pathParameters != null) + return pathParameters; + var accountType = DetermineAccountType(); + return new {accountType}; + } + + public void PaginateOfType(Func, bool> getNextPage, object pathParameters = null) where TAccount : AccountResource + { + Client.Paginate(Client.RootDocument.Link(CollectionLinkName), PathParametersOfType(pathParameters), getNextPage); + } + + public TAccount FindOneOfType(Func search, object pathParameters = null) where TAccount : AccountResource + { + TAccount resource = null; + PaginateOfType(page => + { + resource = page.Items.FirstOrDefault(search); + return resource == null; + }, pathParameters: PathParametersOfType(pathParameters)); + return resource; + } + + public List FindManyOfType(Func search, object pathParameters = null) where TAccount : AccountResource + { + var resources = new List(); + PaginateOfType(page => + { + resources.AddRange(page.Items.Where(search)); + return true; + }, pathParameters: PathParametersOfType(pathParameters)); + return resources; + } + + public List FindAllOfType(object pathParameters = null) where TAccount : AccountResource + { + return FindManyOfType(x => true, PathParametersOfType(pathParameters)); + } + + public AccountType DetermineAccountType() where TAccount : AccountResource + { + return typeof(TAccount).DetermineAccountType(); + } + + public AccountUsageResource GetAccountUsage(string id) + { + var account = Client.Get(id); + return Client.Get(account.Link("Usages")); + } } } \ No newline at end of file diff --git a/source/Octopus.Client/Repositories/IResourceRepository.cs b/source/Octopus.Client/Repositories/IResourceRepository.cs new file mode 100644 index 000000000..80f430f5e --- /dev/null +++ b/source/Octopus.Client/Repositories/IResourceRepository.cs @@ -0,0 +1,7 @@ +namespace Octopus.Client.Repositories +{ + public interface IResourceRepository + { + IOctopusClient Client { get; } + } +} \ No newline at end of file