Skip to content

Commit

Permalink
BoundedContext short name validation
Browse files Browse the repository at this point in the history
Changes scope of uniqueness constraint for bounded context short name
Fixes bug where short name constraint wasn't checked correctly
  • Loading branch information
K-Dud committed Jul 1, 2024
1 parent 76adaee commit 46b2925
Show file tree
Hide file tree
Showing 7 changed files with 145 additions and 120 deletions.
16 changes: 13 additions & 3 deletions frontend-vue/src/components/bounded-context/canvas/BCCKey.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<div v-if="editMode">
<ContextureHelpfulErrorAlert v-if="submitError" v-bind="submitError" class="mb-4" />
<Form @submit="onUpdate">
<ContextureChangeKey v-model="key" />
<ContextureInputText v-model="key" :rules="boundedContextShortNameValidator" />

<ContexturePrimaryButton type="submit" :label="t('common.save')" class="mt-4" size="sm">
<template #left>
Expand All @@ -27,28 +27,38 @@
</template>

<script setup lang="ts">
import { toFieldValidator } from "@vee-validate/zod";
import { storeToRefs } from "pinia";
import { Form } from "vee-validate";
import { ref } from "vue";
import { useI18n } from "vue-i18n";
import ContextureBoundedContextCanvasElement from "~/components/bounded-context/canvas/ContextureBoundedContextCanvasElement.vue";
import ContextureChangeKey from "~/components/core/change-short-name/ContextureChangeShortName.vue";
import { boundedContextShortNameValidationSchema } from "~/components/core/change-short-name/changeShortNameValidationSchema";
("~/components/core/change-short-name/changeShortNameValidationSchema");
import ContextureHelpfulErrorAlert, {
HelpfulErrorProps,
} from "~/components/primitives/alert/ContextureHelpfulErrorAlert.vue";
import ContexturePrimaryButton from "~/components/primitives/button/ContexturePrimaryButton.vue";
import ContextureInputText from "~/components/primitives/input/ContextureInputText.vue";
import { useAuthStore } from "~/stores/auth";
import { useBoundedContextsStore } from "~/stores/boundedContexts";
import IconsMaterialSymbolsFormatKeyOutline from "~icons/material-symbols/key-outline";
const icon = IconsMaterialSymbolsFormatKeyOutline;
const store = useBoundedContextsStore();
const { activeBoundedContext } = storeToRefs(store);
const { activeBoundedContext, boundedContextsByDomainId } = storeToRefs(store);
const { canModify } = useAuthStore();
const { t } = useI18n();
const key = ref(activeBoundedContext.value.shortName);
const submitError = ref<HelpfulErrorProps | undefined>();
const editMode = ref(false);
const boundedContextShortNameValidator = toFieldValidator(
boundedContextShortNameValidationSchema(
boundedContextsByDomainId.value[activeBoundedContext.value.parentDomainId].filter(
(bc) => bc.id !== activeBoundedContext.value.id
)
)
);
async function onUpdate() {
submitError.value = null;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
<template>
<Form class="flex w-9/12 flex-col gap-8" @submit="submit">
<ContextureChangeKey
<ContextureInputText
:description="t('bounded_context_canvas.edit.form.description.key')"
:model-value="editModel.shortName"
name="key"
:label="t('bounded_context_canvas.edit.form.label.key')"
:rules="boundedContextShortNameValidator"
/>

<ContextureInputText
Expand Down Expand Up @@ -32,10 +33,12 @@ import { Form } from "vee-validate";
import { Ref, toRef } from "vue";
import { useI18n } from "vue-i18n";
import * as zod from "zod";
import { boundedContextShortNameValidationSchema } from "~/components/core/change-short-name/changeShortNameValidationSchema";
import ContexturePrimaryButton from "~/components/primitives/button/ContexturePrimaryButton.vue";
import ContextureChangeKey from "~/components/core/change-short-name/ContextureChangeShortName.vue";
import ContextureInputText from "~/components/primitives/input/ContextureInputText.vue";
import { BoundedContext } from "~/types/boundedContext";
import { useBoundedContextsStore } from "~/stores/boundedContexts";
import { storeToRefs } from "pinia";
interface Props {
initialValue: BoundedContext;
Expand All @@ -45,9 +48,16 @@ const props = defineProps<Props>();
const emit = defineEmits(["submit"]);
const { t } = useI18n();
const editModel: Ref<BoundedContext> = toRef(props, "initialValue");
const requiredString = toFieldValidator(zod.string().min(1, t("validation.required")));
const store = useBoundedContextsStore();
const { boundedContextsByDomainId } = storeToRefs(store);
const boundedContextShortNameValidator = toFieldValidator(
boundedContextShortNameValidationSchema(
boundedContextsByDomainId.value[props.initialValue.parentDomainId].filter((bc) => bc.id !== props.initialValue.id)
)
);
function submit(values: any) {
emit("submit", values);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import { toFieldValidator } from "@vee-validate/zod";
import { storeToRefs } from "pinia";
import { shortNameValidationSchema } from "~/components/core/change-short-name/changeShortNameValidationSchema";
import ContextureInputText from "~/components/primitives/input/ContextureInputText.vue";
import { useBoundedContextsStore } from "~/stores/boundedContexts";
import { useDomainsStore } from "~/stores/domains";
interface Props {
Expand All @@ -25,11 +24,8 @@ const props = defineProps<Props>();
const emit = defineEmits(["update:modelValue"]);
const { allDomains } = storeToRefs(useDomainsStore());
const { boundedContexts } = storeToRefs(useBoundedContextsStore());
const shortNameValidationRules = toFieldValidator(
shortNameValidationSchema(props.modelValue, allDomains.value, boundedContexts.value)
);
const shortNameValidationRules = toFieldValidator(shortNameValidationSchema(props.modelValue, allDomains.value));
console.log(shortNameValidationRules);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,157 +1,146 @@
import { describe, expect, test } from "vitest";
import { shortNameValidationSchema } from "~/components/core/change-short-name/changeShortNameValidationSchema";
import {
shortNameValidationSchema,
boundedContextShortNameValidationSchema,
} from "~/components/core/change-short-name/changeShortNameValidationSchema";

describe("change short name validation rules", () => {
test.each(["", null, undefined])("'%s' can not be null or empty", (shortName) => {
const validation = shortNameValidationSchema("", [], []);
const validation = shortNameValidationSchema("", []);

const { success } = validation.safeParse(shortName);

expect(success).toBeFalsy();
});

test("cannot exceed 16 characters", () => {
const validation = shortNameValidationSchema("", [], []);
const validation = shortNameValidationSchema("", []);

const { success } = validation.safeParse("a".repeat(17));

expect(success).toBeFalsy();
});

test("cannot contain whitespace", () => {
const validation = shortNameValidationSchema("", [], []);
const validation = shortNameValidationSchema("", []);

const { success } = validation.safeParse("a b");

expect(success).toBeFalsy();
});

test("cannot start with a number", () => {
const validation = shortNameValidationSchema("", [], []);
const validation = shortNameValidationSchema("", []);

const { success } = validation.safeParse("1a");

expect(success).toBeFalsy();
});

test("cannot start with hyphen", () => {
const validation = shortNameValidationSchema("", [], []);
const validation = shortNameValidationSchema("", []);

const { success } = validation.safeParse("-a");

expect(success).toBeFalsy();
});

test("cannot end with hyphen", () => {
const validation = shortNameValidationSchema("", [], []);
const validation = shortNameValidationSchema("", []);

const { success } = validation.safeParse("a-");

expect(success).toBeFalsy();
});

test("cannot contain characters other than alphanumeric and hyphen", () => {
const validation = shortNameValidationSchema("", [], []);
const validation = shortNameValidationSchema("", []);

const { success } = validation.safeParse("a/b");

expect(success).toBeFalsy();
});

test("cannot be the same as another domain", () => {
const validation = shortNameValidationSchema(
"",
[
{
id: "1",
name: "Domain",
shortName: "d",
subdomains: [],
boundedContexts: [],
},
],
[]
);
const validation = shortNameValidationSchema("", [
{
id: "1",
name: "Domain",
shortName: "d",
subdomains: [],
boundedContexts: [],
},
]);

const { success } = validation.safeParse("d");

expect(success).toBeFalsy();
});

test("cannot be the same as another bounded context", () => {
const validation = shortNameValidationSchema(
"",
[],
[
{
id: "2",
shortName: "bc",
name: "Bounded Context",
parentDomainId: "1",
classification: {},
businessDecisions: [],
namespaces: [],
ubiquitousLanguage: {},
domain: {
id: "1",
name: "Domain",
subdomains: [],
boundedContexts: [],
},
},
]
);
test("cannot be the same as another domain (case insensitive)", () => {
const validation = shortNameValidationSchema("", [
{
id: "1",
name: "Domain",
shortName: "d",
subdomains: [],
boundedContexts: [],
},
]);

const { success } = validation.safeParse("bc");
const { success } = validation.safeParse("D");

expect(success).toBeFalsy();
});
});

test("cannot be the same as another domain (case insensitive)", () => {
const validation = shortNameValidationSchema(
"",
[
{
describe("bounded context short name validation rules", () => {
test("cannot be the same as another bounded context (case insensitive)", () => {
const validation = boundedContextShortNameValidationSchema([
{
id: "2",
shortName: "bc",
name: "Bounded Context",
parentDomainId: "1",
classification: {},
businessDecisions: [],
namespaces: [],
ubiquitousLanguage: {},
domain: {
id: "1",
name: "Domain",
shortName: "d",
subdomains: [],
boundedContexts: [],
},
],
[]
);
},
]);

const { success } = validation.safeParse("D");
const { success } = validation.safeParse("BC");

expect(success).toBeFalsy();
});

test("cannot be the same as another bounded context (case insensitive)", () => {
const validation = shortNameValidationSchema(
"",
[],
[
{
id: "2",
shortName: "bc",
name: "Bounded Context",
parentDomainId: "1",
classification: {},
businessDecisions: [],
namespaces: [],
ubiquitousLanguage: {},
domain: {
id: "1",
name: "Domain",
subdomains: [],
boundedContexts: [],
},
test("cannot be the same as another bounded context", () => {
const validation = boundedContextShortNameValidationSchema([
{
id: "2",
shortName: "bc",
name: "Bounded Context",
parentDomainId: "1",
classification: {},
businessDecisions: [],
namespaces: [],
ubiquitousLanguage: {},
domain: {
id: "1",
name: "Domain",
subdomains: [],
boundedContexts: [],
},
]
);
},
]);

const { success } = validation.safeParse("BC");
const { success } = validation.safeParse("bc");

expect(success).toBeFalsy();
});
Expand Down
Loading

0 comments on commit 46b2925

Please sign in to comment.