Skip to content

Commit

Permalink
Add operating identity and sub classes
Browse files Browse the repository at this point in the history
  • Loading branch information
dstenroejl committed Nov 19, 2024
1 parent 4fbe22f commit d434aa7
Show file tree
Hide file tree
Showing 15 changed files with 212 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,13 @@ private static OrchestrationDescription CreateOrchestrationDescription()

private static OrchestrationInstance CreateOrchestrationInstance(OrchestrationDescription orchestrationDescription)
{
var userIdentity = new UserIdentity(
new UserId(Guid.NewGuid()),
new ActorId(Guid.NewGuid()));

var orchestrationInstance = OrchestrationInstance.CreateFromDescription(
description: orchestrationDescription,
userIdentity,
orchestrationDescription,
skipStepsBySequence: [3],
clock: SystemClock.Instance);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ public async Task GivenOrchestrationInstancesInDatabase_WhenSearchByNameAndTermi
var isTerminatedAsSucceededV1 = CreateOrchestrationInstance(existingOrchestrationDescriptionV1);
isTerminatedAsSucceededV1.Lifecycle.TransitionToQueued(SystemClock.Instance);
isTerminatedAsSucceededV1.Lifecycle.TransitionToRunning(SystemClock.Instance);
isTerminatedAsSucceededV1.Lifecycle.TransitionToTerminated(SystemClock.Instance, OrchestrationInstanceTerminationStates.Succeeded);
isTerminatedAsSucceededV1.Lifecycle.TransitionToSucceeded(SystemClock.Instance);
await _sut.AddAsync(isTerminatedAsSucceededV1);

var isPendingV2 = CreateOrchestrationInstance(existingOrchestrationDescriptionV2);
Expand All @@ -275,7 +275,7 @@ public async Task GivenOrchestrationInstancesInDatabase_WhenSearchByNameAndTermi
var isTerminatedAsFailedV2 = CreateOrchestrationInstance(existingOrchestrationDescriptionV2);
isTerminatedAsFailedV2.Lifecycle.TransitionToQueued(SystemClock.Instance);
isTerminatedAsFailedV2.Lifecycle.TransitionToRunning(SystemClock.Instance);
isTerminatedAsFailedV2.Lifecycle.TransitionToTerminated(SystemClock.Instance, OrchestrationInstanceTerminationStates.Failed);
isTerminatedAsFailedV2.Lifecycle.TransitionToFailed(SystemClock.Instance);
await _sut.AddAsync(isTerminatedAsFailedV2);

await _sut.UnitOfWork.CommitAsync();
Expand Down Expand Up @@ -353,13 +353,13 @@ public async Task GivenOrchestrationInstancesInDatabase_WhenSearchByNameAndTermi
var isTerminated01 = CreateOrchestrationInstance(existingOrchestrationDescriptionV1);
isTerminated01.Lifecycle.TransitionToQueued(SystemClock.Instance);
isTerminated01.Lifecycle.TransitionToRunning(SystemClock.Instance);
isTerminated01.Lifecycle.TransitionToTerminated(terminatedAtClockMock01.Object, OrchestrationInstanceTerminationStates.Succeeded);
isTerminated01.Lifecycle.TransitionToSucceeded(terminatedAtClockMock01.Object);
await _sut.AddAsync(isTerminated01);

var isTerminated02 = CreateOrchestrationInstance(existingOrchestrationDescriptionV1);
isTerminated02.Lifecycle.TransitionToQueued(SystemClock.Instance);
isTerminated02.Lifecycle.TransitionToRunning(SystemClock.Instance);
isTerminated02.Lifecycle.TransitionToTerminated(SystemClock.Instance, OrchestrationInstanceTerminationStates.Succeeded);
isTerminated02.Lifecycle.TransitionToFailed(SystemClock.Instance);
await _sut.AddAsync(isTerminated02);

await _sut.UnitOfWork.CommitAsync();
Expand Down Expand Up @@ -395,8 +395,13 @@ private static OrchestrationInstance CreateOrchestrationInstance(
OrchestrationDescription orchestrationDescription,
Instant? runAt = default)
{
var userIdentity = new UserIdentity(
new UserId(Guid.NewGuid()),
new ActorId(Guid.NewGuid()));

var orchestrationInstance = OrchestrationInstance.CreateFromDescription(
description: orchestrationDescription,
userIdentity,
orchestrationDescription,
skipStepsBySequence: [],
clock: SystemClock.Instance,
runAt: runAt);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,5 @@ public interface ICancelScheduledOrchestrationInstanceCommand
/// <summary>
/// Cancel a scheduled orchestration instance.
/// </summary>
Task CancelScheduledOrchestrationInstanceAsync(OrchestrationInstanceId id);
Task CancelScheduledOrchestrationInstanceAsync(UserIdentity userIdentity, OrchestrationInstanceId id);
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public interface IStartOrchestrationInstanceCommands
/// Start a new instance of an orchestration.
/// </summary>
Task<OrchestrationInstanceId> StartNewOrchestrationInstanceAsync<TParameter>(
OperatingIdentity identity,
string name,
int version,
TParameter inputParameter,
Expand All @@ -33,6 +34,7 @@ Task<OrchestrationInstanceId> StartNewOrchestrationInstanceAsync<TParameter>(
/// Schedule a new instance of an orchestration.
/// </summary>
Task<OrchestrationInstanceId> ScheduleNewOrchestrationInstanceAsync<TParameter>(
UserIdentity identity,
string name,
int version,
TParameter inputParameter,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,34 +39,57 @@ internal class OrchestrationInstanceManager(

/// <inheritdoc />
public async Task<OrchestrationInstanceId> StartNewOrchestrationInstanceAsync<TParameter>(
OperatingIdentity identity,
string name,
int version,
TParameter inputParameter,
IReadOnlyCollection<int> skipStepsBySequence)
where TParameter : class
{
var orchestrationDescription = await GuardMatchingOrchestrationDescriptionAsync(name, version, inputParameter, skipStepsBySequence).ConfigureAwait(false);
var orchestrationDescription = await GuardMatchingOrchestrationDescriptionAsync(
name,
version,
inputParameter,
skipStepsBySequence).ConfigureAwait(false);

var orchestrationInstance = await CreateOrchestrationInstanceAsync(
identity,
orchestrationDescription,
inputParameter,
skipStepsBySequence).ConfigureAwait(false);

var orchestrationInstance = await CreateOrchestrationInstanceAsync(inputParameter, orchestrationDescription, skipStepsBySequence).ConfigureAwait(false);
await RequestStartOfOrchestrationInstanceAsync(orchestrationDescription, orchestrationInstance).ConfigureAwait(false);
await RequestStartOfOrchestrationInstanceAsync(
orchestrationDescription,
orchestrationInstance).ConfigureAwait(false);

return orchestrationInstance.Id;
}

/// <inheritdoc />
public async Task<OrchestrationInstanceId> ScheduleNewOrchestrationInstanceAsync<TParameter>(
UserIdentity userIdentity,
string name,
int version,
TParameter inputParameter,
Instant runAt,
IReadOnlyCollection<int> skipStepsBySequence)
where TParameter : class
{
var orchestrationDescription = await GuardMatchingOrchestrationDescriptionAsync(name, version, inputParameter, skipStepsBySequence).ConfigureAwait(false);
var orchestrationDescription = await GuardMatchingOrchestrationDescriptionAsync(
name,
version,
inputParameter,
skipStepsBySequence).ConfigureAwait(false);

if (orchestrationDescription.CanBeScheduled == false)
throw new InvalidOperationException("Orchestration description cannot be scheduled.");

var orchestrationInstance = await CreateOrchestrationInstanceAsync(inputParameter, orchestrationDescription, skipStepsBySequence, runAt).ConfigureAwait(false);
var orchestrationInstance = await CreateOrchestrationInstanceAsync(
userIdentity,
orchestrationDescription,
inputParameter,
skipStepsBySequence,
runAt).ConfigureAwait(false);

return orchestrationInstance.Id;
}
Expand All @@ -86,14 +109,14 @@ public async Task StartScheduledOrchestrationInstanceAsync(OrchestrationInstance
}

/// <inheritdoc />
public async Task CancelScheduledOrchestrationInstanceAsync(OrchestrationInstanceId id)
public async Task CancelScheduledOrchestrationInstanceAsync(UserIdentity userIdentity, OrchestrationInstanceId id)
{
var orchestrationInstance = await _repository.GetAsync(id).ConfigureAwait(false);
if (!orchestrationInstance.Lifecycle.IsPendingForScheduledStart())
throw new InvalidOperationException("Orchestration instance cannot be canceled.");

// Transition lifecycle
orchestrationInstance.Lifecycle.TransitionToTerminated(_clock, OrchestrationInstanceTerminationStates.UserCanceled);
orchestrationInstance.Lifecycle.TransitionToUserCanceled(_clock, userIdentity);
await _repository.UnitOfWork.CommitAsync().ConfigureAwait(false);
}

Expand Down Expand Up @@ -129,13 +152,15 @@ private async Task<OrchestrationDescription> GuardMatchingOrchestrationDescripti
}

private async Task<OrchestrationInstance> CreateOrchestrationInstanceAsync<TParameter>(
TParameter inputParameter,
OperatingIdentity identity,
OrchestrationDescription orchestrationDescription,
TParameter inputParameter,
IReadOnlyCollection<int> skipStepsBySequence,
Instant? runAt = default)
where TParameter : class
{
var orchestrationInstance = OrchestrationInstance.CreateFromDescription(
identity,
orchestrationDescription,
skipStepsBySequence,
_clock,
Expand Down
17 changes: 17 additions & 0 deletions source/ProcessManager.Core/Domain/OrchestrationInstance/ActorId.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright 2020 Energinet DataHub A/S
//
// Licensed under the Apache License, Version 2.0 (the "License2");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

namespace Energinet.DataHub.ProcessManagement.Core.Domain.OrchestrationInstance;

public record ActorId(Guid Value);
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright 2020 Energinet DataHub A/S
//
// Licensed under the Apache License, Version 2.0 (the "License2");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

namespace Energinet.DataHub.ProcessManagement.Core.Domain.OrchestrationInstance;

/// <summary>
/// An actor identity performing a Process Manager operation.
/// </summary>
public class ActorIdentity : OperatingIdentity
{
public ActorIdentity(
ActorId actorId)
{
ActorId = actorId;
}

public ActorId ActorId { get; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright 2020 Energinet DataHub A/S
//
// Licensed under the Apache License, Version 2.0 (the "License2");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

namespace Energinet.DataHub.ProcessManagement.Core.Domain.OrchestrationInstance;

/// <summary>
/// An identity performing an Process Manager operation.
/// </summary>
public abstract class OperatingIdentity
{
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,12 @@ public class OrchestrationInstance

private OrchestrationInstance(
OrchestrationDescriptionId orchestrationDescriptionId,
OperatingIdentity identity,
IClock clock,
Instant? runAt = default)
{
Id = new OrchestrationInstanceId(Guid.NewGuid());
Lifecycle = new OrchestrationInstanceLifecycleState(clock, runAt);
Lifecycle = new OrchestrationInstanceLifecycleState(identity, clock, runAt);
ParameterValue = new();
CustomState = new OrchestrationInstanceCustomState(string.Empty);

Expand Down Expand Up @@ -86,6 +87,7 @@ private OrchestrationInstance()
/// orchestration instance.
/// </summary>
internal static OrchestrationInstance CreateFromDescription(
OperatingIdentity identity,
OrchestrationDescription.OrchestrationDescription description,
IReadOnlyCollection<int> skipStepsBySequence,
IClock clock,
Expand All @@ -106,6 +108,7 @@ internal static OrchestrationInstance CreateFromDescription(

var orchestrationInstance = new OrchestrationInstance(
description.Id,
identity,
clock,
runAt);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@ namespace Energinet.DataHub.ProcessManagement.Core.Domain.OrchestrationInstance;

public class OrchestrationInstanceLifecycleState
{
internal OrchestrationInstanceLifecycleState(IClock clock, Instant? runAt)
internal OrchestrationInstanceLifecycleState(OperatingIdentity createdBy, IClock clock, Instant? runAt)
{
CreatedBy = createdBy;
CreatedAt = clock.GetCurrentInstant();
ScheduledToRunAt = runAt;

Expand All @@ -40,6 +41,11 @@ private OrchestrationInstanceLifecycleState()

public OrchestrationInstanceTerminationStates? TerminationState { get; private set; }

/// <summary>
/// The identity that caused this orchestration instance to be created.
/// </summary>
public OperatingIdentity CreatedBy { get; }

/// <summary>
/// The time when the orchestration instance was created (State => Pending).
/// </summary>
Expand Down Expand Up @@ -68,6 +74,11 @@ private OrchestrationInstanceLifecycleState()
/// </summary>
public Instant? TerminatedAt { get; private set; }

/// <summary>
/// The identity that caused this orchestration instance to be canceled.
/// </summary>
public OperatingIdentity? CanceledBy { get; private set; }

public bool IsPendingForScheduledStart()
{
return
Expand All @@ -93,7 +104,22 @@ public void TransitionToRunning(IClock clock)
StartedAt = clock.GetCurrentInstant();
}

public void TransitionToTerminated(IClock clock, OrchestrationInstanceTerminationStates terminationState)
public void TransitionToSucceeded(IClock clock)
{
TransitionToTerminated(clock, OrchestrationInstanceTerminationStates.Succeeded);
}

public void TransitionToFailed(IClock clock)
{
TransitionToTerminated(clock, OrchestrationInstanceTerminationStates.Failed);
}

public void TransitionToUserCanceled(IClock clock, UserIdentity userIdentity)
{
TransitionToTerminated(clock, OrchestrationInstanceTerminationStates.UserCanceled, userIdentity);
}

private void TransitionToTerminated(IClock clock, OrchestrationInstanceTerminationStates terminationState, UserIdentity? userIdentity = default)
{
switch (terminationState)
{
Expand All @@ -105,6 +131,8 @@ public void TransitionToTerminated(IClock clock, OrchestrationInstanceTerminatio
case OrchestrationInstanceTerminationStates.UserCanceled:
if (!IsPendingForScheduledStart())
throw new InvalidOperationException("User cannot cancel orchestration instance.");
CanceledBy = userIdentity
?? throw new InvalidOperationException("User identity must be specified.");
break;
default:
throw new InvalidOperationException($"Unsupported termination state '{terminationState}'.");
Expand Down
17 changes: 17 additions & 0 deletions source/ProcessManager.Core/Domain/OrchestrationInstance/UserId.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright 2020 Energinet DataHub A/S
//
// Licensed under the Apache License, Version 2.0 (the "License2");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

namespace Energinet.DataHub.ProcessManagement.Core.Domain.OrchestrationInstance;

public record UserId(Guid Value);
Loading

0 comments on commit d434aa7

Please sign in to comment.