From 1dbed4c38f965df6bb9bf6fdd5f30bf3c5ab2789 Mon Sep 17 00:00:00 2001 From: danjov Date: Tue, 19 Dec 2023 18:08:10 +0100 Subject: [PATCH 1/2] Add AddBedrock functionality for stratigraphies in .NET API --- src/api/Controllers/StratigraphyController.cs | 58 +++++++++++++ tests/ActionResultAssert.cs | 15 ++++ .../Controllers/StratigraphyControllerTest.cs | 84 +++++++++++++++++-- 3 files changed, 149 insertions(+), 8 deletions(-) diff --git a/src/api/Controllers/StratigraphyController.cs b/src/api/Controllers/StratigraphyController.cs index 9a19657d1..dd0ef70a4 100644 --- a/src/api/Controllers/StratigraphyController.cs +++ b/src/api/Controllers/StratigraphyController.cs @@ -188,4 +188,62 @@ public override async Task> CreateAsync(Stratigraphy return await base.CreateAsync(entity).ConfigureAwait(false); } + + /// + /// Asynchronously adds a bedrock to a . + /// + /// The id. + /// The id of the created bedrock . + [HttpPost("addbedrock")] + [Authorize(Policy = PolicyNames.Viewer)] + public async Task> AddBedrockLayerAsync([Required] int id) + { + var stratigraphy = await Context.Stratigraphies.FindAsync(id).ConfigureAwait(false); + if (stratigraphy == null) + { + return NotFound(); + } + + try + { + // Check if associated borehole is locked + var userName = HttpContext.User.FindFirst(ClaimTypes.Name)?.Value; + if (await boreholeLockService.IsBoreholeLockedAsync(stratigraphy.BoreholeId, userName).ConfigureAwait(false)) + { + return Problem("The borehole is locked by another user."); + } + } + catch (UnauthorizedAccessException) + { + return Unauthorized("You are not authorized to add a bedrock layer to this stratigraphy."); + } + catch (Exception ex) + { + var message = "An error ocurred while adding a bedrock layer to the stratigraphy."; + Logger.LogError(ex, message); + return Problem(message); + } + + // Check if associated borehole has a TopBedrock value + var borehole = await Context.Boreholes.FindAsync(stratigraphy.BoreholeId).ConfigureAwait(false); + if (!borehole.TopBedrock.HasValue) + { + return Problem("Bedrock not yet defined."); + } + + // Add bedrock layer + var bedrockLayer = new Layer + { + StratigraphyId = stratigraphy.Id, + FromDepth = borehole.TopBedrock.Value, + LithologyTopBedrockId = borehole.LithologyTopBedrockId, + LithostratigraphyId = borehole.LithostratigraphyId, + IsLast = false, + }; + + await Context.Layers.AddAsync(bedrockLayer).ConfigureAwait(false); + await Context.UpdateChangeInformationAndSaveChangesAsync(HttpContext).ConfigureAwait(false); + + return Ok(bedrockLayer.Id); + } } diff --git a/tests/ActionResultAssert.cs b/tests/ActionResultAssert.cs index bc839dbd6..4516512f4 100644 --- a/tests/ActionResultAssert.cs +++ b/tests/ActionResultAssert.cs @@ -58,6 +58,21 @@ internal static void IsNotFound(IActionResult? actionResult) internal static void IsInternalServerError(IActionResult? actionResult) => AssertActionResult(actionResult, StatusCodes.Status500InternalServerError); + /// + /// Asserts that the is InternalServerError (500). + /// + internal static void IsInternalServerError(IActionResult? actionResult, string expectedErrorMessageSubstring) + { + AssertActionResult(actionResult, StatusCodes.Status500InternalServerError); + + var problemDetails = (ProblemDetails)((ObjectResult)actionResult!).Value!; + StringAssert.Contains( + problemDetails.Detail, + expectedErrorMessageSubstring, + $"The error message does not contain the expected message '{expectedErrorMessageSubstring}'.", + StringComparison.OrdinalIgnoreCase); + } + private static void AssertActionResult(IActionResult? currentActionResult, int expectedStatusCode) { var statusCodeResult = currentActionResult as IStatusCodeActionResult; diff --git a/tests/Controllers/StratigraphyControllerTest.cs b/tests/Controllers/StratigraphyControllerTest.cs index be0ced3f8..531782f4c 100644 --- a/tests/Controllers/StratigraphyControllerTest.cs +++ b/tests/Controllers/StratigraphyControllerTest.cs @@ -240,10 +240,7 @@ public async Task Create() var createdStratigraphy = (Stratigraphy?)((OkObjectResult)createResult.Result!).Value; createdStratigraphy = GetStratigraphy(createdStratigraphy.Id); - Assert.AreEqual(StratigraphyController.StratigraphyKindId, createdStratigraphy.KindId); - Assert.AreEqual(boreholeWithoutStratigraphy.Id, createdStratigraphy.BoreholeId); - Assert.AreEqual("KODACLUSTER", createdStratigraphy.Name); - Assert.AreEqual("ARGONTITAN", createdStratigraphy.Notes); + AssertStratigraphy(createdStratigraphy, boreholeWithoutStratigraphy.Id, "KODACLUSTER", "ARGONTITAN"); // Because the stratigraphy is the first one for the borehole, it is automatically the primary stratigraphy. Assert.AreEqual(true, createdStratigraphy.IsPrimary); @@ -270,10 +267,7 @@ public async Task CreateAdditionalStratigraphyForExistingBorehole() var createdStratigraphy = (Stratigraphy?)((OkObjectResult)createResult.Result!).Value; createdStratigraphy = GetStratigraphy(createdStratigraphy.Id); - Assert.AreEqual(StratigraphyController.StratigraphyKindId, createdStratigraphy.KindId); - Assert.AreEqual(boreholeWithExistingStratigraphy.Id, createdStratigraphy.BoreholeId); - Assert.AreEqual("STORMSTEED", createdStratigraphy.Name); - Assert.AreEqual("GALAXYJEEP", createdStratigraphy.Notes); + AssertStratigraphy(createdStratigraphy, boreholeWithExistingStratigraphy.Id, "STORMSTEED", "GALAXYJEEP"); // Because the stratigraphy is the second one for the borehole, it is not automatically the primary stratigraphy. Assert.AreEqual(false, createdStratigraphy.IsPrimary); @@ -316,4 +310,78 @@ public async Task CreateForLockedBorehole() var createResult = await controller.CreateAsync(new()); ActionResultAssert.IsInternalServerError(createResult.Result); } + + [TestMethod] + public async Task AddBedrockLayer() + { + // Prepare stratigraphy to add the bedrock layer for. + var boreholeWithBedrock = await context.Boreholes.FirstAsync(x => x.TopBedrock.HasValue); + var stratigraphyWithoutBedrockLayer = new Stratigraphy + { + KindId = StratigraphyController.StratigraphyKindId, + BoreholeId = boreholeWithBedrock.Id, + Name = "MAESTROHEART", + Notes = "BATONTOPPER", + }; + + var createResult = await controller.CreateAsync(stratigraphyWithoutBedrockLayer); + stratigraphyWithoutBedrockLayer = ActionResultAssert.IsOkObjectResult(createResult.Result); + AssertStratigraphy(stratigraphyWithoutBedrockLayer, boreholeWithBedrock.Id, "MAESTROHEART", "BATONTOPPER"); + + // Add bedrock and assert + var addBedrockResult = await controller.AddBedrockLayerAsync(stratigraphyWithoutBedrockLayer.Id); + ActionResultAssert.IsOk(addBedrockResult.Result); + + var bedrockLayerId = (int)((OkObjectResult?)addBedrockResult.Result)?.Value!; + var bedrockLayer = await context.Layers.FindAsync(bedrockLayerId); + Assert.AreEqual(stratigraphyWithoutBedrockLayer.Id, bedrockLayer.StratigraphyId); + Assert.AreEqual(boreholeWithBedrock.TopBedrock.Value, bedrockLayer.FromDepth); + Assert.AreEqual(boreholeWithBedrock.LithologyTopBedrockId, bedrockLayer.LithologyTopBedrockId); + Assert.AreEqual(boreholeWithBedrock.LithostratigraphyId, bedrockLayer.LithostratigraphyId); + Assert.AreEqual(false, bedrockLayer.IsLast); + } + + [TestMethod] + public async Task AddBedrockLayerForBoreholeWithoutTopBedrockValue() + { + // Prepare stratigraphy to add the bedrock layer for. + var boreholeWithoutBedrock = await context.Boreholes.FirstAsync(x => !x.TopBedrock.HasValue); + var stratigraphyWithoutBedrockLayer = new Stratigraphy + { + KindId = StratigraphyController.StratigraphyKindId, + BoreholeId = boreholeWithoutBedrock.Id, + Name = "CHIPPEWARECORD", + Notes = "FIREFALCON", + }; + + var createResult = await controller.CreateAsync(stratigraphyWithoutBedrockLayer); + stratigraphyWithoutBedrockLayer = ActionResultAssert.IsOkObjectResult(createResult.Result); + AssertStratigraphy(stratigraphyWithoutBedrockLayer, boreholeWithoutBedrock.Id, "CHIPPEWARECORD", "FIREFALCON"); + + var addBedrockResult = await controller.AddBedrockLayerAsync(stratigraphyWithoutBedrockLayer.Id); + ActionResultAssert.IsInternalServerError(addBedrockResult.Result, "bedrock"); + } + + [TestMethod] + public async Task AddBedrockLayerForLockedBorehole() + { + var boreholeLockServiceMock = new Mock(MockBehavior.Strict); + boreholeLockServiceMock + .Setup(x => x.IsBoreholeLockedAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(true); + + controller = new StratigraphyController(context, new Mock>().Object, boreholeLockServiceMock.Object) { ControllerContext = GetControllerContextAdmin() }; + + var existingStratigraphy = await context.Stratigraphies.FirstAsync(); + var addBedrockResult = await controller.AddBedrockLayerAsync(existingStratigraphy.Id); + ActionResultAssert.IsInternalServerError(addBedrockResult.Result, "locked"); + } + + private void AssertStratigraphy(Stratigraphy actual, int expectedBoreholeId, string exptectedName, string expectedNotes) + { + Assert.AreEqual(StratigraphyController.StratigraphyKindId, actual.KindId); + Assert.AreEqual(expectedBoreholeId, actual.BoreholeId); + Assert.AreEqual(exptectedName, actual.Name); + Assert.AreEqual(expectedNotes, actual.Notes); + } } From 34441bbfafe4ba68d8f0d2f9f5e6b1c7f2d91593 Mon Sep 17 00:00:00 2001 From: danjov Date: Tue, 19 Dec 2023 18:08:10 +0100 Subject: [PATCH 2/2] Use .NET API for adding bedrock to a stratigraphy --- src/api-legacy/__init__.py | 3 -- src/api-legacy/main.py | 6 --- .../v1/borehole/profile/producer.py | 9 +--- .../v1/borehole/stratigraphy/__init__.py | 5 -- .../v1/borehole/stratigraphy/addbedrock.py | 52 ------------------- .../v1/borehole/stratigraphy/producer.py | 49 ----------------- .../src/api-lib/actions/stratigraphy.js | 8 --- src/client/src/api-lib/index.js | 2 - src/client/src/api/fetchApiV2.js | 4 ++ .../profileLayersError/profileLayersError.js | 11 ++-- 10 files changed, 9 insertions(+), 140 deletions(-) delete mode 100644 src/api-legacy/v1/borehole/stratigraphy/__init__.py delete mode 100644 src/api-legacy/v1/borehole/stratigraphy/addbedrock.py delete mode 100644 src/api-legacy/v1/borehole/stratigraphy/producer.py diff --git a/src/api-legacy/__init__.py b/src/api-legacy/__init__.py index dc917fc1b..3d93452f4 100644 --- a/src/api-legacy/__init__.py +++ b/src/api-legacy/__init__.py @@ -34,9 +34,6 @@ from bms.v1.borehole.identifier import IdentifierAdminHandler from bms.v1.borehole.identifier import IdentifierViewerHandler -# Stratigraphy's ACTION Handlers -from bms.v1.borehole.stratigraphy.producer import StratigraphyProducerHandler - # Profiles's ACTION Handlers from bms.v1.borehole.profile.viewer import ProfileViewerHandler from bms.v1.borehole.profile.producer import ProfileProducerHandler diff --git a/src/api-legacy/main.py b/src/api-legacy/main.py index 40915a940..d09f6db87 100644 --- a/src/api-legacy/main.py +++ b/src/api-legacy/main.py @@ -140,9 +140,6 @@ async def close(application): IdentifierProducerHandler, IdentifierViewerHandler, - # Stratigraphy handlers - StratigraphyProducerHandler, - # Layer handlers LayerViewerHandler, LayerProducerHandler, @@ -218,9 +215,6 @@ async def close(application): # FEEDBACK handlers (r'/api/v1/feedback', FeedbackHandler), - # Stratigraphy handlers (will be deprecated) - (r'/api/v1/borehole/stratigraphy/edit', StratigraphyProducerHandler), - # Layer handlers (will be deprecated) (r'/api/v1/borehole/stratigraphy/layer', LayerViewerHandler), (r'/api/v1/borehole/stratigraphy/layer/edit', LayerProducerHandler), diff --git a/src/api-legacy/v1/borehole/profile/producer.py b/src/api-legacy/v1/borehole/profile/producer.py index 916b6f0b1..603b337e9 100644 --- a/src/api-legacy/v1/borehole/profile/producer.py +++ b/src/api-legacy/v1/borehole/profile/producer.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- from bms.v1.borehole.profile.patch import PatchProfile from bms.v1.handlers import Producer -from bms.v1.borehole.stratigraphy import AddBedrock class ProfileProducerHandler(Producer): @@ -9,7 +8,6 @@ async def execute(self, request): action = request.pop('action', None) if action in [ - 'ADDBEDROCK', 'PATCH' ]: @@ -20,7 +18,6 @@ async def execute(self, request): id_bho = None if action in [ - 'ADDBEDROCK', 'PATCH', ]: # Get Borehole id @@ -38,11 +35,7 @@ async def execute(self, request): id_bho, self.user, conn ) - if action == 'ADDBEDROCK': - exe = AddBedrock(conn) - request['user_id'] = self.user['id'] - - elif action == 'PATCH': + if action == 'PATCH': exe = PatchProfile(conn) request['user_id'] = self.user['id'] diff --git a/src/api-legacy/v1/borehole/stratigraphy/__init__.py b/src/api-legacy/v1/borehole/stratigraphy/__init__.py deleted file mode 100644 index af34fe49d..000000000 --- a/src/api-legacy/v1/borehole/stratigraphy/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# -*- coding: utf-8 -*- - -# Actions -from bms.v1.borehole.stratigraphy.addbedrock import AddBedrock - diff --git a/src/api-legacy/v1/borehole/stratigraphy/addbedrock.py b/src/api-legacy/v1/borehole/stratigraphy/addbedrock.py deleted file mode 100644 index 1935fb422..000000000 --- a/src/api-legacy/v1/borehole/stratigraphy/addbedrock.py +++ /dev/null @@ -1,52 +0,0 @@ -# -*- coding: utf-8 -*- -from bms.v1.action import Action - - -class AddBedrock(Action): - - async def execute(self, id, user_id): - - bedrock = await self.conn.fetchrow(""" - SELECT - b.id_bho, - b.top_bedrock_bho, - b.chronostrat_id_cli, - b.lithology_top_bedrock_id_cli, - b.lithostrat_id_cli - FROM - bdms.stratigraphy as s, - bdms.borehole as b - WHERE - s.id_sty = $1 - AND - s.id_bho_fk = b.id_bho - """, id) - - if bedrock is None: - raise Exception("Borehole not found") - - elif bedrock[1] is None: - raise Exception("Bedrock not yet defined") - - return { - "id": ( - await self.conn.fetchval(""" - INSERT INTO bdms.layer( - id_sty_fk, creator_lay, updater_lay, - depth_from_lay, - lithology_top_bedrock_id_cli, - lithostratigraphy_id_cli, - last_lay - ) - VALUES ( - $1, $2, $3, $4, $5, $6, False - ) - RETURNING - id_lay - """, - id, user_id, user_id, - bedrock[1], - bedrock[2], - bedrock[3] ) - ) - } diff --git a/src/api-legacy/v1/borehole/stratigraphy/producer.py b/src/api-legacy/v1/borehole/stratigraphy/producer.py deleted file mode 100644 index 3569bc7a9..000000000 --- a/src/api-legacy/v1/borehole/stratigraphy/producer.py +++ /dev/null @@ -1,49 +0,0 @@ -# -*- coding: utf-8 -*- -from bms.v1.handlers import Producer -from bms.v1.borehole.stratigraphy import AddBedrock - - -class StratigraphyProducerHandler(Producer): - async def execute(self, request): - action = request.pop('action', None) - - if action in [ - 'ADDBEDROCK' - ]: - - async with self.pool.acquire() as conn: - - exe = None - - id_bho = None - - if action in [ - 'ADDBEDROCK' - ]: - # Get Borehole id - id_bho = await conn.fetchval(""" - SELECT - id_bho_fk - FROM - bdms.stratigraphy - WHERE - id_sty = $1; - """, request['id']) - - # Lock check - await self.check_lock( - id_bho, self.user, conn - ) - - if action == 'ADDBEDROCK': - exe = AddBedrock(conn) - request['user_id'] = self.user['id'] - - request.pop('lang', None) - - if exe is not None: - return ( - await exe.execute(**request) - ) - - raise Exception("Action '%s' unknown" % action) diff --git a/src/client/src/api-lib/actions/stratigraphy.js b/src/client/src/api-lib/actions/stratigraphy.js index 2cf5e7783..7866808b4 100644 --- a/src/client/src/api-lib/actions/stratigraphy.js +++ b/src/client/src/api-lib/actions/stratigraphy.js @@ -1,13 +1,5 @@ import { fetch } from "./index"; -// Create a new stratigraphy for the given borehole id -export function addBedrock(id) { - return fetch("/borehole/stratigraphy/edit", { - action: "ADDBEDROCK", - id: id, - }); -} - export function getLayers(id) { return fetch("/borehole/stratigraphy/layer", { action: "LIST", diff --git a/src/client/src/api-lib/index.js b/src/client/src/api-lib/index.js index 96a5d9a8e..36a974a7d 100644 --- a/src/client/src/api-lib/index.js +++ b/src/client/src/api-lib/index.js @@ -76,7 +76,6 @@ import { } from "./actions/workflow"; import { - addBedrock, createLayer, createInstrument, deleteLayer, @@ -162,7 +161,6 @@ export { submitWorkflow, rejectWorkflow, resetWorkflow, - addBedrock, createLayer, createInstrument, deleteLayer, diff --git a/src/client/src/api/fetchApiV2.js b/src/client/src/api/fetchApiV2.js index 527b65464..1894d0e21 100644 --- a/src/client/src/api/fetchApiV2.js +++ b/src/client/src/api/fetchApiV2.js @@ -186,6 +186,10 @@ export const createStratigraphy = async (boreholeId, kindId) => { }); }; +export const addBedrock = async id => { + return await fetchApiV2(`stratigraphy/addbedrock?id=${id}`, "POST"); +}; + // Enable using react-query outputs across the application. // eslint-disable-next-line react-hooks/rules-of-hooks diff --git a/src/client/src/commons/form/profile/components/profileLayers/components/profileLayersError/profileLayersError.js b/src/client/src/commons/form/profile/components/profileLayers/components/profileLayersError/profileLayersError.js index 6be9d6481..a416d426a 100644 --- a/src/client/src/commons/form/profile/components/profileLayers/components/profileLayersError/profileLayersError.js +++ b/src/client/src/commons/form/profile/components/profileLayers/components/profileLayersError/profileLayersError.js @@ -2,12 +2,11 @@ import React, { useState, useEffect, useContext } from "react"; import * as Styled from "./styles"; import { Icon, Radio } from "semantic-ui-react"; import TranslationText from "../../../../../translationText"; +import { gapLayer, deleteLayer } from "../../../../../../../api-lib/index"; import { - gapLayer, + fetchLayerById, addBedrock, - deleteLayer, -} from "../../../../../../../api-lib/index"; -import { fetchLayerById } from "../../../../../../../api/fetchApiV2"; +} from "../../../../../../../api/fetchApiV2"; import ErrorTypes from "./errorTypes"; import { AlertContext } from "../../../../../../alert/alertContext"; @@ -109,10 +108,8 @@ const ProfileLayersError = props => { } else if (title === "missingBedrock") { addBedrock(id) .then(response => { - if (response.data.success) { + if (response) { onUpdated("fixErrors"); - } else { - alertContext.error(response.data.message); } }) .catch(error => {