Skip to content

Commit

Permalink
merge: #2758
Browse files Browse the repository at this point in the history
2758: chore(web,dal): change action prototype kind setter UX r=paulocsanz a=paulocsanz

![image](https://github.com/systeminit/si/assets/6289779/65865304-ff50-4caf-bd52-bcc918150fea)

The error message is because I made the action delete, but there already is one, so it rolled back and showed the error message

Co-authored-by: Paulo Cabral <[email protected]>
  • Loading branch information
si-bors-ng[bot] and paulocsanz authored Sep 18, 2023
2 parents 4e00f67 + 8f6d192 commit 2741281
Show file tree
Hide file tree
Showing 7 changed files with 133 additions and 55 deletions.
4 changes: 2 additions & 2 deletions app/web/src/components/AssetFuncAttachModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -425,8 +425,8 @@ const attachExistingFunc = async () => {
}
if (updatedAssocations) {
func.associations = updatedAssocations;
const result = await funcStore.updateFuncMetadata(func);
if (result.result.success && props.assetId) {
const response = await funcStore.UPDATE_FUNC(func);
if (response.result.success && props.assetId) {
await reloadAssetAndRoute(props.assetId, func.id);
}
}
Expand Down
101 changes: 72 additions & 29 deletions app/web/src/components/FuncEditor/ActionDetails.vue
Original file line number Diff line number Diff line change
@@ -1,15 +1,33 @@
<template>
<div class="p-3 flex flex-col gap-2">
<ErrorMessage :requestStatus="props.requestStatus" />
<h2 class="pt-4 text-neutral-700 type-bold-sm dark:text-neutral-50">
Kind of Action:
<SiCheckBox
id="create"
v-model="isCreate"
title="This action creates a resource"
:disabled="disabled"
@update:model-value="setCreate"
/>
</h2>
<h2 class="pt-4 text-neutral-700 type-bold-sm dark:text-neutral-50">
<SiCheckBox
id="refresh"
v-model="isRefresh"
title="This action refreshes a resource"
:disabled="disabled"
@update:model-value="setRefresh"
/>
</h2>
<h2 class="pt-4 text-neutral-700 type-bold-sm dark:text-neutral-50">
<SiCheckBox
id="delete"
v-model="isDelete"
title="This action deletes a resource"
:disabled="disabled"
@update:model-value="setDelete"
/>
</h2>
<SelectMenu
v-model="selectedKind"
class="flex-auto"
:options="kindOptions"
:disabled="disabled"
@change="updateKind"
/>
<template v-if="!schemaVariantId">
<h2 class="pt-4 text-neutral-700 type-bold-sm dark:text-neutral-50">
Run on Assets of Type:
Expand All @@ -28,8 +46,11 @@
<script lang="ts" setup>
import { ref, watch, toRef } from "vue";
import { storeToRefs } from "pinia";
import SelectMenu, { Option } from "@/components/SelectMenu.vue";
import { ApiRequestStatus } from "@si/vue-lib/pinia";
import { ErrorMessage } from "@si/vue-lib/design-system";
import { Option } from "@/components/SelectMenu.vue";
import { ActionAssociations, FuncAssociations } from "@/store/func/types";
import SiCheckBox from "@/components/SiCheckBox.vue";
import { toOptionValues } from "@/components/FuncEditor/utils";
import { useFuncStore } from "@/store/func/funcs.store";
import { ActionKind } from "@/store/fixes.store";
Expand All @@ -42,28 +63,44 @@ const props = defineProps<{
modelValue: ActionAssociations;
schemaVariantId?: string;
disabled?: boolean;
requestStatus: ApiRequestStatus;
}>();
const kindToOption = (kind: string): Option => ({
label: kind,
value: kind,
});
const isCreate = ref(false);
const isDelete = ref(false);
const isRefresh = ref(false);
watch(
() => props.modelValue.kind,
() => {
isCreate.value = props.modelValue.kind === ActionKind.Create;
isDelete.value = props.modelValue.kind === ActionKind.Delete;
isRefresh.value = props.modelValue.kind === ActionKind.Refresh;
},
{ immediate: true },
);
const generateKindOptions = () => {
const options: Option[] = [];
for (const kind of Object.values(ActionKind)) {
options.push(kindToOption(kind as ActionKind));
}
return options;
const setCreate = () => {
if (!isCreate.value) return updateKind();
isDelete.value = false;
isRefresh.value = false;
updateKind();
};
const modelValue = toRef(props, "modelValue");
const setRefresh = () => {
if (!isRefresh.value) return updateKind();
isCreate.value = false;
isDelete.value = false;
updateKind();
};
const kindOptions = generateKindOptions();
const setDelete = () => {
if (!isDelete.value) return updateKind();
isCreate.value = false;
isRefresh.value = false;
updateKind();
};
const selectedKind = ref<Option>(
kindToOption(modelValue.value?.kind ?? "other"),
);
const modelValue = toRef(props, "modelValue");
const selectedVariants = ref<Option[]>(
toOptionValues(schemaVariantOptions.value, modelValue.value.schemaVariantIds),
Expand Down Expand Up @@ -91,11 +128,17 @@ const updateKind = () => {
const getUpdatedAssocations = (
schemaVariantIds: string[],
): ActionAssociations => ({
kind: selectedKind.value.value as ActionKind,
schemaVariantIds,
type: "action",
});
): ActionAssociations => {
let kind = ActionKind.Other;
if (isCreate.value) kind = ActionKind.Create;
if (isDelete.value) kind = ActionKind.Delete;
if (isRefresh.value) kind = ActionKind.Refresh;
return {
kind,
schemaVariantIds,
type: "action",
};
};
const updateAssociations = () => {
const associations = getUpdatedAssocations(
Expand Down
16 changes: 8 additions & 8 deletions app/web/src/components/FuncEditor/FuncDetails.vue
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@
ref="detachRef"
v-model="editingFunc.associations"
:disabled="changeSetsStore.headSelected"
:requestStatus="updateFuncReqStatus"
:schemaVariantId="schemaVariantId"
@change="updateFunc"
/>
Expand Down Expand Up @@ -289,6 +290,7 @@ const loadFuncDetailsReqStatus = funcStore.getRequestStatus(
"FETCH_FUNC_DETAILS",
funcId,
);
const updateFuncReqStatus = funcStore.getRequestStatus("UPDATE_FUNC", funcId);
const { selectedFuncId, selectedFuncSummary } = storeToRefs(funcStore);
const funcArgumentsIdMap = computed(() =>
Expand All @@ -303,26 +305,24 @@ const funcArgumentsIdMap = computed(() =>
provide("funcArgumentsIdMap", funcArgumentsIdMap);
const storeFuncDetails = computed(() => funcStore.selectedFuncDetails);
const editingFunc = ref(storeFuncDetails.value);
const editingFunc = ref(_.cloneDeep(storeFuncDetails.value));
function resetEditingFunc() {
editingFunc.value = _.cloneDeep(storeFuncDetails.value);
}
// when the func details finish loading, we copy into our local draft
watch(loadFuncDetailsReqStatus, () => {
if (loadFuncDetailsReqStatus.value.isSuccess) {
resetEditingFunc();
}
watch([loadFuncDetailsReqStatus, updateFuncReqStatus], () => {
resetEditingFunc();
});
const isRevertible = computed(() =>
funcId.value ? funcStore.funcDetailsById[funcId.value]?.isRevertible : false,
);
const updateFunc = () => {
if (!funcId.value || !editingFunc.value) return;
funcStore.updateFuncMetadata(editingFunc.value);
if (!editingFunc.value) return;
funcStore.UPDATE_FUNC(editingFunc.value);
};
const revertFuncReqStatus = funcStore.getRequestStatus("REVERT_FUNC");
Expand Down Expand Up @@ -371,7 +371,7 @@ const detachFunc = async () => {
const associations = detachRef.value.detachFunc();
if (associations && editingFunc.value) {
isDetaching.value = true;
await funcStore.updateFuncMetadata({
await funcStore.UPDATE_FUNC({
...editingFunc.value,
associations,
});
Expand Down
27 changes: 14 additions & 13 deletions app/web/src/store/func/funcs.store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,20 @@ export const useFuncStore = () => {
...func,
...visibility,
},
optimistic: () => {
const current = this.funcById(func.id);
this.funcDetailsById[func.id] = {
...func,
code: current?.code ?? func.code,
};
return () => {
if (current) {
this.funcDetailsById[func.id] = current;
} else {
delete this.funcDetailsById[func.id];
}
};
},
keyRequestStatusBy: func.id,
onSuccess: (response) => {
func.associations = response.associations;
Expand Down Expand Up @@ -496,19 +510,6 @@ export const useFuncStore = () => {
);
},

async updateFuncMetadata(func: FuncWithDetails) {
const currentCode = this.funcById(func.id)?.code ?? "";
this.funcDetailsById[func.id] = {
...func,
code: currentCode,
};

return this.UPDATE_FUNC({
...func,
code: currentCode,
});
},

updateFuncCode(funcId: FuncId, code: string) {
const func = this.funcDetailsById[funcId];
if (func) {
Expand Down
30 changes: 30 additions & 0 deletions lib/dal/src/action_prototype.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ pub enum ActionPrototypeError {
FuncNotFound(FuncId, ActionPrototypeId),
#[error("history event error: {0}")]
HistoryEvent(#[from] HistoryEventError),
#[error("this asset already has an action of this kind")]
MultipleOfSameKind,
#[error("nats txn error: {0}")]
Nats(#[from] NatsError),
#[error("not found with kind {0} for context {1:?}")]
Expand Down Expand Up @@ -221,6 +223,13 @@ impl ActionPrototype {
kind: ActionKind,
context: ActionPrototypeContext,
) -> ActionPrototypeResult<Self> {
let action_prototypes = Self::find_for_context(ctx, context).await?;
for prototype in action_prototypes {
if *prototype.kind() == kind && kind != ActionKind::Other {
return Err(ActionPrototypeError::MultipleOfSameKind);
}
}

let row = ctx
.txns()
.await?
Expand Down Expand Up @@ -330,6 +339,27 @@ impl ActionPrototype {
standard_model_accessor!(func_id, Pk(FuncId), ActionPrototypeResult);
standard_model_accessor!(kind, Enum(ActionKind), ActionPrototypeResult);

pub async fn set_kind_checked(
&mut self,
ctx: &DalContext,
kind: ActionKind,
) -> ActionPrototypeResult<()> {
let action_prototypes = Self::find_for_context(
ctx,
ActionPrototypeContext {
schema_variant_id: self.schema_variant_id(),
},
)
.await?;
for prototype in action_prototypes {
if *prototype.kind() == kind && kind != ActionKind::Other && prototype.id() != self.id()
{
return Err(ActionPrototypeError::MultipleOfSameKind);
}
}
self.set_kind(ctx, kind).await
}

pub fn context(&self) -> ActionPrototypeContext {
let mut context = ActionPrototypeContext::new();
context.set_schema_variant_id(self.schema_variant_id);
Expand Down
8 changes: 6 additions & 2 deletions lib/dal/src/diagram/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ use crate::diagram::DiagramResult;
use crate::schema::SchemaUiMenu;
use crate::socket::{SocketArity, SocketEdgeKind};
use crate::{
history_event, ActionPrototype, ActionPrototypeContext, ActionPrototypeView, ActorView,
Component, ComponentId, ComponentStatus, ComponentType, DalContext, DiagramError,
history_event, ActionKind, ActionPrototype, ActionPrototypeContext, ActionPrototypeView,
ActorView, Component, ComponentId, ComponentStatus, ComponentType, DalContext, DiagramError,
HistoryActorTimestamp, Node, NodeId, ResourceView, SchemaVariant, StandardModel,
};

Expand Down Expand Up @@ -249,6 +249,10 @@ impl DiagramComponentView {
.await?;
let mut action_views: Vec<ActionPrototypeView> = Vec::new();
for action_prototype in action_prototypes {
if *action_prototype.kind() == ActionKind::Refresh {
continue;
}

let view = ActionPrototypeView::new(ctx, action_prototype).await?;
action_views.push(view);
}
Expand Down
2 changes: 1 addition & 1 deletion lib/sdf-server/src/server/service/func/save_func.rs
Original file line number Diff line number Diff line change
Expand Up @@ -494,7 +494,7 @@ async fn save_action_func_prototypes(
{
Some(mut existing_proto) => {
existing_proto.set_func_id(ctx, *func.id()).await?;
existing_proto.set_kind(ctx, kind).await?;
existing_proto.set_kind_checked(ctx, kind).await?;
existing_proto
}
None => ActionPrototype::new(ctx, *func.id(), kind, context).await?,
Expand Down

0 comments on commit 2741281

Please sign in to comment.