Skip to content

Commit bd42892

Browse files
committed
Add stid uploader workflow
1 parent 8d21532 commit bd42892

16 files changed

+571
-22
lines changed

api/Controllers/Models/PlantDataResponse.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,20 @@ public PlantDataResponse(PlantData plantData)
2020
id = plantData.Id;
2121
}
2222
}
23+
24+
public class StidDataResponse(
25+
string inspectionId,
26+
BlobStorageLocation anonymizedBlobStorageLocation,
27+
string tag,
28+
string description,
29+
WorkflowStatus stidWorkflowStatus
30+
)
31+
{
32+
public string inspectionId { get; set; } = inspectionId;
33+
public BlobStorageLocation anonymizedBlobStorageLocation { get; set; } =
34+
anonymizedBlobStorageLocation;
35+
public string tag { get; set; } = tag;
36+
public string description { get; set; } = description;
37+
public WorkflowStatus stidWorkflowStatus { get; set; } = stidWorkflowStatus;
38+
}
2339
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
using api.Controllers.Models;
2+
using api.Database.Models;
3+
using api.Services;
4+
using Microsoft.AspNetCore.Authorization;
5+
using Microsoft.AspNetCore.Mvc;
6+
7+
namespace api.Controllers;
8+
9+
public class StidWorkflowStartedNotification
10+
{
11+
public required string InspectionId { get; set; }
12+
public required string WorkflowName { get; set; }
13+
}
14+
15+
public class StidWorkflowExitedNotification
16+
{
17+
public required string InspectionId { get; set; }
18+
public required int StidMediaId { get; set; }
19+
public required string WorkflowStatus { get; set; }
20+
public required string WorkflowFailures { get; set; }
21+
}
22+
23+
[ApiController]
24+
[Route("[controller]")]
25+
public class StidWorkflowController(
26+
IStidWorkflowService stidWorkflowService,
27+
ILogger<StidWorkflowController> logger
28+
) : ControllerBase
29+
{
30+
/// <summary>
31+
/// Updates status of plant data to started
32+
/// </summary>
33+
[HttpPut]
34+
[AllowAnonymous]
35+
[Route("notify-workflow-started")]
36+
[ProducesResponseType(StatusCodes.Status200OK)]
37+
[ProducesResponseType(StatusCodes.Status404NotFound)]
38+
public async Task<ActionResult<StidDataResponse>> WorkflowStarted(
39+
[FromBody] StidWorkflowStartedNotification notification
40+
)
41+
{
42+
var updatedStidData = await stidWorkflowService.UpdateStidWorkflowStatus(
43+
notification.InspectionId,
44+
WorkflowStatus.Started
45+
);
46+
logger.LogInformation(
47+
"Stid uploader workflow for inspection {inspectionId} started with workflow name {workflowName}",
48+
notification.InspectionId,
49+
notification.WorkflowName
50+
);
51+
if (updatedStidData == null)
52+
{
53+
return NotFound(
54+
$"Could not find workflow with inspection id {notification.InspectionId}"
55+
);
56+
}
57+
return Ok(updatedStidData);
58+
}
59+
60+
/// <summary>
61+
/// Updates status of the workflow to exit with success or failure
62+
/// </summary>
63+
[HttpPut]
64+
[AllowAnonymous]
65+
[Route("notify-workflow-exited")]
66+
[ProducesResponseType(StatusCodes.Status200OK)]
67+
[ProducesResponseType(StatusCodes.Status404NotFound)]
68+
public async Task<ActionResult<StidDataResponse>> WorkflowExited(
69+
[FromBody] StidWorkflowExitedNotification notification
70+
)
71+
{
72+
WorkflowStatus status;
73+
int? mediaId = null;
74+
75+
if (notification.WorkflowStatus == "Succeeded")
76+
{
77+
status = WorkflowStatus.ExitSuccess;
78+
mediaId = notification.StidMediaId;
79+
}
80+
else
81+
{
82+
logger.LogWarning(
83+
"Stid uploader workflow failed with status {status}",
84+
notification.WorkflowStatus
85+
);
86+
status = WorkflowStatus.ExitFailure;
87+
}
88+
89+
await stidWorkflowService.UpdateStidMediaId(notification.InspectionId, mediaId);
90+
91+
var updatedStidData = await stidWorkflowService.UpdateStidWorkflowStatus(
92+
notification.InspectionId,
93+
status
94+
);
95+
96+
logger.LogInformation(
97+
"Stid uploader workflow for inspection {inspectionId} exited with status {status}",
98+
notification.InspectionId,
99+
notification.WorkflowStatus
100+
);
101+
102+
logger.LogInformation(
103+
"StidMediaId for inspection {inspectionId} set to {mediaId}",
104+
notification.InspectionId,
105+
mediaId
106+
);
107+
108+
return Ok(updatedStidData);
109+
}
110+
}

api/Controllers/TriggerAnalysisController.cs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
using api.Controllers.Models;
22
using api.Database.Context;
33
using api.Database.Models;
4+
using api.MQTT;
45
using api.Services;
6+
using api.Services.Models;
57
using Microsoft.AspNetCore.Authorization;
68
using Microsoft.AspNetCore.Mvc;
79

@@ -16,11 +18,26 @@ public class TriggerAnalysisRequest
1618
public required string InstallationCode { get; set; }
1719
}
1820

21+
public class TriggerStidUploadRequest(
22+
string inspectionId,
23+
BlobStorageLocation anonymizedBlobStorageLocation,
24+
string tagId,
25+
string description
26+
)
27+
{
28+
public string InspectionId { get; } = inspectionId;
29+
public BlobStorageLocation AnonymizedBlobStorageLocation { get; } =
30+
anonymizedBlobStorageLocation;
31+
public string TagId { get; } = tagId;
32+
public string Description { get; } = description;
33+
}
34+
1935
[ApiController]
2036
[Route("[controller]")]
2137
public class TriggerAnalysisController(
2238
IArgoWorkflowService argoWorkflowService,
2339
IAnalysisMappingService analysisMappingService,
40+
IStidWorkflowService stidWorkflowService,
2441
SaraDbContext dbContext,
2542
ILogger<TriggerAnalysisController> logger
2643
) : ControllerBase
@@ -93,4 +110,27 @@ await argoWorkflowService.TriggerAnalysis(
93110

94111
return Ok("Analysis workflow triggered successfully.");
95112
}
113+
114+
/// <summary>
115+
/// Triggers the stid upload workflow. NB: STID upload workflow should normally be triggered by MQTT message
116+
/// </summary>
117+
[HttpPost]
118+
[Route("trigger-stid-upload")]
119+
[Authorize(Roles = Role.Any)]
120+
[ProducesResponseType(StatusCodes.Status200OK)]
121+
[ProducesResponseType(StatusCodes.Status400BadRequest)]
122+
public async Task<IActionResult> TriggerUploadToStid(
123+
[FromBody] TriggerStidUploadRequest request
124+
)
125+
{
126+
await stidWorkflowService.TriggerUploadToStid(
127+
new StidUploadMessage
128+
{
129+
InspectionId = request.InspectionId,
130+
AnonymizedBlobStorageLocation = request.AnonymizedBlobStorageLocation,
131+
}
132+
);
133+
134+
return Ok("Upload to stid workflow triggered successfully.");
135+
}
96136
}

api/Controllers/WorkflowsController.cs

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ IMqttMessageService mqttMessageService
5050
/// Updates status of plant data to started
5151
/// </summary>
5252
[HttpPut]
53-
[Authorize(Roles = Role.WorkflowStatusWrite)]
53+
[AllowAnonymous]
5454
[Route("notify-workflow-started")]
5555
[ProducesResponseType(StatusCodes.Status200OK)]
5656
[ProducesResponseType(StatusCodes.Status404NotFound)]
@@ -75,7 +75,7 @@ [FromBody] WorkflowStartedNotification notification
7575
/// Updates status of plant data to anonymized success or failure
7676
/// </summary>
7777
[HttpPut]
78-
[Authorize(Roles = Role.WorkflowStatusWrite)]
78+
[AllowAnonymous]
7979
[Route("notify-anonymizer-done")]
8080
[ProducesResponseType(StatusCodes.Status200OK)]
8181
[ProducesResponseType(StatusCodes.Status404NotFound)]
@@ -119,7 +119,7 @@ [FromBody] AnonymizerDoneNotification notification
119119
/// TODO: Register oil level on plant data
120120
/// </summary>
121121
[HttpPut]
122-
[Authorize(Roles = Role.WorkflowStatusWrite)]
122+
[Authorize(Roles = Role.Any)]
123123
[Route("notify-constant-level-oiler-done")]
124124
[ProducesResponseType(StatusCodes.Status200OK)]
125125
[ProducesResponseType(StatusCodes.Status404NotFound)]
@@ -161,7 +161,7 @@ [FromBody] ConstantLevelOilerDoneNotification notification
161161
/// TODO: Register fencilla results on plant data
162162
/// </summary>
163163
[HttpPut]
164-
[Authorize(Roles = Role.WorkflowStatusWrite)]
164+
[Authorize(Roles = Role.Any)]
165165
[Route("notify-fencilla-done")]
166166
[ProducesResponseType(StatusCodes.Status200OK)]
167167
[ProducesResponseType(StatusCodes.Status404NotFound)]
@@ -184,13 +184,11 @@ [FromBody] FencillaDoneNotification notification
184184
/// Updates status of the workflow to exit with success or failure
185185
/// </summary>
186186
[HttpPut]
187-
[Authorize(Roles = Role.WorkflowStatusWrite)]
187+
[AllowAnonymous]
188188
[Route("notify-workflow-exited")]
189189
[ProducesResponseType(StatusCodes.Status200OK)]
190190
[ProducesResponseType(StatusCodes.Status404NotFound)]
191-
public ActionResult<PlantDataResponse> WorkflowExited(
192-
[FromBody] WorkflowExitedNotification notification
193-
)
191+
public ActionResult WorkflowExited([FromBody] WorkflowExitedNotification notification)
194192
{
195193
// WorkflowStatus status;
196194

api/Database/Context/IdaDbContext.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ public class SaraDbContext(DbContextOptions options) : DbContext(options)
1111

1212
public DbSet<AnalysisMapping> AnalysisMapping { get; set; } = null!;
1313

14+
public DbSet<StidData> StidData { get; set; } = null!;
15+
1416
protected override void OnModelCreating(ModelBuilder modelBuilder)
1517
{
1618
modelBuilder

api/Database/Models/Metadata.cs

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,44 @@
44
#pragma warning disable CS8618
55
namespace api.Database.Models;
66

7-
public class Metadata(string tag, string coordinates)
7+
public class Metadata(string? tag, InspectionType? type, string? inspectionDescription)
88
{
99
[Key]
1010
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
1111
public string Id { get; set; }
1212

1313
public string? Tag { get; set; } = tag;
1414

15-
public string? Coordinates { get; set; } = coordinates;
15+
public InspectionType? Type { get; set; } = type;
16+
17+
public string? InspectionDescription { get; set; } = inspectionDescription;
18+
19+
public static InspectionType? TypeFromString(string? status)
20+
{
21+
if (string.IsNullOrEmpty(status))
22+
return null;
23+
status = status.ToLowerInvariant();
24+
return status switch
25+
{
26+
"image" => InspectionType.Image,
27+
"thermalimage" => InspectionType.ThermalImage,
28+
_ => null,
29+
};
30+
}
31+
32+
public static string? TypeToString(InspectionType type)
33+
{
34+
return type switch
35+
{
36+
InspectionType.Image => "image",
37+
InspectionType.ThermalImage => "thermalimage",
38+
_ => null,
39+
};
40+
}
41+
}
42+
43+
public enum InspectionType
44+
{
45+
Image,
46+
ThermalImage,
1647
}

api/Database/Models/StidData.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#pragma warning disable CS8618
2+
using System.ComponentModel.DataAnnotations;
3+
using System.ComponentModel.DataAnnotations.Schema;
4+
using System.Security.Policy;
5+
6+
namespace api.Database.Models;
7+
8+
public class StidData
9+
{
10+
[Key]
11+
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
12+
public string Id { get; set; }
13+
public string InspectionId { get; set; }
14+
public BlobStorageLocation AnonymizedBlobStorageLocation { get; set; }
15+
public string Tag { get; set; }
16+
public string Description { get; set; }
17+
public WorkflowStatus StidWorkflowStatus { get; set; } = WorkflowStatus.NotStarted; // TODO: Rename this to just WorkflowStatus
18+
public int? StidMediaId { get; set; }
19+
}

api/MQTT/MqttService.cs

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1-
using System.Text;
1+
using System.Reflection.Metadata;
2+
using System.Text;
23
using System.Text.Json;
34
using System.Text.Json.Serialization;
5+
using api.Database.Models;
46
using api.Services;
7+
using api.Services.Models;
58
using api.Utilities;
69
using MQTTnet;
710
using MQTTnet.Client;
@@ -33,11 +36,17 @@ public class MqttService : BackgroundService
3336

3437
private CancellationToken _cancellationToken;
3538
private int _reconnectAttempts;
39+
private readonly IStidWorkflowService _stidWorkflowService;
3640

37-
public MqttService(ILogger<MqttService> logger, IConfiguration config)
41+
public MqttService(
42+
ILogger<MqttService> logger,
43+
IConfiguration config,
44+
IStidWorkflowService stidWorkflowService
45+
)
3846
{
3947
_reconnectAttempts = 0;
4048
_logger = logger;
49+
_stidWorkflowService = stidWorkflowService;
4150
var mqttFactory = new MqttFactory();
4251
_mqttClient = mqttFactory.CreateManagedMqttClient();
4352

@@ -143,6 +152,20 @@ private async void OnSaraVisualizationAvailable(object? sender, MqttMessage e)
143152
_logger.LogError("Message is not of type SaraVisualizationAvailableMessage");
144153
return;
145154
}
155+
156+
var stidData = new StidUploadMessage
157+
{
158+
InspectionId = message.InspectionId,
159+
AnonymizedBlobStorageLocation = new BlobStorageLocation
160+
{
161+
StorageAccount = message.StorageAccount,
162+
BlobContainer = message.BlobContainer,
163+
BlobName = message.BlobName,
164+
},
165+
};
166+
167+
await _stidWorkflowService.TriggerUploadToStid(stidData);
168+
146169
var payload = JsonSerializer.Serialize(message, serializerOptions);
147170

148171
var topic = MqttTopics.MessagesToTopics.GetTopicByItem(message.GetType());

api/Program.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
builder.Services.AddScoped<IMqttMessageService, MqttMessageService>();
6767

6868
builder.Services.AddScoped<IArgoWorkflowService, ArgoWorkflowService>();
69+
builder.Services.AddScoped<IStidWorkflowService, StidWorkflowService>();
6970
builder.Services.AddScoped<ITimeseriesService, TimeseriesService>();
7071

7172
builder.Services.AddHostedService<MqttEventHandler>();

0 commit comments

Comments
 (0)