diff --git a/backend/api/Controllers/InspectionController.cs b/backend/api/Controllers/InspectionController.cs index 6d2e8974..9851c52f 100644 --- a/backend/api/Controllers/InspectionController.cs +++ b/backend/api/Controllers/InspectionController.cs @@ -1,10 +1,8 @@ -using System.Globalization; -using Api.Controllers.Models; -using Api.Database.Models; - +using Api.Controllers.Models; using Api.Services; using Api.Services.MissionLoaders; using Api.Services.Models; +using Api.Utilities; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; @@ -55,56 +53,31 @@ public async Task> Create([FromRoute] string } /// - /// Lookup the inspection image for task with specified isarTaskId + /// Lookup the inspection image for task with specified isarInspectionId /// /// + /// Retrieves the inspection image associated with the given ISAR Inspection ID. /// - [HttpGet] + [HttpGet("{isarInspectionId}")] [Authorize(Roles = Role.User)] - [Route("{installationCode}/{taskId}/taskId")] - [ProducesResponseType(typeof(Inspection), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(FileContentResult), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] - [ProducesResponseType(StatusCodes.Status409Conflict)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status403Forbidden)] + [ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] - public async Task> GetInspectionImageById([FromRoute] string installationCode, string taskId) + public async Task GetInspectionImageByIsarInspectionId([FromRoute] string isarInspectionId) { - Inspection? inspection; - try - { - inspection = await inspectionService.ReadByIsarTaskId(taskId, readOnly: true); - if (inspection == null) return NotFound($"Could not find inspection for task with Id {taskId}."); - - } - catch (Exception e) - { - logger.LogError(e, $"Error while finding an inspection with task Id {taskId}"); - return StatusCode(StatusCodes.Status500InternalServerError); - } - - if (inspection.IsarInspectionId == null) return NotFound($"Could not find isar inspection Id {inspection.IsarInspectionId} for Inspection with task ID {taskId}."); - - var inspectionData = await inspectionService.GetInspectionStorageInfo(inspection.IsarInspectionId); - - if (inspectionData == null) return NotFound($"Could not find inspection data for inspection with isar Id {inspection.IsarInspectionId}."); - - if (!inspectionData.BlobContainer.ToLower(CultureInfo.CurrentCulture).Equals(installationCode.ToLower(CultureInfo.CurrentCulture), StringComparison.Ordinal)) - { - return NotFound($"Could not find inspection data for inspection with isar Id {inspection.IsarInspectionId} because blob name {inspectionData.BlobName} does not match installation {installationCode}."); - } try { - byte[]? inspectionStream = await inspectionService.FetchInpectionImage(inspectionData.BlobName, inspectionData.BlobContainer, inspectionData.StorageAccount); - - if (inspectionStream == null) return NotFound($"Could not retrieve inspection with task Id {taskId}"); - + byte[] inspectionStream = await inspectionService.FetchInpectionImageFromIsarInspectionId(isarInspectionId); return File(inspectionStream, "image/png"); } - catch (Azure.RequestFailedException) + catch (InspectionNotFoundException e) { - return NotFound($"Could not find inspection blob {inspectionData.BlobName} in container {inspectionData.BlobContainer} and storage account {inspectionData.StorageAccount}."); + logger.LogError(e, "{ErrorMessage}", e.Message); + return NotFound($"Could not find inspection image with ISAR Inspection ID {isarInspectionId}"); } } } diff --git a/backend/api/Database/Models/MissionTask.cs b/backend/api/Database/Models/MissionTask.cs index c4e29010..100fe372 100644 --- a/backend/api/Database/Models/MissionTask.cs +++ b/backend/api/Database/Models/MissionTask.cs @@ -111,6 +111,8 @@ public MissionTask(MissionTask copy, TaskStatus? status = null) // ReSharper disable once AutoPropertyCanBeMadeGetOnly.Local public string? IsarTaskId { get; set; } = Guid.NewGuid().ToString(); + // TODO: Add the IsarInspectionId here + [Required] public int TaskOrder { get; set; } diff --git a/backend/api/Services/InspectionService.cs b/backend/api/Services/InspectionService.cs index 243ff7ac..b964a75d 100644 --- a/backend/api/Services/InspectionService.cs +++ b/backend/api/Services/InspectionService.cs @@ -9,15 +9,15 @@ using Api.Utilities; using Microsoft.EntityFrameworkCore; using Microsoft.Identity.Abstractions; + namespace Api.Services { public interface IInspectionService { - public Task FetchInpectionImage(string inpectionName, string installationCode, string storageAccount); + public Task FetchInpectionImageFromIsarInspectionId(string isarInspectionId); public Task UpdateInspectionStatus(string isarTaskId, IsarTaskStatus isarTaskStatus); public Task ReadByIsarTaskId(string id, bool readOnly = true); public Task AddFinding(InspectionFindingQuery inspectionFindingsQuery, string isarTaskId); - public Task GetInspectionStorageInfo(string inspectionId); } @@ -31,9 +31,12 @@ public class InspectionService(FlotillaDbContext context, ILogger FetchInpectionImage(string inpectionName, string installationCode, string storageAccount) + public async Task FetchInpectionImageFromIsarInspectionId(string isarInspectionId) { - return await blobService.DownloadBlob(inpectionName, installationCode, storageAccount); + + var inspectionData = await GetInspectionStorageInfo(isarInspectionId) + ?? throw new InspectionNotFoundException($"Could not find inspection data for inspection with ISAR Inspection Id {isarInspectionId}."); + return await blobService.DownloadBlob(inspectionData.BlobName, inspectionData.BlobContainer, inspectionData.StorageAccount); } public async Task UpdateInspectionStatus(string isarTaskId, IsarTaskStatus isarTaskStatus) @@ -110,7 +113,7 @@ private IQueryable GetInspections(bool readOnly = true) return inspection; } - public async Task GetInspectionStorageInfo(string inspectionId) + private async Task GetInspectionStorageInfo(string inspectionId) { string relativePath = $"InspectionData/{inspectionId}/inspection-data-storage-location"; @@ -123,6 +126,12 @@ private IQueryable GetInspections(bool readOnly = true) } ); + if (response.StatusCode == HttpStatusCode.OK) + { + var inspectionData = await response.Content.ReadFromJsonAsync() ?? throw new JsonException("Failed to deserialize inspection data from IDA."); + return inspectionData; + } + if (response.StatusCode == HttpStatusCode.Accepted) { logger.LogInformation("Inspection data storage location for inspection with Id {inspectionId} is not yet available", inspectionId); @@ -131,7 +140,7 @@ private IQueryable GetInspections(bool readOnly = true) if (response.StatusCode == HttpStatusCode.InternalServerError) { - logger.LogError("Inetrnal server error when trying to get inspection data for inspection with Id {inspectionId}", inspectionId); + logger.LogError("Internal server error when trying to get inspection data for inspection with Id {inspectionId}", inspectionId); return null; } @@ -141,11 +150,15 @@ private IQueryable GetInspections(bool readOnly = true) return null; } - var inspectionData = await response.Content.ReadFromJsonAsync< - IDAInspectionDataResponse - >() ?? throw new JsonException("Failed to deserialize inspection data from IDA."); + if (response.StatusCode == HttpStatusCode.UnprocessableEntity) + { + logger.LogError("Anonymization workflow failed for inspection with Id {inspectionId}", inspectionId); + return null; + } + + logger.LogError("Unexpected error when trying to get inspection data for inspection with Id {inspectionId}", inspectionId); + return null; - return inspectionData; } } } diff --git a/frontend/src/api/ApiCaller.tsx b/frontend/src/api/ApiCaller.tsx index 2d11bfaf..b2f5e9db 100644 --- a/frontend/src/api/ApiCaller.tsx +++ b/frontend/src/api/ApiCaller.tsx @@ -430,8 +430,8 @@ export class BackendAPICaller { return result.content } - static async getInspection(installationCode: string, taskId: string): Promise { - const path: string = 'inspection/' + installationCode + '/' + taskId + '/taskId' + static async getInspection(isarInspectionId: string): Promise { + const path: string = 'inspection/' + isarInspectionId return BackendAPICaller.GET(path, 'image/png') .then((response) => response.content) diff --git a/frontend/src/components/Contexts/InpectionsContext.tsx b/frontend/src/components/Contexts/InpectionsContext.tsx index e8019fb5..f4a0bafa 100644 --- a/frontend/src/components/Contexts/InpectionsContext.tsx +++ b/frontend/src/components/Contexts/InpectionsContext.tsx @@ -1,14 +1,13 @@ import { createContext, FC, useContext, useState, useEffect, useRef } from 'react' import { BackendAPICaller } from 'api/ApiCaller' import { Task } from 'models/Task' -import { useInstallationContext } from './InstallationContext' interface IInspectionsContext { selectedInspectionTask: Task | undefined selectedInspectionTasks: Task[] switchSelectedInspectionTask: (selectedInspectionTask: Task | undefined) => void switchSelectedInspectionTasks: (selectedInspectionTask: Task[]) => void - mappingInspectionTasksObjectURL: { [taskIsarId: string]: string } + mappingInspectionTasksObjectURL: { [isarInspectionId: string]: string } } interface Props { @@ -26,8 +25,6 @@ const defaultInspectionsContext = { const InspectionsContext = createContext(defaultInspectionsContext) export const InspectionsProvider: FC = ({ children }) => { - const { installationCode } = useInstallationContext() - const [selectedInspectionTask, setSelectedInspectionTask] = useState() const [selectedInspectionTasks, setSelectedInspectionTasks] = useState([]) const [selectedInspectionTasksToFetch, setSelectedInspectionTasksToFetch] = useState([]) @@ -51,14 +48,14 @@ export const InspectionsProvider: FC = ({ children }) => { useEffect(() => { Object.values(selectedInspectionTasksToFetch).forEach((task, index) => { - if (task.isarTaskId) { - BackendAPICaller.getInspection(installationCode, task.isarTaskId!) + if (task.isarInspectionId) { + BackendAPICaller.getInspection(task.isarInspectionId!) .then((imageBlob) => { imageObjectURL.current = URL.createObjectURL(imageBlob) }) .then(() => { setMappingInspectionTasksObjectURL((oldMappingInspectionTasksObjectURL) => { - return { ...oldMappingInspectionTasksObjectURL, [task.isarTaskId!]: imageObjectURL.current } + return { ...oldMappingInspectionTasksObjectURL, [task.isarInspectionId!]: imageObjectURL.current } }) setSelectedInspectionTasksToFetch((oldSelectedInspectionTasksToFetch) => { let newInspectionTaksToFetch = { ...oldSelectedInspectionTasksToFetch } @@ -72,7 +69,7 @@ export const InspectionsProvider: FC = ({ children }) => { } }) // eslint-disable-next-line react-hooks/exhaustive-deps - }, [installationCode, selectedInspectionTasksToFetch, triggerFetch]) + }, [selectedInspectionTasksToFetch, triggerFetch]) const switchSelectedInspectionTask = (selectedName: Task | undefined) => { setSelectedInspectionTask(selectedName) diff --git a/frontend/src/components/Pages/InspectionReportPage.tsx/InspectionView.tsx b/frontend/src/components/Pages/InspectionReportPage.tsx/InspectionView.tsx index 2c5ed477..4b37935f 100644 --- a/frontend/src/components/Pages/InspectionReportPage.tsx/InspectionView.tsx +++ b/frontend/src/components/Pages/InspectionReportPage.tsx/InspectionView.tsx @@ -45,8 +45,8 @@ export const InspectionDialogView = ({ task, tasks }: InspectionDialogViewProps) const { switchSelectedInspectionTask, mappingInspectionTasksObjectURL } = useInspectionsContext() const updateImage = useCallback(() => { - if (task.isarTaskId && mappingInspectionTasksObjectURL[task.isarTaskId]) { - imageObjectURL.current = mappingInspectionTasksObjectURL[task.isarTaskId] + if (task.isarInspectionId && mappingInspectionTasksObjectURL[task.isarInspectionId]) { + imageObjectURL.current = mappingInspectionTasksObjectURL[task.isarInspectionId] getMeta(imageObjectURL.current).then((img) => { const inspectionCanvas = document.getElementById('inspectionCanvas') as HTMLCanvasElement @@ -201,11 +201,11 @@ const GetInspectionImage = ({ task, tasks }: GetInspectionImageProps) => { }, [tasks]) const updateImage = useCallback(() => { - if (task.isarTaskId && mappingInspectionTasksObjectURL[task.isarTaskId]) { - imageObjectURL.current = mappingInspectionTasksObjectURL[task.isarTaskId] + if (task.isarInspectionId && mappingInspectionTasksObjectURL[task.isarInspectionId]) { + imageObjectURL.current = mappingInspectionTasksObjectURL[task.isarInspectionId] getMeta(imageObjectURL.current).then((img) => { - const inspectionCanvas = document.getElementById(task.isarTaskId!) as HTMLCanvasElement + const inspectionCanvas = document.getElementById(task.isarInspectionId!) as HTMLCanvasElement if (inspectionCanvas) { inspectionCanvas.width = img.width inspectionCanvas.height = img.height @@ -225,5 +225,5 @@ const GetInspectionImage = ({ task, tasks }: GetInspectionImageProps) => { // eslint-disable-next-line react-hooks/exhaustive-deps }, [mappingInspectionTasksObjectURL, inspectionImage]) - return + return } diff --git a/frontend/src/models/Task.ts b/frontend/src/models/Task.ts index 655d6a7e..c120467e 100644 --- a/frontend/src/models/Task.ts +++ b/frontend/src/models/Task.ts @@ -4,6 +4,7 @@ import { Pose } from './Pose' export interface Task { id: string isarTaskId?: string + isarInspectionId?: string taskOrder: number type: TaskType tagId?: string