Skip to content

Commit 71e397f

Browse files
committed
fix(Dashboard): Added views to create, view and list schedules
1 parent afb3ca6 commit 71e397f

File tree

29 files changed

+1594
-100
lines changed

29 files changed

+1594
-100
lines changed

src/core/Synapse.Application/Commands/Schedules/v1/V1CreateScheduleCommand.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ namespace Synapse.Application.Commands.Schedules
2323
/// <summary>
2424
/// Represents the <see cref="ICommand"/> used to create a new <see cref="V1Schedule"/>
2525
/// </summary>
26+
[DataTransferObjectType(typeof(Integration.Commands.Schedules.V1CreateScheduleCommand))]
2627
public class V1CreateScheduleCommand
2728
: Command<Integration.Models.V1Schedule>
2829
{
@@ -98,8 +99,10 @@ public V1CreateScheduleCommandHandler(ILoggerFactory loggerFactory, IMediator me
9899
/// <inheritdoc/>
99100
public virtual async Task<IOperationResult<Integration.Models.V1Schedule>> HandleAsync(V1CreateScheduleCommand command, CancellationToken cancellationToken = default)
100101
{
101-
var workflow = await this.Workflows.FindAsync(command.WorkflowId, cancellationToken);
102-
if (workflow == null) throw DomainException.NullReference(typeof(V1Workflow), command.WorkflowId);
102+
var workflowId = (await this.Mediator.ExecuteAndUnwrapAsync(Queries.Workflows.V1GetWorkflowByIdQuery.Parse(command.WorkflowId), cancellationToken))?.Id;
103+
if(string.IsNullOrWhiteSpace(workflowId)) throw DomainException.NullReference(typeof(V1Workflow), command.WorkflowId);
104+
var workflow = await this.Workflows.FindAsync(workflowId, cancellationToken);
105+
if (workflow == null) throw DomainException.NullReference(typeof(V1Workflow), workflowId);
103106
var schedule = await this.Schedules.AddAsync(new(command.Type, command.Definition, workflow), cancellationToken);
104107
await this.Schedules.SaveChangesAsync(cancellationToken);
105108
return this.Ok(this.Mapper.Map<Integration.Models.V1Schedule>(schedule));

src/core/Synapse.Application/Commands/Schedules/v1/V1MakeScheduleObsoleteCommand.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ namespace Synapse.Application.Commands.Schedules
2020
/// <summary>
2121
/// Represents the <see cref="ICommand"/> used to make a <see cref="V1Schedule"/> obsolete
2222
/// </summary>
23-
[DataTransferObjectType(typeof(Integration.Commands.Schedules.V1ObsoleteScheduleCommand))]
23+
[DataTransferObjectType(typeof(Integration.Commands.Schedules.V1MakeScheduleObsoleteCommand))]
2424
public class V1MakeScheduleObsoleteCommand
2525
: Command
2626
{

src/core/Synapse.Application/Events/Domain/v1/V1ScheduleDomainEventHandler.cs

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,9 @@ public virtual async Task HandleAsync(V1ScheduleDefinitionChangedDomainEvent e,
6464
public virtual async Task HandleAsync(V1ScheduleSuspendedDomainEvent e, CancellationToken cancellationToken = default)
6565
{
6666
var schedule = await this.GetOrReconcileProjectionAsync(e.AggregateId, cancellationToken);
67-
schedule.LastModified = e.CreatedAt.DateTime;
68-
schedule.SuspendedAt = e.CreatedAt.DateTime;
67+
schedule.LastModified = e.CreatedAt.UtcDateTime;
68+
schedule.SuspendedAt = e.CreatedAt.UtcDateTime;
69+
schedule.NextOccurenceAt = null;
6970
schedule.Status = V1ScheduleStatus.Suspended;
7071
await this.Projections.UpdateAsync(schedule, cancellationToken);
7172
await this.Projections.SaveChangesAsync(cancellationToken);
@@ -76,7 +77,9 @@ public virtual async Task HandleAsync(V1ScheduleSuspendedDomainEvent e, Cancella
7677
public virtual async Task HandleAsync(V1ScheduleResumedDomainEvent e, CancellationToken cancellationToken = default)
7778
{
7879
var schedule = await this.GetOrReconcileProjectionAsync(e.AggregateId, cancellationToken);
79-
schedule.LastModified = e.CreatedAt.DateTime;
80+
schedule.LastModified = e.CreatedAt.UtcDateTime;
81+
schedule.NextOccurenceAt = e.NextOccurenceAt?.UtcDateTime;
82+
schedule.SuspendedAt = null;
8083
schedule.Status = V1ScheduleStatus.Active;
8184
await this.Projections.UpdateAsync(schedule, cancellationToken);
8285
await this.Projections.SaveChangesAsync(cancellationToken);
@@ -87,9 +90,9 @@ public virtual async Task HandleAsync(V1ScheduleResumedDomainEvent e, Cancellati
8790
public virtual async Task HandleAsync(V1ScheduleOccuredDomainEvent e, CancellationToken cancellationToken = default)
8891
{
8992
var schedule = await this.GetOrReconcileProjectionAsync(e.AggregateId, cancellationToken);
90-
schedule.LastModified = e.CreatedAt.DateTime;
91-
schedule.LastOccuredAt = e.CreatedAt.DateTime;
92-
schedule.NextOccurenceAt = e.NextOccurenceAt?.Date;
93+
schedule.LastModified = e.CreatedAt.UtcDateTime;
94+
schedule.LastOccuredAt = e.CreatedAt.UtcDateTime;
95+
schedule.NextOccurenceAt = e.NextOccurenceAt?.UtcDateTime;
9396
schedule.TotalOccurences++;
9497
await this.Projections.UpdateAsync(schedule, cancellationToken);
9598
await this.Projections.SaveChangesAsync(cancellationToken);
@@ -100,7 +103,8 @@ public virtual async Task HandleAsync(V1ScheduleOccuredDomainEvent e, Cancellati
100103
public virtual async Task HandleAsync(V1ScheduleRetiredDomainEvent e, CancellationToken cancellationToken = default)
101104
{
102105
var schedule = await this.GetOrReconcileProjectionAsync(e.AggregateId, cancellationToken);
103-
schedule.LastModified = e.CreatedAt.DateTime;
106+
schedule.LastModified = e.CreatedAt.UtcDateTime;
107+
schedule.NextOccurenceAt = null;
104108
schedule.Status = V1ScheduleStatus.Retired;
105109
await this.Projections.UpdateAsync(schedule, cancellationToken);
106110
await this.Projections.SaveChangesAsync(cancellationToken);
@@ -111,7 +115,8 @@ public virtual async Task HandleAsync(V1ScheduleRetiredDomainEvent e, Cancellati
111115
public virtual async Task HandleAsync(V1ScheduleObsolitedDomainEvent e, CancellationToken cancellationToken = default)
112116
{
113117
var schedule = await this.GetOrReconcileProjectionAsync(e.AggregateId, cancellationToken);
114-
schedule.LastModified = e.CreatedAt.DateTime;
118+
schedule.LastModified = e.CreatedAt.UtcDateTime;
119+
schedule.NextOccurenceAt = null;
115120
schedule.Status = V1ScheduleStatus.Obsolete;
116121
await this.Projections.UpdateAsync(schedule, cancellationToken);
117122
await this.Projections.SaveChangesAsync(cancellationToken);

src/core/Synapse.Domain/Events/Schedules/v1/V1ScheduleCreatedDomainEvent.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,23 +36,23 @@ protected V1ScheduleCreatedDomainEvent() { }
3636
/// Initializes a new <see cref="V1ScheduleCreatedDomainEvent"/>
3737
/// </summary>
3838
/// <param name="id">The id of the newly created <see cref="V1Schedule"/></param>
39-
/// <param name="type">The type of the newly created <see cref="V1Schedule"/></param>
39+
/// <param name="activationType">The activation type of the newly created <see cref="V1Schedule"/></param>
4040
/// <param name="definition">The definition of the newly created <see cref="V1Schedule"/></param>
4141
/// <param name="workflowId">The id of the <see cref="V1Workflow"/> scheduled by the newly created <see cref="V1Schedule"/></param>
4242
/// <param name="nextOccurenceAt">The date and time at which the <see cref="V1Schedule"/> will next occur</param>
43-
public V1ScheduleCreatedDomainEvent(string id, V1ScheduleType type, ScheduleDefinition definition, string workflowId, DateTimeOffset? nextOccurenceAt)
43+
public V1ScheduleCreatedDomainEvent(string id, V1ScheduleType activationType, ScheduleDefinition definition, string workflowId, DateTimeOffset? nextOccurenceAt)
4444
: base(id)
4545
{
46-
this.Type = type;
46+
this.ActivationType = activationType;
4747
this.Definition = definition;
4848
this.WorkflowId = workflowId;
4949
this.NextOccurenceAt = nextOccurenceAt;
5050
}
5151

5252
/// <summary>
53-
/// Gets the type of the newly created <see cref="V1Schedule"/>
53+
/// Gets the activation type of the newly created <see cref="V1Schedule"/>
5454
/// </summary>
55-
public virtual V1ScheduleType Type { get; protected set; }
55+
public virtual V1ScheduleType ActivationType { get; protected set; }
5656

5757
/// <summary>
5858
/// Gets the definition of the newly created <see cref="V1Schedule"/>
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* Copyright © 2022-Present The Synapse Authors
3+
* <p>
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
* <p>
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
* <p>
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
*/
17+
using Synapse.Integration.Events.Schedules;
18+
19+
namespace Synapse.Domain.Events.Schedules
20+
{
21+
/// <summary>
22+
/// Represents the <see cref="IDomainEvent"/> fired whenever a scheduled <see cref="V1Workflow"/> has been executed
23+
/// </summary>
24+
[DataTransferObjectType(typeof(V1ScheduleOccurenceCompletedIntegrationEvent))]
25+
public class V1ScheduleOccurenceCompletedDomainEvent
26+
: DomainEvent<Models.V1Schedule, string>
27+
{
28+
29+
/// <summary>
30+
/// Initializes a new <see cref="V1ScheduleOccurenceCompletedDomainEvent"/>
31+
/// </summary>
32+
protected V1ScheduleOccurenceCompletedDomainEvent() { }
33+
34+
/// <summary>
35+
/// Initializes a new <see cref="V1ScheduleOccurenceCompletedDomainEvent"/>
36+
/// </summary>
37+
/// <param name="id">The id of the <see cref="V1Schedule"/> which's occurence has been completed</param>
38+
/// <param name="nextOccurenceAt">The date and time at which the <see cref="V1Schedule"/> will next occur</param>
39+
public V1ScheduleOccurenceCompletedDomainEvent(string id, DateTimeOffset? nextOccurenceAt)
40+
: base(id)
41+
{
42+
this.NextOccurenceAt = nextOccurenceAt;
43+
}
44+
45+
/// <summary>
46+
/// Gets the <see cref="V1Schedule"/>'s next occurence
47+
/// </summary>
48+
public virtual DateTimeOffset? NextOccurenceAt { get; protected set; }
49+
50+
}
51+
52+
}

src/core/Synapse.Domain/Events/Schedules/v1/V1ScheduleResumedDomainEvent.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,17 @@ protected V1ScheduleResumedDomainEvent() { }
3535
/// Initializes a new <see cref="V1ScheduleResumedDomainEvent"/>
3636
/// </summary>
3737
/// <param name="id">The id of the resumed <see cref="V1Schedule"/></param>
38-
public V1ScheduleResumedDomainEvent(string id) : base(id) { }
38+
/// <param name="nextOccurenceAt">The <see cref="V1Schedule"/>'s next occurence</param>
39+
public V1ScheduleResumedDomainEvent(string id, DateTimeOffset? nextOccurenceAt)
40+
: base(id)
41+
{
42+
this.NextOccurenceAt = nextOccurenceAt;
43+
}
44+
45+
/// <summary>
46+
/// Gets the <see cref="V1Schedule"/>'s next occurence
47+
/// </summary>
48+
public virtual DateTimeOffset? NextOccurenceAt { get; protected set; }
3949

4050
}
4151

src/core/Synapse.Domain/Models/v1/V1Schedule.cs

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
using Microsoft.AspNetCore.JsonPatch;
1919
using Microsoft.AspNetCore.JsonPatch.Operations;
20+
using ServerlessWorkflow.Sdk;
2021
using Synapse.Domain.Events.Schedules;
2122
using System.Text.RegularExpressions;
2223

@@ -44,21 +45,21 @@ protected V1Schedule()
4445
/// <summary>
4546
/// Initializes a new <see cref="V1Schedule"/>
4647
/// </summary>
47-
/// <param name="type">The <see cref="V1Schedule"/>'s type</param>
48+
/// <param name="activationType">The <see cref="V1Schedule"/>'s activation type</param>
4849
/// <param name="definition">The <see cref="V1Schedule"/>'s <see cref="ScheduleDefinition"/></param>
4950
/// <param name="workflow">The <see cref="V1Workflow"/> to schedule</param>
50-
public V1Schedule(V1ScheduleType type, ScheduleDefinition definition, V1Workflow workflow)
51+
public V1Schedule(V1ScheduleType activationType, ScheduleDefinition definition, V1Workflow workflow)
5152
: base(BuildId(workflow?.Id!))
5253
{
5354
if (definition == null) throw DomainException.ArgumentNull(nameof(definition));
5455
if (workflow == null) throw DomainException.ArgumentNull(nameof(workflow));
55-
this.On(this.RegisterEvent(new V1ScheduleCreatedDomainEvent(this.Id, type, definition, workflow.Id, this.Definition.GetNextOccurence())));
56+
this.On(this.RegisterEvent(new V1ScheduleCreatedDomainEvent(this.Id, activationType, definition, workflow.Id, definition.GetNextOccurence())));
5657
}
5758

5859
/// <summary>
59-
/// Gets the <see cref="V1Schedule"/>'s type
60+
/// Gets the <see cref="V1Schedule"/>'s activation type
6061
/// </summary>
61-
public virtual V1ScheduleType Type { get; protected set; }
62+
public virtual V1ScheduleType ActivationType { get; protected set; }
6263

6364
/// <summary>
6465
/// Gets the <see cref="V1Schedule"/>'s status
@@ -95,6 +96,11 @@ public V1Schedule(V1ScheduleType type, ScheduleDefinition definition, V1Workflow
9596
/// </summary>
9697
public virtual DateTimeOffset? LastOccuredAt { get; protected set; }
9798

99+
/// <summary>
100+
/// Gets the date and time at which the scheduled <see cref="V1Workflow"/> has last completed
101+
/// </summary>
102+
public virtual DateTimeOffset? LastCompletedAt { get; protected set; }
103+
98104
/// <summary>
99105
/// Gets the date and time at which the <see cref="V1Schedule"/> will next occur
100106
/// </summary>
@@ -122,15 +128,24 @@ public virtual void SetDefinition(ScheduleDefinition definition)
122128
public virtual void Trigger()
123129
{
124130
if (this.Status != V1ScheduleStatus.Active) throw DomainException.UnexpectedState(typeof(V1Schedule), this.Id, this.Status);
125-
this.On(this.RegisterEvent(new V1ScheduleOccuredDomainEvent(this.Id, this.Definition.GetNextOccurence())));
131+
this.On(this.RegisterEvent(new V1ScheduleOccuredDomainEvent(this.Id, this.Definition.Type == ScheduleDefinitionType.Interval ? null : this.Definition.GetNextOccurence())));
132+
}
133+
134+
/// <summary>
135+
/// Notifies that a triggered occurence has completed
136+
/// </summary>
137+
public virtual void NotifyOccurenceCompleted()
138+
{
139+
if(this.Status != V1ScheduleStatus.Active) throw DomainException.UnexpectedState(typeof(V1Schedule), this.Id, this.Status);
140+
this.On(this.RegisterEvent(new V1ScheduleOccurenceCompletedDomainEvent(this.Id, this.Definition.Type == ScheduleDefinitionType.Interval ? null : this.Definition.GetNextOccurence())));
126141
}
127142

128143
/// <summary>
129144
/// Suspends the <see cref="V1Schedule"/>
130145
/// </summary>
131146
public virtual void Suspend()
132147
{
133-
if (this.Status == V1ScheduleStatus.Active) throw DomainException.UnexpectedState(typeof(V1Schedule), this.Id, this.Status);
148+
if (this.Status != V1ScheduleStatus.Active) throw DomainException.UnexpectedState(typeof(V1Schedule), this.Id, this.Status);
134149
this.On(this.RegisterEvent(new V1ScheduleSuspendedDomainEvent(this.Id)));
135150
}
136151

@@ -139,8 +154,8 @@ public virtual void Suspend()
139154
/// </summary>
140155
public virtual void Resume()
141156
{
142-
if (this.Status == V1ScheduleStatus.Suspended) throw DomainException.UnexpectedState(typeof(V1Schedule), this.Id, this.Status);
143-
this.On(this.RegisterEvent(new V1ScheduleResumedDomainEvent(this.Id)));
157+
if (this.Status != V1ScheduleStatus.Suspended) throw DomainException.UnexpectedState(typeof(V1Schedule), this.Id, this.Status);
158+
this.On(this.RegisterEvent(new V1ScheduleResumedDomainEvent(this.Id, this.Definition.GetNextOccurence())));
144159
}
145160

146161
/// <summary>
@@ -189,7 +204,7 @@ protected virtual void On(V1ScheduleCreatedDomainEvent e)
189204
this.Id = e.AggregateId;
190205
this.CreatedAt = e.CreatedAt;
191206
this.LastModified = e.CreatedAt;
192-
this.Type = e.Type;
207+
this.ActivationType = e.ActivationType;
193208
this.Status = V1ScheduleStatus.Active;
194209
this.Definition = e.Definition;
195210
this.WorkflowId = e.WorkflowId;
@@ -208,6 +223,17 @@ protected virtual void On(V1ScheduleOccuredDomainEvent e)
208223
this.TotalOccurences++;
209224
}
210225

226+
/// <summary>
227+
/// Handles the specified <see cref="V1ScheduleOccurenceCompletedDomainEvent"/>
228+
/// </summary>
229+
/// <param name="e">The <see cref="V1ScheduleOccurenceCompletedDomainEvent"/> to handle</param>
230+
protected virtual void On(V1ScheduleOccurenceCompletedDomainEvent e)
231+
{
232+
this.LastModified = e.CreatedAt;
233+
this.LastCompletedAt = e.CreatedAt;
234+
this.NextOccurenceAt = e.NextOccurenceAt;
235+
}
236+
211237
/// <summary>
212238
/// Handles the specified <see cref="V1ScheduleSuspendedDomainEvent"/>
213239
/// </summary>
@@ -228,6 +254,7 @@ protected virtual void On(V1ScheduleResumedDomainEvent e)
228254
{
229255
this.LastModified = e.CreatedAt;
230256
this.SuspendedAt = null;
257+
this.NextOccurenceAt = e.NextOccurenceAt;
231258
this.Status = V1ScheduleStatus.Active;
232259
}
233260

@@ -274,7 +301,7 @@ protected virtual void On(V1ScheduleDeletedDomainEvent e)
274301
public static string BuildId(string workflowId)
275302
{
276303
if (string.IsNullOrWhiteSpace(workflowId)) throw DomainException.ArgumentNullOrWhitespace(nameof(workflowId));
277-
return $"{workflowId}-{Regex.Replace(Convert.ToBase64String(Guid.NewGuid().ToByteArray()), "[/+=]", string.Empty)}";
304+
return $"{workflowId}-{Regex.Replace(Convert.ToBase64String(Guid.NewGuid().ToByteArray()), "[/+=]", string.Empty).ToLowerInvariant()}";
278305
}
279306

280307
}

src/core/Synapse.Integration/Commands/Schedules/v1/Generated/V1ObsoleteScheduleCommand.cs

Lines changed: 0 additions & 43 deletions
This file was deleted.

src/core/Synapse.Integration/Events/Schedules/v1/Generated/V1ScheduleCreatedIntegrationEvent.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,11 @@ public partial class V1ScheduleCreatedIntegrationEvent
4646
public virtual DateTime CreatedAt { get; set; }
4747

4848
/// <summary>
49-
/// The type of the newly created V1Schedule
49+
/// The activation type of the newly created V1Schedule
5050
/// </summary>
51-
[DataMember(Name = "Type", Order = 3)]
52-
[Description("The type of the newly created V1Schedule")]
53-
public virtual V1ScheduleType Type { get; set; }
51+
[DataMember(Name = "ActivationType", Order = 3)]
52+
[Description("The activation type of the newly created V1Schedule")]
53+
public virtual V1ScheduleType ActivationType { get; set; }
5454

5555
/// <summary>
5656
/// The definition of the newly created V1Schedule

0 commit comments

Comments
 (0)