Skip to content

Commit

Permalink
Cascade deletes well (#2556)
Browse files Browse the repository at this point in the history
  • Loading branch information
robertbasti authored Sep 24, 2024
1 parent f34d0b3 commit c0bd0db
Show file tree
Hide file tree
Showing 12 changed files with 211 additions and 55 deletions.
5 changes: 5 additions & 0 deletions Src/Witsml/ServiceReference/OptionsIn.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ public record OptionsIn(
int? MaxReturnNodes = null,
int? RequestLatestValues = null,
bool? RequestObjectSelectionCapability = null,
bool? CascadedDelete = null,
string OptionsInString = null)
{
public string OptionsInString { get; init; } = ValidateOptionsInString(OptionsInString);
Expand All @@ -34,6 +35,10 @@ public string GetKeywords()
{
keywords.Add($"requestObjectSelectionCapability=true");
}
if (CascadedDelete == true)
{
keywords.Add($"cascadedDelete=true");
}
if (!string.IsNullOrEmpty(OptionsInString))
{
keywords.Add(OptionsInString);
Expand Down
15 changes: 13 additions & 2 deletions Src/Witsml/WitsmlClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ public interface IWitsmlClient
Task<QueryResult> UpdateInStoreAsync<T>(T query) where T : IWitsmlQueryType;
Task<string> UpdateInStoreAsync(string query, OptionsIn optionsIn = null);
Task<QueryResult> DeleteFromStoreAsync<T>(T query) where T : IWitsmlQueryType;
Task<QueryResult> DeleteFromStoreAsync<T>(T query, OptionsIn optionsIn) where T : IWitsmlQueryType;
Task<string> DeleteFromStoreAsync(string query, OptionsIn optionsIn = null);
Task<QueryResult> TestConnectionAsync();
Task<WitsmlCapServers> GetCap();
Expand Down Expand Up @@ -367,15 +368,25 @@ public async Task<string> UpdateInStoreAsync(string query, OptionsIn optionsIn =
throw new Exception($"Error while adding to store: {response.Result} - {errorResponse.Result}. {response.SuppMsgOut}");
}

public async Task<QueryResult> DeleteFromStoreAsync<T>(T query) where T : IWitsmlQueryType
public Task<QueryResult> DeleteFromStoreAsync<T>(T query) where T : IWitsmlQueryType
{
return DeleteFromStoreAsyncImplementation(query);
}

public Task<QueryResult> DeleteFromStoreAsync<T>(T query, OptionsIn optionsIn) where T : IWitsmlQueryType
{
return DeleteFromStoreAsyncImplementation(query, optionsIn);
}

private async Task<QueryResult> DeleteFromStoreAsyncImplementation<T>(T query, OptionsIn optionsIn = null) where T : IWitsmlQueryType
{
try
{
WMLS_DeleteFromStoreRequest request = new()
{
WMLtypeIn = query.TypeName,
QueryIn = XmlHelper.Serialize(query),
OptionsIn = string.Empty,
OptionsIn = optionsIn == null ? string.Empty : optionsIn.GetKeywords(),
CapabilitiesIn = _clientCapabilities
};

Expand Down
10 changes: 8 additions & 2 deletions Src/WitsmlExplorer.Api/Jobs/DeleteJobs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ namespace WitsmlExplorer.Api.Jobs
{
public record DeleteComponentsJob : IDeleteJob<ComponentReferences> { }
public record DeleteObjectsJob : IDeleteJob<ObjectReferences> { }
public record DeleteWellboreJob : IDeleteJob<WellboreReference> { }
public record DeleteWellJob : IDeleteJob<WellReference> { }
public record DeleteWellboreJob : IDeleteJob<WellboreReference>
{
public bool CascadedDelete { get; init; }
}
public record DeleteWellJob : IDeleteJob<WellReference>
{
public bool CascadedDelete { get; init; }
}
}
3 changes: 2 additions & 1 deletion Src/WitsmlExplorer.Api/Workers/Delete/DeleteWellWorker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,11 @@ public DeleteWellWorker(ILogger<DeleteWellJob> logger, IWitsmlClientProvider wit

public override async Task<(WorkerResult, RefreshAction)> Execute(DeleteWellJob job, CancellationToken? cancellationToken = null)
{
bool cascadedDelete = job.CascadedDelete;
string wellUid = job.ToDelete.WellUid;

WitsmlWells witsmlWell = WellQueries.DeleteWitsmlWell(wellUid);
QueryResult result = await GetTargetWitsmlClientOrThrow().DeleteFromStoreAsync(witsmlWell);
QueryResult result = cascadedDelete ? await GetTargetWitsmlClientOrThrow().DeleteFromStoreAsync(witsmlWell, new OptionsIn(CascadedDelete: true)) : await GetTargetWitsmlClientOrThrow().DeleteFromStoreAsync(witsmlWell);
if (result.IsSuccessful)
{
Logger.LogInformation("Deleted well. WellUid: {WellUid}", wellUid);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,12 @@ public DeleteWellboreWorker(ILogger<DeleteWellboreJob> logger, IWitsmlClientProv

public override async Task<(WorkerResult, RefreshAction)> Execute(DeleteWellboreJob job, CancellationToken? cancellationToken = null)
{
bool cascadedDelete = job.CascadedDelete;
string wellUid = job.ToDelete.WellUid;
string wellboreUid = job.ToDelete.WellboreUid;

WitsmlWellbores witsmlWellbore = WellboreQueries.DeleteWitsmlWellbore(wellUid, wellboreUid);
QueryResult result = await GetTargetWitsmlClientOrThrow().DeleteFromStoreAsync(witsmlWellbore);
QueryResult result = cascadedDelete ? await GetTargetWitsmlClientOrThrow().DeleteFromStoreAsync(witsmlWellbore, new OptionsIn(CascadedDelete: true)) : await GetTargetWitsmlClientOrThrow().DeleteFromStoreAsync(witsmlWellbore);
if (result.IsSuccessful)
{
Logger.LogInformation("Deleted wellbore. WellUid: {WellUid}, WellboreUid: {WellboreUid}",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ import { WellRow } from "components/ContentViews/WellsListView";
import ContextMenu from "components/ContextMenus/ContextMenu";
import { StyledIcon } from "components/ContextMenus/ContextMenuUtils";
import NestedMenuItem from "components/ContextMenus/NestedMenuItem";
import ConfirmModal from "components/Modals/ConfirmModal";
import ConfirmDeletionModal, {
ConfirmDeletionModalProps
} from "components/Modals/ConfirmDeletionModal";
import DeleteEmptyMnemonicsModal, {
DeleteEmptyMnemonicsModalProps
} from "components/Modals/DeleteEmptyMnemonicsModal";
Expand Down Expand Up @@ -104,37 +106,31 @@ const WellContextMenu = (props: WellContextMenuProps): React.ReactElement => {
);
};

const deleteWell = async () => {
const deleteWell = async (cascadedDelete: boolean) => {
dispatchOperation({ type: OperationType.HideContextMenu });
dispatchOperation({ type: OperationType.HideModal });
const job: DeleteWellJob = {
toDelete: {
wellUid: well.uid,
wellName: well.name
}
},
cascadedDelete
};
await JobService.orderJob(JobType.DeleteWell, job);
};

const onClickDelete = async () => {
const confirmation = (
<ConfirmModal
heading={"Delete well?"}
content={
<span>
This will permanently delete <strong>{well.name}</strong> with uid:{" "}
<strong>{well.uid}</strong>
</span>
}
onConfirm={deleteWell}
confirmColor={"danger"}
confirmText={"Delete well"}
switchButtonPlaces={true}
/>
);
const userCredentialsModalProps: ConfirmDeletionModalProps = {
componentType: "well",
objectName: well.name,
objectUid: well.uid,
onSubmit(cascadedDelete) {
deleteWell(cascadedDelete);
}
};
dispatchOperation({
type: OperationType.DisplayModal,
payload: confirmation
payload: <ConfirmDeletionModal {...userCredentialsModalProps} />
});
};

Expand Down Expand Up @@ -219,7 +215,11 @@ const WellContextMenu = (props: WellContextMenuProps): React.ReactElement => {
<StyledIcon name="add" color={colors.interactive.primaryResting} />
<Typography color={"primary"}>New Wellbore</Typography>
</MenuItem>,
<MenuItem key={"deleteWell"} onClick={onClickDelete}>
<MenuItem
key={"deleteWell"}
onClick={onClickDelete}
disabled={!!checkedWellRows && checkedWellRows.length !== 1}
>
<StyledIcon
name="deleteToTrash"
color={colors.interactive.primaryResting}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ import {
import { pasteObjectOnWellbore } from "components/ContextMenus/CopyUtils";
import NestedMenuItem from "components/ContextMenus/NestedMenuItem";
import { useClipboardReferences } from "components/ContextMenus/UseClipboardReferences";
import ConfirmModal from "components/Modals/ConfirmModal";
import ConfirmDeletionModal, {
ConfirmDeletionModalProps
} from "components/Modals/ConfirmDeletionModal";
import DeleteEmptyMnemonicsModal, {
DeleteEmptyMnemonicsModalProps
} from "components/Modals/DeleteEmptyMnemonicsModal";
Expand Down Expand Up @@ -107,7 +109,7 @@ const WellboreContextMenu = (
);
};

const deleteWellbore = async () => {
const deleteWellbore = async (cascadedDelete: boolean) => {
dispatchOperation({ type: OperationType.HideContextMenu });
dispatchOperation({ type: OperationType.HideModal });
const job: DeleteWellboreJob = {
Expand All @@ -116,30 +118,24 @@ const WellboreContextMenu = (
wellboreUid: wellbore.uid,
wellName: wellbore.wellName,
wellboreName: wellbore.name
}
},
cascadedDelete
};
await JobService.orderJob(JobType.DeleteWellbore, job);
};

const onClickDelete = async () => {
const confirmation = (
<ConfirmModal
heading={"Delete wellbore?"}
content={
<span>
This will permanently delete <strong>{wellbore.name}</strong> with
uid: <strong>{wellbore.uid}</strong>
</span>
}
onConfirm={deleteWellbore}
confirmColor={"danger"}
confirmText={"Delete wellbore"}
switchButtonPlaces={true}
/>
);
const userCredentialsModalProps: ConfirmDeletionModalProps = {
componentType: "wellbore",
objectName: wellbore.name,
objectUid: wellbore.uid,
onSubmit(cascadedDelete) {
deleteWellbore(cascadedDelete);
}
};
dispatchOperation({
type: OperationType.DisplayModal,
payload: confirmation
payload: <ConfirmDeletionModal {...userCredentialsModalProps} />
});
};

Expand Down Expand Up @@ -237,7 +233,11 @@ const WellboreContextMenu = (
)}
</Typography>
</MenuItem>,
<MenuItem key={"deleteWellbore"} onClick={onClickDelete}>
<MenuItem
key={"deleteWellbore"}
onClick={onClickDelete}
disabled={!!checkedWellboreRows && checkedWellboreRows.length !== 1}
>
<StyledIcon
name="deleteToTrash"
color={colors.interactive.primaryResting}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { ModalContentLayout } from "components/Modals/ModalDialog";
import { Checkbox } from "components/StyledComponents/Checkbox";
import OperationType from "contexts/operationType";
import { useOperationState } from "hooks/useOperationState";
import React, { ChangeEvent, useState } from "react";
import ConfirmModal from "./ConfirmModal";
import WarningBar from "components/WarningBar";

export interface ConfirmDeletionModalProps {
componentType: string;
objectName: string;
objectUid: string;
onSubmit: (cascadedDelete: boolean) => void;
}

const ConfirmDeletionModal = (
props: ConfirmDeletionModalProps
): React.ReactElement => {
const {
operationState: { colors },
dispatchOperation
} = useOperationState();

const [cascadedDelete, setCascadedDelete] = useState<boolean>(false);

const onConfirmClick = async () => {
props.onSubmit(cascadedDelete);
dispatchOperation({ type: OperationType.HideModal });
};

return (
<ConfirmModal
heading={"Delete " + props.componentType + "?"}
content={
<>
<ModalContentLayout>
<span>
This will permanently delete {props.componentType}{" "}
<strong>{props.objectName}</strong> with uid:{" "}
<strong>{props.objectUid}</strong>
</span>

<Checkbox
label={`Delete all objects under ` + props.componentType + `?`}
onChange={(e: ChangeEvent<HTMLInputElement>) => {
setCascadedDelete(e.target.checked);
}}
colors={colors}
/>

{cascadedDelete && (
<WarningBar
message={
`This action will permanently delete the ${props.componentType} ` +
`'${props.objectName}' and all associated objects. ` +
`This cannot be undone. Please ensure you want to delete the entire structure.`
}
/>
)}
</ModalContentLayout>
</>
}
onConfirm={onConfirmClick}
confirmColor={"danger"}
confirmText={"Delete " + props.componentType}
switchButtonPlaces={true}
/>
);
};

export default ConfirmDeletionModal;
2 changes: 2 additions & 0 deletions Src/WitsmlExplorer.Frontend/models/jobs/deleteJobs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@ export interface DeleteComponentsJob {

export interface DeleteWellboreJob {
toDelete: WellboreReference;
cascadedDelete: boolean;
}

export interface DeleteWellJob {
toDelete: {
wellUid: string;
wellName: string;
};
cascadedDelete: boolean;
}
2 changes: 1 addition & 1 deletion Tests/Witsml.Tests/ServiceReference/OptionsInTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public void GetKeywords_OptionsInString_MultipleKeywords_ReturnsCorrectValue()
[Fact]
public void GetKeywords_OptionsInStringAndOtherOptions_ReturnsCorrectValue()
{
OptionsIn optionsIn = new(ReturnElements.DataOnly, 50, 100, true, "foo=bar;baz=qux");
OptionsIn optionsIn = new(ReturnElements.DataOnly, 50, 100, true, false, "foo=bar;baz=qux");
Assert.Equal("returnElements=data-only;maxReturnNodes=50;requestLatestValues=100;requestObjectSelectionCapability=true;foo=bar;baz=qux", optionsIn.GetKeywords());
}

Expand Down
Loading

0 comments on commit c0bd0db

Please sign in to comment.