From 9ae29f97218706ea87ced152cdc26c371ca73dd7 Mon Sep 17 00:00:00 2001 From: Steven Cleve <107827476+stevencl840@users.noreply.github.com> Date: Mon, 16 Dec 2024 16:17:06 +1100 Subject: [PATCH 1/5] First pass --- .../Editors/Async/RunbookEditor.cs | 66 ++++++++++++++-- .../Editors/Async/RunbookProcessEditor.cs | 23 +++++- .../Model/ModifyRunbookCommand.cs | 6 ++ .../Model/ModifyRunbookProcessCommand.cs | 6 ++ .../Async/RunbookProcessRepository.cs | 28 ++++++- .../Repositories/Async/RunbookRepository.cs | 78 +++++++++++++++++++ 6 files changed, 199 insertions(+), 8 deletions(-) create mode 100644 source/Octopus.Server.Client/Model/ModifyRunbookCommand.cs create mode 100644 source/Octopus.Server.Client/Model/ModifyRunbookProcessCommand.cs diff --git a/source/Octopus.Server.Client/Editors/Async/RunbookEditor.cs b/source/Octopus.Server.Client/Editors/Async/RunbookEditor.cs index cfa01ea97..081fe53d9 100644 --- a/source/Octopus.Server.Client/Editors/Async/RunbookEditor.cs +++ b/source/Octopus.Server.Client/Editors/Async/RunbookEditor.cs @@ -1,4 +1,5 @@ using System; +using System.Threading; using System.Threading.Tasks; using Octopus.Client.Model; using Octopus.Client.Repositories.Async; @@ -21,10 +22,14 @@ public RunbookEditor(IRunbookRepository repository, public Task RunbookProcess => runbookProcess.Value; + public async Task CreateOrModify(ProjectResource project, string name, string description) + => await CreateOrModify(project, name, description, CancellationToken.None).ConfigureAwait(false); + + public async Task CreateOrModify(ProjectResource project, string name, string description, CancellationToken cancellationToken) { var existing = await repository.FindByName(project, name).ConfigureAwait(false); - + if (existing == null) { Instance = await repository.Create(new RunbookResource @@ -32,22 +37,59 @@ public async Task CreateOrModify(ProjectResource project, string ProjectId = project.Id, Name = name, Description = description - }).ConfigureAwait(false); + }, cancellationToken).ConfigureAwait(false); } else { existing.Name = name; existing.Description = description; - Instance = await repository.Modify(existing).ConfigureAwait(false); + Instance = await repository.Modify(existing, cancellationToken).ConfigureAwait(false); } return this; } + public async Task CreateOrModifyInGit(ProjectResource project, string slug, string name, string description, string gitRef, string commitMessage, CancellationToken cancellationToken) + { + var existing = await repository.GetInGit(slug, project, gitRef, cancellationToken).ConfigureAwait(false); + + if (existing == null) + { + Instance = await repository.CreateInGit(new RunbookResource + { + Slug = slug, + ProjectId = project.Id, + Name = name, + Description = description + }, + gitRef, + commitMessage, + cancellationToken).ConfigureAwait(false); + } + else + { + existing.Name = name; + existing.Description = description; + + Instance = await repository.ModifyInGit(existing, gitRef, commitMessage, cancellationToken).ConfigureAwait(false); + } + + return this; + } + public async Task Load(string id) + => await Load(id, CancellationToken.None).ConfigureAwait(false); + + public async Task Load(string id, CancellationToken cancellationToken) { - Instance = await repository.Get(id).ConfigureAwait(false); + Instance = await repository.Get(id, cancellationToken).ConfigureAwait(false); + return this; + } + + public async Task LoadInGit(string id, ProjectResource project, string gitRef, CancellationToken cancellationToken) + { + Instance = await repository.GetInGit(id, project, gitRef, cancellationToken).ConfigureAwait(false); return this; } @@ -58,8 +100,9 @@ public RunbookEditor Customize(Action customize) } public async Task Save() - { + { Instance = await repository.Modify(Instance).ConfigureAwait(false); + if (runbookProcess.IsValueCreated) { var steps = await runbookProcess.Value.ConfigureAwait(false); @@ -67,5 +110,18 @@ public async Task Save() } return this; } + + public async Task SaveInGit(string gitRef, string commitMessage, CancellationToken cancellationToken) + { + Instance = await repository.ModifyInGit(Instance, gitRef, commitMessage, cancellationToken).ConfigureAwait(false); + + if (runbookProcess.IsValueCreated) + { + var steps = await runbookProcess.Value.ConfigureAwait(false); + await steps.SaveInGit(gitRef, commitMessage, cancellationToken).ConfigureAwait(false); + } + + return this; + } } } \ No newline at end of file diff --git a/source/Octopus.Server.Client/Editors/Async/RunbookProcessEditor.cs b/source/Octopus.Server.Client/Editors/Async/RunbookProcessEditor.cs index 6fe70dc5f..2d032514b 100644 --- a/source/Octopus.Server.Client/Editors/Async/RunbookProcessEditor.cs +++ b/source/Octopus.Server.Client/Editors/Async/RunbookProcessEditor.cs @@ -1,4 +1,5 @@ using System; +using System.Threading; using System.Threading.Tasks; using Octopus.Client.Model; using Octopus.Client.Repositories.Async; @@ -17,8 +18,17 @@ public RunbookProcessEditor(IRunbookProcessRepository repository) public RunbookProcessResource Instance { get; private set; } public async Task Load(string id) + => await Load(id, CancellationToken.None); + + public async Task Load(string id, CancellationToken cancellationToken) { - Instance = await repository.Get(id).ConfigureAwait(false); + Instance = await repository.Get(id, cancellationToken).ConfigureAwait(false); + return this; + } + + public async Task LoadInGit(ProjectResource project, string id, string gitRef, CancellationToken cancellationToken) + { + Instance = await repository.GetInGit(project, id, gitRef, cancellationToken).ConfigureAwait(false); return this; } @@ -51,8 +61,17 @@ public RunbookProcessEditor Customize(Action customize) } public async Task Save() + => await Save(CancellationToken.None); + + public async Task Save(CancellationToken cancellationToken) + { + Instance = await repository.Modify(Instance, cancellationToken).ConfigureAwait(false); + return this; + } + + public async Task SaveInGit(string gitRef, string commitMessage, CancellationToken cancellationToken) { - Instance = await repository.Modify(Instance).ConfigureAwait(false); + Instance = await repository.ModifyInGit(gitRef, commitMessage, Instance, cancellationToken).ConfigureAwait(false); return this; } } diff --git a/source/Octopus.Server.Client/Model/ModifyRunbookCommand.cs b/source/Octopus.Server.Client/Model/ModifyRunbookCommand.cs new file mode 100644 index 000000000..0acea7ad4 --- /dev/null +++ b/source/Octopus.Server.Client/Model/ModifyRunbookCommand.cs @@ -0,0 +1,6 @@ +namespace Octopus.Client.Model; + +public class ModifyRunbookCommand: RunbookResource, ICommitCommand +{ + public string ChangeDescription { get; set; } +} \ No newline at end of file diff --git a/source/Octopus.Server.Client/Model/ModifyRunbookProcessCommand.cs b/source/Octopus.Server.Client/Model/ModifyRunbookProcessCommand.cs new file mode 100644 index 000000000..b94e92ca9 --- /dev/null +++ b/source/Octopus.Server.Client/Model/ModifyRunbookProcessCommand.cs @@ -0,0 +1,6 @@ +namespace Octopus.Client.Model; + +public class ModifyRunbookProcessCommand : RunbookProcessResource, ICommitCommand +{ + public string ChangeDescription { get; set; } +} \ No newline at end of file diff --git a/source/Octopus.Server.Client/Repositories/Async/RunbookProcessRepository.cs b/source/Octopus.Server.Client/Repositories/Async/RunbookProcessRepository.cs index 74f05ddb3..e2ee7ce9f 100644 --- a/source/Octopus.Server.Client/Repositories/Async/RunbookProcessRepository.cs +++ b/source/Octopus.Server.Client/Repositories/Async/RunbookProcessRepository.cs @@ -1,11 +1,19 @@ -using System.Threading.Tasks; +using System.Threading; +using System.Threading.Tasks; using Octopus.Client.Model; +using Octopus.Client.Serialization; namespace Octopus.Client.Repositories.Async { public interface IRunbookProcessRepository : IGet, IModify { Task GetTemplate(RunbookProcessResource runbookProcess); + + Task GetInGit(ProjectResource project, string slug, string gitRef, CancellationToken cancellationToken); + + Task ModifyInGit(string gitRef, string commitMessage, RunbookProcessResource instance, + CancellationToken cancellationToken); + } class RunbookProcessRepository : BasicRepository, IRunbookProcessRepository @@ -19,5 +27,23 @@ public Task GetTemplate(RunbookProcessResource { return Client.Get(runbookProcess.Link("RunbookSnapshotTemplate")); } + + public async Task GetInGit(ProjectResource project, string slug, string gitRef, CancellationToken cancellationToken) + => await GetInGit(project.SpaceId, project.Id, slug, gitRef, cancellationToken); + + async Task GetInGit(string spaceId, string projectId, string slug, string gitRef, CancellationToken cancellationToken) + { + return await Client.Get($"/api/spaces/{spaceId}/projects/{projectId}/{gitRef}/runbookProcesses/{slug}", cancellationToken); + } + + public async Task ModifyInGit(string gitRef, string commitMessage, RunbookProcessResource resource, CancellationToken cancellationToken) + { + var json = Serializer.Serialize(resource); + var command = Serializer.Deserialize(json); + command.ChangeDescription = commitMessage; + + await Client.Put($"/api/spaces/{resource.SpaceId}/projects/{resource.ProjectId}/{gitRef}/runbookProcesses/{resource.Id}", resource, cancellationToken); + return await GetInGit(resource.SpaceId, resource.ProjectId, resource.Id, gitRef, cancellationToken); + } } } diff --git a/source/Octopus.Server.Client/Repositories/Async/RunbookRepository.cs b/source/Octopus.Server.Client/Repositories/Async/RunbookRepository.cs index 21c14eac0..dd2fed1bb 100644 --- a/source/Octopus.Server.Client/Repositories/Async/RunbookRepository.cs +++ b/source/Octopus.Server.Client/Repositories/Async/RunbookRepository.cs @@ -1,15 +1,21 @@ using System.Linq; +using System.Threading; using System.Threading.Tasks; using Octopus.Client.Editors.Async; using Octopus.Client.Exceptions; using Octopus.Client.Model; +using Octopus.Client.Serialization; namespace Octopus.Client.Repositories.Async { public interface IRunbookRepository : IFindByName, IGet, ICreate, IModify, IDelete { Task FindByName(ProjectResource project, string name); + Task GetInGit(string id, ProjectResource project, string gitRef, CancellationToken cancellationToken); + Task CreateInGit(RunbookResource resource, string gitRef, string commitMessage, CancellationToken cancellationToken); + Task ModifyInGit(RunbookResource resource, string gitRef, string commitMessage, CancellationToken cancellationToken); Task CreateOrModify(ProjectResource project, string name, string description); + Task CreateOrModifyInGit(ProjectResource project, string slug, string name, string description, string gitRef, string commitMessage, CancellationToken cancellationToken); Task GetRunbookSnapshotTemplate(RunbookResource runbook); Task GetRunbookRunTemplate(RunbookResource runbook); Task GetPreview(DeploymentPromotionTarget promotionTarget); @@ -29,6 +35,36 @@ public RunbookRepository(IOctopusAsyncRepository repository) versionAfterWhichRunbookRunParametersAreAvailable = SemanticVersion.Parse("2020.3.1"); } + public Task GetInGit(string id, ProjectResource project, string gitRef, CancellationToken cancellationToken) + { + return GetInGit(id, project.SpaceId, project.Id, gitRef, cancellationToken); + } + + Task GetInGit(string id, string spaceId, string projectId, string gitRef, CancellationToken cancellationToken) + => Client.Get($"/api/{spaceId}/projects/{projectId}/{gitRef}/runbooks/{id}", cancellationToken); + + public async Task CreateInGit(RunbookResource resource, string gitRef, string commitMessage, CancellationToken cancellationToken) + { + var json = Serializer.Serialize(resource); + var command = Serializer.Deserialize(json); + command.ChangeDescription = commitMessage; + + await Client.Post($"/api/{resource.SpaceId}/projects/{resource.ProjectId}/{gitRef}/runbooks/", command, cancellationToken); + return await GetInGit(resource.Id, resource.SpaceId, resource.ProjectId, gitRef, cancellationToken); + } + + public async Task ModifyInGit(RunbookResource resource, string gitRef, string commitMessage, CancellationToken cancellationToken) + { + // TODO: revisit/obsolete this API when we have converters + // until then we need a way to re-use the response from previous client calls + var json = Serializer.Serialize(resource); + var command = Serializer.Deserialize(json); + command.ChangeDescription = commitMessage; + + await Client.Put($"/api/{resource.SpaceId}/projects/{resource.ProjectId}/{gitRef}/runbooks/{resource.Id}", command, cancellationToken); + return await GetInGit(resource.Id, resource.SpaceId, resource.ProjectId, gitRef, cancellationToken); + } + public Task FindByName(ProjectResource project, string name) { return FindByName(name, path: project.Link("Runbooks")); @@ -39,6 +75,11 @@ public Task CreateOrModify(ProjectResource project, string name, return new RunbookEditor(this, new RunbookProcessRepository(Repository)).CreateOrModify(project, name, description); } + public Task CreateOrModifyInGit(ProjectResource project, string slug, string name, string description, string gitRef, string commitMessage, CancellationToken cancellationToken) + { + return new RunbookEditor(this, new RunbookProcessRepository(Repository)).CreateOrModifyInGit(project, slug, name, description, gitRef, commitMessage, cancellationToken); + } + public Task GetRunbookSnapshotTemplate(RunbookResource runbook) { return Client.Get(runbook.Link("RunbookSnapshotTemplate")); @@ -48,12 +89,23 @@ public Task GetRunbookRunTemplate(RunbookResource ru { return Client.Get(runbook.Link("RunbookRunTemplate")); } + + public Task GetRunbookRunTemplate(RunbookResource runbook, string gitRef) + { + return Client.Get(runbook.Link("RunbookRunTemplate")); + } + public Task GetPreview(DeploymentPromotionTarget promotionTarget) { return Client.Get(promotionTarget.Link("RunbookRunPreview")); } + public Task GetPreview(DeploymentPromotionTarget promotionTarget, string gitRef) + { + return Client.Get(promotionTarget.Link("RunbookRunPreview")); + } + private bool ServerSupportsRunbookRunParameters(string version) { var serverVersion = SemanticVersion.Parse(version); @@ -75,6 +127,15 @@ public async Task Run(RunbookResource runbook, RunbookRunRes : await Client.Post(runbook.Link("CreateRunbookRun"), runbookRun); } + public async Task Run(RunbookResource runbook, RunbookRunResource runbookRun, string gitRef) + { + var serverSupportsRunbookRunParameters = ServerSupportsRunbookRunParameters((await Repository.LoadRootDocument()).Version); + + return serverSupportsRunbookRunParameters + ? (await Run(runbook, RunbookRunParameters.MapFrom(runbookRun), gitRef)).FirstOrDefault() + : await Client.Post(runbook.Link("CreateRunbookRun"), runbookRun); + } + public async Task Run(RunbookResource runbook, RunbookRunParameters runbookRunParameters) { var serverVersion = (await Repository.LoadRootDocument()).Version; @@ -86,5 +147,22 @@ public async Task Run(RunbookResource runbook, RunbookRunP return await Client.Post(runbook.Link("CreateRunbookRun"), runbookRunParameters); } + + public async Task Run(RunbookResource runbook, RunbookRunParameters runbookRunParameters, string gitRef) + { + var serverVersion = (await Repository.LoadRootDocument()).Version; + var serverSupportsRunbookRunParameters = ServerSupportsRunbookRunParameters(serverVersion); + + if (serverSupportsRunbookRunParameters == false) + throw new UnsupportedApiVersionException($"This Octopus Deploy server is an older version ({serverVersion}) that does not yet support RunbookRunParameters. " + + $"Please update your Octopus Deploy server to 2020.3.* or newer to access this feature."); + + return await Client.Post(runbook.Link("CreateRunbookRun"), runbookRunParameters); + } + + bool ProjectHasRunbooksInGit(ProjectResource projectResource) + { + return projectResource.PersistenceSettings is GitPersistenceSettingsResource gitSettings && gitSettings.ConversionState.RunbooksAreInGit; + } } } From b2fe474d74df628308b660a7a59560b26d3e23b0 Mon Sep 17 00:00:00 2001 From: Steven Cleve <107827476+stevencl840@users.noreply.github.com> Date: Wed, 18 Dec 2024 08:28:37 +1100 Subject: [PATCH 2/5] Update surfacre area approved files --- ...AreaShouldNotRegress..NETCore.approved.txt | 38 +++++++++++++++ ...houldNotRegress..NETFramework.approved.txt | 48 +++++++++++++++++-- 2 files changed, 81 insertions(+), 5 deletions(-) diff --git a/source/Octopus.Client.Tests/PublicSurfaceAreaFixture.ThePublicSurfaceAreaShouldNotRegress..NETCore.approved.txt b/source/Octopus.Client.Tests/PublicSurfaceAreaFixture.ThePublicSurfaceAreaShouldNotRegress..NETCore.approved.txt index b56784fd8..d53c4e8ca 100644 --- a/source/Octopus.Client.Tests/PublicSurfaceAreaFixture.ThePublicSurfaceAreaShouldNotRegress..NETCore.approved.txt +++ b/source/Octopus.Client.Tests/PublicSurfaceAreaFixture.ThePublicSurfaceAreaShouldNotRegress..NETCore.approved.txt @@ -1304,9 +1304,14 @@ Octopus.Client.Editors.Async Octopus.Client.Model.RunbookResource Instance { get; } Task RunbookProcess { get; } Task CreateOrModify(Octopus.Client.Model.ProjectResource, String, String) + Task CreateOrModify(Octopus.Client.Model.ProjectResource, String, String, CancellationToken) + Task CreateOrModifyInGit(Octopus.Client.Model.ProjectResource, String, String, String, String, String, CancellationToken) Octopus.Client.Editors.Async.RunbookEditor Customize(Action) Task Load(String) + Task Load(String, CancellationToken) + Task LoadInGit(String, Octopus.Client.Model.ProjectResource, String, CancellationToken) Task Save() + Task SaveInGit(String, String, CancellationToken) } class RunbookProcessEditor Octopus.Client.Editors.Async.IResourceEditor @@ -1319,8 +1324,12 @@ Octopus.Client.Editors.Async Octopus.Client.Editors.Async.RunbookProcessEditor Customize(Action) Octopus.Client.Model.DeploymentStepResource FindStep(String) Task Load(String) + Task Load(String, CancellationToken) + Task LoadInGit(Octopus.Client.Model.ProjectResource, String, String, CancellationToken) Octopus.Client.Editors.Async.RunbookProcessEditor RemoveStep(String) Task Save() + Task Save(CancellationToken) + Task SaveInGit(String, String, CancellationToken) } class SshKeyPairAccountEditor Octopus.Client.Editors.Async.IResourceEditor @@ -4050,6 +4059,29 @@ Octopus.Client.Model .ctor() String ChangeDescription { get; set; } } + class ModifyRunbookCommand + Octopus.Client.Extensibility.IResource + Octopus.Client.Model.IAuditedResource + Octopus.Client.Extensibility.INamedResource + Octopus.Client.Extensibility.IHaveSpaceResource + Octopus.Client.Model.IHaveSlugResource + Octopus.Client.Model.ICommitCommand + Octopus.Client.Model.RunbookResource + { + .ctor() + String ChangeDescription { get; set; } + } + class ModifyRunbookProcessCommand + Octopus.Client.Extensibility.IResource + Octopus.Client.Model.IAuditedResource + Octopus.Client.Model.IProcessResource + Octopus.Client.Extensibility.IHaveSpaceResource + Octopus.Client.Model.ICommitCommand + Octopus.Client.Model.RunbookProcessResource + { + .ctor() + String ChangeDescription { get; set; } + } MultipleAccountType { None = 0 @@ -9193,7 +9225,9 @@ Octopus.Client.Repositories.Async Octopus.Client.Repositories.Async.IGet Octopus.Client.Repositories.Async.IModify { + Task GetInGit(Octopus.Client.Model.ProjectResource, String, String, CancellationToken) Task GetTemplate(Octopus.Client.Model.RunbookProcessResource) + Task ModifyInGit(String, String, Octopus.Client.Model.RunbookProcessResource, CancellationToken) } interface IRunbookRepository Octopus.Client.Repositories.Async.IFindByName @@ -9203,11 +9237,15 @@ Octopus.Client.Repositories.Async Octopus.Client.Repositories.Async.IModify Octopus.Client.Repositories.Async.IDelete { + Task CreateInGit(Octopus.Client.Model.RunbookResource, String, String, CancellationToken) Task CreateOrModify(Octopus.Client.Model.ProjectResource, String, String) + Task CreateOrModifyInGit(Octopus.Client.Model.ProjectResource, String, String, String, String, String, CancellationToken) Task FindByName(Octopus.Client.Model.ProjectResource, String) + Task GetInGit(String, Octopus.Client.Model.ProjectResource, String, CancellationToken) Task GetPreview(Octopus.Client.Model.DeploymentPromotionTarget) Task GetRunbookRunTemplate(Octopus.Client.Model.RunbookResource) Task GetRunbookSnapshotTemplate(Octopus.Client.Model.RunbookResource) + Task ModifyInGit(Octopus.Client.Model.RunbookResource, String, String, CancellationToken) Task Run(Octopus.Client.Model.RunbookResource, Octopus.Client.Model.RunbookRunResource) Task Run(Octopus.Client.Model.RunbookResource, Octopus.Client.Model.RunbookRunParameters) } diff --git a/source/Octopus.Client.Tests/PublicSurfaceAreaFixture.ThePublicSurfaceAreaShouldNotRegress..NETFramework.approved.txt b/source/Octopus.Client.Tests/PublicSurfaceAreaFixture.ThePublicSurfaceAreaShouldNotRegress..NETFramework.approved.txt index 527f635cf..41250b7a9 100644 --- a/source/Octopus.Client.Tests/PublicSurfaceAreaFixture.ThePublicSurfaceAreaShouldNotRegress..NETFramework.approved.txt +++ b/source/Octopus.Client.Tests/PublicSurfaceAreaFixture.ThePublicSurfaceAreaShouldNotRegress..NETFramework.approved.txt @@ -34,10 +34,10 @@ Octopus.Client Task Create(String, Octopus.Client.TResource, CancellationToken) Task Create(String, Octopus.Client.TResource, Object, CancellationToken) Task Create(String, Octopus.Client.TCommand, Object, CancellationToken) - Task Delete(String, Object, Object) - Task Delete(String, CancellationToken) Task Delete(String, Object, Object, CancellationToken) Task Delete(String, Octopus.Client.TCommand, CancellationToken) + Task Delete(String, Object, Object) + Task Delete(String, CancellationToken) Task Do(Octopus.Server.MessageContracts.Base.ICommand, CancellationToken) Octopus.Client.IOctopusSpaceAsyncRepository ForSpace(Octopus.Client.Model.SpaceResource) Octopus.Client.IOctopusSystemAsyncRepository ForSystem() @@ -348,12 +348,12 @@ Octopus.Client Task Post(String, Octopus.Client.TResource, Object, CancellationToken) Task Post(String) Task Post(String, CancellationToken) - Task Put(String, Octopus.Client.TResource) - Task Put(String, Octopus.Client.TResource, CancellationToken) Task Put(String, Octopus.Client.TResource, Object) Task Put(String, Octopus.Client.TResource, Object, CancellationToken) Task Put(String) Task Put(String, CancellationToken) + Task Put(String, Octopus.Client.TResource) + Task Put(String, Octopus.Client.TResource, CancellationToken) Task PutContent(String, Stream) Task PutContent(String, Stream, CancellationToken) Uri QualifyUri(String, Object) @@ -1305,9 +1305,14 @@ Octopus.Client.Editors.Async Octopus.Client.Model.RunbookResource Instance { get; } Task RunbookProcess { get; } Task CreateOrModify(Octopus.Client.Model.ProjectResource, String, String) + Task CreateOrModify(Octopus.Client.Model.ProjectResource, String, String, CancellationToken) + Task CreateOrModifyInGit(Octopus.Client.Model.ProjectResource, String, String, String, String, String, CancellationToken) Octopus.Client.Editors.Async.RunbookEditor Customize(Action) Task Load(String) + Task Load(String, CancellationToken) + Task LoadInGit(String, Octopus.Client.Model.ProjectResource, String, CancellationToken) Task Save() + Task SaveInGit(String, String, CancellationToken) } class RunbookProcessEditor Octopus.Client.Editors.Async.IResourceEditor @@ -1320,8 +1325,12 @@ Octopus.Client.Editors.Async Octopus.Client.Editors.Async.RunbookProcessEditor Customize(Action) Octopus.Client.Model.DeploymentStepResource FindStep(String) Task Load(String) + Task Load(String, CancellationToken) + Task LoadInGit(Octopus.Client.Model.ProjectResource, String, String, CancellationToken) Octopus.Client.Editors.Async.RunbookProcessEditor RemoveStep(String) Task Save() + Task Save(CancellationToken) + Task SaveInGit(String, String, CancellationToken) } class SshKeyPairAccountEditor Octopus.Client.Editors.Async.IResourceEditor @@ -4067,6 +4076,29 @@ Octopus.Client.Model .ctor() String ChangeDescription { get; set; } } + class ModifyRunbookCommand + Octopus.Client.Extensibility.IResource + Octopus.Client.Model.IAuditedResource + Octopus.Client.Extensibility.INamedResource + Octopus.Client.Extensibility.IHaveSpaceResource + Octopus.Client.Model.IHaveSlugResource + Octopus.Client.Model.ICommitCommand + Octopus.Client.Model.RunbookResource + { + .ctor() + String ChangeDescription { get; set; } + } + class ModifyRunbookProcessCommand + Octopus.Client.Extensibility.IResource + Octopus.Client.Model.IAuditedResource + Octopus.Client.Model.IProcessResource + Octopus.Client.Extensibility.IHaveSpaceResource + Octopus.Client.Model.ICommitCommand + Octopus.Client.Model.RunbookProcessResource + { + .ctor() + String ChangeDescription { get; set; } + } MultipleAccountType { None = 0 @@ -4652,8 +4684,8 @@ Octopus.Client.Model Octopus.Client.Model.ProjectResource AddOrUpdateSensitiveTemplate(String, String, String, String) Octopus.Client.Model.ProjectResource AddOrUpdateSingleLineTextTemplate(String, String) Octopus.Client.Model.ProjectResource AddOrUpdateSingleLineTextTemplate(String, String, String, String) - Octopus.Client.Model.ProjectResource AddOrUpdateVariableTemplate(String, String, IDictionary, String, String) Octopus.Client.Model.ProjectResource AddOrUpdateVariableTemplate(String, String, IDictionary) + Octopus.Client.Model.ProjectResource AddOrUpdateVariableTemplate(String, String, IDictionary, String, String) Octopus.Client.Model.ProjectResource Clear() Octopus.Client.Model.ProjectResource IncludingLibraryVariableSets(Octopus.Client.Model.LibraryVariableSetResource[]) } @@ -9218,7 +9250,9 @@ Octopus.Client.Repositories.Async Octopus.Client.Repositories.Async.IGet Octopus.Client.Repositories.Async.IModify { + Task GetInGit(Octopus.Client.Model.ProjectResource, String, String, CancellationToken) Task GetTemplate(Octopus.Client.Model.RunbookProcessResource) + Task ModifyInGit(String, String, Octopus.Client.Model.RunbookProcessResource, CancellationToken) } interface IRunbookRepository Octopus.Client.Repositories.Async.IFindByName @@ -9228,11 +9262,15 @@ Octopus.Client.Repositories.Async Octopus.Client.Repositories.Async.IModify Octopus.Client.Repositories.Async.IDelete { + Task CreateInGit(Octopus.Client.Model.RunbookResource, String, String, CancellationToken) Task CreateOrModify(Octopus.Client.Model.ProjectResource, String, String) + Task CreateOrModifyInGit(Octopus.Client.Model.ProjectResource, String, String, String, String, String, CancellationToken) Task FindByName(Octopus.Client.Model.ProjectResource, String) + Task GetInGit(String, Octopus.Client.Model.ProjectResource, String, CancellationToken) Task GetPreview(Octopus.Client.Model.DeploymentPromotionTarget) Task GetRunbookRunTemplate(Octopus.Client.Model.RunbookResource) Task GetRunbookSnapshotTemplate(Octopus.Client.Model.RunbookResource) + Task ModifyInGit(Octopus.Client.Model.RunbookResource, String, String, CancellationToken) Task Run(Octopus.Client.Model.RunbookResource, Octopus.Client.Model.RunbookRunResource) Task Run(Octopus.Client.Model.RunbookResource, Octopus.Client.Model.RunbookRunParameters) } From bf98e2111240a4a11fd559456d9d494be0fde7e6 Mon Sep 17 00:00:00 2001 From: Steven Cleve <107827476+stevencl840@users.noreply.github.com> Date: Wed, 18 Dec 2024 08:30:47 +1100 Subject: [PATCH 3/5] Surface area changes --- ...faceAreaShouldNotRegress..NETFramework.approved.txt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/source/Octopus.Client.Tests/PublicSurfaceAreaFixture.ThePublicSurfaceAreaShouldNotRegress..NETFramework.approved.txt b/source/Octopus.Client.Tests/PublicSurfaceAreaFixture.ThePublicSurfaceAreaShouldNotRegress..NETFramework.approved.txt index 41250b7a9..35d017866 100644 --- a/source/Octopus.Client.Tests/PublicSurfaceAreaFixture.ThePublicSurfaceAreaShouldNotRegress..NETFramework.approved.txt +++ b/source/Octopus.Client.Tests/PublicSurfaceAreaFixture.ThePublicSurfaceAreaShouldNotRegress..NETFramework.approved.txt @@ -34,10 +34,10 @@ Octopus.Client Task Create(String, Octopus.Client.TResource, CancellationToken) Task Create(String, Octopus.Client.TResource, Object, CancellationToken) Task Create(String, Octopus.Client.TCommand, Object, CancellationToken) - Task Delete(String, Object, Object, CancellationToken) - Task Delete(String, Octopus.Client.TCommand, CancellationToken) Task Delete(String, Object, Object) Task Delete(String, CancellationToken) + Task Delete(String, Object, Object, CancellationToken) + Task Delete(String, Octopus.Client.TCommand, CancellationToken) Task Do(Octopus.Server.MessageContracts.Base.ICommand, CancellationToken) Octopus.Client.IOctopusSpaceAsyncRepository ForSpace(Octopus.Client.Model.SpaceResource) Octopus.Client.IOctopusSystemAsyncRepository ForSystem() @@ -348,12 +348,12 @@ Octopus.Client Task Post(String, Octopus.Client.TResource, Object, CancellationToken) Task Post(String) Task Post(String, CancellationToken) + Task Put(String, Octopus.Client.TResource) + Task Put(String, Octopus.Client.TResource, CancellationToken) Task Put(String, Octopus.Client.TResource, Object) Task Put(String, Octopus.Client.TResource, Object, CancellationToken) Task Put(String) Task Put(String, CancellationToken) - Task Put(String, Octopus.Client.TResource) - Task Put(String, Octopus.Client.TResource, CancellationToken) Task PutContent(String, Stream) Task PutContent(String, Stream, CancellationToken) Uri QualifyUri(String, Object) @@ -4684,8 +4684,8 @@ Octopus.Client.Model Octopus.Client.Model.ProjectResource AddOrUpdateSensitiveTemplate(String, String, String, String) Octopus.Client.Model.ProjectResource AddOrUpdateSingleLineTextTemplate(String, String) Octopus.Client.Model.ProjectResource AddOrUpdateSingleLineTextTemplate(String, String, String, String) - Octopus.Client.Model.ProjectResource AddOrUpdateVariableTemplate(String, String, IDictionary) Octopus.Client.Model.ProjectResource AddOrUpdateVariableTemplate(String, String, IDictionary, String, String) + Octopus.Client.Model.ProjectResource AddOrUpdateVariableTemplate(String, String, IDictionary) Octopus.Client.Model.ProjectResource Clear() Octopus.Client.Model.ProjectResource IncludingLibraryVariableSets(Octopus.Client.Model.LibraryVariableSetResource[]) } From 670dbafd5cd9309d96b64198dd89f0c5c0193c34 Mon Sep 17 00:00:00 2001 From: Steven Cleve <107827476+stevencl840@users.noreply.github.com> Date: Mon, 6 Jan 2025 09:04:25 +1100 Subject: [PATCH 4/5] Add Git endpoints --- .../Editors/RunbookEditor.cs | 46 +++++++++++++ .../Editors/RunbookProcessEditor.cs | 12 ++++ .../Model/DeleteRunbookCommand.cs | 6 ++ .../Model/RunbookRunGitParameters.cs | 62 ++++++++++++++++++ .../Repositories/Async/RunbookRepository.cs | 53 ++++++--------- .../Repositories/RunbookProcessRepository.cs | 23 +++++++ .../Repositories/RunbookRepository.cs | 65 +++++++++++++++++++ .../Repositories/RunbookRunRepository.cs | 1 - 8 files changed, 235 insertions(+), 33 deletions(-) create mode 100644 source/Octopus.Server.Client/Model/DeleteRunbookCommand.cs create mode 100644 source/Octopus.Server.Client/Model/RunbookRunGitParameters.cs diff --git a/source/Octopus.Server.Client/Editors/RunbookEditor.cs b/source/Octopus.Server.Client/Editors/RunbookEditor.cs index de51403b4..1f6310c30 100644 --- a/source/Octopus.Server.Client/Editors/RunbookEditor.cs +++ b/source/Octopus.Server.Client/Editors/RunbookEditor.cs @@ -47,11 +47,44 @@ public RunbookEditor CreateOrModify(ProjectResource project, string name, string return this; } + public RunbookEditor CreateOrModifyInGit(ProjectResource project, string slug, string name, string description, string gitRef, string commitMessage) + { + var existing = repository.GetInGit(slug, project, gitRef); + + if (existing == null) + { + Instance = repository.CreateInGit(new RunbookResource + { + Slug = slug, + ProjectId = project.Id, + Name = name, + Description = description + }, + gitRef, + commitMessage); + } + else + { + existing.Name = name; + existing.Description = description; + + Instance = repository.ModifyInGit(existing, gitRef, commitMessage); + } + + return this; + } + public RunbookEditor Load(string id) { Instance = repository.Get(id); return this; } + + public RunbookEditor LoadInGit(string id, ProjectResource project, string gitRef) + { + Instance = repository.GetInGit(id, project, gitRef); + return this; + } public RunbookEditor Customize(Action customize) { @@ -68,5 +101,18 @@ public RunbookEditor Save() } return this; } + + public RunbookEditor SaveInGit(string gitRef, string commitMessage) + { + Instance = repository.ModifyInGit(Instance, gitRef, commitMessage); + + if (runbookProcess.IsValueCreated) + { + var steps = runbookProcess.Value; + steps.SaveInGit(gitRef, commitMessage); + } + + return this; + } } } \ No newline at end of file diff --git a/source/Octopus.Server.Client/Editors/RunbookProcessEditor.cs b/source/Octopus.Server.Client/Editors/RunbookProcessEditor.cs index 4d48f097e..d96c65e76 100644 --- a/source/Octopus.Server.Client/Editors/RunbookProcessEditor.cs +++ b/source/Octopus.Server.Client/Editors/RunbookProcessEditor.cs @@ -21,6 +21,12 @@ public RunbookProcessEditor Load(string id) return this; } + public RunbookProcessEditor LoadInGit(ProjectResource project, string id, string gitRef) + { + Instance = repository.GetInGit(project, id, gitRef); + return this; + } + public DeploymentStepResource FindStep(string name) { return Instance.FindStep(name); @@ -54,5 +60,11 @@ public RunbookProcessEditor Save() Instance = repository.Modify(Instance); return this; } + + public RunbookProcessEditor SaveInGit(string gitRef, string commitMessage) + { + Instance = repository.ModifyInGit(gitRef, commitMessage, Instance); + return this; + } } } \ No newline at end of file diff --git a/source/Octopus.Server.Client/Model/DeleteRunbookCommand.cs b/source/Octopus.Server.Client/Model/DeleteRunbookCommand.cs new file mode 100644 index 000000000..b7e1d72ae --- /dev/null +++ b/source/Octopus.Server.Client/Model/DeleteRunbookCommand.cs @@ -0,0 +1,6 @@ +namespace Octopus.Client.Model; + +public class DeleteRunbookCommand: RunbookResource, ICommitCommand +{ + public string ChangeDescription { get; set; } +} \ No newline at end of file diff --git a/source/Octopus.Server.Client/Model/RunbookRunGitParameters.cs b/source/Octopus.Server.Client/Model/RunbookRunGitParameters.cs new file mode 100644 index 000000000..d35138a98 --- /dev/null +++ b/source/Octopus.Server.Client/Model/RunbookRunGitParameters.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; + +namespace Octopus.Client.Model +{ + public class RunbookRunGitParameters + { + /// + /// Parameter class for marshalling params between OctopusCLI and Octopus Server + /// This class is used to facilitate backwards compatibility while extending /runbooks/{id}/run" + /// + public RunbookRunGitParameters() + { + + } + + public string RunbookId { get; set; } + public string ProjectId { get; set; } + public string Notes { get; set; } + + public List SelectedPackages { get; set; } = new(); + + public List SelectedGitResources { get; set; } = new(); + + } + + public class RunGitRunbookRun + { +#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. + protected RunGitRunbookRun() + { + } +#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. + + public RunGitRunbookRun(string environmentId) + { + EnvironmentId = environmentId; + } + + public string EnvironmentId { get; set; } + + public string TenantId { get; set; } + + public bool ForcePackageDownload { get; set; } = false; + + public string[] SkipActions { get; set; } + + public string[] SpecificMachineIds { get; set; } + + public string[] ExcludedMachineIds { get; set; } + + public bool UseGuidedFailure { get; set; } = false; + + public Dictionary FormValues { get; set; } = new(); + + public DateTimeOffset? QueueTime { get; set; } + + public DateTimeOffset? QueueTimeExpiry { get; set; } + + public string Comments { get; set; } + } +} \ No newline at end of file diff --git a/source/Octopus.Server.Client/Repositories/Async/RunbookRepository.cs b/source/Octopus.Server.Client/Repositories/Async/RunbookRepository.cs index dd2fed1bb..5d8dc360f 100644 --- a/source/Octopus.Server.Client/Repositories/Async/RunbookRepository.cs +++ b/source/Octopus.Server.Client/Repositories/Async/RunbookRepository.cs @@ -21,6 +21,8 @@ public interface IRunbookRepository : IFindByName, IGet GetPreview(DeploymentPromotionTarget promotionTarget); Task Run(RunbookResource runbook, RunbookRunResource runbookRun); Task Run(RunbookResource runbook, RunbookRunParameters runbookRunParameters); + + Task RunInGit(RunbookResource runbook, RunbookRunGitParameters runbookRunParameters, string gitRef, CancellationToken cancellationToken); } class RunbookRepository : BasicRepository, IRunbookRepository @@ -35,13 +37,13 @@ public RunbookRepository(IOctopusAsyncRepository repository) versionAfterWhichRunbookRunParametersAreAvailable = SemanticVersion.Parse("2020.3.1"); } - public Task GetInGit(string id, ProjectResource project, string gitRef, CancellationToken cancellationToken) + public async Task GetInGit(string id, ProjectResource project, string gitRef, CancellationToken cancellationToken) { - return GetInGit(id, project.SpaceId, project.Id, gitRef, cancellationToken); + return await GetInGit(id, project.SpaceId, project.Id, gitRef, cancellationToken); } - Task GetInGit(string id, string spaceId, string projectId, string gitRef, CancellationToken cancellationToken) - => Client.Get($"/api/{spaceId}/projects/{projectId}/{gitRef}/runbooks/{id}", cancellationToken); + async Task GetInGit(string id, string spaceId, string projectId, string gitRef, CancellationToken cancellationToken) + => await Client.Get($"/api/{spaceId}/projects/{projectId}/{gitRef}/runbooks/{id}", cancellationToken); public async Task CreateInGit(RunbookResource resource, string gitRef, string commitMessage, CancellationToken cancellationToken) { @@ -65,6 +67,17 @@ public async Task ModifyInGit(RunbookResource resource, string return await GetInGit(resource.Id, resource.SpaceId, resource.ProjectId, gitRef, cancellationToken); } + public async Task DeleteInGit(RunbookResource resource, string gitRef, string commitMessage) + { + // TODO: revisit/obsolete this API when we have converters + // until then we need a way to re-use the response from previous client calls + var json = Serializer.Serialize(resource); + var command = Serializer.Deserialize(json); + command.ChangeDescription = commitMessage; + + await Client.Delete($"/api/{resource.SpaceId}/projects/{resource.ProjectId}/{gitRef}/runbooks/{resource.Id}", command); + } + public Task FindByName(ProjectResource project, string name) { return FindByName(name, path: project.Link("Runbooks")); @@ -89,22 +102,11 @@ public Task GetRunbookRunTemplate(RunbookResource ru { return Client.Get(runbook.Link("RunbookRunTemplate")); } - - public Task GetRunbookRunTemplate(RunbookResource runbook, string gitRef) - { - return Client.Get(runbook.Link("RunbookRunTemplate")); - } - public Task GetPreview(DeploymentPromotionTarget promotionTarget) { return Client.Get(promotionTarget.Link("RunbookRunPreview")); } - - public Task GetPreview(DeploymentPromotionTarget promotionTarget, string gitRef) - { - return Client.Get(promotionTarget.Link("RunbookRunPreview")); - } private bool ServerSupportsRunbookRunParameters(string version) { @@ -127,15 +129,7 @@ public async Task Run(RunbookResource runbook, RunbookRunRes : await Client.Post(runbook.Link("CreateRunbookRun"), runbookRun); } - public async Task Run(RunbookResource runbook, RunbookRunResource runbookRun, string gitRef) - { - var serverSupportsRunbookRunParameters = ServerSupportsRunbookRunParameters((await Repository.LoadRootDocument()).Version); - return serverSupportsRunbookRunParameters - ? (await Run(runbook, RunbookRunParameters.MapFrom(runbookRun), gitRef)).FirstOrDefault() - : await Client.Post(runbook.Link("CreateRunbookRun"), runbookRun); - } - public async Task Run(RunbookResource runbook, RunbookRunParameters runbookRunParameters) { var serverVersion = (await Repository.LoadRootDocument()).Version; @@ -147,8 +141,8 @@ public async Task Run(RunbookResource runbook, RunbookRunP return await Client.Post(runbook.Link("CreateRunbookRun"), runbookRunParameters); } - - public async Task Run(RunbookResource runbook, RunbookRunParameters runbookRunParameters, string gitRef) + + public async Task RunInGit(RunbookResource runbook, RunbookRunGitParameters runbookRunParameters, string gitRef, CancellationToken cancellationToken) { var serverVersion = (await Repository.LoadRootDocument()).Version; var serverSupportsRunbookRunParameters = ServerSupportsRunbookRunParameters(serverVersion); @@ -156,13 +150,8 @@ public async Task Run(RunbookResource runbook, RunbookRunP if (serverSupportsRunbookRunParameters == false) throw new UnsupportedApiVersionException($"This Octopus Deploy server is an older version ({serverVersion}) that does not yet support RunbookRunParameters. " + $"Please update your Octopus Deploy server to 2020.3.* or newer to access this feature."); - - return await Client.Post(runbook.Link("CreateRunbookRun"), runbookRunParameters); - } - - bool ProjectHasRunbooksInGit(ProjectResource projectResource) - { - return projectResource.PersistenceSettings is GitPersistenceSettingsResource gitSettings && gitSettings.ConversionState.RunbooksAreInGit; + + return await Client.Post($"/api/{runbook.SpaceId}/projects/{runbook.ProjectId}/{gitRef}/runbooks/{runbook.Id}/run/v1", runbookRunParameters); } } } diff --git a/source/Octopus.Server.Client/Repositories/RunbookProcessRepository.cs b/source/Octopus.Server.Client/Repositories/RunbookProcessRepository.cs index 7747abab7..f92576123 100644 --- a/source/Octopus.Server.Client/Repositories/RunbookProcessRepository.cs +++ b/source/Octopus.Server.Client/Repositories/RunbookProcessRepository.cs @@ -1,10 +1,15 @@ using Octopus.Client.Model; +using Octopus.Client.Serialization; namespace Octopus.Client.Repositories { public interface IRunbookProcessRepository : IGet, IModify { RunbookSnapshotTemplateResource GetTemplate(RunbookProcessResource runbookProcess); + + RunbookProcessResource GetInGit(ProjectResource project, string slug, string gitRef); + + RunbookProcessResource ModifyInGit(string gitRef, string commitMessage, RunbookProcessResource instance); } class RunbookProcessRepository : BasicRepository, IRunbookProcessRepository @@ -18,5 +23,23 @@ public RunbookSnapshotTemplateResource GetTemplate(RunbookProcessResource runboo { return Client.Get(runbookProcess.Link("RunbookSnapshotTemplate")); } + + public RunbookProcessResource GetInGit(ProjectResource project, string slug, string gitRef) + => GetInGit(project.SpaceId, project.Id, slug, gitRef); + + RunbookProcessResource GetInGit(string spaceId, string projectId, string slug, string gitRef) + { + return Client.Get($"/api/spaces/{spaceId}/projects/{projectId}/{gitRef}/runbookProcesses/{slug}"); + } + + public RunbookProcessResource ModifyInGit(string gitRef, string commitMessage, RunbookProcessResource resource) + { + var json = Serializer.Serialize(resource); + var command = Serializer.Deserialize(json); + command.ChangeDescription = commitMessage; + + Client.Put($"/api/spaces/{resource.SpaceId}/projects/{resource.ProjectId}/{gitRef}/runbookProcesses/{resource.Id}", resource); + return GetInGit(resource.SpaceId, resource.ProjectId, resource.Id, gitRef); + } } } \ No newline at end of file diff --git a/source/Octopus.Server.Client/Repositories/RunbookRepository.cs b/source/Octopus.Server.Client/Repositories/RunbookRepository.cs index 4941fed43..9a003fa6e 100644 --- a/source/Octopus.Server.Client/Repositories/RunbookRepository.cs +++ b/source/Octopus.Server.Client/Repositories/RunbookRepository.cs @@ -2,18 +2,25 @@ using Octopus.Client.Editors; using Octopus.Client.Exceptions; using Octopus.Client.Model; +using Octopus.Client.Serialization; namespace Octopus.Client.Repositories { public interface IRunbookRepository : IFindByName, IGet, ICreate, IModify, IDelete { RunbookResource FindByName(ProjectResource project, string name); + RunbookResource GetInGit(string id, ProjectResource project, string gitRef); + RunbookResource CreateInGit(RunbookResource resource, string gitRef, string commitMessage); + RunbookResource ModifyInGit(RunbookResource resource, string gitRef, string commitMessage); + void DeleteInGit(RunbookResource resource, string gitRef, string commitMessage); RunbookEditor CreateOrModify(ProjectResource project, string name, string description); + RunbookEditor CreateOrModifyInGit(ProjectResource project, string slug, string name, string description, string gitRef, string commitMessage); RunbookSnapshotTemplateResource GetRunbookSnapshotTemplate(RunbookResource runbook); RunbookRunTemplateResource GetRunbookRunTemplate(RunbookResource runbook); RunbookRunPreviewResource GetPreview(DeploymentPromotionTarget promotionTarget); RunbookRunResource Run(RunbookResource runbook, RunbookRunResource runbookRun); RunbookRunResource[] Run(RunbookResource runbook, RunbookRunParameters runbookRunParameters); + RunbookRunResource[] RunInGit(RunbookResource runbook, RunbookRunGitParameters runbookRunParameters, string gitRef); } class RunbookRepository : BasicRepository, IRunbookRepository @@ -27,6 +34,47 @@ public RunbookRepository(IOctopusRepository repository) integrationTestVersion = SemanticVersion.Parse("0.0.0-local"); versionAfterWhichRunbookRunParametersAreAvailable = SemanticVersion.Parse("2020.3.1"); } + + public RunbookResource GetInGit(string id, ProjectResource project, string gitRef) + { + return GetInGit(id, project.SpaceId, project.Id, gitRef); + } + + RunbookResource GetInGit(string id, string spaceId, string projectId, string gitRef) + => Client.Get($"/api/{spaceId}/projects/{projectId}/{gitRef}/runbooks/{id}"); + + public RunbookResource CreateInGit(RunbookResource resource, string gitRef, string commitMessage) + { + var json = Serializer.Serialize(resource); + var command = Serializer.Deserialize(json); + command.ChangeDescription = commitMessage; + + Client.Post($"/api/{resource.SpaceId}/projects/{resource.ProjectId}/{gitRef}/runbooks/", command); + return GetInGit(resource.Id, resource.SpaceId, resource.ProjectId, gitRef); + } + + public RunbookResource ModifyInGit(RunbookResource resource, string gitRef, string commitMessage) + { + // TODO: revisit/obsolete this API when we have converters + // until then we need a way to re-use the response from previous client calls + var json = Serializer.Serialize(resource); + var command = Serializer.Deserialize(json); + command.ChangeDescription = commitMessage; + + Client.Put($"/api/{resource.SpaceId}/projects/{resource.ProjectId}/{gitRef}/runbooks/{resource.Id}", command); + return GetInGit(resource.Id, resource.SpaceId, resource.ProjectId, gitRef); + } + + public void DeleteInGit(RunbookResource resource, string gitRef, string commitMessage) + { + // TODO: revisit/obsolete this API when we have converters + // until then we need a way to re-use the response from previous client calls + var json = Serializer.Serialize(resource); + var command = Serializer.Deserialize(json); + command.ChangeDescription = commitMessage; + + Client.Delete($"/api/{resource.SpaceId}/projects/{resource.ProjectId}/{gitRef}/runbooks/{resource.Id}", command); + } public RunbookResource FindByName(ProjectResource project, string name) { @@ -37,6 +85,11 @@ public RunbookEditor CreateOrModify(ProjectResource project, string name, string { return new RunbookEditor(this, new RunbookProcessRepository(Repository)).CreateOrModify(project, name, description); } + + public RunbookEditor CreateOrModifyInGit(ProjectResource project, string slug, string name, string description, string gitRef, string commitMessage) + { + return new RunbookEditor(this, new RunbookProcessRepository(Repository)).CreateOrModifyInGit(project, slug, name, description, gitRef, commitMessage); + } public RunbookSnapshotTemplateResource GetRunbookSnapshotTemplate(RunbookResource runbook) { @@ -85,5 +138,17 @@ public RunbookRunResource[] Run(RunbookResource runbook, RunbookRunParameters ru return Client.Post(runbook.Link("CreateRunbookRun"), runbookRunParameters); } + + public RunbookRunResource[] RunInGit(RunbookResource runbook, RunbookRunGitParameters runbookRunParameters, string gitRef) + { + var serverVersion = Repository.LoadRootDocument().Version; + var serverSupportsRunbookRunParameters = ServerSupportsRunbookRunParameters(serverVersion); + + if (serverSupportsRunbookRunParameters == false) + throw new UnsupportedApiVersionException($"This Octopus Deploy server is an older version ({serverVersion}) that does not yet support RunbookRunParameters. " + + "Please update your Octopus Deploy server to 2020.3.* or newer to access this feature."); + + return Client.Post($"/api/{runbook.SpaceId}/projects/{runbook.ProjectId}/{gitRef}/runbooks/{runbook.Id}/run/v1", runbookRunParameters); + } } } \ No newline at end of file diff --git a/source/Octopus.Server.Client/Repositories/RunbookRunRepository.cs b/source/Octopus.Server.Client/Repositories/RunbookRunRepository.cs index 23799f9ed..4346c0cac 100644 --- a/source/Octopus.Server.Client/Repositories/RunbookRunRepository.cs +++ b/source/Octopus.Server.Client/Repositories/RunbookRunRepository.cs @@ -19,7 +19,6 @@ public interface IRunbookRunRepository : IGet, ICreate FindBy(string[] projects, string[] runbooks, string[] environments, int skip = 0, int? take = null); void Paginate(string[] projects, string[] runbooks, string[] environments, Func, bool> getNextPage); void Paginate(string[] projects, string[] runbooks, string[] environments, string[] tenants, Func, bool> getNextPage); - } class RunbookRunRepository : BasicRepository, IRunbookRunRepository From ca601a4598f3b4ec2b4cf38245c4028ae7ac4014 Mon Sep 17 00:00:00 2001 From: Steven Cleve <107827476+stevencl840@users.noreply.github.com> Date: Wed, 8 Jan 2025 14:37:54 +1100 Subject: [PATCH 5/5] Added Notebooks so we can exercise the the new repository methods --- source/Notebooks/Notebooks.csproj | 36 ++++++++++++++++ source/Notebooks/Runbooks ASync.dib | 43 +++++++++++++++++++ source/Notebooks/Runbooks Sync.dib | 21 +++++++++ .../Repositories/Async/RunbookRepository.cs | 4 +- source/OctopusClient.sln | 14 ++++++ 5 files changed, 116 insertions(+), 2 deletions(-) create mode 100644 source/Notebooks/Notebooks.csproj create mode 100644 source/Notebooks/Runbooks ASync.dib create mode 100644 source/Notebooks/Runbooks Sync.dib diff --git a/source/Notebooks/Notebooks.csproj b/source/Notebooks/Notebooks.csproj new file mode 100644 index 000000000..df4dc15f5 --- /dev/null +++ b/source/Notebooks/Notebooks.csproj @@ -0,0 +1,36 @@ + + + + $(NoWarn);CS1591 + true + true + Notebooks + false + Octopus Deploy + Octopus Deploy Pty Ltd + + Octopus Deploy is an automated release management tool for modern developers and DevOps teams. + + This package contains the client library for the HTTP API in Octopus. + + Octopus.Client.nuspec + + true + 8 + e33919d5-7abc-48db-bb00-0d49c717b69e + + + + + net462;net48;netstandard2.0 + + + + netstandard2.0 + + + + + + + diff --git a/source/Notebooks/Runbooks ASync.dib b/source/Notebooks/Runbooks ASync.dib new file mode 100644 index 000000000..b9bd3c832 --- /dev/null +++ b/source/Notebooks/Runbooks ASync.dib @@ -0,0 +1,43 @@ +#!meta + +{"kernelInfo":{"defaultKernelName":"csharp","items":[{"aliases":[],"languageName":"csharp","name":"csharp"}]}} + +#!csharp + +#r "..\Octopus.Server.Client\bin\Debug\netstandard2.0\Octopus.Server.Client.dll" + +using Octopus.Client.Repositories; +using System.Threading; + +var apiKey = Environment.GetEnvironmentVariable("Server__ApiKey"); + +var endpoint = new Octopus.Client.OctopusServerEndpoint("http://localhost:8066", apiKey); +var client = await Octopus.Client.OctopusAsyncClient.Create(endpoint, new Octopus.Client.OctopusClientOptions()); +var repository = new Octopus.Client.OctopusAsyncRepository(client); + +var project = await repository.Projects.FindByName("Git Example", CancellationToken.None); + +var runbook = await repository.Runbooks.GetInGit("mr-runbooks", project, "main", CancellationToken.None); +runbook.Name ="Aync New Test"; + +await repository.Runbooks.ModifyInGit(runbook, "main", "Test", CancellationToken.None); + +#!csharp + +#r "..\Octopus.Server.Client\bin\Debug\netstandard2.0\Octopus.Server.Client.dll" + +using Octopus.Client.Repositories; +using System.Threading; + +var apiKey = Environment.GetEnvironmentVariable("Server__ApiKey"); + +var endpoint = new Octopus.Client.OctopusServerEndpoint("http://localhost:8066", apiKey); +var client = await Octopus.Client.OctopusAsyncClient.Create(endpoint, new Octopus.Client.OctopusClientOptions()); +var repository = new Octopus.Client.OctopusAsyncRepository(client); + +var project = await repository.Projects.FindByName("Git Example", CancellationToken.None); + +var runbook = await repository.Runbooks.CreateOrModifyInGit(project, "test-1", "Test One", "Allows testing", "main", "Test", CancellationToken.None); +runbook = await repository.Runbooks.CreateOrModifyInGit(project, "test-1", "Test One", "Allows more testing", "main", "Test 2", CancellationToken.None); + +await repository.Runbooks.DeleteInGit(runbook, "main", "test delete", CancellationToken.None); diff --git a/source/Notebooks/Runbooks Sync.dib b/source/Notebooks/Runbooks Sync.dib new file mode 100644 index 000000000..e0ad9daa8 --- /dev/null +++ b/source/Notebooks/Runbooks Sync.dib @@ -0,0 +1,21 @@ +#!meta + +{"kernelInfo":{"defaultKernelName":"csharp","items":[{"aliases":[],"languageName":"csharp","name":"csharp"}]}} + +#!csharp + +#r "..\Octopus.Server.Client\bin\Debug\netstandard2.0\Octopus.Server.Client.dll" + +using Octopus.Client.Repositories; + +var apiKey = Environment.GetEnvironmentVariable("Server__ApiKey"); + +var endpoint = new Octopus.Client.OctopusServerEndpoint("http://localhost:8066", apiKey); + +var repository = new Octopus.Client.OctopusRepository(endpoint); +var project = repository.Projects.FindByName("Git Example"); + +var runbook = repository.Runbooks.GetInGit("mr-runbooks", project, "main"); +runbook.Name ="New Test"; + +repository.Runbooks.ModifyInGit(runbook, "main", "Test"); diff --git a/source/Octopus.Server.Client/Repositories/Async/RunbookRepository.cs b/source/Octopus.Server.Client/Repositories/Async/RunbookRepository.cs index 5d8dc360f..de7535214 100644 --- a/source/Octopus.Server.Client/Repositories/Async/RunbookRepository.cs +++ b/source/Octopus.Server.Client/Repositories/Async/RunbookRepository.cs @@ -67,7 +67,7 @@ public async Task ModifyInGit(RunbookResource resource, string return await GetInGit(resource.Id, resource.SpaceId, resource.ProjectId, gitRef, cancellationToken); } - public async Task DeleteInGit(RunbookResource resource, string gitRef, string commitMessage) + public async Task DeleteInGit(RunbookResource resource, string gitRef, string commitMessage, CancellationToken cancellationToken) { // TODO: revisit/obsolete this API when we have converters // until then we need a way to re-use the response from previous client calls @@ -75,7 +75,7 @@ public async Task DeleteInGit(RunbookResource resource, string gitRef, string co var command = Serializer.Deserialize(json); command.ChangeDescription = commitMessage; - await Client.Delete($"/api/{resource.SpaceId}/projects/{resource.ProjectId}/{gitRef}/runbooks/{resource.Id}", command); + await Client.Delete($"/api/{resource.SpaceId}/projects/{resource.ProjectId}/{gitRef}/runbooks/{resource.Id}", command, cancellationToken); } public Task FindByName(ProjectResource project, string name) diff --git a/source/OctopusClient.sln b/source/OctopusClient.sln index fca2de576..77d88c13f 100644 --- a/source/OctopusClient.sln +++ b/source/OctopusClient.sln @@ -24,6 +24,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Octopus.Server.Client", "Oc EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Octopus.Client", "Octopus.Client\Octopus.Client.csproj", "{5B15F5A6-C1B3-4339-AB28-71B3309E46C2}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Notebooks", "Notebooks\Notebooks.csproj", "{96A6FF8A-B381-4A45-AF5B-C39B7CFD4636}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -88,6 +90,18 @@ Global {5B15F5A6-C1B3-4339-AB28-71B3309E46C2}.Release|Mixed Platforms.Build.0 = Release|Any CPU {5B15F5A6-C1B3-4339-AB28-71B3309E46C2}.Release|x86.ActiveCfg = Release|Any CPU {5B15F5A6-C1B3-4339-AB28-71B3309E46C2}.Release|x86.Build.0 = Release|Any CPU + {96A6FF8A-B381-4A45-AF5B-C39B7CFD4636}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {96A6FF8A-B381-4A45-AF5B-C39B7CFD4636}.Debug|Any CPU.Build.0 = Debug|Any CPU + {96A6FF8A-B381-4A45-AF5B-C39B7CFD4636}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {96A6FF8A-B381-4A45-AF5B-C39B7CFD4636}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {96A6FF8A-B381-4A45-AF5B-C39B7CFD4636}.Debug|x86.ActiveCfg = Debug|Any CPU + {96A6FF8A-B381-4A45-AF5B-C39B7CFD4636}.Debug|x86.Build.0 = Debug|Any CPU + {96A6FF8A-B381-4A45-AF5B-C39B7CFD4636}.Release|Any CPU.ActiveCfg = Release|Any CPU + {96A6FF8A-B381-4A45-AF5B-C39B7CFD4636}.Release|Any CPU.Build.0 = Release|Any CPU + {96A6FF8A-B381-4A45-AF5B-C39B7CFD4636}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {96A6FF8A-B381-4A45-AF5B-C39B7CFD4636}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {96A6FF8A-B381-4A45-AF5B-C39B7CFD4636}.Release|x86.ActiveCfg = Release|Any CPU + {96A6FF8A-B381-4A45-AF5B-C39B7CFD4636}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE