@@ -14,10 +14,9 @@ namespace Aspire.Hosting.Publishing;
14
14
[ Experimental ( "ASPIREPUBLISHERS001" ) ]
15
15
public sealed class PublishingActivity
16
16
{
17
- internal PublishingActivity ( string id , string initialStatusText , bool isPrimary = false )
17
+ internal PublishingActivity ( string id , bool isPrimary = false )
18
18
{
19
19
Id = id ;
20
- StatusMessage = initialStatusText ;
21
20
IsPrimary = isPrimary ;
22
21
}
23
22
@@ -27,25 +26,41 @@ internal PublishingActivity(string id, string initialStatusText, bool isPrimary
27
26
public string Id { get ; private set ; }
28
27
29
28
/// <summary>
30
- /// Status message of the publishing activity.
29
+ /// Indicates whether the publishing activity is the primary activity.
31
30
/// </summary>
32
- public string StatusMessage { get ; set ; }
31
+ public bool IsPrimary { get ; private set ; }
33
32
34
33
/// <summary>
35
- /// Indicates whether the publishing activity is complete .
34
+ /// The status text of the publishing activity.
36
35
/// </summary>
37
- public bool IsComplete { get ; set ; }
36
+ public PublishingActivityStatus ? LastStatus { get ; internal set ; }
37
+ }
38
38
39
+ /// <summary>
40
+ /// Represents the status of a publishing activity.
41
+ /// </summary>
42
+ [ Experimental ( "ASPIREPUBLISHERS001" ) ]
43
+ public sealed record PublishingActivityStatus
44
+ {
39
45
/// <summary>
40
- /// Indicates whether the publishing activity is the primary activity .
46
+ /// The publishing activity associated with this status .
41
47
/// </summary>
42
- public bool IsPrimary { get ; private set ; }
48
+ public required PublishingActivity Activity { get ; init ; }
43
49
44
50
/// <summary>
45
- /// Indicates whether the publishing activity has encountered an error .
51
+ /// The status text of the publishing activity.
46
52
/// </summary>
47
- public bool IsError { get ; set ; }
53
+ public required string StatusText { get ; init ; }
48
54
55
+ /// <summary>
56
+ /// Indicates whether the publishing activity is complete.
57
+ /// </summary>
58
+ public required bool IsComplete { get ; init ; }
59
+
60
+ /// <summary>
61
+ /// Indicates whether the publishing activity encountered an error.
62
+ /// </summary>
63
+ public required bool IsError { get ; init ; }
49
64
}
50
65
51
66
/// <summary>
@@ -73,31 +88,68 @@ public interface IPublishingActivityProgressReporter
73
88
/// Updates the status of an existing publishing activity.
74
89
/// </summary>
75
90
/// <param name="publishingActivity">The activity with updated properties.</param>
91
+ /// <param name="statusUpdate"></param>
76
92
/// <param name="cancellationToken">The cancellation token.</param>
77
93
/// <returns></returns>
78
- Task UpdateActivityAsync ( PublishingActivity publishingActivity , CancellationToken cancellationToken ) ;
94
+ Task UpdateActivityStatusAsync ( PublishingActivity publishingActivity , Func < PublishingActivityStatus , PublishingActivityStatus > statusUpdate , CancellationToken cancellationToken ) ;
79
95
}
80
96
81
97
internal sealed class PublishingActivityProgressReporter : IPublishingActivityProgressReporter
82
98
{
83
99
public async Task < PublishingActivity > CreateActivityAsync ( string id , string initialStatusText , bool isPrimary , CancellationToken cancellationToken )
84
100
{
85
- var publishingActivity = new PublishingActivity ( id , initialStatusText , isPrimary ) ;
86
- await ActivitiyUpdated . Writer . WriteAsync ( publishingActivity , cancellationToken ) . ConfigureAwait ( false ) ;
101
+ var publishingActivity = new PublishingActivity ( id , isPrimary ) ;
102
+ await UpdateActivityStatusAsync (
103
+ publishingActivity ,
104
+ ( status ) => status with
105
+ {
106
+ StatusText = initialStatusText ,
107
+ IsComplete = false ,
108
+ IsError = false
109
+ } ,
110
+ cancellationToken
111
+ ) . ConfigureAwait ( false ) ;
112
+
87
113
return publishingActivity ;
88
114
}
89
115
90
- public async Task UpdateActivityAsync ( PublishingActivity publishingActivity , CancellationToken cancellationToken )
116
+ private readonly object _updateLock = new object ( ) ;
117
+
118
+ public async Task UpdateActivityStatusAsync ( PublishingActivity publishingActivity , Func < PublishingActivityStatus , PublishingActivityStatus > statusUpdate , CancellationToken cancellationToken )
91
119
{
92
- await ActivitiyUpdated . Writer . WriteAsync ( publishingActivity , cancellationToken ) . ConfigureAwait ( false ) ;
120
+ PublishingActivityStatus ? lastStatus ;
121
+ PublishingActivityStatus ? newStatus ;
122
+
123
+ lock ( _updateLock )
124
+ {
125
+ lastStatus = publishingActivity . LastStatus ?? new PublishingActivityStatus
126
+ {
127
+ Activity = publishingActivity ,
128
+ StatusText = string . Empty ,
129
+ IsComplete = false ,
130
+ IsError = false
131
+ } ;
132
+
133
+ newStatus = statusUpdate ( lastStatus ) ;
134
+ publishingActivity . LastStatus = newStatus ;
135
+ }
136
+
137
+ if ( lastStatus == newStatus )
138
+ {
139
+ throw new DistributedApplicationException (
140
+ $ "The status of the publishing activity '{ publishingActivity . Id } ' was not updated. The status update function must return a new instance of the status."
141
+ ) ;
142
+ }
143
+
144
+ await ActivityStatusUpdated . Writer . WriteAsync ( newStatus , cancellationToken ) . ConfigureAwait ( false ) ;
93
145
94
- if ( publishingActivity . IsPrimary && ( publishingActivity . IsComplete || publishingActivity . IsError ) )
146
+ if ( publishingActivity . IsPrimary && ( newStatus . IsComplete || newStatus . IsError ) )
95
147
{
96
148
// If the activity is complete or an error and it is the primary activity,
97
149
// we can stop listening for updates.
98
- ActivitiyUpdated . Writer . Complete ( ) ;
150
+ ActivityStatusUpdated . Writer . Complete ( ) ;
99
151
}
100
152
}
101
153
102
- internal Channel < PublishingActivity > ActivitiyUpdated { get ; } = Channel . CreateUnbounded < PublishingActivity > ( ) ;
154
+ internal Channel < PublishingActivityStatus > ActivityStatusUpdated { get ; } = Channel . CreateUnbounded < PublishingActivityStatus > ( ) ;
103
155
}
0 commit comments