diff --git a/migrations/.snapshot-cathedral.json b/migrations/.snapshot-cathedral.json index 0caaf51c..c29d5764 100644 --- a/migrations/.snapshot-cathedral.json +++ b/migrations/.snapshot-cathedral.json @@ -161,7 +161,7 @@ "unsigned": false, "autoincrement": false, "primary": false, - "nullable": true, + "nullable": false, "mappedType": "uuid" }, "organization_id": { @@ -170,7 +170,7 @@ "unsigned": false, "autoincrement": false, "primary": false, - "nullable": true, + "nullable": false, "mappedType": "uuid" }, "role": { @@ -217,7 +217,7 @@ "id" ], "referencedTableName": "public.app_user", - "deleteRule": "cascade" + "updateRule": "cascade" }, "app_user_organization_role_organization_id_foreign": { "constraintName": "app_user_organization_role_organization_id_foreign", @@ -229,7 +229,7 @@ "id" ], "referencedTableName": "public.organization", - "deleteRule": "cascade" + "updateRule": "cascade" } }, "nativeEnums": {} @@ -365,6 +365,26 @@ "primary": false, "nullable": false, "mappedType": "uuid" + }, + "last_modified": { + "name": "last_modified", + "type": "timestamptz", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "length": 6, + "mappedType": "datetime" + }, + "modified_by_id": { + "name": "modified_by_id", + "type": "uuid", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "default": "'ac594919-50e3-438a-b9bc-efb8a8654243'", + "mappedType": "uuid" } }, "name": "product", @@ -394,6 +414,18 @@ ], "referencedTableName": "public.solution", "updateRule": "cascade" + }, + "product_modified_by_id_foreign": { + "constraintName": "product_modified_by_id_foreign", + "columnNames": [ + "modified_by_id" + ], + "localTableName": "public.product", + "referencedColumnNames": [ + "id" + ], + "referencedTableName": "public.app_user", + "updateRule": "cascade" } }, "nativeEnums": {} @@ -438,6 +470,26 @@ "nullable": false, "mappedType": "uuid" }, + "last_modified": { + "name": "last_modified", + "type": "timestamptz", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "length": 6, + "mappedType": "datetime" + }, + "modified_by_id": { + "name": "modified_by_id", + "type": "uuid", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "default": "'ac594919-50e3-438a-b9bc-efb8a8654243'", + "mappedType": "uuid" + }, "email": { "name": "email", "type": "varchar(255)", @@ -476,6 +528,18 @@ ], "referencedTableName": "public.solution", "updateRule": "cascade" + }, + "person_modified_by_id_foreign": { + "constraintName": "person_modified_by_id_foreign", + "columnNames": [ + "modified_by_id" + ], + "localTableName": "public.person", + "referencedColumnNames": [ + "id" + ], + "referencedTableName": "public.app_user", + "updateRule": "cascade" } }, "nativeEnums": {} @@ -519,6 +583,26 @@ "primary": false, "nullable": false, "mappedType": "uuid" + }, + "last_modified": { + "name": "last_modified", + "type": "timestamptz", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "length": 6, + "mappedType": "datetime" + }, + "modified_by_id": { + "name": "modified_by_id", + "type": "uuid", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "default": "'ac594919-50e3-438a-b9bc-efb8a8654243'", + "mappedType": "uuid" } }, "name": "outcome", @@ -548,6 +632,18 @@ ], "referencedTableName": "public.solution", "updateRule": "cascade" + }, + "outcome_modified_by_id_foreign": { + "constraintName": "outcome_modified_by_id_foreign", + "columnNames": [ + "modified_by_id" + ], + "localTableName": "public.outcome", + "referencedColumnNames": [ + "id" + ], + "referencedTableName": "public.app_user", + "updateRule": "cascade" } }, "nativeEnums": {} @@ -591,6 +687,26 @@ "primary": false, "nullable": false, "mappedType": "uuid" + }, + "last_modified": { + "name": "last_modified", + "type": "timestamptz", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "length": 6, + "mappedType": "datetime" + }, + "modified_by_id": { + "name": "modified_by_id", + "type": "uuid", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "default": "'ac594919-50e3-438a-b9bc-efb8a8654243'", + "mappedType": "uuid" } }, "name": "obstacle", @@ -620,6 +736,18 @@ ], "referencedTableName": "public.solution", "updateRule": "cascade" + }, + "obstacle_modified_by_id_foreign": { + "constraintName": "obstacle_modified_by_id_foreign", + "columnNames": [ + "modified_by_id" + ], + "localTableName": "public.obstacle", + "referencedColumnNames": [ + "id" + ], + "referencedTableName": "public.app_user", + "updateRule": "cascade" } }, "nativeEnums": {} @@ -664,6 +792,26 @@ "nullable": false, "mappedType": "uuid" }, + "last_modified": { + "name": "last_modified", + "type": "timestamptz", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "length": 6, + "mappedType": "datetime" + }, + "modified_by_id": { + "name": "modified_by_id", + "type": "uuid", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "default": "'ac594919-50e3-438a-b9bc-efb8a8654243'", + "mappedType": "uuid" + }, "priority": { "name": "priority", "type": "text", @@ -707,6 +855,18 @@ ], "referencedTableName": "public.solution", "updateRule": "cascade" + }, + "non_functional_behavior_modified_by_id_foreign": { + "constraintName": "non_functional_behavior_modified_by_id_foreign", + "columnNames": [ + "modified_by_id" + ], + "localTableName": "public.non_functional_behavior", + "referencedColumnNames": [ + "id" + ], + "referencedTableName": "public.app_user", + "updateRule": "cascade" } }, "nativeEnums": {} @@ -750,6 +910,26 @@ "primary": false, "nullable": false, "mappedType": "uuid" + }, + "last_modified": { + "name": "last_modified", + "type": "timestamptz", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "length": 6, + "mappedType": "datetime" + }, + "modified_by_id": { + "name": "modified_by_id", + "type": "uuid", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "default": "'ac594919-50e3-438a-b9bc-efb8a8654243'", + "mappedType": "uuid" } }, "name": "limit", @@ -779,6 +959,18 @@ ], "referencedTableName": "public.solution", "updateRule": "cascade" + }, + "limit_modified_by_id_foreign": { + "constraintName": "limit_modified_by_id_foreign", + "columnNames": [ + "modified_by_id" + ], + "localTableName": "public.limit", + "referencedColumnNames": [ + "id" + ], + "referencedTableName": "public.app_user", + "updateRule": "cascade" } }, "nativeEnums": {} @@ -822,6 +1014,26 @@ "primary": false, "nullable": false, "mappedType": "uuid" + }, + "last_modified": { + "name": "last_modified", + "type": "timestamptz", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "length": 6, + "mappedType": "datetime" + }, + "modified_by_id": { + "name": "modified_by_id", + "type": "uuid", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "default": "'ac594919-50e3-438a-b9bc-efb8a8654243'", + "mappedType": "uuid" } }, "name": "justification", @@ -851,6 +1063,18 @@ ], "referencedTableName": "public.solution", "updateRule": "cascade" + }, + "justification_modified_by_id_foreign": { + "constraintName": "justification_modified_by_id_foreign", + "columnNames": [ + "modified_by_id" + ], + "localTableName": "public.justification", + "referencedColumnNames": [ + "id" + ], + "referencedTableName": "public.app_user", + "updateRule": "cascade" } }, "nativeEnums": {} @@ -894,6 +1118,26 @@ "primary": false, "nullable": false, "mappedType": "uuid" + }, + "last_modified": { + "name": "last_modified", + "type": "timestamptz", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "length": 6, + "mappedType": "datetime" + }, + "modified_by_id": { + "name": "modified_by_id", + "type": "uuid", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "default": "'ac594919-50e3-438a-b9bc-efb8a8654243'", + "mappedType": "uuid" } }, "name": "invariant", @@ -923,6 +1167,18 @@ ], "referencedTableName": "public.solution", "updateRule": "cascade" + }, + "invariant_modified_by_id_foreign": { + "constraintName": "invariant_modified_by_id_foreign", + "columnNames": [ + "modified_by_id" + ], + "localTableName": "public.invariant", + "referencedColumnNames": [ + "id" + ], + "referencedTableName": "public.app_user", + "updateRule": "cascade" } }, "nativeEnums": {} @@ -966,6 +1222,26 @@ "primary": false, "nullable": false, "mappedType": "uuid" + }, + "last_modified": { + "name": "last_modified", + "type": "timestamptz", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "length": 6, + "mappedType": "datetime" + }, + "modified_by_id": { + "name": "modified_by_id", + "type": "uuid", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "default": "'ac594919-50e3-438a-b9bc-efb8a8654243'", + "mappedType": "uuid" } }, "name": "hint", @@ -995,6 +1271,18 @@ ], "referencedTableName": "public.solution", "updateRule": "cascade" + }, + "hint_modified_by_id_foreign": { + "constraintName": "hint_modified_by_id_foreign", + "columnNames": [ + "modified_by_id" + ], + "localTableName": "public.hint", + "referencedColumnNames": [ + "id" + ], + "referencedTableName": "public.app_user", + "updateRule": "cascade" } }, "nativeEnums": {} @@ -1039,6 +1327,26 @@ "nullable": false, "mappedType": "uuid" }, + "last_modified": { + "name": "last_modified", + "type": "timestamptz", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "length": 6, + "mappedType": "datetime" + }, + "modified_by_id": { + "name": "modified_by_id", + "type": "uuid", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "default": "'ac594919-50e3-438a-b9bc-efb8a8654243'", + "mappedType": "uuid" + }, "parent_component_id": { "name": "parent_component_id", "type": "uuid", @@ -1077,6 +1385,18 @@ "referencedTableName": "public.solution", "updateRule": "cascade" }, + "glossary_term_modified_by_id_foreign": { + "constraintName": "glossary_term_modified_by_id_foreign", + "columnNames": [ + "modified_by_id" + ], + "localTableName": "public.glossary_term", + "referencedColumnNames": [ + "id" + ], + "referencedTableName": "public.app_user", + "updateRule": "cascade" + }, "glossary_term_parent_component_id_foreign": { "constraintName": "glossary_term_parent_component_id_foreign", "columnNames": [ @@ -1133,6 +1453,26 @@ "nullable": false, "mappedType": "uuid" }, + "last_modified": { + "name": "last_modified", + "type": "timestamptz", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "length": 6, + "mappedType": "datetime" + }, + "modified_by_id": { + "name": "modified_by_id", + "type": "uuid", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "default": "'ac594919-50e3-438a-b9bc-efb8a8654243'", + "mappedType": "uuid" + }, "priority": { "name": "priority", "type": "text", @@ -1176,6 +1516,18 @@ ], "referencedTableName": "public.solution", "updateRule": "cascade" + }, + "functional_behavior_modified_by_id_foreign": { + "constraintName": "functional_behavior_modified_by_id_foreign", + "columnNames": [ + "modified_by_id" + ], + "localTableName": "public.functional_behavior", + "referencedColumnNames": [ + "id" + ], + "referencedTableName": "public.app_user", + "updateRule": "cascade" } }, "nativeEnums": {} @@ -1220,6 +1572,26 @@ "nullable": false, "mappedType": "uuid" }, + "last_modified": { + "name": "last_modified", + "type": "timestamptz", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "length": 6, + "mappedType": "datetime" + }, + "modified_by_id": { + "name": "modified_by_id", + "type": "uuid", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "default": "'ac594919-50e3-438a-b9bc-efb8a8654243'", + "mappedType": "uuid" + }, "parent_component_id": { "name": "parent_component_id", "type": "uuid", @@ -1258,6 +1630,18 @@ "referencedTableName": "public.solution", "updateRule": "cascade" }, + "environment_component_modified_by_id_foreign": { + "constraintName": "environment_component_modified_by_id_foreign", + "columnNames": [ + "modified_by_id" + ], + "localTableName": "public.environment_component", + "referencedColumnNames": [ + "id" + ], + "referencedTableName": "public.app_user", + "updateRule": "cascade" + }, "environment_component_parent_component_id_foreign": { "constraintName": "environment_component_parent_component_id_foreign", "columnNames": [ @@ -1313,6 +1697,26 @@ "primary": false, "nullable": false, "mappedType": "uuid" + }, + "last_modified": { + "name": "last_modified", + "type": "timestamptz", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "length": 6, + "mappedType": "datetime" + }, + "modified_by_id": { + "name": "modified_by_id", + "type": "uuid", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "default": "'ac594919-50e3-438a-b9bc-efb8a8654243'", + "mappedType": "uuid" } }, "name": "effect", @@ -1342,6 +1746,18 @@ ], "referencedTableName": "public.solution", "updateRule": "cascade" + }, + "effect_modified_by_id_foreign": { + "constraintName": "effect_modified_by_id_foreign", + "columnNames": [ + "modified_by_id" + ], + "localTableName": "public.effect", + "referencedColumnNames": [ + "id" + ], + "referencedTableName": "public.app_user", + "updateRule": "cascade" } }, "nativeEnums": {} @@ -1386,6 +1802,26 @@ "nullable": false, "mappedType": "uuid" }, + "last_modified": { + "name": "last_modified", + "type": "timestamptz", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "length": 6, + "mappedType": "datetime" + }, + "modified_by_id": { + "name": "modified_by_id", + "type": "uuid", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "default": "'ac594919-50e3-438a-b9bc-efb8a8654243'", + "mappedType": "uuid" + }, "category": { "name": "category", "type": "text", @@ -1428,6 +1864,18 @@ ], "referencedTableName": "public.solution", "updateRule": "cascade" + }, + "constraint_modified_by_id_foreign": { + "constraintName": "constraint_modified_by_id_foreign", + "columnNames": [ + "modified_by_id" + ], + "localTableName": "public.constraint", + "referencedColumnNames": [ + "id" + ], + "referencedTableName": "public.app_user", + "updateRule": "cascade" } }, "nativeEnums": {} @@ -1471,6 +1919,26 @@ "primary": false, "nullable": false, "mappedType": "uuid" + }, + "last_modified": { + "name": "last_modified", + "type": "timestamptz", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "length": 6, + "mappedType": "datetime" + }, + "modified_by_id": { + "name": "modified_by_id", + "type": "uuid", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "default": "'ac594919-50e3-438a-b9bc-efb8a8654243'", + "mappedType": "uuid" } }, "name": "assumption", @@ -1500,6 +1968,18 @@ ], "referencedTableName": "public.solution", "updateRule": "cascade" + }, + "assumption_modified_by_id_foreign": { + "constraintName": "assumption_modified_by_id_foreign", + "columnNames": [ + "modified_by_id" + ], + "localTableName": "public.assumption", + "referencedColumnNames": [ + "id" + ], + "referencedTableName": "public.app_user", + "updateRule": "cascade" } }, "nativeEnums": {} @@ -1544,6 +2024,26 @@ "nullable": false, "mappedType": "uuid" }, + "last_modified": { + "name": "last_modified", + "type": "timestamptz", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "length": 6, + "mappedType": "datetime" + }, + "modified_by_id": { + "name": "modified_by_id", + "type": "uuid", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "default": "'ac594919-50e3-438a-b9bc-efb8a8654243'", + "mappedType": "uuid" + }, "influence": { "name": "influence", "type": "int", @@ -1628,6 +2128,18 @@ "referencedTableName": "public.solution", "updateRule": "cascade" }, + "stakeholder_modified_by_id_foreign": { + "constraintName": "stakeholder_modified_by_id_foreign", + "columnNames": [ + "modified_by_id" + ], + "localTableName": "public.stakeholder", + "referencedColumnNames": [ + "id" + ], + "referencedTableName": "public.app_user", + "updateRule": "cascade" + }, "stakeholder_parent_component_id_foreign": { "constraintName": "stakeholder_parent_component_id_foreign", "columnNames": [ @@ -1684,6 +2196,26 @@ "nullable": false, "mappedType": "uuid" }, + "last_modified": { + "name": "last_modified", + "type": "timestamptz", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "length": 6, + "mappedType": "datetime" + }, + "modified_by_id": { + "name": "modified_by_id", + "type": "uuid", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "default": "'ac594919-50e3-438a-b9bc-efb8a8654243'", + "mappedType": "uuid" + }, "parent_component_id": { "name": "parent_component_id", "type": "uuid", @@ -1722,6 +2254,18 @@ "referencedTableName": "public.solution", "updateRule": "cascade" }, + "system_component_modified_by_id_foreign": { + "constraintName": "system_component_modified_by_id_foreign", + "columnNames": [ + "modified_by_id" + ], + "localTableName": "public.system_component", + "referencedColumnNames": [ + "id" + ], + "referencedTableName": "public.app_user", + "updateRule": "cascade" + }, "system_component_parent_component_id_foreign": { "constraintName": "system_component_parent_component_id_foreign", "columnNames": [ @@ -1778,6 +2322,26 @@ "nullable": false, "mappedType": "uuid" }, + "last_modified": { + "name": "last_modified", + "type": "timestamptz", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "length": 6, + "mappedType": "datetime" + }, + "modified_by_id": { + "name": "modified_by_id", + "type": "uuid", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "default": "'ac594919-50e3-438a-b9bc-efb8a8654243'", + "mappedType": "uuid" + }, "priority": { "name": "priority", "type": "text", @@ -1908,6 +2472,18 @@ "referencedTableName": "public.solution", "updateRule": "cascade" }, + "use_case_modified_by_id_foreign": { + "constraintName": "use_case_modified_by_id_foreign", + "columnNames": [ + "modified_by_id" + ], + "localTableName": "public.use_case", + "referencedColumnNames": [ + "id" + ], + "referencedTableName": "public.app_user", + "updateRule": "cascade" + }, "use_case_primary_actor_id_foreign": { "constraintName": "use_case_primary_actor_id_foreign", "columnNames": [ @@ -1987,6 +2563,26 @@ "nullable": false, "mappedType": "uuid" }, + "last_modified": { + "name": "last_modified", + "type": "timestamptz", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "length": 6, + "mappedType": "datetime" + }, + "modified_by_id": { + "name": "modified_by_id", + "type": "uuid", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "default": "'ac594919-50e3-438a-b9bc-efb8a8654243'", + "mappedType": "uuid" + }, "priority": { "name": "priority", "type": "text", @@ -2058,6 +2654,18 @@ "referencedTableName": "public.solution", "updateRule": "cascade" }, + "user_story_modified_by_id_foreign": { + "constraintName": "user_story_modified_by_id_foreign", + "columnNames": [ + "modified_by_id" + ], + "localTableName": "public.user_story", + "referencedColumnNames": [ + "id" + ], + "referencedTableName": "public.app_user", + "updateRule": "cascade" + }, "user_story_primary_actor_id_foreign": { "constraintName": "user_story_primary_actor_id_foreign", "columnNames": [ diff --git a/migrations/Migration20240903022539.ts b/migrations/Migration20240903022539.ts new file mode 100644 index 00000000..ad676d1c --- /dev/null +++ b/migrations/Migration20240903022539.ts @@ -0,0 +1,142 @@ +import { Migration } from '@mikro-orm/migrations'; + +export class Migration20240903022539 extends Migration { + + override async up(): Promise { + this.addSql('alter table "product" add column "last_modified" timestamptz not null, add column "modified_by_id" uuid not null default \'ac594919-50e3-438a-b9bc-efb8a8654243\';'); + this.addSql('alter table "product" add constraint "product_modified_by_id_foreign" foreign key ("modified_by_id") references "app_user" ("id") on update cascade;'); + + this.addSql('alter table "person" add column "last_modified" timestamptz not null, add column "modified_by_id" uuid not null default \'ac594919-50e3-438a-b9bc-efb8a8654243\';'); + this.addSql('alter table "person" add constraint "person_modified_by_id_foreign" foreign key ("modified_by_id") references "app_user" ("id") on update cascade;'); + + this.addSql('alter table "outcome" add column "last_modified" timestamptz not null, add column "modified_by_id" uuid not null default \'ac594919-50e3-438a-b9bc-efb8a8654243\';'); + this.addSql('alter table "outcome" add constraint "outcome_modified_by_id_foreign" foreign key ("modified_by_id") references "app_user" ("id") on update cascade;'); + + this.addSql('alter table "obstacle" add column "last_modified" timestamptz not null, add column "modified_by_id" uuid not null default \'ac594919-50e3-438a-b9bc-efb8a8654243\';'); + this.addSql('alter table "obstacle" add constraint "obstacle_modified_by_id_foreign" foreign key ("modified_by_id") references "app_user" ("id") on update cascade;'); + + this.addSql('alter table "non_functional_behavior" add column "last_modified" timestamptz not null, add column "modified_by_id" uuid not null default \'ac594919-50e3-438a-b9bc-efb8a8654243\';'); + this.addSql('alter table "non_functional_behavior" add constraint "non_functional_behavior_modified_by_id_foreign" foreign key ("modified_by_id") references "app_user" ("id") on update cascade;'); + + this.addSql('alter table "limit" add column "last_modified" timestamptz not null, add column "modified_by_id" uuid not null default \'ac594919-50e3-438a-b9bc-efb8a8654243\';'); + this.addSql('alter table "limit" add constraint "limit_modified_by_id_foreign" foreign key ("modified_by_id") references "app_user" ("id") on update cascade;'); + + this.addSql('alter table "justification" add column "last_modified" timestamptz not null, add column "modified_by_id" uuid not null default \'ac594919-50e3-438a-b9bc-efb8a8654243\';'); + this.addSql('alter table "justification" add constraint "justification_modified_by_id_foreign" foreign key ("modified_by_id") references "app_user" ("id") on update cascade;'); + + this.addSql('alter table "invariant" add column "last_modified" timestamptz not null, add column "modified_by_id" uuid not null default \'ac594919-50e3-438a-b9bc-efb8a8654243\';'); + this.addSql('alter table "invariant" add constraint "invariant_modified_by_id_foreign" foreign key ("modified_by_id") references "app_user" ("id") on update cascade;'); + + this.addSql('alter table "hint" add column "last_modified" timestamptz not null, add column "modified_by_id" uuid not null default \'ac594919-50e3-438a-b9bc-efb8a8654243\';'); + this.addSql('alter table "hint" add constraint "hint_modified_by_id_foreign" foreign key ("modified_by_id") references "app_user" ("id") on update cascade;'); + + this.addSql('alter table "glossary_term" add column "last_modified" timestamptz not null, add column "modified_by_id" uuid not null default \'ac594919-50e3-438a-b9bc-efb8a8654243\';'); + this.addSql('alter table "glossary_term" add constraint "glossary_term_modified_by_id_foreign" foreign key ("modified_by_id") references "app_user" ("id") on update cascade;'); + + this.addSql('alter table "functional_behavior" add column "last_modified" timestamptz not null, add column "modified_by_id" uuid not null default \'ac594919-50e3-438a-b9bc-efb8a8654243\';'); + this.addSql('alter table "functional_behavior" add constraint "functional_behavior_modified_by_id_foreign" foreign key ("modified_by_id") references "app_user" ("id") on update cascade;'); + + this.addSql('alter table "environment_component" add column "last_modified" timestamptz not null, add column "modified_by_id" uuid not null default \'ac594919-50e3-438a-b9bc-efb8a8654243\';'); + this.addSql('alter table "environment_component" add constraint "environment_component_modified_by_id_foreign" foreign key ("modified_by_id") references "app_user" ("id") on update cascade;'); + + this.addSql('alter table "effect" add column "last_modified" timestamptz not null, add column "modified_by_id" uuid not null default \'ac594919-50e3-438a-b9bc-efb8a8654243\';'); + this.addSql('alter table "effect" add constraint "effect_modified_by_id_foreign" foreign key ("modified_by_id") references "app_user" ("id") on update cascade;'); + + this.addSql('alter table "constraint" add column "last_modified" timestamptz not null, add column "modified_by_id" uuid not null default \'ac594919-50e3-438a-b9bc-efb8a8654243\';'); + this.addSql('alter table "constraint" add constraint "constraint_modified_by_id_foreign" foreign key ("modified_by_id") references "app_user" ("id") on update cascade;'); + + this.addSql('alter table "assumption" add column "last_modified" timestamptz not null, add column "modified_by_id" uuid not null default \'ac594919-50e3-438a-b9bc-efb8a8654243\';'); + this.addSql('alter table "assumption" add constraint "assumption_modified_by_id_foreign" foreign key ("modified_by_id") references "app_user" ("id") on update cascade;'); + + this.addSql('alter table "stakeholder" add column "last_modified" timestamptz not null, add column "modified_by_id" uuid not null default \'ac594919-50e3-438a-b9bc-efb8a8654243\';'); + this.addSql('alter table "stakeholder" add constraint "stakeholder_modified_by_id_foreign" foreign key ("modified_by_id") references "app_user" ("id") on update cascade;'); + + this.addSql('alter table "system_component" add column "last_modified" timestamptz not null, add column "modified_by_id" uuid not null default \'ac594919-50e3-438a-b9bc-efb8a8654243\';'); + this.addSql('alter table "system_component" add constraint "system_component_modified_by_id_foreign" foreign key ("modified_by_id") references "app_user" ("id") on update cascade;'); + + this.addSql('alter table "use_case" add column "last_modified" timestamptz not null, add column "modified_by_id" uuid not null default \'ac594919-50e3-438a-b9bc-efb8a8654243\';'); + this.addSql('alter table "use_case" add constraint "use_case_modified_by_id_foreign" foreign key ("modified_by_id") references "app_user" ("id") on update cascade;'); + + this.addSql('alter table "user_story" add column "last_modified" timestamptz not null, add column "modified_by_id" uuid not null default \'ac594919-50e3-438a-b9bc-efb8a8654243\';'); + this.addSql('alter table "user_story" add constraint "user_story_modified_by_id_foreign" foreign key ("modified_by_id") references "app_user" ("id") on update cascade;'); + } + + override async down(): Promise { + this.addSql('alter table "product" drop constraint "product_modified_by_id_foreign";'); + + this.addSql('alter table "person" drop constraint "person_modified_by_id_foreign";'); + + this.addSql('alter table "outcome" drop constraint "outcome_modified_by_id_foreign";'); + + this.addSql('alter table "obstacle" drop constraint "obstacle_modified_by_id_foreign";'); + + this.addSql('alter table "non_functional_behavior" drop constraint "non_functional_behavior_modified_by_id_foreign";'); + + this.addSql('alter table "limit" drop constraint "limit_modified_by_id_foreign";'); + + this.addSql('alter table "justification" drop constraint "justification_modified_by_id_foreign";'); + + this.addSql('alter table "invariant" drop constraint "invariant_modified_by_id_foreign";'); + + this.addSql('alter table "hint" drop constraint "hint_modified_by_id_foreign";'); + + this.addSql('alter table "glossary_term" drop constraint "glossary_term_modified_by_id_foreign";'); + + this.addSql('alter table "functional_behavior" drop constraint "functional_behavior_modified_by_id_foreign";'); + + this.addSql('alter table "environment_component" drop constraint "environment_component_modified_by_id_foreign";'); + + this.addSql('alter table "effect" drop constraint "effect_modified_by_id_foreign";'); + + this.addSql('alter table "constraint" drop constraint "constraint_modified_by_id_foreign";'); + + this.addSql('alter table "assumption" drop constraint "assumption_modified_by_id_foreign";'); + + this.addSql('alter table "stakeholder" drop constraint "stakeholder_modified_by_id_foreign";'); + + this.addSql('alter table "system_component" drop constraint "system_component_modified_by_id_foreign";'); + + this.addSql('alter table "use_case" drop constraint "use_case_modified_by_id_foreign";'); + + this.addSql('alter table "user_story" drop constraint "user_story_modified_by_id_foreign";'); + + this.addSql('alter table "product" drop column "last_modified", drop column "modified_by_id";'); + + this.addSql('alter table "person" drop column "last_modified", drop column "modified_by_id";'); + + this.addSql('alter table "outcome" drop column "last_modified", drop column "modified_by_id";'); + + this.addSql('alter table "obstacle" drop column "last_modified", drop column "modified_by_id";'); + + this.addSql('alter table "non_functional_behavior" drop column "last_modified", drop column "modified_by_id";'); + + this.addSql('alter table "limit" drop column "last_modified", drop column "modified_by_id";'); + + this.addSql('alter table "justification" drop column "last_modified", drop column "modified_by_id";'); + + this.addSql('alter table "invariant" drop column "last_modified", drop column "modified_by_id";'); + + this.addSql('alter table "hint" drop column "last_modified", drop column "modified_by_id";'); + + this.addSql('alter table "glossary_term" drop column "last_modified", drop column "modified_by_id";'); + + this.addSql('alter table "functional_behavior" drop column "last_modified", drop column "modified_by_id";'); + + this.addSql('alter table "environment_component" drop column "last_modified", drop column "modified_by_id";'); + + this.addSql('alter table "effect" drop column "last_modified", drop column "modified_by_id";'); + + this.addSql('alter table "constraint" drop column "last_modified", drop column "modified_by_id";'); + + this.addSql('alter table "assumption" drop column "last_modified", drop column "modified_by_id";'); + + this.addSql('alter table "stakeholder" drop column "last_modified", drop column "modified_by_id";'); + + this.addSql('alter table "system_component" drop column "last_modified", drop column "modified_by_id";'); + + this.addSql('alter table "use_case" drop column "last_modified", drop column "modified_by_id";'); + + this.addSql('alter table "user_story" drop column "last_modified", drop column "modified_by_id";'); + } + +} diff --git a/migrations/Migration20240904172417.ts b/migrations/Migration20240904172417.ts new file mode 100644 index 00000000..1cb2c770 --- /dev/null +++ b/migrations/Migration20240904172417.ts @@ -0,0 +1,33 @@ +import { Migration } from '@mikro-orm/migrations'; + +export class Migration20240904172417 extends Migration { + + override async up(): Promise { + this.addSql('alter table "app_user_organization_role" drop constraint "app_user_organization_role_app_user_id_foreign";'); + this.addSql('alter table "app_user_organization_role" drop constraint "app_user_organization_role_organization_id_foreign";'); + + this.addSql('alter table "app_user_organization_role" alter column "app_user_id" drop default;'); + this.addSql('alter table "app_user_organization_role" alter column "app_user_id" type uuid using ("app_user_id"::text::uuid);'); + this.addSql('alter table "app_user_organization_role" alter column "app_user_id" set not null;'); + this.addSql('alter table "app_user_organization_role" alter column "organization_id" drop default;'); + this.addSql('alter table "app_user_organization_role" alter column "organization_id" type uuid using ("organization_id"::text::uuid);'); + this.addSql('alter table "app_user_organization_role" alter column "organization_id" set not null;'); + this.addSql('alter table "app_user_organization_role" add constraint "app_user_organization_role_app_user_id_foreign" foreign key ("app_user_id") references "app_user" ("id") on update cascade;'); + this.addSql('alter table "app_user_organization_role" add constraint "app_user_organization_role_organization_id_foreign" foreign key ("organization_id") references "organization" ("id") on update cascade;'); + } + + override async down(): Promise { + this.addSql('alter table "app_user_organization_role" drop constraint "app_user_organization_role_app_user_id_foreign";'); + this.addSql('alter table "app_user_organization_role" drop constraint "app_user_organization_role_organization_id_foreign";'); + + this.addSql('alter table "app_user_organization_role" alter column "app_user_id" drop default;'); + this.addSql('alter table "app_user_organization_role" alter column "app_user_id" type uuid using ("app_user_id"::text::uuid);'); + this.addSql('alter table "app_user_organization_role" alter column "app_user_id" drop not null;'); + this.addSql('alter table "app_user_organization_role" alter column "organization_id" drop default;'); + this.addSql('alter table "app_user_organization_role" alter column "organization_id" type uuid using ("organization_id"::text::uuid);'); + this.addSql('alter table "app_user_organization_role" alter column "organization_id" drop not null;'); + this.addSql('alter table "app_user_organization_role" add constraint "app_user_organization_role_app_user_id_foreign" foreign key ("app_user_id") references "app_user" ("id") on delete cascade;'); + this.addSql('alter table "app_user_organization_role" add constraint "app_user_organization_role_organization_id_foreign" foreign key ("organization_id") references "organization" ("id") on delete cascade;'); + } + +} diff --git a/migrations/Migration20240904183539.ts b/migrations/Migration20240904183539.ts new file mode 100644 index 00000000..398585ee --- /dev/null +++ b/migrations/Migration20240904183539.ts @@ -0,0 +1,311 @@ +import { Migration } from '@mikro-orm/migrations'; + +export class Migration20240904183539 extends Migration { + + override async up(): Promise { + this.addSql('alter table "app_user_organization_role" drop constraint "app_user_organization_role_app_user_id_foreign";'); + this.addSql('alter table "app_user_organization_role" drop constraint "app_user_organization_role_organization_id_foreign";'); + + this.addSql('alter table "solution" drop constraint "solution_organization_id_foreign";'); + + this.addSql('alter table "product" drop constraint "product_solution_id_foreign";'); + + this.addSql('alter table "person" drop constraint "person_solution_id_foreign";'); + + this.addSql('alter table "outcome" drop constraint "outcome_solution_id_foreign";'); + + this.addSql('alter table "obstacle" drop constraint "obstacle_solution_id_foreign";'); + + this.addSql('alter table "non_functional_behavior" drop constraint "non_functional_behavior_solution_id_foreign";'); + + this.addSql('alter table "limit" drop constraint "limit_solution_id_foreign";'); + + this.addSql('alter table "justification" drop constraint "justification_solution_id_foreign";'); + + this.addSql('alter table "invariant" drop constraint "invariant_solution_id_foreign";'); + + this.addSql('alter table "hint" drop constraint "hint_solution_id_foreign";'); + + this.addSql('alter table "glossary_term" drop constraint "glossary_term_solution_id_foreign";'); + + this.addSql('alter table "functional_behavior" drop constraint "functional_behavior_solution_id_foreign";'); + + this.addSql('alter table "environment_component" drop constraint "environment_component_solution_id_foreign";'); + + this.addSql('alter table "effect" drop constraint "effect_solution_id_foreign";'); + + this.addSql('alter table "constraint" drop constraint "constraint_solution_id_foreign";'); + + this.addSql('alter table "assumption" drop constraint "assumption_solution_id_foreign";'); + + this.addSql('alter table "stakeholder" drop constraint "stakeholder_solution_id_foreign";'); + + this.addSql('alter table "system_component" drop constraint "system_component_solution_id_foreign";'); + + this.addSql('alter table "use_case" drop constraint "use_case_solution_id_foreign";'); + + this.addSql('alter table "user_story" drop constraint "user_story_solution_id_foreign";'); + + this.addSql('alter table "app_user_organization_role" alter column "app_user_id" drop default;'); + this.addSql('alter table "app_user_organization_role" alter column "app_user_id" type uuid using ("app_user_id"::text::uuid);'); + this.addSql('alter table "app_user_organization_role" alter column "organization_id" drop default;'); + this.addSql('alter table "app_user_organization_role" alter column "organization_id" type uuid using ("organization_id"::text::uuid);'); + this.addSql('alter table "app_user_organization_role" add constraint "app_user_organization_role_app_user_id_foreign" foreign key ("app_user_id") references "app_user" ("id") on delete cascade;'); + this.addSql('alter table "app_user_organization_role" add constraint "app_user_organization_role_organization_id_foreign" foreign key ("organization_id") references "organization" ("id") on delete cascade;'); + + this.addSql('alter table "solution" alter column "organization_id" drop default;'); + this.addSql('alter table "solution" alter column "organization_id" type uuid using ("organization_id"::text::uuid);'); + this.addSql('alter table "solution" alter column "organization_id" drop not null;'); + this.addSql('alter table "solution" add constraint "solution_organization_id_foreign" foreign key ("organization_id") references "organization" ("id") on delete cascade;'); + + this.addSql('alter table "product" alter column "solution_id" drop default;'); + this.addSql('alter table "product" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "product" alter column "solution_id" drop not null;'); + this.addSql('alter table "product" add constraint "product_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on delete cascade;'); + + this.addSql('alter table "person" alter column "solution_id" drop default;'); + this.addSql('alter table "person" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "person" alter column "solution_id" drop not null;'); + this.addSql('alter table "person" add constraint "person_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on delete cascade;'); + + this.addSql('alter table "outcome" alter column "solution_id" drop default;'); + this.addSql('alter table "outcome" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "outcome" alter column "solution_id" drop not null;'); + this.addSql('alter table "outcome" add constraint "outcome_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on delete cascade;'); + + this.addSql('alter table "obstacle" alter column "solution_id" drop default;'); + this.addSql('alter table "obstacle" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "obstacle" alter column "solution_id" drop not null;'); + this.addSql('alter table "obstacle" add constraint "obstacle_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on delete cascade;'); + + this.addSql('alter table "non_functional_behavior" alter column "solution_id" drop default;'); + this.addSql('alter table "non_functional_behavior" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "non_functional_behavior" alter column "solution_id" drop not null;'); + this.addSql('alter table "non_functional_behavior" add constraint "non_functional_behavior_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on delete cascade;'); + + this.addSql('alter table "limit" alter column "solution_id" drop default;'); + this.addSql('alter table "limit" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "limit" alter column "solution_id" drop not null;'); + this.addSql('alter table "limit" add constraint "limit_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on delete cascade;'); + + this.addSql('alter table "justification" alter column "solution_id" drop default;'); + this.addSql('alter table "justification" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "justification" alter column "solution_id" drop not null;'); + this.addSql('alter table "justification" add constraint "justification_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on delete cascade;'); + + this.addSql('alter table "invariant" alter column "solution_id" drop default;'); + this.addSql('alter table "invariant" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "invariant" alter column "solution_id" drop not null;'); + this.addSql('alter table "invariant" add constraint "invariant_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on delete cascade;'); + + this.addSql('alter table "hint" alter column "solution_id" drop default;'); + this.addSql('alter table "hint" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "hint" alter column "solution_id" drop not null;'); + this.addSql('alter table "hint" add constraint "hint_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on delete cascade;'); + + this.addSql('alter table "glossary_term" alter column "solution_id" drop default;'); + this.addSql('alter table "glossary_term" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "glossary_term" alter column "solution_id" drop not null;'); + this.addSql('alter table "glossary_term" add constraint "glossary_term_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on delete cascade;'); + + this.addSql('alter table "functional_behavior" alter column "solution_id" drop default;'); + this.addSql('alter table "functional_behavior" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "functional_behavior" alter column "solution_id" drop not null;'); + this.addSql('alter table "functional_behavior" add constraint "functional_behavior_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on delete cascade;'); + + this.addSql('alter table "environment_component" alter column "solution_id" drop default;'); + this.addSql('alter table "environment_component" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "environment_component" alter column "solution_id" drop not null;'); + this.addSql('alter table "environment_component" add constraint "environment_component_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on delete cascade;'); + + this.addSql('alter table "effect" alter column "solution_id" drop default;'); + this.addSql('alter table "effect" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "effect" alter column "solution_id" drop not null;'); + this.addSql('alter table "effect" add constraint "effect_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on delete cascade;'); + + this.addSql('alter table "constraint" alter column "solution_id" drop default;'); + this.addSql('alter table "constraint" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "constraint" alter column "solution_id" drop not null;'); + this.addSql('alter table "constraint" add constraint "constraint_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on delete cascade;'); + + this.addSql('alter table "assumption" alter column "solution_id" drop default;'); + this.addSql('alter table "assumption" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "assumption" alter column "solution_id" drop not null;'); + this.addSql('alter table "assumption" add constraint "assumption_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on delete cascade;'); + + this.addSql('alter table "stakeholder" alter column "solution_id" drop default;'); + this.addSql('alter table "stakeholder" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "stakeholder" alter column "solution_id" drop not null;'); + this.addSql('alter table "stakeholder" add constraint "stakeholder_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on delete cascade;'); + + this.addSql('alter table "system_component" alter column "solution_id" drop default;'); + this.addSql('alter table "system_component" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "system_component" alter column "solution_id" drop not null;'); + this.addSql('alter table "system_component" add constraint "system_component_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on delete cascade;'); + + this.addSql('alter table "use_case" alter column "solution_id" drop default;'); + this.addSql('alter table "use_case" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "use_case" alter column "solution_id" drop not null;'); + this.addSql('alter table "use_case" add constraint "use_case_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on delete cascade;'); + + this.addSql('alter table "user_story" alter column "solution_id" drop default;'); + this.addSql('alter table "user_story" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "user_story" alter column "solution_id" drop not null;'); + this.addSql('alter table "user_story" add constraint "user_story_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on delete cascade;'); + } + + override async down(): Promise { + this.addSql('alter table "app_user_organization_role" drop constraint "app_user_organization_role_app_user_id_foreign";'); + this.addSql('alter table "app_user_organization_role" drop constraint "app_user_organization_role_organization_id_foreign";'); + + this.addSql('alter table "solution" drop constraint "solution_organization_id_foreign";'); + + this.addSql('alter table "product" drop constraint "product_solution_id_foreign";'); + + this.addSql('alter table "person" drop constraint "person_solution_id_foreign";'); + + this.addSql('alter table "outcome" drop constraint "outcome_solution_id_foreign";'); + + this.addSql('alter table "obstacle" drop constraint "obstacle_solution_id_foreign";'); + + this.addSql('alter table "non_functional_behavior" drop constraint "non_functional_behavior_solution_id_foreign";'); + + this.addSql('alter table "limit" drop constraint "limit_solution_id_foreign";'); + + this.addSql('alter table "justification" drop constraint "justification_solution_id_foreign";'); + + this.addSql('alter table "invariant" drop constraint "invariant_solution_id_foreign";'); + + this.addSql('alter table "hint" drop constraint "hint_solution_id_foreign";'); + + this.addSql('alter table "glossary_term" drop constraint "glossary_term_solution_id_foreign";'); + + this.addSql('alter table "functional_behavior" drop constraint "functional_behavior_solution_id_foreign";'); + + this.addSql('alter table "environment_component" drop constraint "environment_component_solution_id_foreign";'); + + this.addSql('alter table "effect" drop constraint "effect_solution_id_foreign";'); + + this.addSql('alter table "constraint" drop constraint "constraint_solution_id_foreign";'); + + this.addSql('alter table "assumption" drop constraint "assumption_solution_id_foreign";'); + + this.addSql('alter table "stakeholder" drop constraint "stakeholder_solution_id_foreign";'); + + this.addSql('alter table "system_component" drop constraint "system_component_solution_id_foreign";'); + + this.addSql('alter table "use_case" drop constraint "use_case_solution_id_foreign";'); + + this.addSql('alter table "user_story" drop constraint "user_story_solution_id_foreign";'); + + this.addSql('alter table "app_user_organization_role" alter column "app_user_id" drop default;'); + this.addSql('alter table "app_user_organization_role" alter column "app_user_id" type uuid using ("app_user_id"::text::uuid);'); + this.addSql('alter table "app_user_organization_role" alter column "app_user_id" set not null;'); + this.addSql('alter table "app_user_organization_role" alter column "organization_id" drop default;'); + this.addSql('alter table "app_user_organization_role" alter column "organization_id" type uuid using ("organization_id"::text::uuid);'); + this.addSql('alter table "app_user_organization_role" alter column "organization_id" set not null;'); + this.addSql('alter table "app_user_organization_role" add constraint "app_user_organization_role_app_user_id_foreign" foreign key ("app_user_id") references "app_user" ("id") on update cascade;'); + this.addSql('alter table "app_user_organization_role" add constraint "app_user_organization_role_organization_id_foreign" foreign key ("organization_id") references "organization" ("id") on update cascade;'); + + this.addSql('alter table "solution" alter column "organization_id" drop default;'); + this.addSql('alter table "solution" alter column "organization_id" type uuid using ("organization_id"::text::uuid);'); + this.addSql('alter table "solution" alter column "organization_id" set not null;'); + this.addSql('alter table "solution" add constraint "solution_organization_id_foreign" foreign key ("organization_id") references "organization" ("id") on update cascade;'); + + this.addSql('alter table "product" alter column "solution_id" drop default;'); + this.addSql('alter table "product" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "product" alter column "solution_id" set not null;'); + this.addSql('alter table "product" add constraint "product_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on update cascade;'); + + this.addSql('alter table "person" alter column "solution_id" drop default;'); + this.addSql('alter table "person" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "person" alter column "solution_id" set not null;'); + this.addSql('alter table "person" add constraint "person_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on update cascade;'); + + this.addSql('alter table "outcome" alter column "solution_id" drop default;'); + this.addSql('alter table "outcome" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "outcome" alter column "solution_id" set not null;'); + this.addSql('alter table "outcome" add constraint "outcome_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on update cascade;'); + + this.addSql('alter table "obstacle" alter column "solution_id" drop default;'); + this.addSql('alter table "obstacle" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "obstacle" alter column "solution_id" set not null;'); + this.addSql('alter table "obstacle" add constraint "obstacle_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on update cascade;'); + + this.addSql('alter table "non_functional_behavior" alter column "solution_id" drop default;'); + this.addSql('alter table "non_functional_behavior" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "non_functional_behavior" alter column "solution_id" set not null;'); + this.addSql('alter table "non_functional_behavior" add constraint "non_functional_behavior_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on update cascade;'); + + this.addSql('alter table "limit" alter column "solution_id" drop default;'); + this.addSql('alter table "limit" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "limit" alter column "solution_id" set not null;'); + this.addSql('alter table "limit" add constraint "limit_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on update cascade;'); + + this.addSql('alter table "justification" alter column "solution_id" drop default;'); + this.addSql('alter table "justification" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "justification" alter column "solution_id" set not null;'); + this.addSql('alter table "justification" add constraint "justification_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on update cascade;'); + + this.addSql('alter table "invariant" alter column "solution_id" drop default;'); + this.addSql('alter table "invariant" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "invariant" alter column "solution_id" set not null;'); + this.addSql('alter table "invariant" add constraint "invariant_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on update cascade;'); + + this.addSql('alter table "hint" alter column "solution_id" drop default;'); + this.addSql('alter table "hint" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "hint" alter column "solution_id" set not null;'); + this.addSql('alter table "hint" add constraint "hint_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on update cascade;'); + + this.addSql('alter table "glossary_term" alter column "solution_id" drop default;'); + this.addSql('alter table "glossary_term" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "glossary_term" alter column "solution_id" set not null;'); + this.addSql('alter table "glossary_term" add constraint "glossary_term_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on update cascade;'); + + this.addSql('alter table "functional_behavior" alter column "solution_id" drop default;'); + this.addSql('alter table "functional_behavior" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "functional_behavior" alter column "solution_id" set not null;'); + this.addSql('alter table "functional_behavior" add constraint "functional_behavior_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on update cascade;'); + + this.addSql('alter table "environment_component" alter column "solution_id" drop default;'); + this.addSql('alter table "environment_component" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "environment_component" alter column "solution_id" set not null;'); + this.addSql('alter table "environment_component" add constraint "environment_component_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on update cascade;'); + + this.addSql('alter table "effect" alter column "solution_id" drop default;'); + this.addSql('alter table "effect" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "effect" alter column "solution_id" set not null;'); + this.addSql('alter table "effect" add constraint "effect_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on update cascade;'); + + this.addSql('alter table "constraint" alter column "solution_id" drop default;'); + this.addSql('alter table "constraint" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "constraint" alter column "solution_id" set not null;'); + this.addSql('alter table "constraint" add constraint "constraint_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on update cascade;'); + + this.addSql('alter table "assumption" alter column "solution_id" drop default;'); + this.addSql('alter table "assumption" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "assumption" alter column "solution_id" set not null;'); + this.addSql('alter table "assumption" add constraint "assumption_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on update cascade;'); + + this.addSql('alter table "stakeholder" alter column "solution_id" drop default;'); + this.addSql('alter table "stakeholder" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "stakeholder" alter column "solution_id" set not null;'); + this.addSql('alter table "stakeholder" add constraint "stakeholder_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on update cascade;'); + + this.addSql('alter table "system_component" alter column "solution_id" drop default;'); + this.addSql('alter table "system_component" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "system_component" alter column "solution_id" set not null;'); + this.addSql('alter table "system_component" add constraint "system_component_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on update cascade;'); + + this.addSql('alter table "use_case" alter column "solution_id" drop default;'); + this.addSql('alter table "use_case" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "use_case" alter column "solution_id" set not null;'); + this.addSql('alter table "use_case" add constraint "use_case_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on update cascade;'); + + this.addSql('alter table "user_story" alter column "solution_id" drop default;'); + this.addSql('alter table "user_story" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "user_story" alter column "solution_id" set not null;'); + this.addSql('alter table "user_story" add constraint "user_story_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on update cascade;'); + } + +} diff --git a/migrations/Migration20240905000351.ts b/migrations/Migration20240905000351.ts new file mode 100644 index 00000000..45aa8d20 --- /dev/null +++ b/migrations/Migration20240905000351.ts @@ -0,0 +1,313 @@ +import { Migration } from '@mikro-orm/migrations'; + +export class Migration20240905000351 extends Migration { + + override async up(): Promise { + this.addSql('alter table "app_user_organization_role" drop constraint "app_user_organization_role_app_user_id_foreign";'); + this.addSql('alter table "app_user_organization_role" drop constraint "app_user_organization_role_organization_id_foreign";'); + + this.addSql('alter table "solution" drop constraint "solution_organization_id_foreign";'); + + this.addSql('alter table "product" drop constraint "product_solution_id_foreign";'); + + this.addSql('alter table "person" drop constraint "person_solution_id_foreign";'); + + this.addSql('alter table "outcome" drop constraint "outcome_solution_id_foreign";'); + + this.addSql('alter table "obstacle" drop constraint "obstacle_solution_id_foreign";'); + + this.addSql('alter table "non_functional_behavior" drop constraint "non_functional_behavior_solution_id_foreign";'); + + this.addSql('alter table "limit" drop constraint "limit_solution_id_foreign";'); + + this.addSql('alter table "justification" drop constraint "justification_solution_id_foreign";'); + + this.addSql('alter table "invariant" drop constraint "invariant_solution_id_foreign";'); + + this.addSql('alter table "hint" drop constraint "hint_solution_id_foreign";'); + + this.addSql('alter table "glossary_term" drop constraint "glossary_term_solution_id_foreign";'); + + this.addSql('alter table "functional_behavior" drop constraint "functional_behavior_solution_id_foreign";'); + + this.addSql('alter table "environment_component" drop constraint "environment_component_solution_id_foreign";'); + + this.addSql('alter table "effect" drop constraint "effect_solution_id_foreign";'); + + this.addSql('alter table "constraint" drop constraint "constraint_solution_id_foreign";'); + + this.addSql('alter table "assumption" drop constraint "assumption_solution_id_foreign";'); + + this.addSql('alter table "stakeholder" drop constraint "stakeholder_solution_id_foreign";'); + + this.addSql('alter table "system_component" drop constraint "system_component_solution_id_foreign";'); + + this.addSql('alter table "use_case" drop constraint "use_case_solution_id_foreign";'); + + this.addSql('alter table "user_story" drop constraint "user_story_solution_id_foreign";'); + + this.addSql('alter table "app_user_organization_role" alter column "app_user_id" drop default;'); + this.addSql('alter table "app_user_organization_role" alter column "app_user_id" type uuid using ("app_user_id"::text::uuid);'); + this.addSql('alter table "app_user_organization_role" alter column "app_user_id" set not null;'); + this.addSql('alter table "app_user_organization_role" alter column "organization_id" drop default;'); + this.addSql('alter table "app_user_organization_role" alter column "organization_id" type uuid using ("organization_id"::text::uuid);'); + this.addSql('alter table "app_user_organization_role" alter column "organization_id" set not null;'); + this.addSql('alter table "app_user_organization_role" add constraint "app_user_organization_role_app_user_id_foreign" foreign key ("app_user_id") references "app_user" ("id") on update cascade;'); + this.addSql('alter table "app_user_organization_role" add constraint "app_user_organization_role_organization_id_foreign" foreign key ("organization_id") references "organization" ("id") on update cascade;'); + + this.addSql('alter table "solution" alter column "organization_id" drop default;'); + this.addSql('alter table "solution" alter column "organization_id" type uuid using ("organization_id"::text::uuid);'); + this.addSql('alter table "solution" alter column "organization_id" set not null;'); + this.addSql('alter table "solution" add constraint "solution_organization_id_foreign" foreign key ("organization_id") references "organization" ("id") on update cascade;'); + + this.addSql('alter table "product" alter column "solution_id" drop default;'); + this.addSql('alter table "product" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "product" alter column "solution_id" set not null;'); + this.addSql('alter table "product" add constraint "product_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on update cascade;'); + + this.addSql('alter table "person" alter column "solution_id" drop default;'); + this.addSql('alter table "person" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "person" alter column "solution_id" set not null;'); + this.addSql('alter table "person" add constraint "person_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on update cascade;'); + + this.addSql('alter table "outcome" alter column "solution_id" drop default;'); + this.addSql('alter table "outcome" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "outcome" alter column "solution_id" set not null;'); + this.addSql('alter table "outcome" add constraint "outcome_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on update cascade;'); + + this.addSql('alter table "obstacle" alter column "solution_id" drop default;'); + this.addSql('alter table "obstacle" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "obstacle" alter column "solution_id" set not null;'); + this.addSql('alter table "obstacle" add constraint "obstacle_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on update cascade;'); + + this.addSql('alter table "non_functional_behavior" alter column "solution_id" drop default;'); + this.addSql('alter table "non_functional_behavior" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "non_functional_behavior" alter column "solution_id" set not null;'); + this.addSql('alter table "non_functional_behavior" add constraint "non_functional_behavior_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on update cascade;'); + + this.addSql('alter table "limit" alter column "solution_id" drop default;'); + this.addSql('alter table "limit" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "limit" alter column "solution_id" set not null;'); + this.addSql('alter table "limit" add constraint "limit_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on update cascade;'); + + this.addSql('alter table "justification" alter column "solution_id" drop default;'); + this.addSql('alter table "justification" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "justification" alter column "solution_id" set not null;'); + this.addSql('alter table "justification" add constraint "justification_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on update cascade;'); + + this.addSql('alter table "invariant" alter column "solution_id" drop default;'); + this.addSql('alter table "invariant" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "invariant" alter column "solution_id" set not null;'); + this.addSql('alter table "invariant" add constraint "invariant_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on update cascade;'); + + this.addSql('alter table "hint" alter column "solution_id" drop default;'); + this.addSql('alter table "hint" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "hint" alter column "solution_id" set not null;'); + this.addSql('alter table "hint" add constraint "hint_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on update cascade;'); + + this.addSql('alter table "glossary_term" alter column "solution_id" drop default;'); + this.addSql('alter table "glossary_term" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "glossary_term" alter column "solution_id" set not null;'); + this.addSql('alter table "glossary_term" add constraint "glossary_term_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on update cascade;'); + + this.addSql('alter table "functional_behavior" alter column "solution_id" drop default;'); + this.addSql('alter table "functional_behavior" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "functional_behavior" alter column "solution_id" set not null;'); + this.addSql('alter table "functional_behavior" add constraint "functional_behavior_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on update cascade;'); + + this.addSql('alter table "environment_component" alter column "solution_id" drop default;'); + this.addSql('alter table "environment_component" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "environment_component" alter column "solution_id" set not null;'); + this.addSql('alter table "environment_component" add constraint "environment_component_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on update cascade;'); + + this.addSql('alter table "effect" alter column "solution_id" drop default;'); + this.addSql('alter table "effect" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "effect" alter column "solution_id" set not null;'); + this.addSql('alter table "effect" add constraint "effect_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on update cascade;'); + + this.addSql('alter table "constraint" alter column "solution_id" drop default;'); + this.addSql('alter table "constraint" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "constraint" alter column "solution_id" set not null;'); + this.addSql('alter table "constraint" add constraint "constraint_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on update cascade;'); + + this.addSql('alter table "assumption" alter column "solution_id" drop default;'); + this.addSql('alter table "assumption" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "assumption" alter column "solution_id" set not null;'); + this.addSql('alter table "assumption" add constraint "assumption_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on update cascade;'); + + this.addSql('alter table "stakeholder" alter column "solution_id" drop default;'); + this.addSql('alter table "stakeholder" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "stakeholder" alter column "solution_id" set not null;'); + this.addSql('alter table "stakeholder" add constraint "stakeholder_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on update cascade;'); + + this.addSql('alter table "system_component" alter column "solution_id" drop default;'); + this.addSql('alter table "system_component" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "system_component" alter column "solution_id" set not null;'); + this.addSql('alter table "system_component" add constraint "system_component_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on update cascade;'); + + this.addSql('alter table "use_case" alter column "solution_id" drop default;'); + this.addSql('alter table "use_case" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "use_case" alter column "solution_id" set not null;'); + this.addSql('alter table "use_case" add constraint "use_case_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on update cascade;'); + + this.addSql('alter table "user_story" alter column "solution_id" drop default;'); + this.addSql('alter table "user_story" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "user_story" alter column "solution_id" set not null;'); + this.addSql('alter table "user_story" add constraint "user_story_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on update cascade;'); + } + + override async down(): Promise { + this.addSql('alter table "app_user_organization_role" drop constraint "app_user_organization_role_app_user_id_foreign";'); + this.addSql('alter table "app_user_organization_role" drop constraint "app_user_organization_role_organization_id_foreign";'); + + this.addSql('alter table "solution" drop constraint "solution_organization_id_foreign";'); + + this.addSql('alter table "product" drop constraint "product_solution_id_foreign";'); + + this.addSql('alter table "person" drop constraint "person_solution_id_foreign";'); + + this.addSql('alter table "outcome" drop constraint "outcome_solution_id_foreign";'); + + this.addSql('alter table "obstacle" drop constraint "obstacle_solution_id_foreign";'); + + this.addSql('alter table "non_functional_behavior" drop constraint "non_functional_behavior_solution_id_foreign";'); + + this.addSql('alter table "limit" drop constraint "limit_solution_id_foreign";'); + + this.addSql('alter table "justification" drop constraint "justification_solution_id_foreign";'); + + this.addSql('alter table "invariant" drop constraint "invariant_solution_id_foreign";'); + + this.addSql('alter table "hint" drop constraint "hint_solution_id_foreign";'); + + this.addSql('alter table "glossary_term" drop constraint "glossary_term_solution_id_foreign";'); + + this.addSql('alter table "functional_behavior" drop constraint "functional_behavior_solution_id_foreign";'); + + this.addSql('alter table "environment_component" drop constraint "environment_component_solution_id_foreign";'); + + this.addSql('alter table "effect" drop constraint "effect_solution_id_foreign";'); + + this.addSql('alter table "constraint" drop constraint "constraint_solution_id_foreign";'); + + this.addSql('alter table "assumption" drop constraint "assumption_solution_id_foreign";'); + + this.addSql('alter table "stakeholder" drop constraint "stakeholder_solution_id_foreign";'); + + this.addSql('alter table "system_component" drop constraint "system_component_solution_id_foreign";'); + + this.addSql('alter table "use_case" drop constraint "use_case_solution_id_foreign";'); + + this.addSql('alter table "user_story" drop constraint "user_story_solution_id_foreign";'); + + this.addSql('alter table "app_user_organization_role" alter column "app_user_id" drop default;'); + this.addSql('alter table "app_user_organization_role" alter column "app_user_id" type uuid using ("app_user_id"::text::uuid);'); + this.addSql('alter table "app_user_organization_role" alter column "app_user_id" drop not null;'); + this.addSql('alter table "app_user_organization_role" alter column "organization_id" drop default;'); + this.addSql('alter table "app_user_organization_role" alter column "organization_id" type uuid using ("organization_id"::text::uuid);'); + this.addSql('alter table "app_user_organization_role" alter column "organization_id" drop not null;'); + this.addSql('alter table "app_user_organization_role" add constraint "app_user_organization_role_app_user_id_foreign" foreign key ("app_user_id") references "app_user" ("id") on delete cascade;'); + this.addSql('alter table "app_user_organization_role" add constraint "app_user_organization_role_organization_id_foreign" foreign key ("organization_id") references "organization" ("id") on delete cascade;'); + + this.addSql('alter table "solution" alter column "organization_id" drop default;'); + this.addSql('alter table "solution" alter column "organization_id" type uuid using ("organization_id"::text::uuid);'); + this.addSql('alter table "solution" alter column "organization_id" drop not null;'); + this.addSql('alter table "solution" add constraint "solution_organization_id_foreign" foreign key ("organization_id") references "organization" ("id") on delete cascade;'); + + this.addSql('alter table "product" alter column "solution_id" drop default;'); + this.addSql('alter table "product" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "product" alter column "solution_id" drop not null;'); + this.addSql('alter table "product" add constraint "product_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on delete cascade;'); + + this.addSql('alter table "person" alter column "solution_id" drop default;'); + this.addSql('alter table "person" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "person" alter column "solution_id" drop not null;'); + this.addSql('alter table "person" add constraint "person_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on delete cascade;'); + + this.addSql('alter table "outcome" alter column "solution_id" drop default;'); + this.addSql('alter table "outcome" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "outcome" alter column "solution_id" drop not null;'); + this.addSql('alter table "outcome" add constraint "outcome_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on delete cascade;'); + + this.addSql('alter table "obstacle" alter column "solution_id" drop default;'); + this.addSql('alter table "obstacle" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "obstacle" alter column "solution_id" drop not null;'); + this.addSql('alter table "obstacle" add constraint "obstacle_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on delete cascade;'); + + this.addSql('alter table "non_functional_behavior" alter column "solution_id" drop default;'); + this.addSql('alter table "non_functional_behavior" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "non_functional_behavior" alter column "solution_id" drop not null;'); + this.addSql('alter table "non_functional_behavior" add constraint "non_functional_behavior_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on delete cascade;'); + + this.addSql('alter table "limit" alter column "solution_id" drop default;'); + this.addSql('alter table "limit" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "limit" alter column "solution_id" drop not null;'); + this.addSql('alter table "limit" add constraint "limit_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on delete cascade;'); + + this.addSql('alter table "justification" alter column "solution_id" drop default;'); + this.addSql('alter table "justification" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "justification" alter column "solution_id" drop not null;'); + this.addSql('alter table "justification" add constraint "justification_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on delete cascade;'); + + this.addSql('alter table "invariant" alter column "solution_id" drop default;'); + this.addSql('alter table "invariant" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "invariant" alter column "solution_id" drop not null;'); + this.addSql('alter table "invariant" add constraint "invariant_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on delete cascade;'); + + this.addSql('alter table "hint" alter column "solution_id" drop default;'); + this.addSql('alter table "hint" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "hint" alter column "solution_id" drop not null;'); + this.addSql('alter table "hint" add constraint "hint_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on delete cascade;'); + + this.addSql('alter table "glossary_term" alter column "solution_id" drop default;'); + this.addSql('alter table "glossary_term" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "glossary_term" alter column "solution_id" drop not null;'); + this.addSql('alter table "glossary_term" add constraint "glossary_term_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on delete cascade;'); + + this.addSql('alter table "functional_behavior" alter column "solution_id" drop default;'); + this.addSql('alter table "functional_behavior" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "functional_behavior" alter column "solution_id" drop not null;'); + this.addSql('alter table "functional_behavior" add constraint "functional_behavior_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on delete cascade;'); + + this.addSql('alter table "environment_component" alter column "solution_id" drop default;'); + this.addSql('alter table "environment_component" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "environment_component" alter column "solution_id" drop not null;'); + this.addSql('alter table "environment_component" add constraint "environment_component_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on delete cascade;'); + + this.addSql('alter table "effect" alter column "solution_id" drop default;'); + this.addSql('alter table "effect" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "effect" alter column "solution_id" drop not null;'); + this.addSql('alter table "effect" add constraint "effect_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on delete cascade;'); + + this.addSql('alter table "constraint" alter column "solution_id" drop default;'); + this.addSql('alter table "constraint" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "constraint" alter column "solution_id" drop not null;'); + this.addSql('alter table "constraint" add constraint "constraint_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on delete cascade;'); + + this.addSql('alter table "assumption" alter column "solution_id" drop default;'); + this.addSql('alter table "assumption" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "assumption" alter column "solution_id" drop not null;'); + this.addSql('alter table "assumption" add constraint "assumption_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on delete cascade;'); + + this.addSql('alter table "stakeholder" alter column "solution_id" drop default;'); + this.addSql('alter table "stakeholder" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "stakeholder" alter column "solution_id" drop not null;'); + this.addSql('alter table "stakeholder" add constraint "stakeholder_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on delete cascade;'); + + this.addSql('alter table "system_component" alter column "solution_id" drop default;'); + this.addSql('alter table "system_component" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "system_component" alter column "solution_id" drop not null;'); + this.addSql('alter table "system_component" add constraint "system_component_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on delete cascade;'); + + this.addSql('alter table "use_case" alter column "solution_id" drop default;'); + this.addSql('alter table "use_case" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "use_case" alter column "solution_id" drop not null;'); + this.addSql('alter table "use_case" add constraint "use_case_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on delete cascade;'); + + this.addSql('alter table "user_story" alter column "solution_id" drop default;'); + this.addSql('alter table "user_story" alter column "solution_id" type uuid using ("solution_id"::text::uuid);'); + this.addSql('alter table "user_story" alter column "solution_id" drop not null;'); + this.addSql('alter table "user_story" add constraint "user_story_solution_id_foreign" foreign key ("solution_id") references "solution" ("id") on delete cascade;'); + } + +} diff --git a/pages/o/[organization-slug]/[solution-slug]/environment/assumptions.client.vue b/pages/o/[organization-slug]/[solution-slug]/environment/assumptions.client.vue index aacd65d7..5be3593a 100644 --- a/pages/o/[organization-slug]/[solution-slug]/environment/assumptions.client.vue +++ b/pages/o/[organization-slug]/[solution-slug]/environment/assumptions.client.vue @@ -25,7 +25,9 @@ type AssumptionViewModel = { statement: string; } -const { data: assumptions, refresh, status, error: getAssumptionsError } = await useFetch(`/api/assumptions/?solutionId=${solutionId}`), +const { data: assumptions, refresh, status, error: getAssumptionsError } = await useFetch(`/api/assumptions`, { + query: { solutionId } +}), emptyAssumption = { id: emptyUuid, name: '', statement: '' }; if (getAssumptionsError.value) @@ -37,7 +39,7 @@ const filters = ref>({ }); const onCreate = async (data: AssumptionViewModel) => { - await $fetch(`/api/assumptions/`, { + await $fetch(`/api/assumptions`, { method: 'post', body: { name: data.name, @@ -50,7 +52,10 @@ const onCreate = async (data: AssumptionViewModel) => { } const onDelete = async (id: string) => { - await $fetch(`/api/assumptions/${id}`, { method: 'delete' }) + await $fetch(`/api/assumptions/${id}`, { + method: 'delete', + body: { solutionId } + }) .catch((e) => $eventBus.$emit('page-error', e)) refresh() diff --git a/pages/o/[organization-slug]/[solution-slug]/environment/components.client.vue b/pages/o/[organization-slug]/[solution-slug]/environment/components.client.vue index 449809c1..e715a052 100644 --- a/pages/o/[organization-slug]/[solution-slug]/environment/components.client.vue +++ b/pages/o/[organization-slug]/[solution-slug]/environment/components.client.vue @@ -14,7 +14,7 @@ const { $eventBus } = useNuxtApp(), } }), solutionId = solutions.value?.[0].id, - { data: environmentComponents, status, refresh, error: getEnvironmentComponentsError } = await useFetch('/api/environment-components', { + { data: environmentComponents, status, refresh, error: getEnvironmentComponentsError } = await useFetch(`/api/environment-components`, { query: { solutionId } }), emptyComponent = { id: emptyUuid, name: '', statement: '' } @@ -50,8 +50,10 @@ const onCreate = async (data: EnvironmentComponentViewModel) => { } const onDelete = async (id: string) => { - await $fetch(`/api/environment-components/${id}`, { method: 'DELETE' }) - .catch((e) => $eventBus.$emit('page-error', e)) + await $fetch(`/api/environment-components/${id}`, { + method: 'DELETE', + body: { solutionId } + }).catch((e) => $eventBus.$emit('page-error', e)) refresh() } diff --git a/pages/o/[organization-slug]/[solution-slug]/environment/constraints.client.vue b/pages/o/[organization-slug]/[solution-slug]/environment/constraints.client.vue index 25a0fa56..1f9abfdf 100644 --- a/pages/o/[organization-slug]/[solution-slug]/environment/constraints.client.vue +++ b/pages/o/[organization-slug]/[solution-slug]/environment/constraints.client.vue @@ -17,7 +17,7 @@ const { $eventBus } = useNuxtApp(), }), solution = (solutions.value ?? [])[0], solutionId = solution.id, - { data: constraints, status, refresh, error: getConstraintsError } = await useFetch('/api/constraints', { + { data: constraints, status, refresh, error: getConstraintsError } = await useFetch(`/api/constraints`, { query: { solutionId } }); @@ -51,8 +51,8 @@ const onCreate = async (data: ConstraintViewModel) => { body: { name: data.name, statement: data.statement, - solutionId, - category: data.category + category: data.category, + solutionId } }).catch((e) => $eventBus.$emit('page-error', e)) @@ -60,8 +60,10 @@ const onCreate = async (data: ConstraintViewModel) => { } const onDelete = async (id: string) => { - await $fetch(`/api/constraints/${id}`, { method: 'DELETE' }) - .catch((e) => $eventBus.$emit('page-error', e)) + await $fetch(`/api/constraints/${id}`, { + method: 'DELETE', + body: { solutionId } + }).catch((e) => $eventBus.$emit('page-error', e)) refresh() } @@ -71,8 +73,8 @@ const onUpdate = async (data: ConstraintViewModel) => { body: { name: data.name, statement: data.statement, - solutionId, - category: data.category + category: data.category, + solutionId } }).catch((e) => $eventBus.$emit('page-error', e)) refresh() diff --git a/pages/o/[organization-slug]/[solution-slug]/environment/effects.client.vue b/pages/o/[organization-slug]/[solution-slug]/environment/effects.client.vue index e7f803ea..9013d331 100644 --- a/pages/o/[organization-slug]/[solution-slug]/environment/effects.client.vue +++ b/pages/o/[organization-slug]/[solution-slug]/environment/effects.client.vue @@ -24,7 +24,9 @@ type EffectViewModel = { statement: string; } -const { data: effects, refresh, status, error: getEffectsError } = await useFetch(`/api/effects?solutionId=${solutionId}`), +const { data: effects, refresh, status, error: getEffectsError } = await useFetch(`/api/effects`, { + query: { solutionId } +}), emptyEffect: EffectViewModel = { id: emptyUuid, name: '', statement: '' } if (getEffectsError.value) @@ -36,8 +38,9 @@ const filters = ref({ }); const onCreate = async (data: EffectViewModel) => { - await $fetch('/api/effects', { - method: 'POST', body: { + await $fetch(`/api/effects`, { + method: 'POST', + body: { name: data.name, statement: data.statement, solutionId @@ -60,7 +63,10 @@ const onUpdate = async (data: EffectViewModel) => { } const onDelete = async (id: string) => { - await $fetch(`/api/effects/${id}`, { method: 'DELETE' }) + await $fetch(`/api/effects/${id}`, { + method: 'DELETE', + body: { solutionId } + }).catch((e) => $eventBus.$emit('page-error', e)) refresh() } diff --git a/pages/o/[organization-slug]/[solution-slug]/environment/glossary.client.vue b/pages/o/[organization-slug]/[solution-slug]/environment/glossary.client.vue index a56408bc..211725ca 100644 --- a/pages/o/[organization-slug]/[solution-slug]/environment/glossary.client.vue +++ b/pages/o/[organization-slug]/[solution-slug]/environment/glossary.client.vue @@ -24,7 +24,9 @@ type GlossaryTermViewModel = { statement: string; } -const { data: glossaryTerms, refresh, status, error: getGlossaryTermsError } = await useFetch(`/api/glossary-terms?solutionId=${solutionId}`), +const { data: glossaryTerms, refresh, status, error: getGlossaryTermsError } = await useFetch(`/api/glossary-terms`, { + query: { solutionId } +}), emptyGlossaryTerm: GlossaryTermViewModel = { id: emptyUuid, name: '', statement: '' } if (getGlossaryTermsError.value) @@ -37,7 +39,8 @@ const filters = ref({ const onCreate = async (data: GlossaryTermViewModel) => { await $fetch(`/api/glossary-terms`, { - method: 'POST', body: { + method: 'POST', + body: { name: data.name, statement: data.statement, solutionId @@ -49,7 +52,8 @@ const onCreate = async (data: GlossaryTermViewModel) => { const onUpdate = async (data: GlossaryTermViewModel) => { await $fetch(`/api/glossary-terms/${data.id}`, { - method: 'PUT', body: { + method: 'PUT', + body: { id: data.id, name: data.name, statement: data.statement, @@ -61,8 +65,10 @@ const onUpdate = async (data: GlossaryTermViewModel) => { } const onDelete = async (id: string) => { - await $fetch(`/api/glossary-terms/${id}`, { method: 'DELETE' }) - .catch((e) => $eventBus.$emit('page-error', e)) + await $fetch(`/api/glossary-terms/${id}`, { + method: 'DELETE', + body: { solutionId } + }).catch((e) => $eventBus.$emit('page-error', e)) refresh() } diff --git a/pages/o/[organization-slug]/[solution-slug]/environment/invariants.client.vue b/pages/o/[organization-slug]/[solution-slug]/environment/invariants.client.vue index fc98a8fe..ccae3599 100644 --- a/pages/o/[organization-slug]/[solution-slug]/environment/invariants.client.vue +++ b/pages/o/[organization-slug]/[solution-slug]/environment/invariants.client.vue @@ -24,7 +24,9 @@ type InvariantViewModel = { statement: string; } -const { data: invariants, refresh, status, error: getInvariantsError } = await useFetch(`/api/invariants?solutionId=${solutionId}`), +const { data: invariants, refresh, status, error: getInvariantsError } = await useFetch(`/api/invariants`, { + query: { solutionId } +}), emptyInvariant: InvariantViewModel = { id: emptyUuid, name: '', statement: '' }; if (getInvariantsError.value) @@ -37,7 +39,8 @@ const filters = ref({ const onCreate = async (data: InvariantViewModel) => { await useFetch(`/api/invariants`, { - method: 'POST', body: { + method: 'POST', + body: { name: data.name, statement: data.statement, solutionId @@ -61,8 +64,10 @@ const onUpdate = async (data: InvariantViewModel) => { } const onDelete = async (id: string) => { - await useFetch(`/api/invariants/${id}`, { method: 'DELETE' }) - .catch((e) => $eventBus.$emit('page-error', e)) + await useFetch(`/api/invariants/${id}`, { + method: 'DELETE', + body: { solutionId } + }).catch((e) => $eventBus.$emit('page-error', e)) refresh() } diff --git a/pages/o/[organization-slug]/[solution-slug]/goals/functionality.client.vue b/pages/o/[organization-slug]/[solution-slug]/goals/functionality.client.vue index 1f0ac092..08bd604b 100644 --- a/pages/o/[organization-slug]/[solution-slug]/goals/functionality.client.vue +++ b/pages/o/[organization-slug]/[solution-slug]/goals/functionality.client.vue @@ -26,7 +26,9 @@ type FunctionalBehaviorViewModel = { priority: MoscowPriority; } -const { data: functionalBehaviors, refresh, status, error: getFunctionalBehaviorsError } = await useFetch(`/api/functional-behaviors?solutionId=${solutionId}`), +const { data: functionalBehaviors, refresh, status, error: getFunctionalBehaviorsError } = await useFetch(`/api/functional-behaviors`, { + query: { solutionId } +}), emptyFunctionalBehavior: FunctionalBehaviorViewModel = { id: emptyUuid, name: '', @@ -43,12 +45,12 @@ const filters = ref({ }); const onCreate = async (data: FunctionalBehaviorViewModel) => { - await $fetch('/api/functional-behaviors', { + await $fetch(`/api/functional-behaviors`, { method: 'POST', body: { + solutionId, name: data.name, statement: data.statement, - solutionId, priority: MoscowPriority.MUST } }).catch((e) => $eventBus.$emit('page-error', e)) @@ -60,10 +62,10 @@ const onUpdate = async (data: FunctionalBehaviorViewModel) => { await $fetch(`/api/functional-behaviors/${data.id}`, { method: 'PUT', body: { + solutionId, id: data.id, name: data.name, statement: data.statement, - solutionId, priority: data.priority } }).catch((e) => $eventBus.$emit('page-error', e)) @@ -72,8 +74,10 @@ const onUpdate = async (data: FunctionalBehaviorViewModel) => { } const onDelete = async (id: string) => { - await $fetch(`/api/functional-behaviors/${id}`, { method: 'DELETE' }) - .catch((e) => $eventBus.$emit('page-error', e)) + await $fetch(`/api/functional-behaviors/${id}`, { + method: 'DELETE', + body: { solutionId } + }).catch((e) => $eventBus.$emit('page-error', e)) refresh() } diff --git a/pages/o/[organization-slug]/[solution-slug]/goals/limitations.client.vue b/pages/o/[organization-slug]/[solution-slug]/goals/limitations.client.vue index 4fdc56f8..8ccc9604 100644 --- a/pages/o/[organization-slug]/[solution-slug]/goals/limitations.client.vue +++ b/pages/o/[organization-slug]/[solution-slug]/goals/limitations.client.vue @@ -24,7 +24,9 @@ type LimitViewModel = { statement: string; } -const { data: limits, status, refresh, error: getLimitsError } = await useFetch(`/api/limits?solutionId=${solutionId}`), +const { data: limits, status, refresh, error: getLimitsError } = await useFetch(`/api/limits`, { + query: { solutionId } +}), emptyLimit: LimitViewModel = { id: emptyUuid, name: '', statement: '' }; if (getLimitsError.value) @@ -39,9 +41,9 @@ const onCreate = async (data: LimitViewModel) => { await $fetch(`/api/limits`, { method: 'POST', body: { + solutionId, name: data.name, - statement: data.statement, - solutionId + statement: data.statement } }).catch((e) => $eventBus.$emit('page-error', e)) @@ -51,10 +53,10 @@ const onCreate = async (data: LimitViewModel) => { const onUpdate = async (data: LimitViewModel) => { await $fetch(`/api/limits/${data.id}`, { method: 'PUT', body: { + solutionId, id: data.id, name: data.name, - statement: data.statement, - solutionId + statement: data.statement } }).catch((e) => $eventBus.$emit('page-error', e)) @@ -62,8 +64,10 @@ const onUpdate = async (data: LimitViewModel) => { } const onDelete = async (id: string) => { - await $fetch(`/api/limits/${id}`, { method: 'DELETE' }) - .catch((e) => $eventBus.$emit('page-error', e)) + await $fetch(`/api/limits/${id}`, { + method: 'DELETE', + body: { solutionId } + }).catch((e) => $eventBus.$emit('page-error', e)) refresh() } diff --git a/pages/o/[organization-slug]/[solution-slug]/goals/obstacles.client.vue b/pages/o/[organization-slug]/[solution-slug]/goals/obstacles.client.vue index f703aa19..1aacf9e7 100644 --- a/pages/o/[organization-slug]/[solution-slug]/goals/obstacles.client.vue +++ b/pages/o/[organization-slug]/[solution-slug]/goals/obstacles.client.vue @@ -24,7 +24,9 @@ type ObstacleViewModel = { statement: string; } -const { data: obstacles, refresh, status, error: getObstaclesError } = await useFetch(`/api/obstacles?solutionId=${solutionId}`), +const { data: obstacles, refresh, status, error: getObstaclesError } = await useFetch(`/api/obstacles`, { + query: { solutionId } +}), emptyObstacle: ObstacleViewModel = { id: emptyUuid, name: '', statement: '' }; const filters = ref({ @@ -36,9 +38,9 @@ const onCreate = async (data: ObstacleViewModel) => { await $fetch(`/api/obstacles`, { method: 'POST', body: { + solutionId, name: data.name, - statement: data.statement, - solutionId + statement: data.statement } }).catch((e) => $eventBus.$emit('page-error', e)) @@ -49,9 +51,9 @@ const onUpdate = async (data: ObstacleViewModel) => { await $fetch(`/api/obstacles/${data.id}`, { method: 'PUT', body: { + solutionId, name: data.name, - statement: data.statement, - solutionId + statement: data.statement } }).catch((e) => $eventBus.$emit('page-error', e)) @@ -59,8 +61,10 @@ const onUpdate = async (data: ObstacleViewModel) => { } const onDelete = async (id: string) => { - await $fetch(`/api/obstacles/${id}`, { method: 'DELETE' }) - .catch((e) => $eventBus.$emit('page-error', e)) + await $fetch(`/api/obstacles/${id}`, { + method: 'DELETE', + body: { solutionId } + }).catch((e) => $eventBus.$emit('page-error', e)) refresh() } diff --git a/pages/o/[organization-slug]/[solution-slug]/goals/outcomes.client.vue b/pages/o/[organization-slug]/[solution-slug]/goals/outcomes.client.vue index d88b8cf0..cf63d042 100644 --- a/pages/o/[organization-slug]/[solution-slug]/goals/outcomes.client.vue +++ b/pages/o/[organization-slug]/[solution-slug]/goals/outcomes.client.vue @@ -24,7 +24,9 @@ type OutcomeViewModel = { statement: string; } -const { data: outcomes, refresh, status, error: getOutcomesError } = await useFetch(`/api/outcomes?solutionId=${solutionId}`), +const { data: outcomes, refresh, status, error: getOutcomesError } = await useFetch(`/api/outcomes`, { + query: { solutionId } +}), emptyOutcome: OutcomeViewModel = { id: emptyUuid, name: '', statement: '' }; if (getOutcomesError.value) @@ -39,9 +41,9 @@ const onCreate = async (data: OutcomeViewModel) => { await $fetch(`/api/outcomes`, { method: 'POST', body: { + solutionId, name: data.name, - statement: data.statement, - solutionId + statement: data.statement } }).catch((e) => $eventBus.$emit('page-error', e)) @@ -52,9 +54,9 @@ const onUpdate = async (data: OutcomeViewModel) => { await $fetch(`/api/outcomes/${data.id}`, { method: 'PUT', body: { + solutionId, name: data.name, - statement: data.statement, - solutionId + statement: data.statement } }).catch((e) => $eventBus.$emit('page-error', e)) @@ -62,8 +64,10 @@ const onUpdate = async (data: OutcomeViewModel) => { } const onDelete = async (id: string) => { - await $fetch(`/api/outcomes/${id}`, { method: 'DELETE' }) - .catch((e) => $eventBus.$emit('page-error', e)) + await $fetch(`/api/outcomes/${id}`, { + method: 'DELETE', + body: { solutionId } + }).catch((e) => $eventBus.$emit('page-error', e)) refresh() } diff --git a/pages/o/[organization-slug]/[solution-slug]/goals/rationale.client.vue b/pages/o/[organization-slug]/[solution-slug]/goals/rationale.client.vue index 4100e8d8..edeee7c5 100644 --- a/pages/o/[organization-slug]/[solution-slug]/goals/rationale.client.vue +++ b/pages/o/[organization-slug]/[solution-slug]/goals/rationale.client.vue @@ -27,10 +27,10 @@ const [ { data: situationJustifications, error: getSituationError }, { data: objectiveJustifications, error: getObjectiveError } ] = await Promise.all([ - useFetch(`/api/justifications?solutionId=${solutionId}&name=Vision`), - useFetch(`/api/justifications?solutionId=${solutionId}&name=Mission`), - useFetch(`/api/justifications?solutionId=${solutionId}&name=Situation`), - useFetch(`/api/justifications?solutionId=${solutionId}&name=Objective`) + useFetch(`/api/justifications`, { query: { name: 'Vision', solutionId } }), + useFetch(`/api/justifications`, { query: { name: 'Mission', solutionId } }), + useFetch(`/api/justifications`, { query: { name: 'Situation', solutionId } }), + useFetch(`/api/justifications`, { query: { name: 'Objective', solutionId } }) ]); if (getVisionError.value) diff --git a/pages/o/[organization-slug]/[solution-slug]/goals/scenarios.client.vue b/pages/o/[organization-slug]/[solution-slug]/goals/scenarios.client.vue index 977d23ff..47f111f1 100644 --- a/pages/o/[organization-slug]/[solution-slug]/goals/scenarios.client.vue +++ b/pages/o/[organization-slug]/[solution-slug]/goals/scenarios.client.vue @@ -34,10 +34,10 @@ const [ { data: functionalBehaviors, error: getFunctionalBehaviorsError }, { data: outcomes, error: getOutcomesError }, ] = await Promise.all([ - useFetch(`/api/user-stories?solutionId=${solutionId}`), - useFetch(`/api/stakeholders?solutionId=${solutionId}`), - useFetch(`/api/functional-behaviors?solutionId=${solutionId}`), - useFetch(`/api/outcomes?solutionId=${solutionId}`) + useFetch(`/api/user-stories`, { query: { solutionId } }), + useFetch(`/api/stakeholders`, { query: { solutionId } }), + useFetch(`/api/functional-behaviors`, { query: { solutionId } }), + useFetch(`/api/outcomes`, { query: { solutionId } }) ]), emptyUserStory: UserStoryViewModel = { id: emptyUuid, @@ -66,10 +66,11 @@ const userStoryfilters = ref({ const onUserStoryCreate = async (userStory: UserStoryViewModel) => { await $fetch(`/api/user-stories`, { - method: 'POST', body: { + method: 'POST', + body: { + solutionId, name: userStory.name, statement: '', - solutionId, primaryActorId: userStory.primaryActorId, priority: MoscowPriority.MUST, outcomeId: userStory.outcomeId, @@ -82,10 +83,11 @@ const onUserStoryCreate = async (userStory: UserStoryViewModel) => { const onUserStoryUpdate = async (userStory: UserStoryViewModel) => { await $fetch(`/api/user-stories/${userStory.id}`, { - method: 'PUT', body: { + method: 'PUT', + body: { + solutionId, name: userStory.name, statement: '', - solutionId, priority: MoscowPriority.MUST, primaryActorId: userStory.primaryActorId, outcomeId: userStory.outcomeId, @@ -97,8 +99,10 @@ const onUserStoryUpdate = async (userStory: UserStoryViewModel) => { } const onUserStoryDelete = async (id: string) => { - await $fetch(`/api/user-stories/${id}`, { method: 'DELETE' }) - .catch((e) => $eventBus.$emit('page-error', e)); + await $fetch(`/api/user-stories/${id}`, { + method: 'DELETE', + body: { solutionId } + }).catch((e) => $eventBus.$emit('page-error', e)); refresh(); } diff --git a/pages/o/[organization-slug]/[solution-slug]/goals/stakeholders.client.vue b/pages/o/[organization-slug]/[solution-slug]/goals/stakeholders.client.vue index b828a81d..82f54e08 100644 --- a/pages/o/[organization-slug]/[solution-slug]/goals/stakeholders.client.vue +++ b/pages/o/[organization-slug]/[solution-slug]/goals/stakeholders.client.vue @@ -31,7 +31,9 @@ type StakeHolderViewModel = { segmentation: StakeholderSegmentation; } -const { data: stakeholders, refresh, status, error: getStakeholdersError } = await useFetch(`/api/stakeholders?solutionId=${solutionId}`), +const { data: stakeholders, refresh, status, error: getStakeholdersError } = await useFetch(`/api/stakeholders`, { + query: { solutionId } +}), categories = ref<{ id: string, description: string }[]>( Object.values(StakeholderCategory).map((value) => ({ id: value, description: value })) ), @@ -102,7 +104,7 @@ const clientMap = ref(), vendorMap = ref(); const onCreate = async (data: StakeHolderViewModel) => { - await $fetch('/api/stakeholders', { + await $fetch(`/api/stakeholders`, { method: 'POST', body: { ...data, @@ -126,8 +128,10 @@ const onUpdate = async (data: StakeHolderViewModel) => { } const onDelete = async (id: string) => { - await $fetch(`/api/stakeholders/${id}`, { method: 'DELETE' }) - .catch((e) => $eventBus.$emit('page-error', e)) + await $fetch(`/api/stakeholders/${id}`, { + method: 'DELETE', + body: { solutionId } + }).catch((e) => $eventBus.$emit('page-error', e)) refresh() } diff --git a/pages/o/[organization-slug]/[solution-slug]/project/roles-personnel.client.vue b/pages/o/[organization-slug]/[solution-slug]/project/roles-personnel.client.vue index 866e1518..ae38631a 100644 --- a/pages/o/[organization-slug]/[solution-slug]/project/roles-personnel.client.vue +++ b/pages/o/[organization-slug]/[solution-slug]/project/roles-personnel.client.vue @@ -24,7 +24,9 @@ type PersonViewModel = { email: string; } -const { data: personnel, refresh, status, error: getPersonnelError } = await useFetch(`/api/persons?solutionId=${solutionId}`), +const { data: personnel, refresh, status, error: getPersonnelError } = await useFetch(`/api/persons`, { + query: { solutionId } +}), emptyPerson: PersonViewModel = { id: emptyUuid, name: '', email: '' }; if (getPersonnelError.value) @@ -37,7 +39,8 @@ const filters = ref({ const onCreate = async (data: PersonViewModel) => { await $fetch(`/api/persons`, { - method: 'POST', body: { + method: 'POST', + body: { ...data, solutionId, statement: '' @@ -49,7 +52,8 @@ const onCreate = async (data: PersonViewModel) => { const onUpdate = async (data: PersonViewModel) => { await $fetch(`/api/persons/${data.id}`, { - method: 'PUT', body: { + method: 'PUT', + body: { ...data, solutionId, statement: '' @@ -60,8 +64,10 @@ const onUpdate = async (data: PersonViewModel) => { } const onDelete = async (id: string) => { - await $fetch(`/api/persons/${id}`, { method: 'DELETE' }) - .catch((e) => $eventBus.$emit('page-error', e)) + await $fetch(`/api/persons/${id}`, { + method: 'DELETE', + body: { solutionId } + }).catch((e) => $eventBus.$emit('page-error', e)) refresh(); } diff --git a/pages/o/[organization-slug]/[solution-slug]/system/components.client.vue b/pages/o/[organization-slug]/[solution-slug]/system/components.client.vue index 81806ee0..5d93686c 100644 --- a/pages/o/[organization-slug]/[solution-slug]/system/components.client.vue +++ b/pages/o/[organization-slug]/[solution-slug]/system/components.client.vue @@ -25,7 +25,9 @@ type SystemComponentViewModel = { parentComponentId?: string; } -const { data: systemComponents, refresh, status, error: getSystemComponentsError } = await useFetch(`/api/system-components?solutionId=${solutionId}`), +const { data: systemComponents, refresh, status, error: getSystemComponentsError } = await useFetch(`/api/system-components`, { + query: { solutionId } +}), emptyComponent: SystemComponentViewModel = { id: emptyUuid, name: '', @@ -43,9 +45,12 @@ const filters = ref({ }) const onCreate = async (data: SystemComponentViewModel) => { - await $fetch('/api/system-components', { + await $fetch(`/api/system-components`, { method: 'POST', - body: { ...data, solutionId } + body: { + ...data, + solutionId + } }).catch((e) => $eventBus.$emit('page-error', e)) refresh() @@ -54,7 +59,10 @@ const onCreate = async (data: SystemComponentViewModel) => { const onUpdate = async (data: SystemComponentViewModel) => { await $fetch(`/api/system-components/${data.id}`, { method: 'PUT', - body: { ...data, solutionId } + body: { + ...data, + solutionId + } }).catch((e) => $eventBus.$emit('page-error', e)) refresh() @@ -62,7 +70,8 @@ const onUpdate = async (data: SystemComponentViewModel) => { const onDelete = async (id: string) => { await $fetch(`/api/system-components/${id}`, { - method: 'DELETE' + method: 'DELETE', + body: {solutionId} }).catch((e) => $eventBus.$emit('page-error', e)) refresh() diff --git a/pages/o/[organization-slug]/[solution-slug]/system/functionality.client.vue b/pages/o/[organization-slug]/[solution-slug]/system/functionality.client.vue index c2cfbbbf..a1785d97 100644 --- a/pages/o/[organization-slug]/[solution-slug]/system/functionality.client.vue +++ b/pages/o/[organization-slug]/[solution-slug]/system/functionality.client.vue @@ -28,7 +28,9 @@ type BehaviorViewModel = { priority: MoscowPriority; } -const { data: components, status, refresh, error: getComponentsError } = await useFetch(`/api/system-components?solutionId=${solutionId}`), +const { data: components, status, refresh, error: getComponentsError } = await useFetch(`/api/system-components`, { + query: { solutionId } +}), expandedRows = ref({}), emptyBehavior = (componentid: string): BehaviorViewModel => ({ id: emptyUuid, @@ -42,12 +44,14 @@ if (getComponentsError.value) $eventBus.$emit('page-error', getComponentsError.value) const fnFunctionalBehaviors = async (componentId: string) => - await $fetch(`/api/functional-behaviors?componentId=${componentId}`) - .catch((e) => $eventBus.$emit('page-error', e)); + await $fetch(`/api/functional-behaviors`, { + query: { solutionId, componentId } + }).catch((e) => $eventBus.$emit('page-error', e)); const fnNonFunctionalBehaviors = async (componentId: string) => - await $fetch(`/api/non-functional-behaviors?componentId=${componentId}`) - .catch((e) => $eventBus.$emit('page-error', e)) + await $fetch(`/api/non-functional-behaviors`, { + query: { solutionId, componentId } + }).catch((e) => $eventBus.$emit('page-error', e)) const componentSortField = ref('name') diff --git a/pages/o/[organization-slug]/[solution-slug]/system/scenarios.client.vue b/pages/o/[organization-slug]/[solution-slug]/system/scenarios.client.vue index 2e5fa983..d2f87421 100644 --- a/pages/o/[organization-slug]/[solution-slug]/system/scenarios.client.vue +++ b/pages/o/[organization-slug]/[solution-slug]/system/scenarios.client.vue @@ -26,7 +26,6 @@ type UserStoryViewModel = { functionalBehaviorId: string; outcomeId: string; priority: MoscowPriority; - } type UseCaseViewModel = { @@ -44,8 +43,12 @@ type UseCaseViewModel = { priority: MoscowPriority; } -const { data: userStories, refresh: refreshUserStories, error: getUserStoriesError } = await useFetch(`/api/user-stories?solutionId=${solutionId}`), - { data: useCases, refresh: refreshUseCases, error: getUseCasesError } = await useFetch(`/api/use-cases?solutionId=${solutionId}`), +const { data: userStories, refresh: refreshUserStories, error: getUserStoriesError } = await useFetch(`/api/user-stories`, { + query: { solutionId } +}), + { data: useCases, refresh: refreshUseCases, error: getUseCasesError } = await useFetch(`/api/use-cases`, { + query: { solutionId } + }), emptyUserStory: UserStoryViewModel = { id: emptyUuid, name: '', @@ -68,11 +71,11 @@ const { data: userStories, refresh: refreshUserStories, error: getUserStoriesErr triggerid: emptyUuid, priority: MoscowPriority.MUST }, - { data: roles, error: getRolesError } = await useFetch(`/api/stakeholders?solutionId=${solutionId}`), - { data: functionalBehaviors, error: getFunctionalBehaviorsError } = await useFetch(`/api/functional-behaviors?solutionId=${solutionId}`), - { data: outcomes, error: getOutcomesError } = await useFetch(`/api/outcomes?solutionId=${solutionId}`), - { data: assumptions, error: getAssumptionsError } = await useFetch(`/api/assumptions?solutionId=${solutionId}`), - { data: effects, error: getEffectsError } = await useFetch(`/api/effects?solutionId=${solutionId}`), + { data: roles, error: getRolesError } = await useFetch(`/api/stakeholders`, { query: { solutionId } }), + { data: functionalBehaviors, error: getFunctionalBehaviorsError } = await useFetch(`/api/functional-behaviors`, { query: { solutionId } }), + { data: outcomes, error: getOutcomesError } = await useFetch(`/api/outcomes`, { query: { solutionId } }), + { data: assumptions, error: getAssumptionsError } = await useFetch(`/api/assumptions`, { query: { solutionId } }), + { data: effects, error: getEffectsError } = await useFetch(`/api/effects`, { query: { solutionId } }), triggers = ref([]) if (getUserStoriesError.value) @@ -164,15 +167,19 @@ const onUseCaseUpdate = async (useCase: UseCaseViewModel) => { } const onUserStoryDelete = async (id: string) => { - await $fetch(`/api/user-stories/${id}`, { method: 'DELETE' }) - .catch((e) => $eventBus.$emit('page-error', e)); + await $fetch(`/api/user-stories/${id}`, { + method: 'DELETE', + body: { solutionId } + }).catch((e) => $eventBus.$emit('page-error', e)); refreshUserStories(); } const onUseCaseDelete = async (id: string) => { - await $fetch(`/api/use-cases/${id}`, { method: 'DELETE' }) - .catch((e) => $eventBus.$emit('page-error', e)); + await $fetch(`/api/use-cases/${id}`, { + method: 'DELETE', + body: { solutionId } + }).catch((e) => $eventBus.$emit('page-error', e)); refreshUseCases(); } diff --git a/server/api/appusers/[id].delete.ts b/server/api/appusers/[id].delete.ts index ca165051..93c9b1b1 100644 --- a/server/api/appusers/[id].delete.ts +++ b/server/api/appusers/[id].delete.ts @@ -1,9 +1,12 @@ import { z } from "zod" import { fork } from "~/server/data/orm" -import { getServerSession } from "#auth" import AppUserOrganizationRole from "~/server/domain/application/AppUserOrganizationRole" import AppRole from "~/server/domain/application/AppRole" +const paramSchema = z.object({ + id: z.string().uuid() +}) + const bodySchema = z.object({ organizationId: z.string().uuid(), }) @@ -12,34 +15,17 @@ const bodySchema = z.object({ * Delete an appuser by id in a given organization */ export default defineEventHandler(async (event) => { - const id = event.context.params?.id, - body = await readValidatedBody(event, (b) => bodySchema.safeParse(b)) - - if (!body.success) - throw createError({ - statusCode: 400, - statusMessage: 'Bad Request: Invalid body parameters', - message: JSON.stringify(body.error.errors) - }) - if (!id) - throw createError({ - statusCode: 400, - statusMessage: "Bad Request: id is required." - }) - - const em = fork(), - session = (await getServerSession(event))!, - [sessionUserRole, appUserRole, orgAdminCount] = await Promise.all([ - em.findOne(AppUserOrganizationRole, { - appUser: session.id, - organization: body.data.organizationId - }, { populate: ['appUser'] }), + const { id } = await validateEventParams(event, paramSchema), + { organizationId } = await validateEventBody(event, bodySchema), + em = fork(), + { organization } = await assertOrgAdmin(event, organizationId), + [appUserRole, orgAdminCount] = await Promise.all([ em.findOne(AppUserOrganizationRole, { appUser: id, - organization: body.data.organizationId + organization }, { populate: ['appUser'] }), em.count(AppUserOrganizationRole, { - organization: body.data.organizationId, + organization, role: AppRole.ORGANIZATION_ADMIN }) ]) @@ -50,24 +36,12 @@ export default defineEventHandler(async (event) => { statusMessage: "Not Found", message: "AppUser not found for the given ID and organization." }) - if (!sessionUserRole && !session.isSystemAdmin) - throw createError({ - statusCode: 403, - statusMessage: "Forbidden", - message: "You are not associated with the organization." - }) if (appUserRole.role === AppRole.ORGANIZATION_ADMIN && orgAdminCount === 1) throw createError({ statusCode: 403, statusMessage: "Forbidden", message: "You cannot delete the last organization admin." }) - if (!session.isSystemAdmin && sessionUserRole && sessionUserRole.role !== AppRole.ORGANIZATION_ADMIN) - throw createError({ - statusCode: 403, - statusMessage: "Forbidden", - message: "You must be an organization admin or a system admin to delete an appuser." - }) // Removing the relationship to the organization and NOT the appuser itself em.remove(appUserRole) diff --git a/server/api/appusers/[id].get.ts b/server/api/appusers/[id].get.ts index 1ac0c1fb..71dddbe5 100644 --- a/server/api/appusers/[id].get.ts +++ b/server/api/appusers/[id].get.ts @@ -2,6 +2,10 @@ import { z } from "zod" import { fork } from "~/server/data/orm" import AppUserOrganizationRole from "~/server/domain/application/AppUserOrganizationRole" +const paramSchema = z.object({ + id: z.string().uuid() +}) + const querySchema = z.object({ organizationId: z.string().uuid(), }) @@ -10,25 +14,13 @@ const querySchema = z.object({ * Returns an appuser by id in a given organization */ export default defineEventHandler(async (event) => { - const id = event.context.params?.id, - query = await getValidatedQuery(event, (q) => querySchema.safeParse(q)) - - if (!query.success) - throw createError({ - statusCode: 400, - statusMessage: 'Bad Request: Invalid body parameters', - message: JSON.stringify(query.error.errors) - }) - if (!id) - throw createError({ - statusCode: 400, - statusMessage: "Bad Request: id is required." - }) - - const em = fork(), + const { id } = await validateEventParams(event, paramSchema), + { organizationId } = await validateEventQuery(event, querySchema), + { } = await assertOrgReader(event, organizationId), + em = fork(), appUserRole = await em.findOne(AppUserOrganizationRole, { appUser: id, - organization: query.data.organizationId + organization: organizationId }, { populate: ['appUser'] }) if (!appUserRole) @@ -42,4 +34,4 @@ export default defineEventHandler(async (event) => { ...appUserRole.appUser.toJSON(), role: appUserRole.role } -}) +}) \ No newline at end of file diff --git a/server/api/appusers/[id].put.ts b/server/api/appusers/[id].put.ts index 04f461df..e14c0873 100644 --- a/server/api/appusers/[id].put.ts +++ b/server/api/appusers/[id].put.ts @@ -1,9 +1,12 @@ import { z } from "zod" import { fork } from "~/server/data/orm" -import { getServerSession } from "#auth" import AppUserOrganizationRole from "~/server/domain/application/AppUserOrganizationRole" import AppRole from "~/server/domain/application/AppRole" +const paramSchema = z.object({ + id: z.string().uuid() +}) + const bodySchema = z.object({ organizationId: z.string().uuid(), role: z.nativeEnum(AppRole) @@ -13,34 +16,17 @@ const bodySchema = z.object({ * Update an appuser by id in a given organization to have a new role */ export default defineEventHandler(async (event) => { - const id = event.context.params?.id, - body = await readValidatedBody(event, (b) => bodySchema.safeParse(b)) - - if (!body.success) - throw createError({ - statusCode: 400, - statusMessage: 'Bad Request: Invalid body parameters', - message: JSON.stringify(body.error.errors) - }) - if (!id) - throw createError({ - statusCode: 400, - statusMessage: "Bad Request: id is required." - }) - - const em = fork(), - session = (await getServerSession(event))!, - [sessionUserRole, appUserRole, orgAdminCount] = await Promise.all([ - em.findOne(AppUserOrganizationRole, { - appUser: session.id, - organization: body.data.organizationId - }, { populate: ['appUser'] }), + const { id } = await validateEventParams(event, paramSchema), + { organizationId, role } = await validateEventBody(event, bodySchema), + em = fork(), + { } = await assertOrgAdmin(event, organizationId), + [appUserRole, orgAdminCount] = await Promise.all([ em.findOne(AppUserOrganizationRole, { appUser: id, - organization: body.data.organizationId + organization: organizationId }, { populate: ['appUser'] }), em.count(AppUserOrganizationRole, { - organization: body.data.organizationId, + organization: organizationId, role: AppRole.ORGANIZATION_ADMIN }) ]) @@ -51,15 +37,9 @@ export default defineEventHandler(async (event) => { statusMessage: "Not Found", message: "AppUser not found for the given ID and organization." }) - if (!sessionUserRole && !session.isSystemAdmin) - throw createError({ - statusCode: 403, - statusMessage: "Forbidden", - message: "You are not associated with the organization." - }) // you can't remove the last organization admin if (appUserRole.role === AppRole.ORGANIZATION_ADMIN && orgAdminCount === 1 - && body.data.role !== AppRole.ORGANIZATION_ADMIN + && role !== AppRole.ORGANIZATION_ADMIN ) throw createError({ statusCode: 403, @@ -67,13 +47,6 @@ export default defineEventHandler(async (event) => { message: "You can't remove the last organization admin." }) - if (!session.isSystemAdmin && sessionUserRole && sessionUserRole.role !== AppRole.ORGANIZATION_ADMIN) - throw createError({ - statusCode: 403, - statusMessage: "Forbidden", - message: "You must be an organization admin or a system admin to update an appuser." - }) - - appUserRole.role = body.data.role + appUserRole.role = role await em.flush() }) \ No newline at end of file diff --git a/server/api/appusers/index.get.ts b/server/api/appusers/index.get.ts index 9086c596..435cae52 100644 --- a/server/api/appusers/index.get.ts +++ b/server/api/appusers/index.get.ts @@ -1,7 +1,5 @@ import { z } from "zod" -import { getServerSession } from '#auth' import { fork } from "~/server/data/orm" -import Organization from "~/server/domain/application/Organization" import AppUserOrganizationRole from "~/server/domain/application/AppUserOrganizationRole" import { type Properties } from "~/server/domain/Properties" import AppUser from "~/server/domain/application/AppUser" @@ -12,44 +10,12 @@ const querySchema = z.object({ }) /** - * GET /api/appusers?organizationId - * * Returns all appusers for the organization with their associated roles */ export default defineEventHandler(async (event) => { - const query = await getValidatedQuery(event, (q) => querySchema.safeParse(q)), - session = (await getServerSession(event))!, - em = fork() - - if (!query.success) - throw createError({ - statusCode: 400, - statusMessage: "Bad Request: Invalid query parameters", - message: JSON.stringify(query.error.errors) - }) - - const organization = await em.findOne(Organization, { id: query.data.organizationId }), - sessionUserOrg = await em.findOne(AppUserOrganizationRole, { - appUser: session.id, - organization - }) - - if (!organization) - throw createError({ - statusCode: 404, - statusMessage: "Not Found", - message: "Organization not found for the given ID" - }) - - // If the user is not associated with the organization - // or is not a system admin, return a 403 - - if (!sessionUserOrg && !session.isSystemAdmin) - throw createError({ - statusCode: 403, - statusMessage: "Forbidden", - message: "You are not associated with the organization." - }) + const { organizationId } = await validateEventQuery(event, querySchema), + em = fork(), + { organization } = await assertOrgAdmin(event, organizationId) const appUserOrganizationRoles = await em.findAll(AppUserOrganizationRole, { where: { organization }, @@ -60,7 +26,7 @@ export default defineEventHandler(async (event) => { // assign the roles to the appusers const appUsersWithRoles: AppUserRoles[] = appUserOrganizationRoles.map((a) => ({ - // @ts-expect-error: The entity is actuall in an 'entity' property. Is this an ORM bug? + // @ts-expect-error: The entity is actually in an 'entity' property. Is this an ORM bug? ...a.appUser.entity, role: a.role })) diff --git a/server/api/appusers/index.post.ts b/server/api/appusers/index.post.ts index 7e788165..7617bced 100644 --- a/server/api/appusers/index.post.ts +++ b/server/api/appusers/index.post.ts @@ -1,6 +1,5 @@ import { z } from "zod" import { fork } from "~/server/data/orm" -import { getServerSession } from "#auth" import AppUserOrganizationRole from "~/server/domain/application/AppUserOrganizationRole" import AppRole from "~/server/domain/application/AppRole" import AppUser from "~/server/domain/application/AppUser" @@ -15,29 +14,13 @@ const bodySchema = z.object({ * Invite an appuser to an organization with a role */ export default defineEventHandler(async (event) => { - const body = await readValidatedBody(event, (b) => bodySchema.safeParse(b)) - - if (!body.success) - throw createError({ - statusCode: 400, - statusMessage: 'Bad Request: Invalid body parameters', - message: JSON.stringify(body.error.errors) - }) - - const em = fork(), - session = (await getServerSession(event))!, - [sessionUserRole, appUser] = await Promise.all([ - em.findOne(AppUserOrganizationRole, { - appUser: session.id, - organization: body.data.organizationId - }, { populate: ['appUser'] }), - em.findOne(AppUser, { - email: body.data.email - }) - ]), + const { email, organizationId, role } = await validateEventBody(event, bodySchema), + { organization } = await assertOrgAdmin(event, organizationId), + em = fork(), + appUser = await em.findOne(AppUser, { email: email }), existingOrgAppUserRole = await em.findOne(AppUserOrganizationRole, { appUser: appUser, - organization: body.data.organizationId + organization: organizationId }) if (!appUser) @@ -46,18 +29,6 @@ export default defineEventHandler(async (event) => { statusMessage: "Not Found", message: "The appuser with the given email does not exist." }) - if (!sessionUserRole && !session.isSystemAdmin) - throw createError({ - statusCode: 403, - statusMessage: "Forbidden", - message: "You are not associated with the organization." - }) - if (!session.isSystemAdmin && sessionUserRole && sessionUserRole.role !== AppRole.ORGANIZATION_ADMIN) - throw createError({ - statusCode: 403, - statusMessage: "Forbidden", - message: "You must be an organization admin or a system admin to invite an appuser." - }) if (existingOrgAppUserRole) throw createError({ statusCode: 409, @@ -65,11 +36,7 @@ export default defineEventHandler(async (event) => { message: "The appuser is already associated with the organization." }) - em.create(AppUserOrganizationRole, { - appUser: appUser, - organization: body.data.organizationId, - role: body.data.role - }) + em.create(AppUserOrganizationRole, { appUser, organization, role }) await em.flush() }) \ No newline at end of file diff --git a/server/api/assumptions/[id].delete.ts b/server/api/assumptions/[id].delete.ts index 9926d9cf..3351a66a 100644 --- a/server/api/assumptions/[id].delete.ts +++ b/server/api/assumptions/[id].delete.ts @@ -1,20 +1,25 @@ +import { z } from "zod" import { fork } from "~/server/data/orm" -import { Assumption } from "~/server/domain/requirements/index" +import { Assumption } from "~/server/domain/requirements/index.js" + +const paramSchema = z.object({ + id: z.string().uuid() +}) + +const bodySchema = z.object({ + solutionId: z.string().uuid() +}) /** - * Delete an assumption by id. + * Delete an assumption associated with a solution */ export default defineEventHandler(async (event) => { - const id = event.context.params?.id, + const { id } = await validateEventParams(event, paramSchema), + { solutionId } = await validateEventBody(event, bodySchema), em = fork() - if (id) { - em.remove(em.getReference(Assumption, id)) - await em.flush() - } else { - throw createError({ - statusCode: 400, - statusMessage: "Bad Request: id is required." - }) - } -}) + await assertSolutionContributor(event, solutionId) + + em.remove(em.getReference(Assumption, id)) + await em.flush() +}) \ No newline at end of file diff --git a/server/api/assumptions/[id].get.ts b/server/api/assumptions/[id].get.ts index f0270298..b4700663 100644 --- a/server/api/assumptions/[id].get.ts +++ b/server/api/assumptions/[id].get.ts @@ -1,27 +1,32 @@ +import { z } from "zod" import { fork } from "~/server/data/orm" -import { Assumption } from "~/server/domain/requirements/index" +import { Assumption } from "~/server/domain/requirements/index.js" + +const paramSchema = z.object({ + id: z.string().uuid() +}) + +const querySchema = z.object({ + solutionId: z.string().uuid() +}) /** - * Returns an assumption by id + * Returns an assumption associated with a solution */ export default defineEventHandler(async (event) => { - const id = event.context.params?.id, + const { id } = await validateEventParams(event, paramSchema), + { solutionId } = await validateEventQuery(event, querySchema), em = fork() - if (id) { - const result = await em.findOne(Assumption, id) + await assertSolutionReader(event, solutionId) - if (result) - return result - else - throw createError({ - statusCode: 404, - statusMessage: `Item not found with the given id: ${id}` - }) - } else { + const result = await em.findOne(Assumption, id) + + if (!result) throw createError({ - statusCode: 400, - statusMessage: "Bad Request: id is required." + statusCode: 404, + statusMessage: `Item not found with the given id: ${id}` }) - } -}) + + return result +}) \ No newline at end of file diff --git a/server/api/assumptions/[id].put.ts b/server/api/assumptions/[id].put.ts index 12ea5642..fed023f3 100644 --- a/server/api/assumptions/[id].put.ts +++ b/server/api/assumptions/[id].put.ts @@ -1,55 +1,37 @@ import { fork } from "~/server/data/orm" import { z } from "zod" -import { Assumption } from "~/server/domain/requirements/index" -import Solution from "~/server/domain/application/Solution" +import { Assumption } from "~/server/domain/requirements/index.js" + +const paramSchema = z.object({ + id: z.string().uuid() +}) const bodySchema = z.object({ + solutionId: z.string().uuid(), name: z.string().min(1), - statement: z.string(), - solutionId: z.string().uuid() + statement: z.string() }) /** - * PUT /api/assumptions/:id - * * Updates an assumption by id. */ export default defineEventHandler(async (event) => { - const id = event.context.params?.id, - body = await readValidatedBody(event, (b) => bodySchema.safeParse(b)), + const { id } = await validateEventParams(event, paramSchema), + { name, statement, solutionId } = await validateEventBody(event, bodySchema), + { sessionUser } = await assertSolutionContributor(event, solutionId), em = fork() - if (!body.success) + const assumption = await em.findOne(Assumption, id) + + if (!assumption) throw createError({ statusCode: 400, - statusMessage: 'Bad Request: Invalid body parameters', - message: JSON.stringify(body.error.errors) + statusMessage: `Bad Request: No assumption found with id: ${id}` }) - if (id) { - const assumption = await em.findOne(Assumption, id), - solution = await em.findOne(Solution, body.data.solutionId) - - if (!assumption) - throw createError({ - statusCode: 400, - statusMessage: `Bad Request: No assumption found with id: ${id}` - }) - if (!solution) - throw createError({ - statusCode: 400, - statusMessage: `Bad Request: No solution found with id: ${body.data.solutionId}` - }) + assumption.name = name + assumption.statement = statement + assumption.modifiedBy = sessionUser - assumption.name = body.data.name - assumption.statement = body.data.statement - assumption.solution = solution - - await em.flush() - } else { - throw createError({ - statusCode: 400, - statusMessage: "Bad Request: id is required." - }) - } + await em.flush() }) \ No newline at end of file diff --git a/server/api/assumptions/index.get.ts b/server/api/assumptions/index.get.ts index d8f5eb7c..6e21a34e 100644 --- a/server/api/assumptions/index.get.ts +++ b/server/api/assumptions/index.get.ts @@ -1,34 +1,23 @@ import { z } from "zod" +import { Assumption } from "~/server/domain/requirements/index.js" import { fork } from "~/server/data/orm" -import { Assumption } from "~/server/domain/requirements/index" const querySchema = z.object({ + solutionId: z.string().uuid(), name: z.string().optional(), - statement: z.string().optional(), - solutionId: z.string().uuid().optional() + statement: z.string().optional() }) /** - * GET /api/assumptions - * - * Returns all assumptions - * - * GET /api/assumptions?name&statement&solutionId - * * Returns all assumptions that match the query parameters */ export default defineEventHandler(async (event) => { - const query = await getValidatedQuery(event, (q) => querySchema.safeParse(q)), + const query = await validateEventQuery(event, querySchema), em = fork() - if (!query.success) - throw createError({ - statusCode: 400, - statusMessage: "Bad Request: Invalid query parameters", - message: JSON.stringify(query.error.errors) - }) + await assertSolutionReader(event, query.solutionId) - const results = await em.find(Assumption, Object.entries(query.data) + const results = await em.find(Assumption, Object.entries(query) .filter(([_, value]) => value !== undefined) .reduce((acc, [key, value]) => { if (key.endsWith("Id")) @@ -38,4 +27,4 @@ export default defineEventHandler(async (event) => { ); return results -}) +}) \ No newline at end of file diff --git a/server/api/assumptions/index.post.ts b/server/api/assumptions/index.post.ts index 37a4786a..76dcf01f 100644 --- a/server/api/assumptions/index.post.ts +++ b/server/api/assumptions/index.post.ts @@ -1,44 +1,29 @@ import { fork } from "~/server/data/orm" import { z } from "zod" -import { Assumption } from "~/server/domain/requirements/index" -import Solution from "~/server/domain/application/Solution" +import { Assumption } from "~/server/domain/requirements/index.js" const bodySchema = z.object({ + solutionId: z.string().uuid(), name: z.string().min(1), - statement: z.string(), - solutionId: z.string().uuid() + statement: z.string() }) /** - * POST /api/assumptions - * * Creates a new assumption and returns its id */ export default defineEventHandler(async (event) => { - const body = await readValidatedBody(event, (b) => bodySchema.safeParse(b)), - em = fork() - - if (!body.success) - throw createError({ - statusCode: 400, - statusMessage: 'Bad Request: Invalid body parameters', - message: JSON.stringify(body.error.errors) + const { name, statement, solutionId } = await validateEventBody(event, bodySchema), + { solution, sessionUser } = await assertSolutionContributor(event, solutionId) + + const em = fork(), + newAssumption = new Assumption({ + name, + statement, + solution, + modifiedBy: sessionUser, + lastModified: new Date() }) - const solution = await em.findOne(Solution, body.data.solutionId) - - if (!solution) - throw createError({ - statusCode: 400, - statusMessage: `Bad Request: Solution not found for id ${body.data.solutionId}` - }) - - const newAssumption = new Assumption({ - name: body.data.name, - statement: body.data.statement, - solution - }) - await em.persistAndFlush(newAssumption) return newAssumption.id diff --git a/server/api/constraints/[id].delete.ts b/server/api/constraints/[id].delete.ts index 44942cfb..66cf42a9 100644 --- a/server/api/constraints/[id].delete.ts +++ b/server/api/constraints/[id].delete.ts @@ -1,20 +1,25 @@ +import { z } from "zod" import { fork } from "~/server/data/orm" -import { Constraint } from "~/server/domain/requirements/index" +import { Constraint } from "~/server/domain/requirements/index.js" + +const paramSchema = z.object({ + id: z.string().uuid() +}) + +const bodySchema = z.object({ + solutionId: z.string().uuid() +}) /** * Delete constraint by id. */ export default defineEventHandler(async (event) => { - const id = event.context.params?.id, + const { id } = await validateEventParams(event, paramSchema), + { solutionId } = await validateEventBody(event, bodySchema), em = fork() - if (id) { - em.remove(em.getReference(Constraint, id)) - await em.flush() - } else { - throw createError({ - statusCode: 400, - statusMessage: "Bad Request: id is required." - }) - } -}) + await assertSolutionContributor(event, solutionId) + + em.remove(em.getReference(Constraint, id)) + await em.flush() +}) \ No newline at end of file diff --git a/server/api/constraints/[id].get.ts b/server/api/constraints/[id].get.ts index b2a5032d..1f1a7a8c 100644 --- a/server/api/constraints/[id].get.ts +++ b/server/api/constraints/[id].get.ts @@ -1,27 +1,32 @@ +import { z } from "zod" import { fork } from "~/server/data/orm" -import { Constraint } from "~/server/domain/requirements/index" +import { Constraint } from "~/server/domain/requirements/Constraint.js" + +const paramSchema = z.object({ + id: z.string().uuid() +}) + +const querySchema = z.object({ + solutionId: z.string().uuid() +}) /** * Returns a constraint by id */ export default defineEventHandler(async (event) => { - const id = event.context.params?.id, + const { id } = await validateEventParams(event, paramSchema), + { solutionId } = await validateEventQuery(event, querySchema), em = fork() - if (id) { - const result = await em.findOne(Constraint, id) + await assertSolutionReader(event, solutionId) - if (result) - return result - else - throw createError({ - statusCode: 404, - statusMessage: `Item not found with the given id: ${id}` - }) - } else { + const result = await em.findOne(Constraint, id) + + if (!result) throw createError({ - statusCode: 400, - statusMessage: "Bad Request: id is required." + statusCode: 404, + statusMessage: `Item not found with the given id: ${id}` }) - } -}) + + return result +}) \ No newline at end of file diff --git a/server/api/constraints/[id].put.ts b/server/api/constraints/[id].put.ts index d3bd7905..b55e3ff2 100644 --- a/server/api/constraints/[id].put.ts +++ b/server/api/constraints/[id].put.ts @@ -1,57 +1,39 @@ import { z } from "zod" -import { Constraint, ConstraintCategory } from "~/server/domain/requirements/index" +import { Constraint, ConstraintCategory } from "~/server/domain/requirements/index.js" import { fork } from "~/server/data/orm" -import Solution from "~/server/domain/application/Solution" + +const paramSchema = z.object({ + id: z.string().uuid() +}) const bodySchema = z.object({ + solutionId: z.string().uuid(), name: z.string().min(1), statement: z.string(), - solutionId: z.string().uuid(), category: z.nativeEnum(ConstraintCategory) }) /** - * PUT /api/constraints/:id - * * Updates a constraint by id. */ export default defineEventHandler(async (event) => { - const id = event.context.params?.id, - body = await readValidatedBody(event, (b) => bodySchema.safeParse(b)), + const { id } = await validateEventParams(event, paramSchema), + { category, name, statement, solutionId } = await validateEventBody(event, bodySchema), + { sessionUser } = await assertSolutionContributor(event, solutionId), em = fork() - if (!body.success) + const constraint = await em.findOne(Constraint, id) + + if (!constraint) throw createError({ statusCode: 400, - statusMessage: 'Bad Request: Invalid body parameters', - message: JSON.stringify(body.error.errors) + statusMessage: `Bad Request: No constraint found with id: ${id}` }) - if (id) { - const constraint = await em.findOne(Constraint, id), - solution = await em.findOne(Solution, body.data.solutionId) + constraint.name = name + constraint.statement = statement + constraint.category = category + constraint.modifiedBy = sessionUser - if (!constraint) - throw createError({ - statusCode: 400, - statusMessage: `Bad Request: No constraint found with id: ${id}` - }) - if (!solution) - throw createError({ - statusCode: 400, - statusMessage: `Bad Request: No solution found with id: ${body.data.solutionId}` - }) - - constraint.name = body.data.name - constraint.statement = body.data.statement - constraint.solution = solution - constraint.category = body.data.category - - await em.flush() - } else { - throw createError({ - statusCode: 400, - statusMessage: "Bad Request: id is required." - }) - } + await em.flush() }) \ No newline at end of file diff --git a/server/api/constraints/index.get.ts b/server/api/constraints/index.get.ts index cedd1bf3..24a5e41b 100644 --- a/server/api/constraints/index.get.ts +++ b/server/api/constraints/index.get.ts @@ -1,35 +1,24 @@ import { z } from "zod" import { fork } from "~/server/data/orm" -import { Constraint, ConstraintCategory } from "~/server/domain/requirements/index" +import { Constraint, ConstraintCategory } from "~/server/domain/requirements/index.js" const querySchema = z.object({ + solutionId: z.string().uuid(), name: z.string().optional(), statement: z.string().optional(), - solutionId: z.string().uuid().optional(), category: z.nativeEnum(ConstraintCategory).optional() }) /** - * GET /api/constraints - * - * Returns all constraints - * - * GET /api/constraints?name&statement&solutionId&category - * * Returns all constraints that match the query parameters */ export default defineEventHandler(async (event) => { - const query = await getValidatedQuery(event, (q) => querySchema.safeParse(q)), + const query = await validateEventQuery(event, querySchema), em = fork() - if (!query.success) - throw createError({ - statusCode: 400, - statusMessage: "Bad Request: Invalid query parameters", - message: JSON.stringify(query.error.errors) - }) + await assertSolutionReader(event, query.solutionId) - const results = await em.find(Constraint, Object.entries(query.data) + const results = await em.find(Constraint, Object.entries(query) .filter(([_, value]) => value !== undefined) .reduce((acc, [key, value]) => { if (key.endsWith("Id")) @@ -38,4 +27,4 @@ export default defineEventHandler(async (event) => { }, {})) return results -}) +}) \ No newline at end of file diff --git a/server/api/constraints/index.post.ts b/server/api/constraints/index.post.ts index d672bbc1..a637416c 100644 --- a/server/api/constraints/index.post.ts +++ b/server/api/constraints/index.post.ts @@ -1,44 +1,29 @@ import { z } from "zod" import { fork } from "~/server/data/orm" -import { Constraint, ConstraintCategory } from "~/server/domain/requirements/index" -import Solution from "~/server/domain/application/Solution" +import { Constraint, ConstraintCategory } from "~/server/domain/requirements/index.js" const bodySchema = z.object({ + solutionId: z.string().uuid(), name: z.string().min(1), statement: z.string(), - solutionId: z.string().uuid(), category: z.nativeEnum(ConstraintCategory) }) /** - * POST /api/constraints - * * Creates a new constraint and returns its id */ export default defineEventHandler(async (event) => { - const body = await readValidatedBody(event, (b) => bodySchema.safeParse(b)), + const { category, name, statement, solutionId } = await validateEventBody(event, bodySchema), + { solution, sessionUser } = await assertSolutionContributor(event, solutionId), em = fork() - if (!body.success) - throw createError({ - statusCode: 400, - statusMessage: 'Bad Request: Invalid body parameters', - message: JSON.stringify(body.error.errors) - }) - - const solution = await em.findOne(Solution, body.data.solutionId) - - if (!solution) - throw createError({ - statusCode: 400, - statusMessage: `Bad Request: Solution not found for id ${body.data.solutionId}` - }) - const newConstraint = new Constraint({ - name: body.data.name, - statement: body.data.statement, + name, + statement, solution, - category: body.data.category + category, + lastModified: new Date(), + modifiedBy: sessionUser }) await em.persistAndFlush(newConstraint) diff --git a/server/api/effects/[id].delete.ts b/server/api/effects/[id].delete.ts index ff3ca7de..bebb23f6 100644 --- a/server/api/effects/[id].delete.ts +++ b/server/api/effects/[id].delete.ts @@ -1,20 +1,25 @@ +import { z } from "zod" import { fork } from "~/server/data/orm" -import { Effect } from "~/server/domain/requirements/index" +import { Effect } from "~/server/domain/requirements/index.js" + +const paramSchema = z.object({ + id: z.string().uuid() +}) + +const bodySchema = z.object({ + solutionId: z.string().uuid() +}) /** * Delete an effect by id. */ export default defineEventHandler(async (event) => { - const id = event.context.params?.id, + const { id } = await validateEventParams(event, paramSchema), + { solutionId } = await validateEventBody(event, bodySchema), em = fork() - if (id) { - em.remove(em.getReference(Effect, id)) - await em.flush() - } else { - throw createError({ - statusCode: 400, - statusMessage: "Bad Request: id is required." - }) - } -}) + await assertSolutionContributor(event, solutionId) + + em.remove(em.getReference(Effect, id)) + await em.flush() +}) \ No newline at end of file diff --git a/server/api/effects/[id].get.ts b/server/api/effects/[id].get.ts index 93a2995c..50a03a93 100644 --- a/server/api/effects/[id].get.ts +++ b/server/api/effects/[id].get.ts @@ -1,27 +1,32 @@ +import { z } from "zod" import { fork } from "~/server/data/orm" -import { Effect } from "~/server/domain/requirements/index" +import { Effect } from "~/server/domain/requirements/index.js" + +const paramSchema = z.object({ + id: z.string().uuid() +}) + +const querySchema = z.object({ + solutionId: z.string().uuid(), +}) /** * Returns an effect by id */ export default defineEventHandler(async (event) => { - const id = event.context.params?.id, + const { id } = await validateEventParams(event, paramSchema), + { solutionId } = await validateEventQuery(event, querySchema), em = fork() - if (id) { - const result = await em.findOne(Effect, id) + await assertSolutionReader(event, solutionId) - if (result) - return result - else - throw createError({ - statusCode: 404, - statusMessage: `Item not found with the given id: ${id}` - }) - } else { + const result = await em.findOne(Effect, id) + + if (!result) throw createError({ - statusCode: 400, - statusMessage: "Bad Request: id is required." + statusCode: 404, + statusMessage: `Item not found with the given id: ${id}` }) - } -}) + + return result +}) \ No newline at end of file diff --git a/server/api/effects/[id].put.ts b/server/api/effects/[id].put.ts index 2b919dcf..e536d41a 100644 --- a/server/api/effects/[id].put.ts +++ b/server/api/effects/[id].put.ts @@ -1,55 +1,37 @@ import { fork } from "~/server/data/orm" import { z } from "zod" -import { Effect } from "~/server/domain/requirements/index" -import Solution from "~/server/domain/application/Solution" +import { Effect } from "~/server/domain/requirements/index.js" + +const paramSchema = z.object({ + id: z.string().uuid() +}) const bodySchema = z.object({ + solutionId: z.string().uuid(), name: z.string().min(1), - statement: z.string(), - solutionId: z.string().uuid() + statement: z.string() }) /** - * PUT /api/effects/:id - * * Updates an effect by id. */ export default defineEventHandler(async (event) => { - const id = event.context.params?.id, - body = await readValidatedBody(event, (b) => bodySchema.safeParse(b)), + const { id } = await validateEventParams(event, paramSchema), + { name, statement, solutionId } = await validateEventBody(event, bodySchema), + { sessionUser } = await assertSolutionContributor(event, solutionId), em = fork() - if (!body.success) + const effect = await em.findOne(Effect, id) + + if (!effect) throw createError({ statusCode: 400, - statusMessage: 'Bad Request: Invalid body parameters', - message: JSON.stringify(body.error.errors) + statusMessage: `Bad Request: No effect found with id: ${id}` }) - if (id) { - const effect = await em.findOne(Effect, id), - solution = await em.findOne(Solution, body.data.solutionId) - - if (!effect) - throw createError({ - statusCode: 400, - statusMessage: `Bad Request: No effect found with id: ${id}` - }) - if (!solution) - throw createError({ - statusCode: 400, - statusMessage: `Bad Request: No solution found with id: ${body.data.solutionId}` - }) + effect.name = name + effect.statement = statement + effect.modifiedBy = sessionUser - effect.name = body.data.name - effect.statement = body.data.statement - effect.solution = solution - - await em.flush() - } else { - throw createError({ - statusCode: 400, - statusMessage: "Bad Request: id is required." - }) - } + await em.flush() }) \ No newline at end of file diff --git a/server/api/effects/index.get.ts b/server/api/effects/index.get.ts index 9b759bc0..6e707f83 100644 --- a/server/api/effects/index.get.ts +++ b/server/api/effects/index.get.ts @@ -1,34 +1,23 @@ import { z } from "zod" import { fork } from "~/server/data/orm" -import { Effect } from "~/server/domain/requirements/index" +import { Effect } from "~/server/domain/requirements/index.js" const querySchema = z.object({ + solutionId: z.string().uuid(), name: z.string().optional(), - statement: z.string().optional(), - solutionId: z.string().uuid().optional() + statement: z.string().optional() }) /** - * GET /api/effects - * - * Returns all effects - * - * GET /api/effects?name&statement&solutionId - * * Returns all effects that match the query parameters */ export default defineEventHandler(async (event) => { - const query = await getValidatedQuery(event, (q) => querySchema.safeParse(q)), + const query = await validateEventQuery(event, querySchema), em = fork() - if (!query.success) - throw createError({ - statusCode: 400, - statusMessage: "Bad Request: Invalid query parameters", - message: JSON.stringify(query.error.errors) - }) + await assertSolutionReader(event, query.solutionId) - const results = await em.find(Effect, Object.entries(query.data) + const results = await em.find(Effect, Object.entries(query) .filter(([_, value]) => value !== undefined) .reduce((acc, [key, value]) => { if (key.endsWith("Id")) @@ -37,4 +26,4 @@ export default defineEventHandler(async (event) => { }, {})) return results -}) +}) \ No newline at end of file diff --git a/server/api/effects/index.post.ts b/server/api/effects/index.post.ts index 8c462027..a8ee1a4f 100644 --- a/server/api/effects/index.post.ts +++ b/server/api/effects/index.post.ts @@ -1,42 +1,27 @@ import { fork } from "~/server/data/orm" import { z } from "zod" -import { Effect } from "~/server/domain/requirements/index" -import Solution from "~/server/domain/application/Solution" +import { Effect } from "~/server/domain/requirements/index.js" const bodySchema = z.object({ + solutionId: z.string().uuid(), name: z.string().min(1), - statement: z.string(), - solutionId: z.string().uuid() + statement: z.string() }) /** - * POST /api/effects - * * Creates a new effect and returns its id */ export default defineEventHandler(async (event) => { - const body = await readValidatedBody(event, (b) => bodySchema.safeParse(b)), + const { name, statement, solutionId } = await validateEventBody(event, bodySchema), + { solution, sessionUser } = await assertSolutionContributor(event, solutionId), em = fork() - if (!body.success) - throw createError({ - statusCode: 400, - statusMessage: 'Bad Request: Invalid body parameters', - message: JSON.stringify(body.error.errors) - }) - - const solution = await em.findOne(Solution, body.data.solutionId) - - if (!solution) - throw createError({ - statusCode: 400, - statusMessage: `Bad Request: Solution not found for id ${body.data.solutionId}` - }) - const newEffect = new Effect({ - name: body.data.name, - statement: body.data.statement, - solution + name, + statement, + solution, + modifiedBy: sessionUser, + lastModified: new Date() }) await em.persistAndFlush(newEffect) diff --git a/server/api/environment-components/[id].delete.ts b/server/api/environment-components/[id].delete.ts index 40891cb2..cd712706 100644 --- a/server/api/environment-components/[id].delete.ts +++ b/server/api/environment-components/[id].delete.ts @@ -1,20 +1,25 @@ +import { z } from "zod" import { fork } from "~/server/data/orm" -import { EnvironmentComponent } from "~/server/domain/requirements/index" +import { EnvironmentComponent } from "~/server/domain/requirements/index.js" + +const paramSchema = z.object({ + id: z.string().uuid() +}) + +const bodySchema = z.object({ + solutionId: z.string().uuid() +}) /** * Delete an environment component by id. */ export default defineEventHandler(async (event) => { - const id = event.context.params?.id, + const { id } = await validateEventParams(event, paramSchema), + { solutionId } = await validateEventBody(event, bodySchema), em = fork() - if (id) { - em.remove(em.getReference(EnvironmentComponent, id)) - await em.flush() - } else { - throw createError({ - statusCode: 400, - statusMessage: "Bad Request: id is required." - }) - } -}) + await assertSolutionContributor(event, solutionId) + + em.remove(em.getReference(EnvironmentComponent, id)) + await em.flush() +}) \ No newline at end of file diff --git a/server/api/environment-components/[id].get.ts b/server/api/environment-components/[id].get.ts index 04d07696..400ab380 100644 --- a/server/api/environment-components/[id].get.ts +++ b/server/api/environment-components/[id].get.ts @@ -1,27 +1,32 @@ +import { z } from "zod" import { fork } from "~/server/data/orm" -import { EnvironmentComponent } from "~/server/domain/requirements/index" +import { EnvironmentComponent } from "~/server/domain/requirements/index.js" + +const paramSchema = z.object({ + id: z.string().uuid() +}) + +const querySchema = z.object({ + solutionId: z.string().uuid() +}) /** * Returns an environment component by id */ export default defineEventHandler(async (event) => { - const id = event.context.params?.id, + const { id } = await validateEventParams(event, paramSchema), + { solutionId } = await validateEventQuery(event, querySchema), em = fork() - if (id) { - const result = await em.findOne(EnvironmentComponent, id) + await assertSolutionReader(event, solutionId) - if (result) - return result - else - throw createError({ - statusCode: 404, - statusMessage: `Item not found with the given id: ${id}` - }) - } else { + const result = await em.findOne(EnvironmentComponent, id) + + if (!result) throw createError({ - statusCode: 400, - statusMessage: "Bad Request: id is required." + statusCode: 404, + statusMessage: `Item not found with the given id: ${id}` }) - } -}) + + return result +}) \ No newline at end of file diff --git a/server/api/environment-components/[id].put.ts b/server/api/environment-components/[id].put.ts index 3bc53e54..d58578c8 100644 --- a/server/api/environment-components/[id].put.ts +++ b/server/api/environment-components/[id].put.ts @@ -1,58 +1,40 @@ import { z } from "zod" import { fork } from "~/server/data/orm" -import { EnvironmentComponent } from "~/server/domain/requirements/index" -import Solution from "~/server/domain/application/Solution" +import { EnvironmentComponent } from "~/server/domain/requirements/index.js" + +const paramSchema = z.object({ + id: z.string().uuid() +}) const bodySchema = z.object({ + solutionId: z.string().uuid(), name: z.string(), statement: z.string(), - solutionId: z.string().uuid(), parentComponentId: z.string().uuid().optional() }) /** - * PUT /api/environment-components/:id - * * Updates an assumption by id. */ export default defineEventHandler(async (event) => { - const id = event.context.params?.id, - body = await readValidatedBody(event, (b) => bodySchema.safeParse(b)), + const { id } = await validateEventParams(event, paramSchema), + { name, statement, parentComponentId, solutionId } = await validateEventBody(event, bodySchema), + { sessionUser } = await assertSolutionContributor(event, solutionId), em = fork() - if (!body.success) + const environmentComponent = await em.findOne(EnvironmentComponent, id), + parentComponent = parentComponentId ? await em.findOne(EnvironmentComponent, parentComponentId) : null + + if (!environmentComponent) throw createError({ statusCode: 400, - statusMessage: 'Bad Request: Invalid body parameters', - message: JSON.stringify(body.error.errors) + statusMessage: `Bad Request: No effect found with id: ${id}` }) - if (id) { - const environmentComponent = await em.findOne(EnvironmentComponent, id), - parentComponent = body.data.parentComponentId ? await em.findOne(EnvironmentComponent, body.data.parentComponentId) : null, - solution = await em.findOne(Solution, body.data.solutionId) + environmentComponent.name = name + environmentComponent.statement = statement + environmentComponent.parentComponent = parentComponent ?? undefined + environmentComponent.modifiedBy = sessionUser - if (!environmentComponent) - throw createError({ - statusCode: 400, - statusMessage: `Bad Request: No effect found with id: ${id}` - }) - if (!solution) - throw createError({ - statusCode: 400, - statusMessage: `Bad Request: No solution found with id: ${body.data.solutionId}` - }) - - environmentComponent.name = body.data.name - environmentComponent.statement = body.data.statement - environmentComponent.solution = solution - environmentComponent.parentComponent = parentComponent ?? undefined - - await em.flush() - } else { - throw createError({ - statusCode: 400, - statusMessage: "Bad Request: id is required." - }) - } + await em.flush() }) \ No newline at end of file diff --git a/server/api/environment-components/index.get.ts b/server/api/environment-components/index.get.ts index b67b68f8..52bf29cd 100644 --- a/server/api/environment-components/index.get.ts +++ b/server/api/environment-components/index.get.ts @@ -1,35 +1,24 @@ import { z } from "zod" import { fork } from "~/server/data/orm" -import { EnvironmentComponent } from "~/server/domain/requirements/index" +import { EnvironmentComponent } from "~/server/domain/requirements/index.js" const querySchema = z.object({ + solutionId: z.string().uuid(), name: z.string().optional(), statement: z.string().optional(), - solutionId: z.string().uuid().optional(), parentComponentId: z.string().uuid().optional() }) /** - * GET /api/environment-components - * - * Returns all environment components - * - * GET /api/environment-components?name&statement&solutionId&parentComponentId - * * Returns all environment-components that match the query parameters */ export default defineEventHandler(async (event) => { - const query = await getValidatedQuery(event, (q) => querySchema.safeParse(q)), + const query = await validateEventQuery(event, querySchema), em = fork() - if (!query.success) - throw createError({ - statusCode: 400, - statusMessage: "Bad Request: Invalid query parameters", - message: JSON.stringify(query.error.errors) - }) + await assertSolutionReader(event, query.solutionId) - const results = await em.find(EnvironmentComponent, Object.entries(query.data) + const results = await em.find(EnvironmentComponent, Object.entries(query) .filter(([_, value]) => value !== undefined) .reduce((acc, [key, value]) => { if (key.endsWith("Id")) @@ -38,4 +27,4 @@ export default defineEventHandler(async (event) => { }, {})) return results -}) +}) \ No newline at end of file diff --git a/server/api/environment-components/index.post.ts b/server/api/environment-components/index.post.ts index 72c24d87..02c19150 100644 --- a/server/api/environment-components/index.post.ts +++ b/server/api/environment-components/index.post.ts @@ -1,46 +1,31 @@ import { z } from "zod" import { fork } from "~/server/data/orm" -import Solution from "~/server/domain/application/Solution" -import { EnvironmentComponent } from "~/server/domain/requirements/index" +import { EnvironmentComponent } from "~/server/domain/requirements/index.js" const bodySchema = z.object({ + solutionId: z.string().uuid(), name: z.string(), statement: z.string(), - solutionId: z.string().uuid(), parentComponentId: z.string().uuid().optional() }) /** - * POST /api/environment-components - * * Creates a new environment-component and returns its id */ export default defineEventHandler(async (event) => { - const body = await readValidatedBody(event, (b) => bodySchema.safeParse(b)), + const { name, statement, parentComponentId, solutionId } = await validateEventBody(event, bodySchema), + { solution, sessionUser } = await assertSolutionContributor(event, solutionId), em = fork() - if (!body.success) - throw createError({ - statusCode: 400, - statusMessage: 'Bad Request: Invalid body parameters', - message: JSON.stringify(body.error.errors) - }) - - const solution = await em.findOne(Solution, body.data.solutionId) - - if (!solution) - throw createError({ - statusCode: 400, - statusMessage: `Bad Request: Solution not found for id ${body.data.solutionId}` - }) - - const parentComponent = body.data.parentComponentId ? await em.findOne(EnvironmentComponent, body.data.parentComponentId) : null + const parentComponent = parentComponentId ? await em.findOne(EnvironmentComponent, parentComponentId) : null const newEnvironmentComponent = new EnvironmentComponent({ - name: body.data.name, - statement: body.data.statement, + name, + statement, solution, - parentComponent: parentComponent ?? undefined + parentComponent: parentComponent ?? undefined, + lastModified: new Date(), + modifiedBy: sessionUser }) await em.persistAndFlush(newEnvironmentComponent) diff --git a/server/api/functional-behaviors/[id].delete.ts b/server/api/functional-behaviors/[id].delete.ts index c81cf15e..60417250 100644 --- a/server/api/functional-behaviors/[id].delete.ts +++ b/server/api/functional-behaviors/[id].delete.ts @@ -1,20 +1,25 @@ +import { z } from "zod" import { fork } from "~/server/data/orm" -import { FunctionalBehavior } from "~/server/domain/requirements/index" +import { FunctionalBehavior } from "~/server/domain/requirements/index.js" + +const paramSchema = z.object({ + id: z.string().uuid() +}) + +const bodySchema = z.object({ + solutionId: z.string().uuid() +}) /** * Delete an functional behavior by id. */ export default defineEventHandler(async (event) => { - const id = event.context.params?.id, + const { id } = await validateEventParams(event, paramSchema), + { solutionId } = await validateEventBody(event, bodySchema), em = fork() - if (id) { - em.remove(em.getReference(FunctionalBehavior, id)) - await em.flush() - } else { - throw createError({ - statusCode: 400, - statusMessage: "Bad Request: id is required." - }) - } -}) + await assertSolutionContributor(event, solutionId) + + em.remove(em.getReference(FunctionalBehavior, id)) + await em.flush() +}) \ No newline at end of file diff --git a/server/api/functional-behaviors/[id].get.ts b/server/api/functional-behaviors/[id].get.ts index cd282265..ecc32b7d 100644 --- a/server/api/functional-behaviors/[id].get.ts +++ b/server/api/functional-behaviors/[id].get.ts @@ -1,27 +1,32 @@ +import { z } from "zod" import { fork } from "~/server/data/orm" -import { FunctionalBehavior } from "~/server/domain/requirements/index" +import { FunctionalBehavior } from "~/server/domain/requirements/index.js" + +const paramSchema = z.object({ + id: z.string().uuid() +}) + +const querySchema = z.object({ + solutionId: z.string().uuid() +}) /** * Returns a functional behavior by id */ export default defineEventHandler(async (event) => { - const id = event.context.params?.id, + const { id } = await validateEventParams(event, paramSchema), + { solutionId } = await validateEventQuery(event, querySchema), em = fork() - if (id) { - const result = await em.findOne(FunctionalBehavior, id) + await assertSolutionReader(event, solutionId) - if (result) - return result - else - throw createError({ - statusCode: 404, - statusMessage: `Item not found with the given id: ${id}` - }) - } else { + const result = await em.findOne(FunctionalBehavior, id) + + if (!result) throw createError({ - statusCode: 400, - statusMessage: "Bad Request: id is required." + statusCode: 404, + statusMessage: `Item not found with the given id: ${id}` }) - } -}) + + return result +}) \ No newline at end of file diff --git a/server/api/functional-behaviors/[id].put.ts b/server/api/functional-behaviors/[id].put.ts index ba0af089..13bdf43a 100644 --- a/server/api/functional-behaviors/[id].put.ts +++ b/server/api/functional-behaviors/[id].put.ts @@ -1,12 +1,15 @@ import { z } from "zod" +import { FunctionalBehavior, MoscowPriority } from "~/server/domain/requirements/index.js" import { fork } from "~/server/data/orm" -import { FunctionalBehavior, MoscowPriority } from "~/server/domain/requirements/index" -import Solution from "~/server/domain/application/Solution" + +const paramSchema = z.object({ + id: z.string().uuid() +}) const bodySchema = z.object({ + solutionId: z.string().uuid(), name: z.string().min(1), statement: z.string(), - solutionId: z.string().uuid(), priority: z.nativeEnum(MoscowPriority) }) @@ -14,42 +17,23 @@ const bodySchema = z.object({ * Updates a functional behavior by id. */ export default defineEventHandler(async (event) => { - const id = event.context.params?.id, - body = await readValidatedBody(event, (b) => bodySchema.safeParse(b)), + const { id } = await validateEventParams(event, paramSchema), + { name, priority, statement, solutionId } = await validateEventBody(event, bodySchema), + { sessionUser } = await assertSolutionContributor(event, solutionId), em = fork() - if (!body.success) + const functionalBehavior = await em.findOne(FunctionalBehavior, id) + + if (!functionalBehavior) throw createError({ statusCode: 400, - statusMessage: 'Bad Request: Invalid body parameters', - message: JSON.stringify(body.error.errors) + statusMessage: `Bad Request: No effect found with id: ${id}` }) - if (id) { - const functionalBehavior = await em.findOne(FunctionalBehavior, id), - solution = await em.findOne(Solution, body.data.solutionId) + functionalBehavior.name = name + functionalBehavior.statement = statement + functionalBehavior.priority = priority + functionalBehavior.modifiedBy = sessionUser - if (!functionalBehavior) - throw createError({ - statusCode: 400, - statusMessage: `Bad Request: No effect found with id: ${id}` - }) - if (!solution) - throw createError({ - statusCode: 400, - statusMessage: `Bad Request: No solution found with id: ${body.data.solutionId}` - }) - - functionalBehavior.name = body.data.name - functionalBehavior.statement = body.data.statement - functionalBehavior.solution = solution - functionalBehavior.priority = body.data.priority - - await em.flush() - } else { - throw createError({ - statusCode: 400, - statusMessage: "Bad Request: id is required." - }) - } + await em.flush() }) \ No newline at end of file diff --git a/server/api/functional-behaviors/index.get.ts b/server/api/functional-behaviors/index.get.ts index d083fcd5..4457495e 100644 --- a/server/api/functional-behaviors/index.get.ts +++ b/server/api/functional-behaviors/index.get.ts @@ -1,11 +1,11 @@ import { z } from "zod" import { fork } from "~/server/data/orm" -import { FunctionalBehavior, MoscowPriority } from "~/server/domain/requirements/index" +import { FunctionalBehavior, MoscowPriority } from "~/server/domain/requirements/index.js" const querySchema = z.object({ + solutionId: z.string().uuid(), name: z.string().optional(), statement: z.string().optional(), - solutionId: z.string().uuid().optional(), priority: z.nativeEnum(MoscowPriority).optional() }) @@ -13,17 +13,12 @@ const querySchema = z.object({ * Returns all functional behaviors that match the query parameters */ export default defineEventHandler(async (event) => { - const query = await getValidatedQuery(event, (q) => querySchema.safeParse(q)), + const query = await validateEventQuery(event, querySchema), em = fork() - if (!query.success) - throw createError({ - statusCode: 400, - statusMessage: "Bad Request: Invalid query parameters", - message: JSON.stringify(query.error.errors) - }) + await assertSolutionReader(event, query.solutionId) - const results = await em.find(FunctionalBehavior, Object.entries(query.data) + const results = await em.find(FunctionalBehavior, Object.entries(query) .filter(([_, value]) => value !== undefined) .reduce((acc, [key, value]) => { if (key.endsWith("Id")) @@ -32,4 +27,4 @@ export default defineEventHandler(async (event) => { }, {})) return results -}) +}) \ No newline at end of file diff --git a/server/api/functional-behaviors/index.post.ts b/server/api/functional-behaviors/index.post.ts index 1e60bdb1..d3e1ff96 100644 --- a/server/api/functional-behaviors/index.post.ts +++ b/server/api/functional-behaviors/index.post.ts @@ -1,12 +1,11 @@ import { fork } from "~/server/data/orm" import { z } from "zod" -import Solution from "~/server/domain/application/Solution" -import { FunctionalBehavior, MoscowPriority } from "~/server/domain/requirements/index" +import { MoscowPriority, FunctionalBehavior } from "~/server/domain/requirements/index.js" const bodySchema = z.object({ + solutionId: z.string().uuid(), name: z.string().min(1), statement: z.string(), - solutionId: z.string().uuid(), priority: z.nativeEnum(MoscowPriority) }) @@ -14,29 +13,17 @@ const bodySchema = z.object({ * Creates a new functional behavior and returns its id */ export default defineEventHandler(async (event) => { - const body = await readValidatedBody(event, (b) => bodySchema.safeParse(b)), + const { name, priority, statement, solutionId } = await validateEventBody(event, bodySchema), + { solution, sessionUser } = await assertSolutionContributor(event, solutionId), em = fork() - if (!body.success) - throw createError({ - statusCode: 400, - statusMessage: 'Bad Request: Invalid body parameters', - message: JSON.stringify(body.error.errors) - }) - - const solution = await em.findOne(Solution, body.data.solutionId) - - if (!solution) - throw createError({ - statusCode: 400, - statusMessage: `Bad Request: Solution not found for id ${body.data.solutionId}` - }) - const newFunctionalBehavior = new FunctionalBehavior({ - name: body.data.name, - statement: body.data.statement, + name, + statement, solution, - priority: body.data.priority + priority, + lastModified: new Date(), + modifiedBy: sessionUser }) await em.persistAndFlush(newFunctionalBehavior) diff --git a/server/api/glossary-terms/[id].delete.ts b/server/api/glossary-terms/[id].delete.ts index 35af327f..0bc8ea03 100644 --- a/server/api/glossary-terms/[id].delete.ts +++ b/server/api/glossary-terms/[id].delete.ts @@ -1,20 +1,25 @@ +import { z } from "zod" import { fork } from "~/server/data/orm" -import { GlossaryTerm } from "~/server/domain/requirements/index" +import { GlossaryTerm } from "~/server/domain/requirements/index.js" + +const paramSchema = z.object({ + id: z.string().uuid() +}) + +const bodySchema = z.object({ + solutionId: z.string().uuid(), +}) /** * Delete glossary term by id. */ export default defineEventHandler(async (event) => { - const id = event.context.params?.id, + const { id } = await validateEventParams(event, paramSchema), + { solutionId } = await validateEventBody(event, bodySchema), em = fork() - if (id) { - em.remove(em.getReference(GlossaryTerm, id)) - await em.flush() - } else { - throw createError({ - statusCode: 400, - statusMessage: "Bad Request: id is required." - }) - } -}) + await assertSolutionContributor(event, solutionId) + + em.remove(em.getReference(GlossaryTerm, id)) + await em.flush() +}) \ No newline at end of file diff --git a/server/api/glossary-terms/[id].get.ts b/server/api/glossary-terms/[id].get.ts index 13912153..92c85940 100644 --- a/server/api/glossary-terms/[id].get.ts +++ b/server/api/glossary-terms/[id].get.ts @@ -1,27 +1,32 @@ +import { z } from "zod" import { fork } from "~/server/data/orm" -import { GlossaryTerm } from "~/server/domain/requirements/index" +import { GlossaryTerm } from "~/server/domain/requirements/index.js" + +const paramSchema = z.object({ + id: z.string().uuid() +}) + +const querySchema = z.object({ + solutionId: z.string().uuid() +}) /** * Returns a glossary term by id */ export default defineEventHandler(async (event) => { - const id = event.context.params?.id, + const { id } = await validateEventParams(event, paramSchema), + { solutionId } = await validateEventQuery(event, querySchema), em = fork() - if (id) { - const result = await em.findOne(GlossaryTerm, id) + await assertSolutionReader(event, solutionId) - if (result) - return result - else - throw createError({ - statusCode: 404, - statusMessage: `Item not found with the given id: ${id}` - }) - } else { + const result = await em.findOne(GlossaryTerm, id) + + if (!result) throw createError({ - statusCode: 400, - statusMessage: "Bad Request: id is required." + statusCode: 404, + statusMessage: `Item not found with the given id: ${id}` }) - } -}) + + return result +}) \ No newline at end of file diff --git a/server/api/glossary-terms/[id].put.ts b/server/api/glossary-terms/[id].put.ts index da8463d4..cb68a53c 100644 --- a/server/api/glossary-terms/[id].put.ts +++ b/server/api/glossary-terms/[id].put.ts @@ -1,57 +1,39 @@ import { z } from "zod" import { fork } from "~/server/data/orm" -import { GlossaryTerm } from "~/server/domain/requirements/index" -import Solution from "~/server/domain/application/Solution.js" +import { GlossaryTerm } from "~/server/domain/requirements/index.js" + +const paramSchema = z.object({ + id: z.string().uuid() +}) const bodySchema = z.object({ + solutionId: z.string().uuid(), name: z.string().min(1), - statement: z.string(), - solutionId: z.string().uuid() + statement: z.string() }) /** - * PUT /api/glossary-terms/:id - * * Updates a glossary term by id. */ export default defineEventHandler(async (event) => { - const id = event.context.params?.id, - body = await readValidatedBody(event, (b) => bodySchema.safeParse(b)), + const { id } = await validateEventParams(event, paramSchema), + { name, statement, solutionId } = await validateEventBody(event, bodySchema), + { sessionUser } = await assertSolutionContributor(event, solutionId), em = fork() - if (!body.success) + const glossaryTerm = await em.findOne(GlossaryTerm, id) + + if (!glossaryTerm) throw createError({ statusCode: 400, - statusMessage: 'Bad Request: Invalid body parameters', - message: JSON.stringify(body.error.errors) + statusMessage: `Bad Request: No effect found with id: ${id}` }) - if (id) { - const glossaryTerm = await em.findOne(GlossaryTerm, id), - solution = await em.findOne(Solution, body.data.solutionId) - - if (!glossaryTerm) - throw createError({ - statusCode: 400, - statusMessage: `Bad Request: No effect found with id: ${id}` - }) - if (!solution) - throw createError({ - statusCode: 400, - statusMessage: `Bad Request: No solution found with id: ${body.data.solutionId}` - }) + glossaryTerm.name = name + glossaryTerm.statement = statement + // TODO: future use as part of Topic Maps? + glossaryTerm.parentComponent = undefined + glossaryTerm.modifiedBy = sessionUser - glossaryTerm.name = body.data.name - glossaryTerm.statement = body.data.statement - glossaryTerm.solution = solution - // TODO: future use as part of Topic Maps? - glossaryTerm.parentComponent = undefined - - await em.flush() - } else { - throw createError({ - statusCode: 400, - statusMessage: "Bad Request: id is required." - }) - } + await em.flush() }) \ No newline at end of file diff --git a/server/api/glossary-terms/index.get.ts b/server/api/glossary-terms/index.get.ts index ff1672d0..6e9b2466 100644 --- a/server/api/glossary-terms/index.get.ts +++ b/server/api/glossary-terms/index.get.ts @@ -1,34 +1,23 @@ import { z } from "zod" import { fork } from "~/server/data/orm" -import { GlossaryTerm } from "~/server/domain/requirements/index" +import { GlossaryTerm } from "~/server/domain/requirements/index.js" const querySchema = z.object({ + solutionId: z.string().uuid(), name: z.string().optional(), - statement: z.string().optional(), - solutionId: z.string().uuid().optional() + statement: z.string().optional() }) /** - * GET /api/glossary-terms - * - * Returns all glossary terms that match the query parameters - * - * GET /api/glossary-terms?name&statement&solutionId - * * Returns all glossay terms that match the query parameters */ export default defineEventHandler(async (event) => { - const query = await getValidatedQuery(event, (q) => querySchema.safeParse(q)), + const query = await validateEventQuery(event, querySchema), em = fork() - if (!query.success) - throw createError({ - statusCode: 400, - statusMessage: "Bad Request: Invalid query parameters", - message: JSON.stringify(query.error.errors) - }) + await assertSolutionReader(event, query.solutionId) - const results = await em.find(GlossaryTerm, Object.entries(query.data) + const results = await em.find(GlossaryTerm, Object.entries(query) .filter(([_, value]) => value !== undefined) .reduce((acc, [key, value]) => { if (key.endsWith("Id")) @@ -38,4 +27,4 @@ export default defineEventHandler(async (event) => { ); return results -}) +}) \ No newline at end of file diff --git a/server/api/glossary-terms/index.post.ts b/server/api/glossary-terms/index.post.ts index 7a1e769d..b890f025 100644 --- a/server/api/glossary-terms/index.post.ts +++ b/server/api/glossary-terms/index.post.ts @@ -1,43 +1,28 @@ import { z } from "zod" import { fork } from "~/server/data/orm" -import { GlossaryTerm } from "~/server/domain/requirements/index" -import Solution from "~/server/domain/application/Solution.js" +import { GlossaryTerm } from "~/server/domain/requirements/index.js" const bodySchema = z.object({ + solutionId: z.string().uuid(), name: z.string().min(1), - statement: z.string(), - solutionId: z.string().uuid() + statement: z.string() }) /** - * POST /api/glossary-terms - * * Creates a new glossary term and returns its id */ export default defineEventHandler(async (event) => { - const body = await readValidatedBody(event, (b) => bodySchema.safeParse(b)), + const { name, statement, solutionId } = await validateEventBody(event, bodySchema), + { solution, sessionUser } = await assertSolutionContributor(event, solutionId), em = fork() - if (!body.success) - throw createError({ - statusCode: 400, - statusMessage: 'Bad Request: Invalid body parameters', - message: JSON.stringify(body.error.errors) - }) - - const solution = await em.findOne(Solution, body.data.solutionId) - - if (!solution) - throw createError({ - statusCode: 400, - statusMessage: `Bad Request: Solution not found for id ${body.data.solutionId}` - }) - const glossaryTerm = new GlossaryTerm({ - name: body.data.name, - statement: body.data.statement, + name, + statement, solution, - parentComponent: undefined + parentComponent: undefined, + lastModified: new Date(), + modifiedBy: sessionUser }) await em.persistAndFlush(glossaryTerm) diff --git a/server/api/invariants/[id].delete.ts b/server/api/invariants/[id].delete.ts index 8e0f3428..30460fd2 100644 --- a/server/api/invariants/[id].delete.ts +++ b/server/api/invariants/[id].delete.ts @@ -1,20 +1,25 @@ +import { z } from "zod" import { fork } from "~/server/data/orm" -import { Invariant } from "~/server/domain/requirements/index" +import { Invariant } from "~/server/domain/requirements/index.js" + +const paramSchema = z.object({ + id: z.string().uuid() +}) + +const bodySchema = z.object({ + solutionId: z.string().uuid() +}) /** * Delete invariant by id. */ export default defineEventHandler(async (event) => { - const id = event.context.params?.id, + const { id } = await validateEventParams(event, paramSchema), + { solutionId } = await validateEventBody(event, bodySchema), em = fork() - if (id) { - em.remove(em.getReference(Invariant, id)) - await em.flush() - } else { - throw createError({ - statusCode: 400, - statusMessage: "Bad Request: id is required." - }) - } -}) + await assertSolutionContributor(event, solutionId) + + em.remove(em.getReference(Invariant, id)) + await em.flush() +}) \ No newline at end of file diff --git a/server/api/invariants/[id].get.ts b/server/api/invariants/[id].get.ts index 4e2057a5..a7375b94 100644 --- a/server/api/invariants/[id].get.ts +++ b/server/api/invariants/[id].get.ts @@ -1,27 +1,32 @@ +import { z } from "zod" import { fork } from "~/server/data/orm" -import { Invariant } from "~/server/domain/requirements/index" +import { Invariant } from "~/server/domain/requirements/index.js" + +const paramSchema = z.object({ + id: z.string().uuid() +}) + +const querySchema = z.object({ + solutionId: z.string().uuid(), +}) /** * Returns an invariant by id */ export default defineEventHandler(async (event) => { - const id = event.context.params?.id, + const { id } = await validateEventParams(event, paramSchema), + { solutionId } = await validateEventQuery(event, querySchema), em = fork() - if (id) { - const result = await em.findOne(Invariant, id) + await assertSolutionReader(event, solutionId) - if (result) - return result - else - throw createError({ - statusCode: 404, - statusMessage: `Item not found with the given id: ${id}` - }) - } else { + const result = await em.findOne(Invariant, id) + + if (!result) throw createError({ - statusCode: 400, - statusMessage: "Bad Request: id is required." + statusCode: 404, + statusMessage: `Item not found with the given id: ${id}` }) - } -}) + + return result +}) \ No newline at end of file diff --git a/server/api/invariants/[id].put.ts b/server/api/invariants/[id].put.ts index 05820674..4bef46c7 100644 --- a/server/api/invariants/[id].put.ts +++ b/server/api/invariants/[id].put.ts @@ -1,54 +1,37 @@ import { z } from "zod" import { fork } from "~/server/data/orm" -import { Invariant } from "~/server/domain/requirements/index" -import Solution from "~/server/domain/application/Solution" +import { Invariant } from "~/server/domain/requirements/index.js" + +const paramSchema = z.object({ + id: z.string().uuid() +}) const bodySchema = z.object({ + solutionId: z.string().uuid(), name: z.string().min(1), - statement: z.string(), - solutionId: z.string().uuid() + statement: z.string() }) /** - * PUT /api/invariants/:id * Updates an invariant by id. */ export default defineEventHandler(async (event) => { - const id = event.context.params?.id, - body = await readValidatedBody(event, (b) => bodySchema.safeParse(b)), + const { id } = await validateEventParams(event, paramSchema), + { name, statement, solutionId } = await validateEventBody(event, bodySchema), + { sessionUser } = await assertSolutionContributor(event, solutionId), em = fork() - if (!body.success) + const invariant = await em.findOne(Invariant, id) + + if (!invariant) throw createError({ statusCode: 400, - statusMessage: 'Bad Request: Invalid body parameters', - message: JSON.stringify(body.error.errors) + statusMessage: `Bad Request: No effect found with id: ${id}` }) - if (id) { - const invariant = await em.findOne(Invariant, id), - solution = await em.findOne(Solution, body.data.solutionId) - - if (!invariant) - throw createError({ - statusCode: 400, - statusMessage: `Bad Request: No effect found with id: ${id}` - }) - if (!solution) - throw createError({ - statusCode: 400, - statusMessage: `Bad Request: No solution found with id: ${body.data.solutionId}` - }) + invariant.name = name + invariant.statement = statement + invariant.modifiedBy = sessionUser - invariant.name = body.data.name - invariant.statement = body.data.statement - invariant.solution = solution - - await em.flush() - } else { - throw createError({ - statusCode: 400, - statusMessage: "Bad Request: id is required." - }) - } + await em.flush() }) \ No newline at end of file diff --git a/server/api/invariants/index.get.ts b/server/api/invariants/index.get.ts index 173dea2e..e929831c 100644 --- a/server/api/invariants/index.get.ts +++ b/server/api/invariants/index.get.ts @@ -1,34 +1,23 @@ import { z } from "zod" import { fork } from "~/server/data/orm" -import { Invariant } from "~/server/domain/requirements/index" +import { Invariant } from "~/server/domain/requirements/index.js" const querySchema = z.object({ + solutionId: z.string().uuid(), name: z.string().optional(), - statement: z.string().optional(), - solutionId: z.string().uuid().optional() + statement: z.string().optional() }) /** - * GET /api/invariants - * - * Returns all invariants that match the query parameters - * - * GET /api/glossary-terms?name&statement&solutionId - * * Returns all invariants that match the query parameters */ export default defineEventHandler(async (event) => { - const query = await getValidatedQuery(event, (q) => querySchema.safeParse(q)), + const query = await validateEventQuery(event, querySchema), em = fork() - if (!query.success) - throw createError({ - statusCode: 400, - statusMessage: "Bad Request: Invalid query parameters", - message: JSON.stringify(query.error.errors) - }) + await assertSolutionReader(event, query.solutionId) - const results = await em.find(Invariant, Object.entries(query.data) + const results = await em.find(Invariant, Object.entries(query) .filter(([_, value]) => value !== undefined) .reduce((acc, [key, value]) => { if (key.endsWith("Id")) @@ -38,4 +27,4 @@ export default defineEventHandler(async (event) => { ); return results -}) +}) \ No newline at end of file diff --git a/server/api/invariants/index.post.ts b/server/api/invariants/index.post.ts index 96b0eb73..dcb9a189 100644 --- a/server/api/invariants/index.post.ts +++ b/server/api/invariants/index.post.ts @@ -1,42 +1,27 @@ import { z } from "zod" import { fork } from "~/server/data/orm" -import { Invariant } from "~/server/domain/requirements/index" -import Solution from "~/server/domain/application/Solution" +import { Invariant } from "~/server/domain/requirements/index.js" const bodySchema = z.object({ + solutionId: z.string().uuid(), name: z.string().min(1), - statement: z.string(), - solutionId: z.string().uuid() + statement: z.string() }) /** - * POST /api/invariants - * * Creates a new invariant and returns its id */ export default defineEventHandler(async (event) => { - const body = await readValidatedBody(event, (b) => bodySchema.safeParse(b)), + const { name, statement, solutionId } = await validateEventBody(event, bodySchema), + { solution, sessionUser } = await assertSolutionContributor(event, solutionId), em = fork() - if (!body.success) - throw createError({ - statusCode: 400, - statusMessage: 'Bad Request: Invalid body parameters', - message: JSON.stringify(body.error.errors) - }) - - const solution = await em.findOne(Solution, body.data.solutionId) - - if (!solution) - throw createError({ - statusCode: 400, - statusMessage: `Bad Request: Solution not found for id ${body.data.solutionId}` - }) - const invariant = new Invariant({ - name: body.data.name, - statement: body.data.statement, - solution + name, + statement, + solution, + modifiedBy: sessionUser, + lastModified: new Date() }) await em.persistAndFlush(invariant) diff --git a/server/api/justifications/[id].delete.ts b/server/api/justifications/[id].delete.ts index 99550e92..8a7d1961 100644 --- a/server/api/justifications/[id].delete.ts +++ b/server/api/justifications/[id].delete.ts @@ -1,20 +1,25 @@ +import { z } from "zod" import { fork } from "~/server/data/orm" -import { Justification } from "~/server/domain/requirements/index" +import { Justification } from "~/server/domain/requirements/index.js" + +const paramSchema = z.object({ + id: z.string().uuid() +}) + +const bodySchema = z.object({ + solutionId: z.string().uuid() +}) /** * Delete a justification by id. */ export default defineEventHandler(async (event) => { - const id = event.context.params?.id, + const { id } = await validateEventParams(event, paramSchema), + { solutionId } = await validateEventBody(event, bodySchema), em = fork() - if (id) { - em.remove(em.getReference(Justification, id)) - await em.flush() - } else { - throw createError({ - statusCode: 400, - statusMessage: "Bad Request: id is required." - }) - } -}) + await assertSolutionContributor(event, solutionId) + + em.remove(em.getReference(Justification, id)) + await em.flush() +}) \ No newline at end of file diff --git a/server/api/justifications/[id].get.ts b/server/api/justifications/[id].get.ts index b98670c8..4c48ae09 100644 --- a/server/api/justifications/[id].get.ts +++ b/server/api/justifications/[id].get.ts @@ -1,27 +1,32 @@ +import { z } from "zod" import { fork } from "~/server/data/orm" -import { Justification } from "~/server/domain/requirements/index" +import { Justification } from "~/server/domain/requirements/index.js" + +const paramSchema = z.object({ + id: z.string().uuid() +}) + +const querySchema = z.object({ + solutionId: z.string().uuid() +}) /** * Returns a justification by id */ export default defineEventHandler(async (event) => { - const id = event.context.params?.id, + const { id } = await validateEventParams(event, paramSchema), + { solutionId } = await validateEventQuery(event, querySchema), em = fork() - if (id) { - const result = await em.findOne(Justification, id) + await assertSolutionReader(event, solutionId) - if (result) - return result - else - throw createError({ - statusCode: 404, - statusMessage: `Item not found with the given id: ${id}` - }) - } else { + const result = await em.findOne(Justification, id) + + if (!result) throw createError({ - statusCode: 400, - statusMessage: "Bad Request: id is required." + statusCode: 404, + statusMessage: `Item not found with the given id: ${id}` }) - } -}) + + return result +}) \ No newline at end of file diff --git a/server/api/justifications/[id].put.ts b/server/api/justifications/[id].put.ts index 1f253e75..9f930e37 100644 --- a/server/api/justifications/[id].put.ts +++ b/server/api/justifications/[id].put.ts @@ -1,55 +1,38 @@ import { fork } from "~/server/data/orm" import { z } from "zod" -import Solution from "~/server/domain/application/Solution" -import { Justification } from "~/server/domain/requirements/index" +import { Justification } from "~/server/domain/requirements/index.js" + +const paramSchema = z.object({ + id: z.string().uuid() +}) const bodySchema = z.object({ + solutionId: z.string().uuid(), name: z.string().min(1), - statement: z.string(), - solutionId: z.string().uuid() + statement: z.string() }) /** - * PUT /api/justifications/:id - * * Updates a Justification by id. */ export default defineEventHandler(async (event) => { - const id = event.context.params?.id, - body = await readValidatedBody(event, (b) => bodySchema.safeParse(b)), + const { id } = await validateEventParams(event, paramSchema), + { solutionId } = await validateEventBody(event, bodySchema), + { sessionUser } = await assertSolutionContributor(event, solutionId), + { name, statement } = await validateEventBody(event, bodySchema), em = fork() - if (!body.success) + const justification = await em.findOne(Justification, id) + + if (!justification) throw createError({ statusCode: 400, - statusMessage: 'Bad Request: Invalid body parameters', - message: JSON.stringify(body.error.errors) + statusMessage: `Bad Request: No assumption found with id: ${id}` }) - if (id) { - const justification = await em.findOne(Justification, id), - solution = await em.findOne(Solution, body.data.solutionId) - - if (!justification) - throw createError({ - statusCode: 400, - statusMessage: `Bad Request: No assumption found with id: ${id}` - }) - if (!solution) - throw createError({ - statusCode: 400, - statusMessage: `Bad Request: No solution found with id: ${body.data.solutionId}` - }) + justification.name = name + justification.statement = statement + justification.modifiedBy = sessionUser - justification.name = body.data.name - justification.statement = body.data.statement - justification.solution = solution - - await em.flush() - } else { - throw createError({ - statusCode: 400, - statusMessage: "Bad Request: id is required." - }) - } + await em.flush() }) \ No newline at end of file diff --git a/server/api/justifications/index.get.ts b/server/api/justifications/index.get.ts index c76ea22c..2d1c960f 100644 --- a/server/api/justifications/index.get.ts +++ b/server/api/justifications/index.get.ts @@ -1,34 +1,23 @@ import { z } from "zod" import { fork } from "~/server/data/orm" -import { Justification } from "~/server/domain/requirements/index" +import { Justification } from "~/server/domain/requirements/index.js" const querySchema = z.object({ + solutionId: z.string().uuid(), name: z.string().optional(), - statement: z.string().optional(), - solutionId: z.string().uuid().optional() + statement: z.string().optional() }) /** - * GET /api/justifications - * - * Returns all justifications - * - * GET /api/justifications?name&statement&solutionId - * * Returns all justifications that match the query parameters */ export default defineEventHandler(async (event) => { - const query = await getValidatedQuery(event, (q) => querySchema.safeParse(q)), + const query = await validateEventQuery(event, querySchema), em = fork() - if (!query.success) - throw createError({ - statusCode: 400, - statusMessage: "Bad Request: Invalid query parameters", - message: JSON.stringify(query.error.errors) - }) + await assertSolutionReader(event, query.solutionId) - const results = await em.find(Justification, Object.entries(query.data) + const results = await em.find(Justification, Object.entries(query) .filter(([_, value]) => value !== undefined) .reduce((acc, [key, value]) => { if (key.endsWith("Id")) @@ -37,4 +26,4 @@ export default defineEventHandler(async (event) => { }, {})) return results -}) +}) \ No newline at end of file diff --git a/server/api/justifications/index.post.ts b/server/api/justifications/index.post.ts index 6abd1599..bc37b0ea 100644 --- a/server/api/justifications/index.post.ts +++ b/server/api/justifications/index.post.ts @@ -1,42 +1,27 @@ import { fork } from "~/server/data/orm" import { z } from "zod" -import Solution from "~/server/domain/application/Solution" -import { Justification } from "~/server/domain/requirements/index" +import { Justification } from "~/server/domain/requirements/index.js" const bodySchema = z.object({ + solutionId: z.string().uuid(), name: z.string().min(1), - statement: z.string(), - solutionId: z.string().uuid() + statement: z.string() }) /** - * POST /api/justifications - * * Creates a new justifications and returns its id */ export default defineEventHandler(async (event) => { - const body = await readValidatedBody(event, (b) => bodySchema.safeParse(b)), + const { name, statement, solutionId } = await validateEventBody(event, bodySchema), + { solution, sessionUser } = await assertSolutionContributor(event, solutionId), em = fork() - if (!body.success) - throw createError({ - statusCode: 400, - statusMessage: 'Bad Request: Invalid body parameters', - message: JSON.stringify(body.error.errors) - }) - - const solution = await em.findOne(Solution, body.data.solutionId) - - if (!solution) - throw createError({ - statusCode: 400, - statusMessage: `Bad Request: Solution not found for id ${body.data.solutionId}` - }) - const newJustification = new Justification({ - name: body.data.name, - statement: body.data.statement, - solution + name, + statement, + solution, + modifiedBy: sessionUser, + lastModified: new Date() }) await em.persistAndFlush(newJustification) diff --git a/server/api/limits/[id].delete.ts b/server/api/limits/[id].delete.ts index cd504d69..611f510e 100644 --- a/server/api/limits/[id].delete.ts +++ b/server/api/limits/[id].delete.ts @@ -1,20 +1,25 @@ +import { z } from "zod" import { fork } from "~/server/data/orm" -import { Limit } from "~/server/domain/requirements/index" +import { Limit } from "~/server/domain/requirements/index.js" + +const paramSchema = z.object({ + id: z.string().uuid() +}) + +const bodySchema = z.object({ + solutionId: z.string().uuid() +}) /** * Delete limit by id. */ export default defineEventHandler(async (event) => { - const id = event.context.params?.id, + const { id } = await validateEventParams(event, paramSchema), + { solutionId } = await validateEventBody(event, bodySchema), em = fork() - if (id) { - em.remove(em.getReference(Limit, id)) - await em.flush() - } else { - throw createError({ - statusCode: 400, - statusMessage: "Bad Request: id is required." - }) - } -}) + await assertSolutionContributor(event, solutionId) + + em.remove(em.getReference(Limit, id)) + await em.flush() +}) \ No newline at end of file diff --git a/server/api/limits/[id].get.ts b/server/api/limits/[id].get.ts index a0782f9e..ef13b359 100644 --- a/server/api/limits/[id].get.ts +++ b/server/api/limits/[id].get.ts @@ -1,27 +1,32 @@ +import { z } from "zod" import { fork } from "~/server/data/orm" -import { Limit } from "~/server/domain/requirements/index" +import { Limit } from "~/server/domain/requirements/index.js" + +const paramSchema = z.object({ + id: z.string().uuid() +}) + +const querySchema = z.object({ + solutionId: z.string().uuid() +}) /** * Returns a limit by id */ export default defineEventHandler(async (event) => { - const id = event.context.params?.id, + const { id } = await validateEventParams(event, paramSchema), + { solutionId } = await validateEventQuery(event, querySchema), em = fork() - if (id) { - const result = await em.findOne(Limit, id) + await assertSolutionReader(event, solutionId) - if (result) - return result - else - throw createError({ - statusCode: 404, - statusMessage: `Item not found with the given id: ${id}` - }) - } else { + const result = await em.findOne(Limit, id) + + if (!result) throw createError({ - statusCode: 400, - statusMessage: "Bad Request: id is required." + statusCode: 404, + statusMessage: `Item not found with the given id: ${id}` }) - } -}) + + return result +}) \ No newline at end of file diff --git a/server/api/limits/[id].put.ts b/server/api/limits/[id].put.ts index b2423177..845b0bdb 100644 --- a/server/api/limits/[id].put.ts +++ b/server/api/limits/[id].put.ts @@ -1,55 +1,37 @@ import { z } from "zod" import { fork } from "~/server/data/orm" -import { Limit } from "~/server/domain/requirements/index" -import Solution from "~/server/domain/application/Solution" +import { Limit } from "~/server/domain/requirements/index.js" + +const paramSchema = z.object({ + id: z.string().uuid() +}) const bodySchema = z.object({ + solutionId: z.string().uuid(), name: z.string(), - statement: z.string(), - solutionId: z.string() + statement: z.string() }) /** - * PUT /api/limits/:id - * * Updates a limit by id. */ export default defineEventHandler(async (event) => { - const id = event.context.params?.id, - body = await readValidatedBody(event, (b) => bodySchema.safeParse(b)), + const { id } = await validateEventParams(event, paramSchema), + { name, statement, solutionId } = await validateEventBody(event, bodySchema), + { sessionUser } = await assertSolutionContributor(event, solutionId), em = fork() - if (!body.success) + const limit = await em.findOne(Limit, id) + + if (!limit) throw createError({ statusCode: 400, - statusMessage: 'Bad Request: Invalid body parameters', - message: JSON.stringify(body.error.errors) + statusMessage: `Bad Request: No assumption found with id: ${id}` }) - if (id) { - const limit = await em.findOne(Limit, id), - solution = await em.findOne(Solution, body.data.solutionId) - - if (!limit) - throw createError({ - statusCode: 400, - statusMessage: `Bad Request: No assumption found with id: ${id}` - }) - if (!solution) - throw createError({ - statusCode: 400, - statusMessage: `Bad Request: No solution found with id: ${body.data.solutionId}` - }) + limit.name = name + limit.statement = statement + limit.modifiedBy = sessionUser - limit.name = body.data.name - limit.statement = body.data.statement - limit.solution = solution - - await em.flush() - } else { - throw createError({ - statusCode: 400, - statusMessage: "Bad Request: id is required." - }) - } + await em.flush() }) \ No newline at end of file diff --git a/server/api/limits/index.get.ts b/server/api/limits/index.get.ts index 8daea77d..8019b1a9 100644 --- a/server/api/limits/index.get.ts +++ b/server/api/limits/index.get.ts @@ -1,30 +1,23 @@ import { z } from "zod" import { fork } from "~/server/data/orm" -import { Limit } from "~/server/domain/requirements/index" +import { Limit } from "~/server/domain/requirements/index.js" const querySchema = z.object({ + solutionId: z.string().uuid(), name: z.string().optional(), - statement: z.string().optional(), - solutionId: z.string().uuid().optional() + statement: z.string().optional() }) /** - * GET /api/effects?name&statement&solutionId - * * Returns all limits that match the query parameters */ export default defineEventHandler(async (event) => { - const query = await getValidatedQuery(event, (q) => querySchema.safeParse(q)), + const query = await validateEventQuery(event, querySchema), em = fork() - if (!query.success) - throw createError({ - statusCode: 400, - statusMessage: "Bad Request: Invalid query parameters", - message: JSON.stringify(query.error.errors) - }) + await assertSolutionReader(event, query.solutionId) - const results = await em.find(Limit, Object.entries(query.data) + const results = await em.find(Limit, Object.entries(query) .filter(([_, value]) => value !== undefined) .reduce((acc, [key, value]) => { if (key.endsWith("Id")) @@ -33,4 +26,4 @@ export default defineEventHandler(async (event) => { }, {})) return results -}) +}) \ No newline at end of file diff --git a/server/api/limits/index.post.ts b/server/api/limits/index.post.ts index 4f20b9e4..c799528f 100644 --- a/server/api/limits/index.post.ts +++ b/server/api/limits/index.post.ts @@ -1,42 +1,27 @@ import { z } from "zod" import { fork } from "~/server/data/orm" -import { Limit } from "~/server/domain/requirements/index" -import Solution from "~/server/domain/application/Solution" +import { Limit } from "~/server/domain/requirements/index.js" const bodySchema = z.object({ + solutionId: z.string().uuid(), name: z.string().min(1), - statement: z.string(), - solutionId: z.string().uuid() + statement: z.string() }) /** - * POST /api/limits - * * Creates a new limit and returns its id */ export default defineEventHandler(async (event) => { - const body = await readValidatedBody(event, (b) => bodySchema.safeParse(b)), + const { name, statement, solutionId } = await validateEventBody(event, bodySchema), + { solution, sessionUser } = await assertSolutionContributor(event, solutionId), em = fork() - if (!body.success) - throw createError({ - statusCode: 400, - statusMessage: 'Bad Request: Invalid body parameters', - message: JSON.stringify(body.error.errors) - }) - - const solution = await em.findOne(Solution, body.data.solutionId) - - if (!solution) - throw createError({ - statusCode: 400, - statusMessage: `Bad Request: Solution not found for id ${body.data.solutionId}` - }) - const newLimit = new Limit({ - name: body.data.name, - statement: body.data.statement, - solution + name, + statement, + solution, + modifiedBy: sessionUser, + lastModified: new Date() }) await em.persistAndFlush(newLimit) diff --git a/server/api/non-functional-behaviors/[id].delete.ts b/server/api/non-functional-behaviors/[id].delete.ts index ccd89bc4..8b8f5988 100644 --- a/server/api/non-functional-behaviors/[id].delete.ts +++ b/server/api/non-functional-behaviors/[id].delete.ts @@ -1,20 +1,25 @@ +import { z } from "zod" import { fork } from "~/server/data/orm" -import { NonFunctionalBehavior } from "~/server/domain/requirements/index" +import { NonFunctionalBehavior } from "~/server/domain/requirements/index.js" + +const paramSchema = z.object({ + id: z.string().uuid() +}) + +const bodySchema = z.object({ + solutionId: z.string().uuid() +}) /** * Delete non-functional behavior by id. */ export default defineEventHandler(async (event) => { - const id = event.context.params?.id, + const { id } = await validateEventParams(event, paramSchema), + { solutionId } = await validateEventBody(event, bodySchema), em = fork() - if (id) { - em.remove(em.getReference(NonFunctionalBehavior, id)) - await em.flush() - } else { - throw createError({ - statusCode: 400, - statusMessage: "Bad Request: id is required." - }) - } -}) + await assertSolutionContributor(event, solutionId) + + em.remove(em.getReference(NonFunctionalBehavior, id)) + await em.flush() +}) \ No newline at end of file diff --git a/server/api/non-functional-behaviors/[id].get.ts b/server/api/non-functional-behaviors/[id].get.ts index 2ade296d..1de7d89f 100644 --- a/server/api/non-functional-behaviors/[id].get.ts +++ b/server/api/non-functional-behaviors/[id].get.ts @@ -1,27 +1,32 @@ +import { z } from "zod" import { fork } from "~/server/data/orm" -import { NonFunctionalBehavior } from "~/server/domain/requirements/index" +import { NonFunctionalBehavior } from "~/server/domain/requirements/index.js" + +const paramSchema = z.object({ + id: z.string().uuid() +}) + +const querySchema = z.object({ + solutionId: z.string().uuid() +}) /** * Returns a non-functional behavior by id */ export default defineEventHandler(async (event) => { - const id = event.context.params?.id, + const { id } = await validateEventParams(event, paramSchema), + { solutionId } = await validateEventQuery(event, querySchema), em = fork() - if (id) { - const result = await em.findOne(NonFunctionalBehavior, id) + await assertSolutionReader(event, solutionId) - if (result) - return result - else - throw createError({ - statusCode: 404, - statusMessage: `Item not found with the given id: ${id}` - }) - } else { + const result = await em.findOne(NonFunctionalBehavior, id) + + if (!result) throw createError({ - statusCode: 400, - statusMessage: "Bad Request: id is required." + statusCode: 404, + statusMessage: `Item not found with the given id: ${id}` }) - } -}) + + return result +}) \ No newline at end of file diff --git a/server/api/non-functional-behaviors/[id].put.ts b/server/api/non-functional-behaviors/[id].put.ts index 192503be..85826c9e 100644 --- a/server/api/non-functional-behaviors/[id].put.ts +++ b/server/api/non-functional-behaviors/[id].put.ts @@ -1,12 +1,15 @@ import { z } from "zod" import { fork } from "~/server/data/orm" -import { NonFunctionalBehavior, MoscowPriority } from "~/server/domain/requirements/index" -import Solution from "~/server/domain/application/Solution" +import { NonFunctionalBehavior, MoscowPriority } from "~/server/domain/requirements/index.js" + +const paramSchema = z.object({ + id: z.string().uuid() +}) const bodySchema = z.object({ + solutionId: z.string().uuid(), name: z.string().min(1), statement: z.string(), - solutionId: z.string().uuid(), priority: z.nativeEnum(MoscowPriority) }) @@ -14,42 +17,23 @@ const bodySchema = z.object({ * Updates a non functional behavior by id. */ export default defineEventHandler(async (event) => { - const id = event.context.params?.id, - body = await readValidatedBody(event, (b) => bodySchema.safeParse(b)), + const { id } = await validateEventParams(event, paramSchema), + { name, priority, statement, solutionId } = await validateEventBody(event, bodySchema), + { sessionUser } = await assertSolutionContributor(event, solutionId), em = fork() - if (!body.success) + const nonFunctionalBehavior = await em.findOne(NonFunctionalBehavior, id) + + if (!nonFunctionalBehavior) throw createError({ statusCode: 400, - statusMessage: 'Bad Request: Invalid body parameters', - message: JSON.stringify(body.error.errors) + statusMessage: `Bad Request: No assumption found with id: ${id}` }) - if (id) { - const nonFunctionalBehavior = await em.findOne(NonFunctionalBehavior, id), - solution = await em.findOne(Solution, body.data.solutionId) + nonFunctionalBehavior.name = name + nonFunctionalBehavior.statement = statement + nonFunctionalBehavior.priority = priority + nonFunctionalBehavior.modifiedBy = sessionUser - if (!nonFunctionalBehavior) - throw createError({ - statusCode: 400, - statusMessage: `Bad Request: No assumption found with id: ${id}` - }) - if (!solution) - throw createError({ - statusCode: 400, - statusMessage: `Bad Request: No solution found with id: ${body.data.solutionId}` - }) - - nonFunctionalBehavior.name = body.data.name - nonFunctionalBehavior.statement = body.data.statement - nonFunctionalBehavior.solution = solution - nonFunctionalBehavior.priority = body.data.priority - - await em.flush() - } else { - throw createError({ - statusCode: 400, - statusMessage: "Bad Request: id is required." - }) - } + await em.flush() }) \ No newline at end of file diff --git a/server/api/non-functional-behaviors/index.get.ts b/server/api/non-functional-behaviors/index.get.ts index 3e40fa21..8251f26f 100644 --- a/server/api/non-functional-behaviors/index.get.ts +++ b/server/api/non-functional-behaviors/index.get.ts @@ -1,11 +1,11 @@ import { z } from "zod" import { fork } from "~/server/data/orm" -import { NonFunctionalBehavior, MoscowPriority } from "~/server/domain/requirements/index" +import { MoscowPriority, NonFunctionalBehavior } from "~/server/domain/requirements/index.js" const querySchema = z.object({ + solutionId: z.string().uuid(), name: z.string().optional(), statement: z.string().optional(), - solutionId: z.string().uuid().optional(), priority: z.nativeEnum(MoscowPriority).optional() }) @@ -13,17 +13,12 @@ const querySchema = z.object({ * Returns all non functional behaviors that match the query parameters */ export default defineEventHandler(async (event) => { - const query = await getValidatedQuery(event, (q) => querySchema.safeParse(q)), + const query = await validateEventQuery(event, querySchema), em = fork() - if (!query.success) - throw createError({ - statusCode: 400, - statusMessage: "Bad Request: Invalid query parameters", - message: JSON.stringify(query.error.errors) - }) + await assertSolutionReader(event, query.solutionId) - const results = await em.find(NonFunctionalBehavior, Object.entries(query.data) + const results = await em.find(NonFunctionalBehavior, Object.entries(query) .filter(([_, value]) => value !== undefined) .reduce((acc, [key, value]) => { if (key.endsWith("Id")) @@ -32,4 +27,4 @@ export default defineEventHandler(async (event) => { }, {})) return results -}) +}) \ No newline at end of file diff --git a/server/api/non-functional-behaviors/index.post.ts b/server/api/non-functional-behaviors/index.post.ts index 8e788dd8..514113d3 100644 --- a/server/api/non-functional-behaviors/index.post.ts +++ b/server/api/non-functional-behaviors/index.post.ts @@ -1,12 +1,11 @@ import { z } from "zod" import { fork } from "~/server/data/orm" -import { NonFunctionalBehavior, MoscowPriority } from "~/server/domain/requirements/index" -import Solution from "~/server/domain/application/Solution" +import { MoscowPriority, NonFunctionalBehavior } from "~/server/domain/requirements/index.js" const bodySchema = z.object({ + solutionId: z.string().uuid(), name: z.string().min(1), statement: z.string(), - solutionId: z.string().uuid(), priority: z.nativeEnum(MoscowPriority) }) @@ -14,29 +13,17 @@ const bodySchema = z.object({ * Creates a new non functional behavior and returns its id */ export default defineEventHandler(async (event) => { - const body = await readValidatedBody(event, (b) => bodySchema.safeParse(b)), + const { name, priority, statement, solutionId } = await validateEventBody(event, bodySchema), + { solution, sessionUser } = await assertSolutionContributor(event, solutionId), em = fork() - if (!body.success) - throw createError({ - statusCode: 400, - statusMessage: 'Bad Request: Invalid body parameters', - message: JSON.stringify(body.error.errors) - }) - - const solution = await em.findOne(Solution, body.data.solutionId) - - if (!solution) - throw createError({ - statusCode: 400, - statusMessage: `Bad Request: Solution not found for id ${body.data.solutionId}` - }) - const newNonFunctionalBehavior = new NonFunctionalBehavior({ - name: body.data.name, - statement: body.data.statement, + name, + statement, solution, - priority: body.data.priority + priority, + lastModified: new Date(), + modifiedBy: sessionUser }) await em.persistAndFlush(newNonFunctionalBehavior) diff --git a/server/api/obstacles/[id].delete.ts b/server/api/obstacles/[id].delete.ts index 9d1abd3b..cf5d06ca 100644 --- a/server/api/obstacles/[id].delete.ts +++ b/server/api/obstacles/[id].delete.ts @@ -1,20 +1,25 @@ +import { z } from "zod" import { fork } from "~/server/data/orm" -import { Obstacle } from "~/server/domain/requirements/index" +import { Obstacle } from "~/server/domain/requirements/index.js" + +const paramSchema = z.object({ + id: z.string().uuid() +}) + +const bodySchema = z.object({ + solutionId: z.string().uuid() +}) /** * Delete obstacle by id. */ export default defineEventHandler(async (event) => { - const id = event.context.params?.id, + const { id } = await validateEventParams(event, paramSchema), + { solutionId } = await validateEventBody(event, bodySchema), em = fork() - if (id) { - em.remove(em.getReference(Obstacle, id)) - await em.flush() - } else { - throw createError({ - statusCode: 400, - statusMessage: "Bad Request: id is required." - }) - } -}) + await assertSolutionContributor(event, solutionId) + + em.remove(em.getReference(Obstacle, id)) + await em.flush() +}) \ No newline at end of file diff --git a/server/api/obstacles/[id].get.ts b/server/api/obstacles/[id].get.ts index d11c81d3..55764bb8 100644 --- a/server/api/obstacles/[id].get.ts +++ b/server/api/obstacles/[id].get.ts @@ -1,27 +1,32 @@ +import { z } from "zod" import { fork } from "~/server/data/orm" -import { Obstacle } from "~/server/domain/requirements/index" +import { Obstacle } from "~/server/domain/requirements/index.js" + +const paramSchema = z.object({ + id: z.string().uuid() +}) + +const querySchema = z.object({ + solutionId: z.string().uuid(), +}) /** * Returns a obstacle by id */ export default defineEventHandler(async (event) => { - const id = event.context.params?.id, + const { id } = await validateEventParams(event, paramSchema), + { solutionId } = await validateEventQuery(event, querySchema), em = fork() - if (id) { - const result = await em.findOne(Obstacle, id) + await assertSolutionReader(event, solutionId) - if (result) - return result - else - throw createError({ - statusCode: 404, - statusMessage: `Item not found with the given id: ${id}` - }) - } else { + const result = await em.findOne(Obstacle, id) + + if (!result) throw createError({ - statusCode: 400, - statusMessage: "Bad Request: id is required." + statusCode: 404, + statusMessage: `Item not found with the given id: ${id}` }) - } -}) + + return result +}) \ No newline at end of file diff --git a/server/api/obstacles/[id].put.ts b/server/api/obstacles/[id].put.ts index bb81c27a..6a26444c 100644 --- a/server/api/obstacles/[id].put.ts +++ b/server/api/obstacles/[id].put.ts @@ -1,55 +1,37 @@ import { z } from "zod" import { fork } from "~/server/data/orm" -import { Obstacle } from "~/server/domain/requirements/index" -import Solution from "~/server/domain/application/Solution" +import { Obstacle } from "~/server/domain/requirements/index.js" + +const paramSchema = z.object({ + id: z.string().uuid() +}) const bodySchema = z.object({ + solutionId: z.string().uuid(), name: z.string(), - statement: z.string(), - solutionId: z.string() + statement: z.string() }) /** - * PUT /api/obstacles/:id - * * Updates an obstacle by id. */ export default defineEventHandler(async (event) => { - const id = event.context.params?.id, - body = await readValidatedBody(event, (b) => bodySchema.safeParse(b)), + const { id } = await validateEventParams(event, paramSchema), + { name, statement, solutionId } = await validateEventBody(event, bodySchema), + { sessionUser } = await assertSolutionContributor(event, solutionId), em = fork() - if (!body.success) + const obstacle = await em.findOne(Obstacle, id) + + if (!obstacle) throw createError({ statusCode: 400, - statusMessage: 'Bad Request: Invalid body parameters', - message: JSON.stringify(body.error.errors) + statusMessage: `Bad Request: No assumption found with id: ${id}` }) - if (id) { - const obstacle = await em.findOne(Obstacle, id), - solution = await em.findOne(Solution, body.data.solutionId) - - if (!obstacle) - throw createError({ - statusCode: 400, - statusMessage: `Bad Request: No assumption found with id: ${id}` - }) - if (!solution) - throw createError({ - statusCode: 400, - statusMessage: `Bad Request: No solution found with id: ${body.data.solutionId}` - }) + obstacle.name = name + obstacle.statement = statement + obstacle.modifiedBy = sessionUser - obstacle.name = body.data.name - obstacle.statement = body.data.statement - obstacle.solution = solution - - await em.flush() - } else { - throw createError({ - statusCode: 400, - statusMessage: "Bad Request: id is required." - }) - } + await em.flush() }) \ No newline at end of file diff --git a/server/api/obstacles/index.get.ts b/server/api/obstacles/index.get.ts index 888b1323..32f05c58 100644 --- a/server/api/obstacles/index.get.ts +++ b/server/api/obstacles/index.get.ts @@ -1,30 +1,23 @@ import { z } from "zod" import { fork } from "~/server/data/orm" -import { Obstacle } from "~/server/domain/requirements/index" +import { Obstacle } from "~/server/domain/requirements/index.js" const querySchema = z.object({ + solutionId: z.string().uuid(), name: z.string().optional(), - statement: z.string().optional(), - solutionId: z.string().uuid().optional() + statement: z.string().optional() }) /** - * GET /api/obstacles?name&statement&solutionId - * * Returns all obstacles that match the query parameters */ export default defineEventHandler(async (event) => { - const query = await getValidatedQuery(event, (q) => querySchema.safeParse(q)), + const query = await validateEventQuery(event, querySchema), em = fork() - if (!query.success) - throw createError({ - statusCode: 400, - statusMessage: "Bad Request: Invalid query parameters", - message: JSON.stringify(query.error.errors) - }) + await assertSolutionReader(event, query.solutionId) - const results = await em.find(Obstacle, Object.entries(query.data) + const results = await em.find(Obstacle, Object.entries(query) .filter(([_, value]) => value !== undefined) .reduce((acc, [key, value]) => { if (key.endsWith("Id")) @@ -34,4 +27,4 @@ export default defineEventHandler(async (event) => { ); return results -}) +}) \ No newline at end of file diff --git a/server/api/obstacles/index.post.ts b/server/api/obstacles/index.post.ts index bb634bce..4a2a1633 100644 --- a/server/api/obstacles/index.post.ts +++ b/server/api/obstacles/index.post.ts @@ -1,42 +1,27 @@ import { z } from "zod" -import Solution from "~/server/domain/application/Solution" import { fork } from "~/server/data/orm" -import { Obstacle } from "~/server/domain/requirements/index" +import { Obstacle } from "~/server/domain/requirements/index.js" const bodySchema = z.object({ + solutionId: z.string().uuid(), name: z.string().min(1), - statement: z.string(), - solutionId: z.string().uuid() + statement: z.string() }) /** - * POST /api/obstacles - * * Creates a new obstacle and returns its id */ export default defineEventHandler(async (event) => { - const body = await readValidatedBody(event, (b) => bodySchema.safeParse(b)), + const { name, statement, solutionId } = await validateEventBody(event, bodySchema), + { solution, sessionUser } = await assertSolutionContributor(event, solutionId), em = fork() - if (!body.success) - throw createError({ - statusCode: 400, - statusMessage: 'Bad Request: Invalid body parameters', - message: JSON.stringify(body.error.errors) - }) - - const solution = await em.findOne(Solution, body.data.solutionId) - - if (!solution) - throw createError({ - statusCode: 400, - statusMessage: `Bad Request: Solution not found for id ${body.data.solutionId}` - }) - const newObstacle = new Obstacle({ - name: body.data.name, - statement: body.data.statement, - solution + name, + statement, + solution, + modifiedBy: sessionUser, + lastModified: new Date() }) await em.persistAndFlush(newObstacle) diff --git a/server/api/organizations/[id].delete.ts b/server/api/organizations/[id].delete.ts index 31ff33da..04b95c90 100644 --- a/server/api/organizations/[id].delete.ts +++ b/server/api/organizations/[id].delete.ts @@ -1,38 +1,46 @@ +import { Collection } from "@mikro-orm/core" +import { z } from "zod" import { fork } from "~/server/data/orm" -import Organization from "~/server/domain/application/Organization" import AppUserOrganizationRole from "~/server/domain/application/AppUserOrganizationRole" -import { getServerSession } from '#auth' -import AppRole from "~/server/domain/application/AppRole" + +const paramSchema = z.object({ + id: z.string().uuid() +}) /** * Delete an organization by id. */ export default defineEventHandler(async (event) => { - const id = event.context.params?.id; - - if (!id) - throw createError({ - statusCode: 400, - statusMessage: "Bad Request: id is required." - }) - - const em = fork(), - session = (await getServerSession(event))!, - organization = em.getReference(Organization, id), - sessionUserOrgRole = await em.findOne(AppUserOrganizationRole, { - appUser: session.id, - organization - }) - - // An organization can only be deleted by a system admin - // or the associated organization admin - if (session.isSystemAdmin || sessionUserOrgRole?.role === AppRole.ORGANIZATION_ADMIN) { - em.remove(organization) + const { id } = await validateEventParams(event, paramSchema), + { organization } = await assertOrgAdmin(event, id), + em = fork() + + const solutions = await organization.solutions.load() + + for (const solution of solutions) { + // for each property of the solution that is a collection, remove all items + for (const key in solution) { + const maybeCollection = Reflect.get(solution, key) as Collection + if (maybeCollection instanceof Collection) { + await maybeCollection.load() + maybeCollection.getItems().map(item => em.remove(item)) + maybeCollection.removeAll() + } + } await em.flush() - } else { - throw createError({ - statusCode: 403, - statusMessage: "Forbidden: You must be a system admin or an organization admin to delete an organization." - }) + await em.removeAndFlush(solution) } + + organization.solutions.removeAll() + + const appUserOrganizationRoles = await em.findAll(AppUserOrganizationRole, { + where: { organization } + }) + + for (const auor of appUserOrganizationRoles) + em.remove(auor) + + em.remove(organization) + + await em.removeAndFlush(organization) }) diff --git a/server/api/organizations/[id].get.ts b/server/api/organizations/[id].get.ts index c4f067e2..f53fa641 100644 --- a/server/api/organizations/[id].get.ts +++ b/server/api/organizations/[id].get.ts @@ -1,34 +1,15 @@ -import { fork } from "~/server/data/orm" -import Organization from "~/server/domain/application/Organization" -import AppUserOrganizationRole from "~/server/domain/application/AppUserOrganizationRole" -import { getServerSession } from '#auth' +import { z } from "zod" + +const paramSchema = z.object({ + id: z.string().uuid() +}) /** * Returns an organization by id */ export default defineEventHandler(async (event) => { - const config = useRuntimeConfig(), - id = event.context.params?.id - - if (!id) - throw createError({ - statusCode: 400, - statusMessage: "Bad Request: id is required." - }) - - const em = fork(), - session = (await getServerSession(event))!, - organization = await em.findOne(Organization, id), - sessionUserOrgRoleCount = await em.count(AppUserOrganizationRole, { appUser: session.id, organization }) - - // check if the user is a member of the organization or a system admin before returning it - const result = session.isSystemAdmin || sessionUserOrgRoleCount ? organization : null - - if (!result) - throw createError({ - statusCode: 403, - statusMessage: "Forbidden: You must be a member of the organization or a system admin to view it." - }) + const { id } = await validateEventParams(event, paramSchema), + { organization } = await assertOrgReader(event, id) - return result + return organization }) diff --git a/server/api/organizations/[id].put.ts b/server/api/organizations/[id].put.ts index a917a08c..90d4fa6e 100644 --- a/server/api/organizations/[id].put.ts +++ b/server/api/organizations/[id].put.ts @@ -1,10 +1,10 @@ import { z } from "zod" import { fork } from "~/server/data/orm" -import Organization from "~/server/domain/application/Organization" -import AppUserOrganizationRole from "~/server/domain/application/AppUserOrganizationRole" -import { getServerSession } from '#auth' import slugify from "~/utils/slugify" -import AppRole from "~/server/domain/application/AppRole" + +const paramSchema = z.object({ + id: z.string().uuid() +}) const bodySchema = z.object({ name: z.string().min(1), @@ -15,45 +15,14 @@ const bodySchema = z.object({ * Updates an organization by id. */ export default defineEventHandler(async (event) => { - const id = event.context.params?.id; - - if (!id) - throw createError({ - statusCode: 400, - statusMessage: "Bad Request: id is required." - }) - - const em = fork(), - body = await readValidatedBody(event, (b) => bodySchema.safeParse(b)), - session = (await getServerSession(event))!, - organization = await em.findOne(Organization, id), - sessionUserOrgRole = await em.findOne(AppUserOrganizationRole, { appUser: session.id, organization }) - - if (!body.success) - throw createError({ - statusCode: 400, - statusMessage: "Bad Request: Invalid body parameters", - message: JSON.stringify(body.error.errors) - }) - - if (!organization) - throw createError({ - statusCode: 400, - statusMessage: `Bad Request: No organization found with id: ${id}` - }) + const { id } = await validateEventParams(event, paramSchema), + { name, description } = await validateEventBody(event, bodySchema), + { organization } = await assertOrgContributor(event, id), + em = fork() - // An organization can only be updated by a system admin - // or the associated organization admin, or organization contributor - if (session.isSystemAdmin || sessionUserOrgRole && [AppRole.ORGANIZATION_ADMIN, AppRole.ORGANIZATION_CONTRIBUTOR].includes(sessionUserOrgRole.role)) { - organization.name = body.data.name - organization.slug = slugify(body.data.name); - organization.description = body.data.description + organization.name = name + organization.slug = slugify(name); + organization.description = description - await em.flush() - } else { - throw createError({ - statusCode: 403, - statusMessage: "Forbidden: You must be a system admin or an organization admin to update an organization." - }) - } + await em.flush() }) \ No newline at end of file diff --git a/server/api/organizations/index.get.ts b/server/api/organizations/index.get.ts index ccd7c556..19d77b4a 100644 --- a/server/api/organizations/index.get.ts +++ b/server/api/organizations/index.get.ts @@ -11,39 +11,23 @@ const querySchema = z.object({ }) /** - * GET /api/organizations - * - * Returns all organizations - * - * GET /api/organizations?name&description&slug - * * Returns all organizations that match the query parameters */ export default defineEventHandler(async (event) => { - const query = await getValidatedQuery(event, (q) => querySchema.safeParse(q)), - session = (await getServerSession(event))! - - if (!query.success) - throw createError({ - statusCode: 400, - statusMessage: "Bad Request: Invalid query parameters", - message: JSON.stringify(query.error.errors) - }) - - const em = fork() + const { description, name, slug } = await validateEventParams(event, querySchema), + session = (await getServerSession(event))!, + em = fork() // If the user is a system admin, return all organizations // filtered by the query parameters if (session.isSystemAdmin) { - const organizations = em.findAll(Organization, { + return em.findAll(Organization, { where: { - ...(query.data.name ? { name: query.data.name } : {}), - ...(query.data.description ? { description: query.data.description } : {}), - ...(query.data.slug ? { slug: query.data.slug } : {}) + ...(name ? { name } : {}), + ...(description ? { description } : {}), + ...(slug ? { slug } : {}) } }) - - return organizations } // If the user is not a system admin, return only organizations @@ -52,9 +36,9 @@ export default defineEventHandler(async (event) => { where: { appUser: session.id, organization: { - ...(query.data.name ? { name: query.data.name } : {}), - ...(query.data.description ? { description: query.data.description } : {}), - ...(query.data.slug ? { slug: query.data.slug } : {}) + ...(name ? { name } : {}), + ...(description ? { description } : {}), + ...(slug ? { slug } : {}) } }, populate: ['organization'] diff --git a/server/api/organizations/index.post.ts b/server/api/organizations/index.post.ts index 9aa01fb3..dd960ec1 100644 --- a/server/api/organizations/index.post.ts +++ b/server/api/organizations/index.post.ts @@ -12,27 +12,18 @@ const bodySchema = z.object({ }) /** - * POST /api/organizations - * * Creates a new organization and returns its id */ export default defineEventHandler(async (event) => { - const body = await readValidatedBody(event, (b) => bodySchema.safeParse(b)), + const { name, description } = await validateEventBody(event, bodySchema), session = (await getServerSession(event))!, em = fork() - if (!body.success) - throw createError({ - statusCode: 400, - statusMessage: 'Bad Request: Invalid body parameters', - message: JSON.stringify(body.error.errors) - }) - const sessionUser = em.getReference(AppUser, session.id) const newOrg = new Organization({ - name: body.data.name, - description: body.data.description, + name, + description, solutions: [] }) diff --git a/server/api/outcomes/[id].delete.ts b/server/api/outcomes/[id].delete.ts index 6ce81c32..b1f8ebc6 100644 --- a/server/api/outcomes/[id].delete.ts +++ b/server/api/outcomes/[id].delete.ts @@ -1,20 +1,25 @@ +import { z } from "zod" import { fork } from "~/server/data/orm" -import { Outcome } from "~/server/domain/requirements/index" +import { Outcome } from "~/server/domain/requirements/index.js" + +const paramSchema = z.object({ + id: z.string().uuid() +}) + +const bodySchema = z.object({ + solutionId: z.string().uuid() +}) /** * Delete outcome by id. */ export default defineEventHandler(async (event) => { - const id = event.context.params?.id, + const { id } = await validateEventParams(event, paramSchema), + { solutionId } = await validateEventBody(event, bodySchema), em = fork() - if (id) { - em.remove(em.getReference(Outcome, id)) - await em.flush() - } else { - throw createError({ - statusCode: 400, - statusMessage: "Bad Request: id is required." - }) - } -}) + await assertSolutionContributor(event, solutionId) + + em.remove(em.getReference(Outcome, id)) + await em.flush() +}) \ No newline at end of file diff --git a/server/api/outcomes/[id].get.ts b/server/api/outcomes/[id].get.ts index fe688263..719aaf53 100644 --- a/server/api/outcomes/[id].get.ts +++ b/server/api/outcomes/[id].get.ts @@ -1,27 +1,32 @@ +import { z } from "zod" import { fork } from "~/server/data/orm" -import { Outcome } from "~/server/domain/requirements/index" +import { Outcome } from "~/server/domain/requirements/index.js" + +const paramSchema = z.object({ + id: z.string().uuid() +}) + +const querySchema = z.object({ + solutionId: z.string().uuid() +}) /** * Returns an outcome by id */ export default defineEventHandler(async (event) => { - const id = event.context.params?.id, + const { id } = await validateEventParams(event, paramSchema), + { solutionId } = await validateEventQuery(event, querySchema), em = fork() - if (id) { - const result = await em.findOne(Outcome, id) + await assertSolutionReader(event, solutionId) - if (result) - return result - else - throw createError({ - statusCode: 404, - statusMessage: `Item not found with the given id: ${id}` - }) - } else { + const result = await em.findOne(Outcome, id) + + if (!result) throw createError({ - statusCode: 400, - statusMessage: "Bad Request: id is required." + statusCode: 404, + statusMessage: `Item not found with the given id: ${id}` }) - } -}) + + return result +}) \ No newline at end of file diff --git a/server/api/outcomes/[id].put.ts b/server/api/outcomes/[id].put.ts index 76450a7e..b347fad9 100644 --- a/server/api/outcomes/[id].put.ts +++ b/server/api/outcomes/[id].put.ts @@ -1,53 +1,37 @@ import { z } from "zod" import { fork } from "~/server/data/orm" -import { Outcome } from "~/server/domain/requirements/index" -import Solution from "~/server/domain/application/Solution" +import { Outcome } from "~/server/domain/requirements/index.js" + +const paramSchema = z.object({ + id: z.string().uuid() +}) const bodySchema = z.object({ + solutionId: z.string().uuid(), name: z.string(), - statement: z.string(), - solutionId: z.string() + statement: z.string() }) /** * Updates an outcome by id. */ export default defineEventHandler(async (event) => { - const id = event.context.params?.id, - body = await readValidatedBody(event, (b) => bodySchema.safeParse(b)), + const { id } = await validateEventParams(event, paramSchema), + { name, statement, solutionId } = await validateEventBody(event, bodySchema), + { sessionUser } = await assertSolutionContributor(event, solutionId), em = fork() - if (!body.success) + const outcome = await em.findOne(Outcome, id) + + if (!outcome) throw createError({ statusCode: 400, - statusMessage: 'Bad Request: Invalid body parameters', - message: JSON.stringify(body.error.errors) + statusMessage: `Bad Request: No assumption found with id: ${id}` }) - if (id) { - const outcome = await em.findOne(Outcome, id), - solution = await em.findOne(Solution, body.data.solutionId) - - if (!outcome) - throw createError({ - statusCode: 400, - statusMessage: `Bad Request: No assumption found with id: ${id}` - }) - if (!solution) - throw createError({ - statusCode: 400, - statusMessage: `Bad Request: No solution found with id: ${body.data.solutionId}` - }) + outcome.name = name + outcome.statement = statement + outcome.modifiedBy = sessionUser - outcome.name = body.data.name - outcome.statement = body.data.statement - outcome.solution = solution - - await em.flush() - } else { - throw createError({ - statusCode: 400, - statusMessage: "Bad Request: id is required." - }) - } + await em.flush() }) \ No newline at end of file diff --git a/server/api/outcomes/index.get.ts b/server/api/outcomes/index.get.ts index 1e0d7c74..0964c02a 100644 --- a/server/api/outcomes/index.get.ts +++ b/server/api/outcomes/index.get.ts @@ -1,30 +1,23 @@ import { z } from "zod" import { fork } from "~/server/data/orm" -import { Outcome } from "~/server/domain/requirements/index" +import { Outcome } from "~/server/domain/requirements/index.js" const querySchema = z.object({ + solutionId: z.string().uuid(), name: z.string().optional(), - statement: z.string().optional(), - solutionId: z.string().uuid().optional() + statement: z.string().optional() }) /** - * GET /api/obstacles?name&statement&solutionId - * * Returns all obstacles that match the query parameters */ export default defineEventHandler(async (event) => { - const query = await getValidatedQuery(event, (q) => querySchema.safeParse(q)), + const query = await validateEventQuery(event, querySchema), em = fork() - if (!query.success) - throw createError({ - statusCode: 400, - statusMessage: "Bad Request: Invalid query parameters", - message: JSON.stringify(query.error.errors) - }) + await assertSolutionReader(event, query.solutionId) - const results = await em.find(Outcome, Object.entries(query.data) + const results = await em.find(Outcome, Object.entries(query) .filter(([_, value]) => value !== undefined) .reduce((acc, [key, value]) => { if (key.endsWith("Id")) @@ -34,4 +27,4 @@ export default defineEventHandler(async (event) => { ); return results -}) +}) \ No newline at end of file diff --git a/server/api/outcomes/index.post.ts b/server/api/outcomes/index.post.ts index adb56d6c..70cbb458 100644 --- a/server/api/outcomes/index.post.ts +++ b/server/api/outcomes/index.post.ts @@ -1,42 +1,27 @@ import { z } from "zod" import { fork } from "~/server/data/orm" -import { Outcome } from "~/server/domain/requirements/index" -import Solution from "~/server/domain/application/Solution" +import { Outcome } from "~/server/domain/requirements/index.js" const bodySchema = z.object({ + solutionId: z.string().uuid(), name: z.string().min(1), - statement: z.string(), - solutionId: z.string().uuid() + statement: z.string() }) /** - * POST /api/obstacles - * * Creates a new obstacle and returns its id */ export default defineEventHandler(async (event) => { - const body = await readValidatedBody(event, (b) => bodySchema.safeParse(b)), + const { name, statement, solutionId } = await validateEventBody(event, bodySchema), + { solution, sessionUser } = await assertSolutionContributor(event, solutionId), em = fork() - if (!body.success) - throw createError({ - statusCode: 400, - statusMessage: 'Bad Request: Invalid body parameters', - message: JSON.stringify(body.error.errors) - }) - - const solution = await em.findOne(Solution, body.data.solutionId) - - if (!solution) - throw createError({ - statusCode: 400, - statusMessage: `Bad Request: Solution not found for id ${body.data.solutionId}` - }) - const newOutcome = new Outcome({ - name: body.data.name, - statement: body.data.statement, - solution + name, + statement, + solution, + modifiedBy: sessionUser, + lastModified: new Date() }) await em.persistAndFlush(newOutcome) diff --git a/server/api/persons/[id].delete.ts b/server/api/persons/[id].delete.ts index e2d04473..04661209 100644 --- a/server/api/persons/[id].delete.ts +++ b/server/api/persons/[id].delete.ts @@ -1,20 +1,25 @@ +import { z } from "zod" import { fork } from "~/server/data/orm" -import { Person } from "~/server/domain/requirements/index" +import { Person } from "~/server/domain/requirements/index.js" + +const paramSchema = z.object({ + id: z.string().uuid() +}) + +const bodySchema = z.object({ + solutionId: z.string().uuid() +}) /** * Delete Person by id. */ export default defineEventHandler(async (event) => { - const id = event.context.params?.id, + const { id } = await validateEventParams(event, paramSchema), + { solutionId } = await validateEventBody(event, bodySchema), em = fork() - if (id) { - em.remove(em.getReference(Person, id)) - await em.flush() - } else { - throw createError({ - statusCode: 400, - statusMessage: "Bad Request: id is required." - }) - } -}) + await assertSolutionContributor(event, solutionId) + + em.remove(em.getReference(Person, id)) + await em.flush() +}) \ No newline at end of file diff --git a/server/api/persons/[id].get.ts b/server/api/persons/[id].get.ts index d4f59dd4..01afd3aa 100644 --- a/server/api/persons/[id].get.ts +++ b/server/api/persons/[id].get.ts @@ -1,27 +1,32 @@ +import { z } from "zod" import { fork } from "~/server/data/orm" -import { Person } from "~/server/domain/requirements/index" +import { Person } from "~/server/domain/requirements/index.js" + +const paramSchema = z.object({ + id: z.string().uuid() +}) + +const querySchema = z.object({ + solutionId: z.string().uuid(), +}) /** * Returns a person by id */ export default defineEventHandler(async (event) => { - const id = event.context.params?.id, + const { id } = await validateEventParams(event, paramSchema), + { solutionId } = await validateEventQuery(event, querySchema), em = fork() - if (id) { - const result = await em.findOne(Person, id) + await assertSolutionReader(event, solutionId) - if (result) - return result - else - throw createError({ - statusCode: 404, - statusMessage: `Item not found with the given id: ${id}` - }) - } else { + const result = await em.findOne(Person, id) + + if (!result) throw createError({ - statusCode: 400, - statusMessage: "Bad Request: id is required." + statusCode: 404, + statusMessage: `Item not found with the given id: ${id}` }) - } -}) + + return result +}) \ No newline at end of file diff --git a/server/api/persons/[id].put.ts b/server/api/persons/[id].put.ts index 037118cf..0ab5f92c 100644 --- a/server/api/persons/[id].put.ts +++ b/server/api/persons/[id].put.ts @@ -1,12 +1,15 @@ import { z } from "zod" import { fork } from "~/server/data/orm" -import { Person } from "~/server/domain/requirements/index" -import Solution from "~/server/domain/application/Solution" +import { Person } from "~/server/domain/requirements/index.js" + +const paramSchema = z.object({ + id: z.string().uuid() +}) const bodySchema = z.object({ + solutionId: z.string().uuid(), name: z.string(), statement: z.string(), - solutionId: z.string().uuid(), email: z.string().email() }) @@ -14,42 +17,23 @@ const bodySchema = z.object({ * Updates a person by id. */ export default defineEventHandler(async (event) => { - const id = event.context.params?.id, - body = await readValidatedBody(event, (b) => bodySchema.safeParse(b)), + const { id } = await validateEventParams(event, paramSchema), + { email, name, statement, solutionId } = await validateEventBody(event, bodySchema), + { sessionUser } = await assertSolutionContributor(event, solutionId), em = fork() - if (!body.success) + const person = await em.findOne(Person, id) + + if (!person) throw createError({ statusCode: 400, - statusMessage: 'Bad Request: Invalid body parameters', - message: JSON.stringify(body.error.errors) + statusMessage: `Bad Request: No assumption found with id: ${id}` }) - if (id) { - const person = await em.findOne(Person, id), - solution = await em.findOne(Solution, body.data.solutionId) + person.name = name + person.statement = statement + person.email = email + person.modifiedBy = sessionUser - if (!person) - throw createError({ - statusCode: 400, - statusMessage: `Bad Request: No assumption found with id: ${id}` - }) - if (!solution) - throw createError({ - statusCode: 400, - statusMessage: `Bad Request: No solution found with id: ${body.data.solutionId}` - }) - - person.name = body.data.name - person.statement = body.data.statement - person.solution = solution - person.email = body.data.email - - await em.flush() - } else { - throw createError({ - statusCode: 400, - statusMessage: "Bad Request: id is required." - }) - } + await em.flush() }) \ No newline at end of file diff --git a/server/api/persons/index.get.ts b/server/api/persons/index.get.ts index 03e82f31..ff4d5907 100644 --- a/server/api/persons/index.get.ts +++ b/server/api/persons/index.get.ts @@ -1,11 +1,11 @@ import { z } from "zod" import { fork } from "~/server/data/orm" -import { Person } from "~/server/domain/requirements/index" +import { Person } from "~/server/domain/requirements/index.js" const querySchema = z.object({ + solutionId: z.string().uuid(), name: z.string().optional(), statement: z.string().optional(), - solutionId: z.string().uuid().optional(), email: z.string().email().optional() }) @@ -13,17 +13,12 @@ const querySchema = z.object({ * Returns all persons that match the query parameters */ export default defineEventHandler(async (event) => { - const query = await getValidatedQuery(event, (q) => querySchema.safeParse(q)), + const query = await validateEventQuery(event, querySchema), em = fork() - if (!query.success) - throw createError({ - statusCode: 400, - statusMessage: "Bad Request: Invalid query parameters", - message: JSON.stringify(query.error.errors) - }) + await assertSolutionReader(event, query.solutionId) - const results = await em.find(Person, Object.entries(query.data) + const results = await em.find(Person, Object.entries(query) .filter(([_, value]) => value !== undefined) .reduce((acc, [key, value]) => { if (key.endsWith("Id")) @@ -33,4 +28,4 @@ export default defineEventHandler(async (event) => { ); return results -}) +}) \ No newline at end of file diff --git a/server/api/persons/index.post.ts b/server/api/persons/index.post.ts index 71875149..31c2150c 100644 --- a/server/api/persons/index.post.ts +++ b/server/api/persons/index.post.ts @@ -1,12 +1,11 @@ import { z } from "zod" import { fork } from "~/server/data/orm" -import Solution from "~/server/domain/application/Solution" -import { Person } from "~/server/domain/requirements/index" +import { Person } from "~/server/domain/requirements/index.js" const bodySchema = z.object({ + solutionId: z.string().uuid(), name: z.string(), statement: z.string(), - solutionId: z.string().uuid(), email: z.string().email() }) @@ -14,29 +13,17 @@ const bodySchema = z.object({ * Creates a new person and returns its id */ export default defineEventHandler(async (event) => { - const body = await readValidatedBody(event, (b) => bodySchema.safeParse(b)), + const { email, name, statement, solutionId } = await validateEventBody(event, bodySchema), + { solution, sessionUser } = await assertSolutionContributor(event, solutionId), em = fork() - if (!body.success) - throw createError({ - statusCode: 400, - statusMessage: 'Bad Request: Invalid body parameters', - message: JSON.stringify(body.error.errors) - }) - - const solution = await em.findOne(Solution, body.data.solutionId) - - if (!solution) - throw createError({ - statusCode: 400, - statusMessage: `Bad Request: Solution not found for id ${body.data.solutionId}` - }) - const newPerson = new Person({ - name: body.data.name, - statement: body.data.statement, + name, + statement, solution, - email: body.data.email + email, + modifiedBy: sessionUser, + lastModified: new Date() }) await em.persistAndFlush(newPerson) diff --git a/server/api/solutions/[id].delete.ts b/server/api/solutions/[id].delete.ts index 6cb29874..30515393 100644 --- a/server/api/solutions/[id].delete.ts +++ b/server/api/solutions/[id].delete.ts @@ -1,51 +1,31 @@ import { fork } from "~/server/data/orm" -import Solution from "~/server/domain/application/Solution" -import Organization from "~/server/domain/application/Organization"; -import AppUserOrganizationRole from "~/server/domain/application/AppUserOrganizationRole"; -import { getServerSession } from '#auth' -import AppRole from "~/server/domain/application/AppRole"; +import { z } from "zod" import { Collection } from "@mikro-orm/core"; +import assertSolutionAdmin from "~/server/utils/assertSolutionAdmin"; +import { Requirement } from "~/server/domain/requirements"; + +const paramSchema = z.object({ + id: z.string().uuid() +}) /** * Delete a solution by id. */ export default defineEventHandler(async (event) => { - const id = event.context.params?.id; - - if (!id) - throw createError({ - statusCode: 400, - statusMessage: "Bad Request: id is required." - }) - - const em = fork(), - session = (await getServerSession(event))!, - solution = await em.findOne(Solution, { id }) + const { id } = await validateEventParams(event, paramSchema), + { solution } = await assertSolutionAdmin(event, id), + em = fork() - if (!solution) - throw createError({ - statusCode: 404, - statusMessage: "Not Found: Solution not found." - }) - - const organization = await em.findOne(Organization, { id: solution.organization.id }), - appUserOrgRoles = await em.find(AppUserOrganizationRole, { appUser: session.id, organization }) - - // A solution can only be deleted by a system admin - // or the associated organization admin - if (session.isSystemAdmin || appUserOrgRoles.some(r => r.role === AppRole.ORGANIZATION_ADMIN)) { - // for each property of the solution that is a collection, remove all items - for (const key in solution) { - const maybeCollection = Reflect.get(solution, key) as Collection - if (maybeCollection instanceof Collection) - maybeCollection.removeAll() + // for each property of the solution that is a collection, remove all items + for (const key in solution) { + const maybeCollection = Reflect.get(solution, key) as Collection + if (maybeCollection instanceof Collection) { + await maybeCollection.load() + maybeCollection.getItems().map(item => em.remove(item)) + maybeCollection.removeAll() } - await em.flush() - await em.removeAndFlush(solution) - } else { - throw createError({ - statusCode: 403, - statusMessage: "Forbidden: You must be a system admin or an organization admin to delete a solution." - }) } + + await em.flush() + await em.removeAndFlush(solution) }) diff --git a/server/api/solutions/[id].get.ts b/server/api/solutions/[id].get.ts index f8879073..fb5dde5a 100644 --- a/server/api/solutions/[id].get.ts +++ b/server/api/solutions/[id].get.ts @@ -1,42 +1,15 @@ -import { fork } from "~/server/data/orm" -import Solution from "~/server/domain/application/Solution" -import Organization from "~/server/domain/application/Organization" -import AppUserOrganizationRole from "~/server/domain/application/AppUserOrganizationRole" -import { getServerSession } from '#auth' +import { z } from "zod" + +const paramSchema = z.object({ + id: z.string().uuid() +}) /** * Returns a solution by id */ export default defineEventHandler(async (event) => { - const id = event.context.params?.id - - if (!id) - throw createError({ - statusCode: 400, - statusMessage: "Bad Request: id is required." - }) - - const em = fork(), - session = (await getServerSession(event))!, - solution = await em.findOne(Solution, { id }) - - if (!solution) - throw createError({ - statusCode: 404, - statusMessage: `Item not found with the given id: ${id}` - }) - - const organization = await em.findOne(Organization, { id: solution.organization.id }), - sessionUserOrgRoles = await em.find(AppUserOrganizationRole, { appUser: session.id, organization }) - - // check if the user is a member of the organization or a system admin before returning it - const result = session.isSystemAdmin || sessionUserOrgRoles.length > 0 ? solution : null - - if (!result) - throw createError({ - statusCode: 403, - statusMessage: "Forbidden: You must be a member of the organization or a system admin to view this item." - }) + const { id } = await validateEventParams(event, paramSchema), + { solution } = await assertSolutionReader(event, id) - return result + return solution }) diff --git a/server/api/solutions/[id].put.ts b/server/api/solutions/[id].put.ts index 477e3a82..e882b615 100644 --- a/server/api/solutions/[id].put.ts +++ b/server/api/solutions/[id].put.ts @@ -1,10 +1,9 @@ import { z } from "zod" import { fork } from "~/server/data/orm" -import Solution from "~/server/domain/application/Solution" -import AppUserOrganizationRole from "~/server/domain/application/AppUserOrganizationRole" -import Organization from "~/server/domain/application/Organization" -import { getServerSession } from '#auth' -import AppRole from "~/server/domain/application/AppRole" + +const paramSchema = z.object({ + id: z.string().uuid() +}) const bodySchema = z.object({ name: z.string().min(1).max(100), @@ -12,55 +11,16 @@ const bodySchema = z.object({ }) /** - * PUT /api/solutions/:id - * * Updates a solution by id. */ export default defineEventHandler(async (event) => { - const id = event.context.params?.id - - if (!id) - throw createError({ - statusCode: 400, - statusMessage: "Bad Request: id is required." - }) - - const em = fork(), - body = await readValidatedBody(event, (b) => bodySchema.safeParse(b)), - session = (await getServerSession(event))!, - solution = await em.findOne(Solution, id); - - if (!solution) - throw createError({ - statusCode: 400, - statusMessage: `Bad Request: No solution found with id: ${id}` - }) - if (!body.success) - throw createError({ - statusCode: 400, - statusMessage: 'Bad Request: Invalid body parameters', - message: JSON.stringify(body.error.errors) - }) - - const organization = await em.findOne(Organization, { id: solution!.organization.id }), - sessionUserOrgRoles = await em.find(AppUserOrganizationRole, { appUser: session.id, organization }) - - // A solution can only be updated by a system admin - // or the associated organization admin, or organization contributor - - if (session.isSystemAdmin || sessionUserOrgRoles.some(r => { - return r.role === AppRole.ORGANIZATION_CONTRIBUTOR || r.role === AppRole.ORGANIZATION_ADMIN - })) { - solution!.name = body.data.name - solution!.description = body.data.description + const { id } = await validateEventParams(event, paramSchema), + { description, name } = await validateEventBody(event, bodySchema), + { solution } = await assertSolutionContributor(event, id), + em = fork() - await em.flush() - } else { - throw createError({ - statusCode: 403, - statusMessage: "Forbidden: You must be a system admin, an organization admin," + - " or an organization contributor to update a solution." - }) - } + solution!.name = name + solution!.description = description + await em.flush() }) \ No newline at end of file diff --git a/server/api/solutions/index.get.ts b/server/api/solutions/index.get.ts index 4b676424..fbebd6f6 100644 --- a/server/api/solutions/index.get.ts +++ b/server/api/solutions/index.get.ts @@ -1,9 +1,6 @@ import { fork } from "~/server/data/orm" import { z } from "zod" -import Solution from "~/server/domain/application/Solution" -import AppUserOrganizationRole from "~/server/domain/application/AppUserOrganizationRole" import Organization from "~/server/domain/application/Organization" -import { getServerSession } from '#auth' const querySchema = z.object({ name: z.string().max(100).optional(), @@ -16,70 +13,23 @@ const querySchema = z.object({ }, "At least one of organizationId or organizationSlug should be provided"); /** - * GET /api/solutions - * - * Returns all solutions - * - * GET /api/solutions?name&description&slug&organizationId - * * Returns all solutions that match the query parameters */ export default defineEventHandler(async (event) => { - const - query = await getValidatedQuery(event, (q) => querySchema.safeParse(q)), - session = (await getServerSession(event))! - - if (!query.success) - throw createError({ - statusCode: 400, - statusMessage: "Bad Request: Invalid query parameters", - message: JSON.stringify(query.error.errors) - }) - - const em = fork() + const { description, name, organizationId, organizationSlug, slug } = await validateEventQuery(event, querySchema), + em = fork() const organization = await em.findOne(Organization, { - ...(query.data.organizationId ? { id: query.data.organizationId } : {}), - ...(query.data.organizationSlug ? { slug: query.data.organizationSlug } : {}), - }) - - if (!organization) - throw createError({ - statusCode: 404, - statusMessage: "Not Found: Organization not found" - }) - - // If the user is a system admin, return all solutions - // filtered by the query parameters - const allOrgSolutions = await em.findAll(Solution, { - where: { - ...{ organization }, - ...(query.data.name ? { name: query.data.name } : {}), - ...(query.data.description ? { description: query.data.description } : {}), - ...(query.data.slug ? { slug: query.data.slug } : {}), - } - }) - - if (allOrgSolutions.length === 0) - return [] - - if (session.isSystemAdmin) - return allOrgSolutions - - // If the user is not a system admin, return only solutions - // that the user is associated with - const appUserOrgs = await em.findAll(AppUserOrganizationRole, { - where: { - appUser: session.id, - organization - }, - }) - - if (appUserOrgs.length === 0) - throw createError({ - statusCode: 403, - statusMessage: "Forbidden: You do not have access to this organization" - }) - - return allOrgSolutions.filter((sol) => appUserOrgs.some((aou) => aou.organization.id === sol.organization.id)) + ...(organizationId ? { id: organizationId } : {}), + ...(organizationSlug ? { slug: organizationSlug } : {}), + }, { populate: ['solutions'] }) + + await assertOrgReader(event, organization!.id) + + return organization!.solutions + .filter((sol) => + name ? sol.name === name : true && + description ? sol.description === description : true && + slug ? sol.slug === slug : true + ) }) \ No newline at end of file diff --git a/server/api/solutions/index.post.ts b/server/api/solutions/index.post.ts index 11ea7c46..c51ac272 100644 --- a/server/api/solutions/index.post.ts +++ b/server/api/solutions/index.post.ts @@ -1,10 +1,6 @@ import { z } from "zod" import { fork } from "~/server/data/orm" -import Organization from "~/server/domain/application/Organization" import Solution from "~/server/domain/application/Solution" -import AppUserOrganizationRole from "~/server/domain/application/AppUserOrganizationRole" -import { getServerSession } from '#auth' -import AppRole from "~/server/domain/application/AppRole" import { Justification } from "~/server/domain/requirements/index" const bodySchema = z.object({ @@ -14,47 +10,16 @@ const bodySchema = z.object({ }) /** - * POST /api/solutions - * * Creates a new solution and returns its id */ export default defineEventHandler(async (event) => { - const config = useRuntimeConfig(), - body = await readValidatedBody(event, (b) => bodySchema.safeParse(b)), - session = (await getServerSession(event))!, + const { description, name, organizationId } = await validateEventBody(event, bodySchema), + { organization, sessionUser } = await assertOrgAdmin(event, organizationId), em = fork() - if (!body.success) - throw createError({ - statusCode: 400, - statusMessage: 'Bad Request: Invalid body parameters', - message: JSON.stringify(body.error.errors) - }) - - const organization = await em.findOne(Organization, { id: body.data.organizationId }) - - if (!organization) - throw createError({ - statusCode: 400, - statusMessage: `Bad Request: No organization found with id: ${body.data.organizationId}` - }) - - // Only System Admins and Organization Admins can create solutions - // An Organization Admin can only create solutions for their organization - const appUserId = session.id, - appUserOrgRoles = await em.find(AppUserOrganizationRole, { appUser: appUserId, organization }) - - if (!session.isSystemAdmin && !appUserOrgRoles.some(r => { - return r.role === AppRole.ORGANIZATION_ADMIN - })) - throw createError({ - statusCode: 403, - statusMessage: 'Forbidden: You do not have permission to create solutions for this organization' - }) - const newSolution = new Solution({ - name: body.data.name, - description: body.data.description, + name, + description, organization, assumptions: [], constraints: [], @@ -78,22 +43,30 @@ export default defineEventHandler(async (event) => { newSolution.justifications.add(new Justification({ name: 'Vision', solution: newSolution, - statement: '' + statement: '', + lastModified: new Date(), + modifiedBy: sessionUser })) newSolution.justifications.add(new Justification({ name: 'Mission', solution: newSolution, - statement: '' + statement: '', + lastModified: new Date(), + modifiedBy: sessionUser })) newSolution.justifications.add(new Justification({ name: 'Situation', solution: newSolution, - statement: '' + statement: '', + lastModified: new Date(), + modifiedBy: sessionUser })) newSolution.justifications.add(new Justification({ name: 'Objective', solution: newSolution, - statement: '' + statement: '', + lastModified: new Date(), + modifiedBy: sessionUser })) await em.persistAndFlush(newSolution) diff --git a/server/api/stakeholders/[id].delete.ts b/server/api/stakeholders/[id].delete.ts index 3f0f7f4d..d952fcd4 100644 --- a/server/api/stakeholders/[id].delete.ts +++ b/server/api/stakeholders/[id].delete.ts @@ -1,20 +1,25 @@ +import { z } from "zod" import { fork } from "~/server/data/orm" -import { Stakeholder } from "~/server/domain/requirements/index" +import { Stakeholder } from "~/server/domain/requirements/index.js" + +const paramSchema = z.object({ + id: z.string().uuid() +}) + +const bodySchema = z.object({ + solutionId: z.string().uuid() +}) /** * Delete Stakeholder by id. */ export default defineEventHandler(async (event) => { - const id = event.context.params?.id, + const { id } = await validateEventParams(event, paramSchema), + { solutionId } = await validateEventBody(event, bodySchema), em = fork() - if (id) { - em.remove(em.getReference(Stakeholder, id)) - await em.flush() - } else { - throw createError({ - statusCode: 400, - statusMessage: "Bad Request: id is required." - }) - } -}) + await assertSolutionContributor(event, solutionId) + + em.remove(em.getReference(Stakeholder, id)) + await em.flush() +}) \ No newline at end of file diff --git a/server/api/stakeholders/[id].get.ts b/server/api/stakeholders/[id].get.ts index 84659b1c..c24946a9 100644 --- a/server/api/stakeholders/[id].get.ts +++ b/server/api/stakeholders/[id].get.ts @@ -1,27 +1,32 @@ +import { z } from "zod" import { fork } from "~/server/data/orm" -import { Stakeholder } from "~/server/domain/requirements/index" +import { Stakeholder } from "~/server/domain/requirements/index.js" + +const paramSchema = z.object({ + id: z.string().uuid() +}) + +const querySchema = z.object({ + solutionId: z.string().uuid() +}) /** * Returns a stakeholder by id */ export default defineEventHandler(async (event) => { - const id = event.context.params?.id, + const { id } = await validateEventParams(event, paramSchema), + { solutionId } = await validateEventQuery(event, querySchema), em = fork() - if (id) { - const result = await em.findOne(Stakeholder, id) + await assertSolutionReader(event, solutionId) - if (result) - return result - else - throw createError({ - statusCode: 404, - statusMessage: `Item not found with the given id: ${id}` - }) - } else { + const result = await em.findOne(Stakeholder, id) + + if (!result) throw createError({ - statusCode: 400, - statusMessage: "Bad Request: id is required." + statusCode: 404, + statusMessage: `Item not found with the given id: ${id}` }) - } -}) + + return result +}) \ No newline at end of file diff --git a/server/api/stakeholders/[id].put.ts b/server/api/stakeholders/[id].put.ts index 30194eb7..3984a0b1 100644 --- a/server/api/stakeholders/[id].put.ts +++ b/server/api/stakeholders/[id].put.ts @@ -1,12 +1,15 @@ import { z } from "zod" import { fork } from "~/server/data/orm" -import Solution from "~/server/domain/application/Solution" -import { Stakeholder, StakeholderCategory, StakeholderSegmentation } from "~/server/domain/requirements/index" +import { Stakeholder, StakeholderSegmentation, StakeholderCategory } from "~/server/domain/requirements/index.js" + +const paramSchema = z.object({ + id: z.string().uuid() +}) const bodySchema = z.object({ + solutionId: z.string().uuid(), name: z.string(), statement: z.string(), - solutionId: z.string().uuid(), parentComponentId: z.string().uuid().optional(), availability: z.number().min(0).max(100), influence: z.number().min(0).max(100), @@ -15,54 +18,34 @@ const bodySchema = z.object({ }) /** - * PUT /api/stakeholders/:id - * * Updates a stakeholder by id. */ export default defineEventHandler(async (event) => { - const id = event.context.params?.id, - body = await readValidatedBody(event, (b) => bodySchema.safeParse(b)), + const { id } = await validateEventParams(event, paramSchema), + { availability, category, influence, name, segmentation, statement, parentComponentId, solutionId } = + await validateEventBody(event, bodySchema), + { sessionUser } = await assertSolutionContributor(event, solutionId), em = fork() - if (!body.success) + const stakeholder = await em.findOne(Stakeholder, id), + parentStakeholder = parentComponentId ? + await em.findOne(Stakeholder, parentComponentId) + : undefined + + if (!stakeholder) throw createError({ statusCode: 400, - statusMessage: 'Bad Request: Invalid body parameters', - message: JSON.stringify(body.error.errors) + statusMessage: `Bad Request: No stakeholder found with id: ${id}` }) - if (id) { - const stakeholder = await em.findOne(Stakeholder, id), - solution = await em.findOne(Solution, body.data.solutionId), - parentStakeholder = body.data.parentComponentId ? - await em.findOne(Stakeholder, body.data.parentComponentId) - : undefined + stakeholder.name = name + stakeholder.statement = statement + stakeholder.availability = availability + stakeholder.influence = influence + stakeholder.segmentation = segmentation + stakeholder.category = category + stakeholder.parentComponent = parentStakeholder || undefined + stakeholder.modifiedBy = sessionUser - if (!stakeholder) - throw createError({ - statusCode: 400, - statusMessage: `Bad Request: No stakeholder found with id: ${id}` - }) - if (!solution) - throw createError({ - statusCode: 400, - statusMessage: `Bad Request: No solution found with id: ${body.data.solutionId}` - }) - - stakeholder.name = body.data.name - stakeholder.statement = body.data.statement - stakeholder.solution = solution - stakeholder.availability = body.data.availability - stakeholder.influence = body.data.influence - stakeholder.segmentation = body.data.segmentation - stakeholder.category = body.data.category - stakeholder.parentComponent = parentStakeholder || undefined - - await em.flush() - } else { - throw createError({ - statusCode: 400, - statusMessage: "Bad Request: id is required." - }) - } + await em.flush() }) \ No newline at end of file diff --git a/server/api/stakeholders/index.get.ts b/server/api/stakeholders/index.get.ts index 698bfb9b..619a59a9 100644 --- a/server/api/stakeholders/index.get.ts +++ b/server/api/stakeholders/index.get.ts @@ -1,11 +1,11 @@ import { z } from "zod" import { fork } from "~/server/data/orm" -import { Stakeholder, StakeholderCategory, StakeholderSegmentation } from "~/server/domain/requirements/index" +import { Stakeholder, StakeholderSegmentation, StakeholderCategory } from "~/server/domain/requirements/index.js" const querySchema = z.object({ + solutionId: z.string().uuid(), name: z.string().optional(), statement: z.string().optional(), - solutionId: z.string().uuid().optional(), parentComponentId: z.string().uuid().optional(), availability: z.number().min(0).max(100).optional(), influence: z.number().min(0).max(100).optional(), @@ -14,26 +14,15 @@ const querySchema = z.object({ }) /** - * GET /api/stakeholders - * - * Returns all stakeholders that match the query parameters - * - * GET /api/stakeholders?name&statement&solutionId - * * Returns all stakeholders that match the query parameters */ export default defineEventHandler(async (event) => { - const query = await getValidatedQuery(event, (q) => querySchema.safeParse(q)), + const query = await validateEventQuery(event, querySchema), em = fork() - if (!query.success) - throw createError({ - statusCode: 400, - statusMessage: "Bad Request: Invalid query parameters", - message: JSON.stringify(query.error.errors) - }) + await assertSolutionReader(event, query.solutionId) - const results = await em.find(Stakeholder, Object.entries(query.data) + const results = await em.find(Stakeholder, Object.entries(query) .filter(([_, value]) => value !== undefined) .reduce((acc, [key, value]) => { if (key.endsWith("Id")) @@ -42,4 +31,4 @@ export default defineEventHandler(async (event) => { }, {} as Record)); return results -}) +}) \ No newline at end of file diff --git a/server/api/stakeholders/index.post.ts b/server/api/stakeholders/index.post.ts index e7d0cf41..098c3cdd 100644 --- a/server/api/stakeholders/index.post.ts +++ b/server/api/stakeholders/index.post.ts @@ -1,12 +1,11 @@ import { z } from "zod" import { fork } from "~/server/data/orm" -import Solution from "~/server/domain/application/Solution" -import { Stakeholder, StakeholderCategory, StakeholderSegmentation } from "~/server/domain/requirements/index" +import { Stakeholder, StakeholderCategory, StakeholderSegmentation } from "~/server/domain/requirements/index.js" const bodySchema = z.object({ + solutionId: z.string().uuid(), name: z.string(), statement: z.string(), - solutionId: z.string().uuid(), parentComponentId: z.string().uuid().optional(), availability: z.number().min(0).max(100), influence: z.number().min(0).max(100), @@ -18,36 +17,26 @@ const bodySchema = z.object({ * Creates a new stakeholder and returns its id */ export default defineEventHandler(async (event) => { - const body = await readValidatedBody(event, (b) => bodySchema.safeParse(b)), + const { availability, category, influence, name, segmentation, statement, parentComponentId, solutionId } + = await validateEventBody(event, bodySchema), + { solution, sessionUser } = await assertSolutionContributor(event, solutionId), em = fork() - if (!body.success) - throw createError({ - statusCode: 400, - statusMessage: 'Bad Request: Invalid body parameters', - message: JSON.stringify(body.error.errors) - }) - - const solution = await em.findOne(Solution, body.data.solutionId), - parentStakeholder = body.data.parentComponentId ? - await em.findOne(Stakeholder, body.data.parentComponentId) - : undefined - - if (!solution) - throw createError({ - statusCode: 400, - statusMessage: `Bad Request: Solution not found for id ${body.data.solutionId}` - }) + const parentStakeholder = parentComponentId ? + await em.findOne(Stakeholder, parentComponentId) + : undefined const newStakeholder = new Stakeholder({ - name: body.data.name, - statement: body.data.statement, + name, + statement, solution, parentComponent: parentStakeholder ?? undefined, - availability: body.data.availability, - influence: body.data.influence, - segmentation: body.data.segmentation, - category: body.data.category + availability, + influence, + segmentation, + category, + lastModified: new Date(), + modifiedBy: sessionUser }) await em.persistAndFlush(newStakeholder) diff --git a/server/api/system-components/[id].delete.ts b/server/api/system-components/[id].delete.ts index 27571e0c..5a3ccddc 100644 --- a/server/api/system-components/[id].delete.ts +++ b/server/api/system-components/[id].delete.ts @@ -1,20 +1,25 @@ +import { z } from "zod" import { fork } from "~/server/data/orm" -import { SystemComponent } from "~/server/domain/requirements/index" +import { SystemComponent } from "~/server/domain/requirements/index.js" + +const paramSchema = z.object({ + id: z.string().uuid() +}) + +const bodySchema = z.object({ + solutionId: z.string().uuid() +}) /** * Delete an system component by id. */ export default defineEventHandler(async (event) => { - const id = event.context.params?.id, + const { id } = await validateEventParams(event, paramSchema), + { solutionId } = await validateEventBody(event, bodySchema), em = fork() - if (id) { - em.remove(em.getReference(SystemComponent, id)) - await em.flush() - } else { - throw createError({ - statusCode: 400, - statusMessage: "Bad Request: id is required." - }) - } -}) + await assertSolutionContributor(event, solutionId) + + em.remove(em.getReference(SystemComponent, id)) + await em.flush() +}) \ No newline at end of file diff --git a/server/api/system-components/[id].get.ts b/server/api/system-components/[id].get.ts index e337ee1d..7eb3360f 100644 --- a/server/api/system-components/[id].get.ts +++ b/server/api/system-components/[id].get.ts @@ -1,27 +1,32 @@ +import { z } from "zod" import { fork } from "~/server/data/orm" -import { SystemComponent } from "~/server/domain/requirements/index" +import { SystemComponent } from "~/server/domain/requirements/index.js" + +const paramSchema = z.object({ + id: z.string().uuid() +}) + +const querySchema = z.object({ + solutionId: z.string().uuid() +}) /** * Returns an system component by id */ export default defineEventHandler(async (event) => { - const id = event.context.params?.id, + const { id } = await validateEventParams(event, paramSchema), + { solutionId } = await validateEventQuery(event, querySchema), em = fork() - if (id) { - const result = await em.findOne(SystemComponent, id) + await assertSolutionReader(event, solutionId) - if (result) - return result - else - throw createError({ - statusCode: 404, - statusMessage: `Item not found with the given id: ${id}` - }) - } else { + const result = await em.findOne(SystemComponent, id) + + if (!result) throw createError({ - statusCode: 400, - statusMessage: "Bad Request: id is required." + statusCode: 404, + statusMessage: `Item not found with the given id: ${id}` }) - } -}) + + return result +}) \ No newline at end of file diff --git a/server/api/system-components/[id].put.ts b/server/api/system-components/[id].put.ts index 974191bd..b79eb755 100644 --- a/server/api/system-components/[id].put.ts +++ b/server/api/system-components/[id].put.ts @@ -1,12 +1,15 @@ import { z } from "zod" import { fork } from "~/server/data/orm" -import Solution from "~/server/domain/application/Solution" -import { SystemComponent } from "~/server/domain/requirements/index" +import { SystemComponent } from "~/server/domain/requirements/index.js" + +const paramSchema = z.object({ + id: z.string().uuid() +}) const bodySchema = z.object({ + solutionId: z.string().uuid(), name: z.string(), statement: z.string(), - solutionId: z.string().uuid(), parentComponentId: z.string().uuid().optional() }) @@ -14,43 +17,24 @@ const bodySchema = z.object({ * Updates an environment component by id. */ export default defineEventHandler(async (event) => { - const id = event.context.params?.id, - body = await readValidatedBody(event, (b) => bodySchema.safeParse(b)), + const { id } = await validateEventParams(event, paramSchema), + { name, statement, parentComponentId, solutionId } = await validateEventBody(event, bodySchema), + { sessionUser } = await assertSolutionContributor(event, solutionId), em = fork() - if (!body.success) + const systemComponent = await em.findOne(SystemComponent, id), + parentComponent = parentComponentId ? await em.findOne(SystemComponent, parentComponentId) : undefined + + if (!systemComponent) throw createError({ statusCode: 400, - statusMessage: 'Bad Request: Invalid body parameters', - message: JSON.stringify(body.error.errors) + statusMessage: `Bad Request: No assumption found with id: ${id}` }) - if (id) { - const systemComponent = await em.findOne(SystemComponent, id), - solution = await em.findOne(Solution, body.data.solutionId), - parentComponent = body.data.parentComponentId ? await em.findOne(SystemComponent, body.data.parentComponentId) : undefined + systemComponent.name = name + systemComponent.statement = statement + systemComponent.parentComponent = parentComponent || undefined + systemComponent.modifiedBy = sessionUser - if (!systemComponent) - throw createError({ - statusCode: 400, - statusMessage: `Bad Request: No assumption found with id: ${id}` - }) - if (!solution) - throw createError({ - statusCode: 400, - statusMessage: `Bad Request: No solution found with id: ${body.data.solutionId}` - }) - - systemComponent.name = body.data.name - systemComponent.statement = body.data.statement - systemComponent.solution = solution - systemComponent.parentComponent = parentComponent || undefined - - await em.flush() - } else { - throw createError({ - statusCode: 400, - statusMessage: "Bad Request: id is required." - }) - } + await em.flush() }) \ No newline at end of file diff --git a/server/api/system-components/index.get.ts b/server/api/system-components/index.get.ts index a7b3a635..be0d441a 100644 --- a/server/api/system-components/index.get.ts +++ b/server/api/system-components/index.get.ts @@ -1,31 +1,24 @@ import { z } from "zod" import { fork } from "~/server/data/orm" -import { SystemComponent } from "~/server/domain/requirements/index" +import { SystemComponent } from "~/server/domain/requirements/index.js" const querySchema = z.object({ + solutionId: z.string().uuid(), name: z.string().optional(), statement: z.string().optional(), - solutionId: z.string().uuid().optional(), parentComponentId: z.string().uuid().optional() }) /** - * GET /api/system-components?name&statement&solutionId&parentComponentId - * * Returns all system-components that match the query parameters */ export default defineEventHandler(async (event) => { - const query = await getValidatedQuery(event, (q) => querySchema.safeParse(q)), + const query = await validateEventQuery(event, querySchema), em = fork() - if (!query.success) - throw createError({ - statusCode: 400, - statusMessage: "Bad Request: Invalid query parameters", - message: JSON.stringify(query.error.errors) - }) + await assertSolutionReader(event, query.solutionId) - const results = await em.find(SystemComponent, Object.entries(query.data) + const results = await em.find(SystemComponent, Object.entries(query) .filter(([_, value]) => value !== undefined) .reduce((acc, [key, value]) => { if (key.endsWith("Id")) @@ -34,4 +27,4 @@ export default defineEventHandler(async (event) => { }, {})) return results -}) +}) \ No newline at end of file diff --git a/server/api/system-components/index.post.ts b/server/api/system-components/index.post.ts index 6f794c9e..a5264bb5 100644 --- a/server/api/system-components/index.post.ts +++ b/server/api/system-components/index.post.ts @@ -1,45 +1,30 @@ import { z } from "zod" import { fork } from "~/server/data/orm" -import Solution from "~/server/domain/application/Solution" -import { SystemComponent } from "~/server/domain/requirements/index" +import { SystemComponent } from "~/server/domain/requirements/index.js" const bodySchema = z.object({ + solutionId: z.string().uuid(), name: z.string(), statement: z.string(), - solutionId: z.string().uuid(), parentComponentId: z.string().uuid().optional() }) /** - * POST /api/system-components - * * Creates a new system-component and returns its id */ export default defineEventHandler(async (event) => { - const body = await readValidatedBody(event, (b) => bodySchema.safeParse(b)), - em = fork() - - if (!body.success) - throw createError({ - statusCode: 400, - statusMessage: 'Bad Request: Invalid body parameters', - message: JSON.stringify(body.error.errors) - }) - - const solution = await em.findOne(Solution, body.data.solutionId), - parentComponent = body.data.parentComponentId ? await em.findOne(SystemComponent, body.data.parentComponentId) : undefined - - if (!solution) - throw createError({ - statusCode: 400, - statusMessage: `Bad Request: Solution not found for id ${body.data.solutionId}` - }) + const { name, statement, parentComponentId, solutionId } = await validateEventBody(event, bodySchema), + { solution, sessionUser } = await assertSolutionContributor(event, solutionId), + em = fork(), + parentComponent = parentComponentId ? await em.findOne(SystemComponent, parentComponentId) : undefined const newSystemComponent = new SystemComponent({ - name: body.data.name, - statement: body.data.statement, + name, + statement, solution, - parentComponent: parentComponent || undefined + parentComponent: parentComponent || undefined, + lastModified: new Date(), + modifiedBy: sessionUser }) await em.persistAndFlush(newSystemComponent) diff --git a/server/api/use-cases/[id].delete.ts b/server/api/use-cases/[id].delete.ts index 9b8a3714..a0199279 100644 --- a/server/api/use-cases/[id].delete.ts +++ b/server/api/use-cases/[id].delete.ts @@ -1,20 +1,25 @@ +import { z } from "zod" import { fork } from "~/server/data/orm" -import { UseCase } from "~/server/domain/requirements/index" +import { UseCase } from "~/server/domain/requirements/index.js" + +const paramSchema = z.object({ + id: z.string().uuid() +}) + +const bodySchema = z.object({ + solutionId: z.string().uuid() +}) /** * Delete UseCase by id. */ export default defineEventHandler(async (event) => { - const id = event.context.params?.id, + const { id } = await validateEventParams(event, paramSchema), + { solutionId } = await validateEventBody(event, bodySchema), em = fork() - if (id) { - em.remove(em.getReference(UseCase, id)) - await em.flush() - } else { - throw createError({ - statusCode: 400, - statusMessage: "Bad Request: id is required." - }) - } -}) + await assertSolutionContributor(event, solutionId) + + em.remove(em.getReference(UseCase, id)) + await em.flush() +}) \ No newline at end of file diff --git a/server/api/use-cases/[id].get.ts b/server/api/use-cases/[id].get.ts index c94891b5..0e1e6c99 100644 --- a/server/api/use-cases/[id].get.ts +++ b/server/api/use-cases/[id].get.ts @@ -1,27 +1,32 @@ +import { z } from "zod" import { fork } from "~/server/data/orm" -import { UseCase } from "~/server/domain/requirements/index" +import { UseCase } from "~/server/domain/requirements/index.js" + +const paramSchema = z.object({ + id: z.string().uuid() +}) + +const querySchema = z.object({ + solutionId: z.string().uuid() +}) /** * Returns a UseCase by id */ export default defineEventHandler(async (event) => { - const id = event.context.params?.id, + const { id } = await validateEventParams(event, paramSchema), + { solutionId } = await validateEventQuery(event, querySchema), em = fork() - if (id) { - const result = await em.findOne(UseCase, id) + await assertSolutionReader(event, solutionId) - if (result) - return result - else - throw createError({ - statusCode: 404, - statusMessage: `Item not found with the given id: ${id}` - }) - } else { + const result = await em.findOne(UseCase, id) + + if (!result) throw createError({ - statusCode: 400, - statusMessage: "Bad Request: id is required." + statusCode: 404, + statusMessage: `Item not found with the given id: ${id}` }) - } -}) + + return result +}) \ No newline at end of file diff --git a/server/api/use-cases/[id].put.ts b/server/api/use-cases/[id].put.ts index b6d81438..40d557b8 100644 --- a/server/api/use-cases/[id].put.ts +++ b/server/api/use-cases/[id].put.ts @@ -1,13 +1,16 @@ import { z } from "zod" import { fork } from "~/server/data/orm" -import { Assumption, Effect, MoscowPriority, Stakeholder, UseCase } from "~/server/domain/requirements/index" -import Solution from "~/server/domain/application/Solution" +import { Assumption, Effect, MoscowPriority, Stakeholder, UseCase } from "~/server/domain/requirements/index.js" import { NIL as emptyUuid } from "uuid" +const paramSchema = z.object({ + id: z.string().uuid() +}) + const bodySchema = z.object({ + solutionId: z.string().uuid(), name: z.string(), statement: z.string(), - solutionId: z.string().uuid(), primaryActorId: z.string().uuid(), priority: z.nativeEnum(MoscowPriority), scope: z.string(), @@ -24,69 +27,50 @@ const bodySchema = z.object({ * Updates a UseCase by id */ export default defineEventHandler(async (event) => { - const id = event.context.params?.id, - body = await readValidatedBody(event, (b) => bodySchema.safeParse(b)), + const { id } = await validateEventParams(event, paramSchema), + body = await validateEventBody(event, bodySchema), + { sessionUser } = await assertSolutionContributor(event, body.solutionId), em = fork() - if (!body.success) + const useCase = await em.findOne(UseCase, id), + primaryActor = await em.findOne(Stakeholder, body.primaryActorId), + precondition = await em.findOne(Assumption, body.preconditionId), + successGuarantee = await em.findOne(Effect, body.successGuaranteeId) + + if (!useCase) throw createError({ statusCode: 400, - statusMessage: 'Bad Request: Invalid body parameters', - message: JSON.stringify(body.error.errors) + statusMessage: `Bad Request: No use case found with id: ${id}` }) - - if (id) { - const useCase = await em.findOne(UseCase, id), - solution = await em.findOne(Solution, body.data.solutionId), - primaryActor = await em.findOne(Stakeholder, body.data.primaryActorId), - precondition = await em.findOne(Assumption, body.data.preconditionId), - successGuarantee = await em.findOne(Effect, body.data.successGuaranteeId) - - if (!useCase) - throw createError({ - statusCode: 400, - statusMessage: `Bad Request: No use case found with id: ${id}` - }) - if (!solution) - throw createError({ - statusCode: 400, - statusMessage: `Bad Request: No solution found with id: ${body.data.solutionId}` - }) - if (!primaryActor) - throw createError({ - statusCode: 400, - statusMessage: `Bad Request: No primary actor found with id: ${body.data.primaryActorId}` - }) - if (!precondition) - throw createError({ - statusCode: 400, - statusMessage: `Bad Request: No precondition found with id: ${body.data.preconditionId}` - }) - if (!successGuarantee) - throw createError({ - statusCode: 400, - statusMessage: `Bad Request: No success guarantee found with id: ${body.data.successGuaranteeId}` - }) - - useCase.name = body.data.name - useCase.statement = body.data.statement - useCase.solution = solution - useCase.primaryActor = primaryActor - useCase.priority = body.data.priority - useCase.scope = body.data.scope - useCase.level = body.data.level - useCase.goalInContext = body.data.goalInContext - useCase.precondition = precondition - useCase.triggerId = body.data.triggerId - useCase.mainSuccessScenario = body.data.mainSuccessScenario - useCase.successGuarantee = successGuarantee - useCase.extensions = body.data.extensions - - await em.flush() - } else { + if (!primaryActor) throw createError({ statusCode: 400, - statusMessage: "Bad Request: id is required." + statusMessage: `Bad Request: No primary actor found with id: ${body.primaryActorId}` }) - } + if (!precondition) + throw createError({ + statusCode: 400, + statusMessage: `Bad Request: No precondition found with id: ${body.preconditionId}` + }) + if (!successGuarantee) + throw createError({ + statusCode: 400, + statusMessage: `Bad Request: No success guarantee found with id: ${body.successGuaranteeId}` + }) + + useCase.name = body.name + useCase.statement = body.statement + useCase.primaryActor = primaryActor + useCase.priority = body.priority + useCase.scope = body.scope + useCase.level = body.level + useCase.goalInContext = body.goalInContext + useCase.precondition = precondition + useCase.triggerId = body.triggerId + useCase.mainSuccessScenario = body.mainSuccessScenario + useCase.successGuarantee = successGuarantee + useCase.extensions = body.extensions + useCase.modifiedBy = sessionUser + + await em.flush() }) \ No newline at end of file diff --git a/server/api/use-cases/index.get.ts b/server/api/use-cases/index.get.ts index ddbba1fc..ac0c2310 100644 --- a/server/api/use-cases/index.get.ts +++ b/server/api/use-cases/index.get.ts @@ -1,12 +1,12 @@ import { z } from "zod" import { fork } from "~/server/data/orm" -import { UseCase, MoscowPriority } from "~/server/domain/requirements/index" +import { MoscowPriority, UseCase } from "~/server/domain/requirements/index.js" import { NIL as emptyUuid } from "uuid" const querySchema = z.object({ + solutionId: z.string().uuid(), name: z.string().optional(), statement: z.string().optional(), - solutionId: z.string().uuid().optional(), primaryActorId: z.string().uuid().optional(), priority: z.nativeEnum(MoscowPriority).optional(), scope: z.string().optional(), @@ -23,17 +23,12 @@ const querySchema = z.object({ * Returns all stakeholders that match the query parameters */ export default defineEventHandler(async (event) => { - const query = await getValidatedQuery(event, (q) => querySchema.safeParse(q)), + const query = await validateEventQuery(event, querySchema), em = fork() - if (!query.success) - throw createError({ - statusCode: 400, - statusMessage: "Bad Request: Invalid query parameters", - message: JSON.stringify(query.error.errors) - }) + await assertSolutionReader(event, query.solutionId) - const results = await em.find(UseCase, Object.entries(query.data) + const results = await em.find(UseCase, Object.entries(query) .filter(([_, value]) => value !== undefined) .reduce((acc, [key, value]) => { if (key.endsWith("Id")) @@ -42,4 +37,4 @@ export default defineEventHandler(async (event) => { }, {} as Record)); return results -}) +}) \ No newline at end of file diff --git a/server/api/use-cases/index.post.ts b/server/api/use-cases/index.post.ts index 06fc84b1..c895d7f8 100644 --- a/server/api/use-cases/index.post.ts +++ b/server/api/use-cases/index.post.ts @@ -1,15 +1,12 @@ import { NIL as emptyUuid } from "uuid" import { z } from "zod" +import { MoscowPriority, Stakeholder, Assumption, Effect, UseCase } from "~/server/domain/requirements/index.js" import { fork } from "~/server/data/orm" -import Solution from "~/server/domain/application/Solution" -import { Assumption, MoscowPriority, Stakeholder } from "~/server/domain/requirements/index" -import { Effect } from "~/server/domain/requirements/index" -import { UseCase } from "~/server/domain/requirements/index" const bodySchema = z.object({ + solutionId: z.string().uuid(), name: z.string(), statement: z.string(), - solutionId: z.string().uuid(), primaryActorId: z.string().uuid(), priority: z.nativeEnum(MoscowPriority), scope: z.string(), @@ -26,56 +23,46 @@ const bodySchema = z.object({ * Creates a new use case and returns its id */ export default defineEventHandler(async (event) => { - const body = await readValidatedBody(event, (b) => bodySchema.safeParse(b)), + const body = await validateEventBody(event, bodySchema), + { solution, sessionUser } = await assertSolutionContributor(event, body.solutionId), em = fork() - if (!body.success) - throw createError({ - statusCode: 400, - statusMessage: 'Bad Request: Invalid body parameters', - message: JSON.stringify(body.error.errors) - }) + const primaryActor = await em.findOne(Stakeholder, body.primaryActorId), + precondition = await em.findOne(Assumption, body.preconditionId), + successGuarantee = await em.findOne(Effect, body.successGuaranteeId) - const solution = await em.findOne(Solution, body.data.solutionId), - primaryActor = await em.findOne(Stakeholder, body.data.primaryActorId), - precondition = await em.findOne(Assumption, body.data.preconditionId), - successGuarantee = await em.findOne(Effect, body.data.successGuaranteeId) - - if (!solution) - throw createError({ - statusCode: 400, - statusMessage: `Bad Request: No solution found with id: ${body.data.solutionId}` - }) if (!primaryActor) throw createError({ statusCode: 400, - statusMessage: `Bad Request: No primary actor found with id: ${body.data.primaryActorId}` + statusMessage: `Bad Request: No primary actor found with id: ${body.primaryActorId}` }) if (!precondition) throw createError({ statusCode: 400, - statusMessage: `Bad Request: No precondition found with id: ${body.data.preconditionId}` + statusMessage: `Bad Request: No precondition found with id: ${body.preconditionId}` }) if (!successGuarantee) throw createError({ statusCode: 400, - statusMessage: `Bad Request: No success guarantee found with id: ${body.data.successGuaranteeId}` + statusMessage: `Bad Request: No success guarantee found with id: ${body.successGuaranteeId}` }) const newUseCase = new UseCase({ - name: body.data.name, - statement: body.data.statement, + name: body.name, + statement: body.statement, solution, primaryActor, - priority: body.data.priority, - scope: body.data.scope, - level: body.data.level, - goalInContext: body.data.goalInContext, + priority: body.priority, + scope: body.scope, + level: body.level, + goalInContext: body.goalInContext, precondition, - triggerId: body.data.triggerId, - mainSuccessScenario: body.data.mainSuccessScenario, + triggerId: body.triggerId, + mainSuccessScenario: body.mainSuccessScenario, successGuarantee, - extensions: body.data.extensions + extensions: body.extensions, + lastModified: new Date(), + modifiedBy: sessionUser }) await em.persistAndFlush(newUseCase) diff --git a/server/api/user-stories/[id].delete.ts b/server/api/user-stories/[id].delete.ts index 5c41238f..736773fa 100644 --- a/server/api/user-stories/[id].delete.ts +++ b/server/api/user-stories/[id].delete.ts @@ -1,20 +1,25 @@ +import { z } from "zod" import { fork } from "~/server/data/orm" -import { UserStory } from "~/server/domain/requirements/index" +import { UserStory } from "~/server/domain/requirements/index.js" + +const paramSchema = z.object({ + id: z.string().uuid() +}) + +const bodySchema = z.object({ + solutionId: z.string().uuid() +}) /** * Delete User Story by id. */ export default defineEventHandler(async (event) => { - const id = event.context.params?.id, + const { id } = await validateEventParams(event, paramSchema), + { solutionId } = await validateEventBody(event, bodySchema), em = fork() - if (id) { - em.remove(em.getReference(UserStory, id)) - await em.flush() - } else { - throw createError({ - statusCode: 400, - statusMessage: "Bad Request: id is required." - }) - } -}) + await assertSolutionContributor(event, solutionId) + + em.remove(em.getReference(UserStory, id)) + await em.flush() +}) \ No newline at end of file diff --git a/server/api/user-stories/[id].get.ts b/server/api/user-stories/[id].get.ts index 92916764..4595f9e7 100644 --- a/server/api/user-stories/[id].get.ts +++ b/server/api/user-stories/[id].get.ts @@ -1,27 +1,32 @@ +import { z } from "zod" import { fork } from "~/server/data/orm" -import { UserStory } from "~/server/domain/requirements/index" +import { UserStory } from "~/server/domain/requirements/index.js" + +const paramSchema = z.object({ + id: z.string().uuid() +}) + +const querySchema = z.object({ + solutionId: z.string().uuid() +}) /** * Returns a User Story by id */ export default defineEventHandler(async (event) => { - const id = event.context.params?.id, + const { id } = await validateEventParams(event, paramSchema), + { solutionId } = await validateEventQuery(event, querySchema), em = fork() - if (id) { - const result = await em.findOne(UserStory, id) + await assertSolutionReader(event, solutionId) - if (result) - return result - else - throw createError({ - statusCode: 404, - statusMessage: `Item not found with the given id: ${id}` - }) - } else { + const result = await em.findOne(UserStory, id) + + if (!result) throw createError({ - statusCode: 400, - statusMessage: "Bad Request: id is required." + statusCode: 404, + statusMessage: `Item not found with the given id: ${id}` }) - } -}) + + return result +}) \ No newline at end of file diff --git a/server/api/user-stories/[id].put.ts b/server/api/user-stories/[id].put.ts index f4fda6c4..77c4a66c 100644 --- a/server/api/user-stories/[id].put.ts +++ b/server/api/user-stories/[id].put.ts @@ -1,12 +1,15 @@ import { z } from "zod" import { fork } from "~/server/data/orm" -import { FunctionalBehavior, MoscowPriority, Outcome, Stakeholder, UserStory } from "~/server/domain/requirements/index" -import Solution from "~/server/domain/application/Solution" +import { FunctionalBehavior, MoscowPriority, Outcome, Stakeholder, UserStory } from "~/server/domain/requirements/index.js" + +const paramSchema = z.object({ + id: z.string().uuid() +}) const bodySchema = z.object({ + solutionId: z.string().uuid(), name: z.string(), statement: z.string(), - solutionId: z.string().uuid(), primaryActorId: z.string().uuid(), priority: z.nativeEnum(MoscowPriority), outcomeId: z.string().uuid(), @@ -17,63 +20,44 @@ const bodySchema = z.object({ * Updates a User Story by id */ export default defineEventHandler(async (event) => { - const id = event.context.params?.id, - body = await readValidatedBody(event, (b) => bodySchema.safeParse(b)), + const { id } = await validateEventParams(event, paramSchema), + body = await validateEventBody(event, bodySchema), + { sessionUser } = await assertSolutionContributor(event, body.solutionId), em = fork() - if (!body.success) + const userStory = await em.findOne(UserStory, id), + primaryActor = await em.findOne(Stakeholder, body.primaryActorId), + outcome = await em.findOne(Outcome, body.outcomeId), + functionalBehavior = await em.findOne(FunctionalBehavior, body.functionalBehaviorId) + + if (!userStory) throw createError({ statusCode: 400, - statusMessage: 'Bad Request: Invalid body parameters', - message: JSON.stringify(body.error.errors) + statusMessage: `Bad Request: No user story found with id: ${id}` }) - - if (id) { - const userStory = await em.findOne(UserStory, id), - solution = await em.findOne(Solution, body.data.solutionId), - primaryActor = await em.findOne(Stakeholder, body.data.primaryActorId), - outcome = await em.findOne(Outcome, body.data.outcomeId), - functionalBehavior = await em.findOne(FunctionalBehavior, body.data.functionalBehaviorId) - - if (!userStory) - throw createError({ - statusCode: 400, - statusMessage: `Bad Request: No user story found with id: ${id}` - }) - if (!solution) - throw createError({ - statusCode: 400, - statusMessage: `Bad Request: No solution found with id: ${body.data.solutionId}` - }) - if (!primaryActor) - throw createError({ - statusCode: 400, - statusMessage: `Bad Request: No primary actor found with id: ${body.data.primaryActorId}` - }) - if (!outcome) - throw createError({ - statusCode: 400, - statusMessage: `Bad Request: No outcome found with id: ${body.data.outcomeId}` - }) - if (!functionalBehavior) - throw createError({ - statusCode: 400, - statusMessage: `Bad Request: No functional behavior found with id: ${body.data.functionalBehaviorId}` - }) - - userStory.name = body.data.name - userStory.statement = body.data.statement - userStory.solution = solution - userStory.primaryActor = primaryActor - userStory.priority = body.data.priority - userStory.outcome = outcome - userStory.functionalBehavior = functionalBehavior - - await em.flush() - } else { + if (!primaryActor) throw createError({ statusCode: 400, - statusMessage: "Bad Request: id is required." + statusMessage: `Bad Request: No primary actor found with id: ${body.primaryActorId}` }) - } + if (!outcome) + throw createError({ + statusCode: 400, + statusMessage: `Bad Request: No outcome found with id: ${body.outcomeId}` + }) + if (!functionalBehavior) + throw createError({ + statusCode: 400, + statusMessage: `Bad Request: No functional behavior found with id: ${body.functionalBehaviorId}` + }) + + userStory.name = body.name + userStory.statement = body.statement + userStory.primaryActor = primaryActor + userStory.priority = body.priority + userStory.outcome = outcome + userStory.functionalBehavior = functionalBehavior + userStory.modifiedBy = sessionUser + + await em.flush() }) \ No newline at end of file diff --git a/server/api/user-stories/index.get.ts b/server/api/user-stories/index.get.ts index caa77b33..da1a84c7 100644 --- a/server/api/user-stories/index.get.ts +++ b/server/api/user-stories/index.get.ts @@ -1,11 +1,11 @@ import { z } from "zod" import { fork } from "~/server/data/orm" -import { MoscowPriority, UserStory } from "~/server/domain/requirements/index" +import { UserStory, MoscowPriority } from "~/server/domain/requirements/index.js" const querySchema = z.object({ + solutionId: z.string().uuid(), name: z.string().optional(), statement: z.string().optional(), - solutionId: z.string().uuid().optional(), primaryActorId: z.string().uuid().optional(), priority: z.nativeEnum(MoscowPriority).optional(), outcomeId: z.string().uuid().optional(), @@ -16,17 +16,12 @@ const querySchema = z.object({ * Returns all user stories that match the query parameters */ export default defineEventHandler(async (event) => { - const query = await getValidatedQuery(event, (q) => querySchema.safeParse(q)), + const query = await validateEventQuery(event, querySchema), em = fork() - if (!query.success) - throw createError({ - statusCode: 400, - statusMessage: "Bad Request: Invalid query parameters", - message: JSON.stringify(query.error.errors) - }) + await assertSolutionReader(event, query.solutionId) - const results = await em.find(UserStory, Object.entries(query.data) + const results = await em.find(UserStory, Object.entries(query) .filter(([_, value]) => value !== undefined) .reduce((acc, [key, value]) => { if (key.endsWith("Id")) @@ -35,4 +30,4 @@ export default defineEventHandler(async (event) => { }, {})) return results -}) +}) \ No newline at end of file diff --git a/server/api/user-stories/index.post.ts b/server/api/user-stories/index.post.ts index d48eb2d9..703ec1d6 100644 --- a/server/api/user-stories/index.post.ts +++ b/server/api/user-stories/index.post.ts @@ -1,13 +1,11 @@ import { z } from "zod" -import Solution from "~/server/domain/application/Solution" +import { MoscowPriority, Outcome, Stakeholder, FunctionalBehavior, UserStory } from "~/server/domain/requirements/index.js" import { fork } from "~/server/data/orm" -import { FunctionalBehavior, MoscowPriority, Outcome, Stakeholder } from "~/server/domain/requirements/index" -import { UserStory } from "~/server/domain/requirements/index" const bodySchema = z.object({ + solutionId: z.string().uuid(), name: z.string(), statement: z.string(), - solutionId: z.string().uuid(), primaryActorId: z.string().uuid(), priority: z.nativeEnum(MoscowPriority), outcomeId: z.string().uuid(), @@ -18,52 +16,42 @@ const bodySchema = z.object({ * Creates a new user story and returns its id */ export default defineEventHandler(async (event) => { - const body = await readValidatedBody(event, (b) => bodySchema.safeParse(b)), + const body = await validateEventBody(event, bodySchema), + { solution, sessionUser } = await assertSolutionContributor(event, body.solutionId), em = fork() - if (!body.success) - throw createError({ - statusCode: 400, - statusMessage: 'Bad Request: Invalid body parameters', - message: JSON.stringify(body.error.errors) - }) - - const [solution, primaryActor, outcome, functionalBehavior] = await Promise.all([ - em.findOne(Solution, body.data.solutionId), - em.findOne(Stakeholder, body.data.primaryActorId), - em.findOne(Outcome, body.data.outcomeId), - em.findOne(FunctionalBehavior, body.data.functionalBehaviorId) + const [primaryActor, outcome, functionalBehavior] = await Promise.all([ + em.findOne(Stakeholder, body.primaryActorId), + em.findOne(Outcome, body.outcomeId), + em.findOne(FunctionalBehavior, body.functionalBehaviorId) ]); - if (!solution) - throw createError({ - statusCode: 400, - statusMessage: `Bad Request: No solution found with id: ${body.data.solutionId}` - }) if (!primaryActor) throw createError({ statusCode: 400, - statusMessage: `Bad Request: No primary actor found with id: ${body.data.primaryActorId}` + statusMessage: `Bad Request: No primary actor found with id: ${body.primaryActorId}` }) if (!outcome) throw createError({ statusCode: 400, - statusMessage: `Bad Request: No outcome found with id: ${body.data.outcomeId}` + statusMessage: `Bad Request: No outcome found with id: ${body.outcomeId}` }) if (!functionalBehavior) throw createError({ statusCode: 400, - statusMessage: `Bad Request: No functional behavior found with id: ${body.data.functionalBehaviorId}` + statusMessage: `Bad Request: No functional behavior found with id: ${body.functionalBehaviorId}` }) const newUserStory = new UserStory({ functionalBehavior, outcome, - name: body.data.name, - statement: body.data.statement, + name: body.name, + statement: body.statement, solution, primaryActor, - priority: body.data.priority + priority: body.priority, + lastModified: new Date(), + modifiedBy: sessionUser }) await em.persistAndFlush(newUserStory) diff --git a/server/data/models/AppUserOrganizationRoleSchema.ts b/server/data/models/AppUserOrganizationRoleSchema.ts index e682b364..a220c324 100644 --- a/server/data/models/AppUserOrganizationRoleSchema.ts +++ b/server/data/models/AppUserOrganizationRoleSchema.ts @@ -8,16 +8,12 @@ export default new EntitySchema({ appUser: { kind: 'm:1', entity: 'AppUser', - primary: true, - ref: true, - cascade: [Cascade.REMOVE] + primary: true }, organization: { kind: 'm:1', entity: 'Organization', - ref: true, - primary: true, - cascade: [Cascade.REMOVE] + primary: true }, role: { enum: true, diff --git a/server/data/models/EnvironmentComponentSchema.ts b/server/data/models/EnvironmentComponentSchema.ts index 4ac0721e..429b3a93 100644 --- a/server/data/models/EnvironmentComponentSchema.ts +++ b/server/data/models/EnvironmentComponentSchema.ts @@ -6,6 +6,6 @@ export default new EntitySchema({ class: EnvironmentComponent, extends: RequirementSchema, properties: { - parentComponent: { kind: 'm:1', entity: 'EnvironmentComponent', ref: true, nullable: true } + parentComponent: { kind: 'm:1', entity: 'EnvironmentComponent', nullable: true } } }) \ No newline at end of file diff --git a/server/data/models/GlossaryTermSchema.ts b/server/data/models/GlossaryTermSchema.ts index f729cb70..14ac79c0 100644 --- a/server/data/models/GlossaryTermSchema.ts +++ b/server/data/models/GlossaryTermSchema.ts @@ -6,6 +6,6 @@ export default new EntitySchema({ class: GlossaryTerm, extends: RequirementSchema, properties: { - parentComponent: { kind: 'm:1', entity: 'GlossaryTerm', ref: true, nullable: true } + parentComponent: { kind: 'm:1', entity: 'GlossaryTerm', nullable: true } } }) \ No newline at end of file diff --git a/server/data/models/RequirementSchema.ts b/server/data/models/RequirementSchema.ts index 539d1d0e..32c7fcdb 100644 --- a/server/data/models/RequirementSchema.ts +++ b/server/data/models/RequirementSchema.ts @@ -1,4 +1,4 @@ -import { EntitySchema } from "@mikro-orm/core"; +import { Cascade, EntitySchema } from "@mikro-orm/core"; import { Requirement } from "../../domain/requirements/index.js"; export default new EntitySchema({ @@ -8,6 +8,13 @@ export default new EntitySchema({ id: { type: 'uuid', primary: true }, name: { type: 'string', nullable: false }, statement: { type: 'string', nullable: false }, - solution: { kind: 'm:1', entity: 'Solution', ref: true, nullable: false } + solution: { kind: 'm:1', entity: 'Solution' }, + lastModified: { type: 'datetime', nullable: false, onCreate: () => new Date(), onUpdate: () => new Date() }, + modifiedBy: { + kind: 'm:1', entity: 'AppUser', nullable: false, + // System Admin is the default user for the initial migration + // This can be removed in v0.14.0 or later + default: 'ac594919-50e3-438a-b9bc-efb8a8654243' + } } }) \ No newline at end of file diff --git a/server/data/models/ScenarioSchema.ts b/server/data/models/ScenarioSchema.ts index 685d9b39..d347960d 100644 --- a/server/data/models/ScenarioSchema.ts +++ b/server/data/models/ScenarioSchema.ts @@ -7,6 +7,6 @@ export default new EntitySchema({ class: Scenario, extends: BehaviorSchema, properties: { - primaryActor: { kind: 'm:1', entity: 'Stakeholder', ref: true, nullable: false } + primaryActor: { kind: 'm:1', entity: 'Stakeholder', nullable: false } } }) \ No newline at end of file diff --git a/server/data/models/StakeholderSchema.ts b/server/data/models/StakeholderSchema.ts index 645b0d28..5c5fb580 100644 --- a/server/data/models/StakeholderSchema.ts +++ b/server/data/models/StakeholderSchema.ts @@ -12,6 +12,6 @@ export default new EntitySchema({ availability: { type: 'number', nullable: false, check: 'availability >= 0 AND availability <= 100' }, segmentation: { enum: true, items: () => StakeholderSegmentation, nullable: false }, category: { enum: true, items: () => StakeholderCategory, nullable: false }, - parentComponent: { kind: 'm:1', entity: 'Stakeholder', ref: true, nullable: true } + parentComponent: { kind: 'm:1', entity: 'Stakeholder', nullable: true } } }) \ No newline at end of file diff --git a/server/data/models/SystemComponentSchema.ts b/server/data/models/SystemComponentSchema.ts index 1b28f437..fabe4cae 100644 --- a/server/data/models/SystemComponentSchema.ts +++ b/server/data/models/SystemComponentSchema.ts @@ -6,6 +6,6 @@ export default new EntitySchema({ class: SystemComponent, extends: RequirementSchema, properties: { - parentComponent: { kind: 'm:1', entity: 'SystemComponent', ref: true, nullable: true } + parentComponent: { kind: 'm:1', entity: 'SystemComponent', nullable: true } } }) \ No newline at end of file diff --git a/server/domain/requirements/Requirement.ts b/server/domain/requirements/Requirement.ts index 77c0c5fa..fb6acfa0 100644 --- a/server/domain/requirements/Requirement.ts +++ b/server/domain/requirements/Requirement.ts @@ -1,16 +1,19 @@ import { v7 as uuidv7 } from 'uuid'; import type { Properties } from "../Properties.js"; import Solution from '../application/Solution.js'; +import AppUser from '../application/AppUser.js'; /** * A Requirement is a statement that specifies a property. */ export abstract class Requirement { - constructor({ name, statement, solution }: Omit, 'id'>) { + constructor({ name, statement, solution, lastModified, modifiedBy }: Omit, 'id'>) { this.id = uuidv7(); this.name = name this.statement = statement this.solution = solution + this.lastModified = lastModified + this.modifiedBy = modifiedBy } /** @@ -39,12 +42,24 @@ export abstract class Requirement { */ solution: Solution + /** + * The date and time when the requirement was last modified + */ + lastModified: Date + + /** + * The user who last modified the requirement + */ + modifiedBy: AppUser + toJSON() { return { id: this.id, name: this.name, statement: this.statement, - solutionId: this.solution.id + solutionId: this.solution.id, + lastModified: this.lastModified.toISOString(), + modifiedById: this.modifiedBy.id } } } diff --git a/server/utils/assertOrgAdmin.ts b/server/utils/assertOrgAdmin.ts new file mode 100644 index 00000000..38a9823b --- /dev/null +++ b/server/utils/assertOrgAdmin.ts @@ -0,0 +1,41 @@ +import type { H3Event, EventHandlerRequest } from 'h3' +import { getServerSession } from '#auth' +import AppUserOrganizationRole from "~/server/domain/application/AppUserOrganizationRole" +import { fork } from "~/server/data/orm" +import AppRole from '../domain/application/AppRole' +import AppUser from '../domain/application/AppUser'; +import Organization from '../domain/application/Organization' + +/** + * Asserts that the user is an admin of the organization that owns the solution or is a system admin + * @param event + * @param organizationId + */ +export default async function assertOrgAdmin(event: H3Event, organizationId: string): Promise<{ organization: Organization, sessionUser: AppUser }> { + const session = (await getServerSession(event))!, + em = fork(), + organization = await em.findOne(Organization, { id: organizationId }) + + if (!organization) + throw createError({ + statusCode: 400, + statusMessage: `Bad Request: No organization found with id: ${organizationId}` + }) + + const sessionUserOrgRole = await em.findOne(AppUserOrganizationRole, { + appUser: session.id, + organization + }), + isOrgAdmin = sessionUserOrgRole?.role && [AppRole.ORGANIZATION_ADMIN].includes(sessionUserOrgRole?.role) + + if (!session.isSystemAdmin && !isOrgAdmin) + throw createError({ + statusCode: 403, + statusMessage: 'Forbidden: You do not have permission to access these items' + }) + + return { + organization, + sessionUser: sessionUserOrgRole?.appUser ?? (await em.findOne(AppUser, session.id))! + } +} \ No newline at end of file diff --git a/server/utils/assertOrgContributor.ts b/server/utils/assertOrgContributor.ts new file mode 100644 index 00000000..75b5d713 --- /dev/null +++ b/server/utils/assertOrgContributor.ts @@ -0,0 +1,41 @@ +import type { H3Event, EventHandlerRequest } from 'h3' +import { getServerSession } from '#auth' +import AppUserOrganizationRole from "~/server/domain/application/AppUserOrganizationRole" +import { fork } from "~/server/data/orm" +import AppRole from '../domain/application/AppRole' +import AppUser from '../domain/application/AppUser'; +import Organization from '../domain/application/Organization' + +/** + * Asserts that the user is a contributor of the organization that owns the solution or is a system admin + * @param event + * @param organizationId + */ +export default async function assertOrgContributor(event: H3Event, organizationId: string): Promise<{ organization: Organization, sessionUser: AppUser }> { + const session = (await getServerSession(event))!, + em = fork(), + organization = await em.findOne(Organization, { id: organizationId }) + + if (!organization) + throw createError({ + statusCode: 400, + statusMessage: `Bad Request: No organization found with id: ${organizationId}` + }) + + const sessionUserOrgRole = await em.findOne(AppUserOrganizationRole, { + appUser: session.id, + organization + }), + isOrgContributor = sessionUserOrgRole?.role && [AppRole.ORGANIZATION_ADMIN, AppRole.ORGANIZATION_CONTRIBUTOR].includes(sessionUserOrgRole?.role) + + if (!session.isSystemAdmin && !isOrgContributor) + throw createError({ + statusCode: 403, + statusMessage: 'Forbidden: You do not have permission to access these items' + }) + + return { + organization, + sessionUser: sessionUserOrgRole?.appUser ?? (await em.findOne(AppUser, session.id))! + } +} \ No newline at end of file diff --git a/server/utils/assertOrgReader.ts b/server/utils/assertOrgReader.ts new file mode 100644 index 00000000..8f84946d --- /dev/null +++ b/server/utils/assertOrgReader.ts @@ -0,0 +1,41 @@ +import type { H3Event, EventHandlerRequest } from 'h3' +import { getServerSession } from '#auth' +import AppUserOrganizationRole from "~/server/domain/application/AppUserOrganizationRole" +import { fork } from "~/server/data/orm" +import AppUser from '../domain/application/AppUser'; +import Organization from '../domain/application/Organization' +import AppRole from '../domain/application/AppRole'; + +/** + * Asserts that the user is a member of the organization that owns the solution or is a system admin + * @param event + * @param organizationId + */ +export default async function assertOrgReader(event: H3Event, organizationId: string): Promise<{ organization: Organization, sessionUser: AppUser }> { + const session = (await getServerSession(event))!, + em = fork(), + organization = await em.findOne(Organization, { id: organizationId }) + + if (!organization) + throw createError({ + statusCode: 400, + statusMessage: `Bad Request: No organization found with id: ${organizationId}` + }) + + const sessionUserOrgRole = await em.findOne(AppUserOrganizationRole, { + appUser: session.id, + organization + }), + isOrgReader = sessionUserOrgRole?.role && [AppRole.ORGANIZATION_ADMIN, AppRole.ORGANIZATION_CONTRIBUTOR, AppRole.ORGANIZATION_READER].includes(sessionUserOrgRole?.role) + + if (!session.isSystemAdmin && !isOrgReader) + throw createError({ + statusCode: 403, + statusMessage: 'Forbidden: You do not have permission to access these items' + }) + + return { + organization: organization, + sessionUser: sessionUserOrgRole?.appUser ?? (await em.findOne(AppUser, session.id))! + } +} \ No newline at end of file diff --git a/server/utils/assertSolutionAdmin.ts b/server/utils/assertSolutionAdmin.ts new file mode 100644 index 00000000..58e6d0a4 --- /dev/null +++ b/server/utils/assertSolutionAdmin.ts @@ -0,0 +1,42 @@ +import type { H3Event, EventHandlerRequest } from 'h3' +import { getServerSession } from '#auth' +import AppUserOrganizationRole from "~/server/domain/application/AppUserOrganizationRole" +import { fork } from "~/server/data/orm" +import AppRole from '../domain/application/AppRole' +import AppUser from '../domain/application/AppUser'; +import Solution from '../domain/application/Solution' + +/** + * Asserts that the user is an admin of the organization that owns the solution or is a system admin + * @param event + * @param solutionId + */ +// Currently, this is no different from assertOrgAdmin, but it's a good idea to keep them separate in case they diverge in the future +export default async function assertSolutionAdmin(event: H3Event, solutionId: string): Promise<{ solution: Solution, sessionUser: AppUser }> { + const session = (await getServerSession(event))!, + em = fork(), + solution = await em.findOne(Solution, { id: solutionId }) + + if (!solution) + throw createError({ + statusCode: 400, + statusMessage: `Bad Request: No solution found with id: ${solutionId}` + }) + + const sessionUserOrgRole = await em.findOne(AppUserOrganizationRole, { + appUser: session.id, + organization: solution.organization.id + }), + isOrgAdmin = sessionUserOrgRole?.role && [AppRole.ORGANIZATION_ADMIN].includes(sessionUserOrgRole?.role) + + if (!session.isSystemAdmin && !isOrgAdmin) + throw createError({ + statusCode: 403, + statusMessage: 'Forbidden: You do not have permission to access these items' + }) + + return { + solution: solution, + sessionUser: sessionUserOrgRole?.appUser ?? (await em.findOne(AppUser, session.id))! + } +} \ No newline at end of file diff --git a/server/utils/assertSolutionContributor.ts b/server/utils/assertSolutionContributor.ts new file mode 100644 index 00000000..d2683ebe --- /dev/null +++ b/server/utils/assertSolutionContributor.ts @@ -0,0 +1,42 @@ +import type { H3Event, EventHandlerRequest } from 'h3' +import { getServerSession } from '#auth' +import AppUserOrganizationRole from "~/server/domain/application/AppUserOrganizationRole" +import Solution from "~/server/domain/application/Solution" +import { fork } from "~/server/data/orm" +import AppRole from '../domain/application/AppRole' +import AppUser from '../domain/application/AppUser'; + +/** + * Asserts that the user is a contributor of the organization that owns the solution or is a system admin + * @param event + * @param solutionId + */ +// Currently, this is no different from assertOrgContributor, but it's a good idea to keep them separate in case they diverge in the future +export default async function assertSolutionContributor(event: H3Event, solutionId: string): Promise<{ solution: Solution, sessionUser: AppUser }> { + const session = (await getServerSession(event))!, + em = fork(), + solution = await em.findOne(Solution, { id: solutionId }) + + if (!solution) + throw createError({ + statusCode: 400, + statusMessage: `Bad Request: No solution found with id: ${solutionId}` + }) + + const sessionUserOrgRole = await em.findOne(AppUserOrganizationRole, { + appUser: session.id, + organization: solution.organization.id + }), + isOrgContributor = sessionUserOrgRole?.role && [AppRole.ORGANIZATION_ADMIN, AppRole.ORGANIZATION_CONTRIBUTOR].includes(sessionUserOrgRole?.role) + + if (!session.isSystemAdmin && !isOrgContributor) + throw createError({ + statusCode: 403, + statusMessage: 'Forbidden: You do not have permission to access these items' + }) + + return { + solution, + sessionUser: sessionUserOrgRole?.appUser ?? (await em.findOne(AppUser, session.id))! + } +} \ No newline at end of file diff --git a/server/utils/assertSolutionReader.ts b/server/utils/assertSolutionReader.ts new file mode 100644 index 00000000..99b7fde3 --- /dev/null +++ b/server/utils/assertSolutionReader.ts @@ -0,0 +1,42 @@ +import type { H3Event, EventHandlerRequest } from 'h3' +import { getServerSession } from '#auth' +import AppUserOrganizationRole from "~/server/domain/application/AppUserOrganizationRole" +import { fork } from "~/server/data/orm" +import AppUser from '../domain/application/AppUser'; +import Solution from '../domain/application/Solution'; +import AppRole from '../domain/application/AppRole'; + +/** + * Asserts that the user is a member of the organization that owns the solution or is a system admin + * @param event + * @param solutionId + */ +// Currently, this is no different from assertOrgReader, but it's a good idea to keep them separate in case they diverge in the future +export default async function assertSolutionReader(event: H3Event, solutionId: string): Promise<{ solution: Solution, sessionUser: AppUser }> { + const session = (await getServerSession(event))!, + em = fork(), + solution = await em.findOne(Solution, { id: solutionId }) + + if (!solution) + throw createError({ + statusCode: 400, + statusMessage: `Bad Request: No solution found with id: ${solutionId}` + }) + + const sessionUserOrgRole = await em.findOne(AppUserOrganizationRole, { + appUser: session.id, + organization: solution.organization.id + }), + isOrgReader = sessionUserOrgRole?.role && [AppRole.ORGANIZATION_ADMIN, AppRole.ORGANIZATION_CONTRIBUTOR, AppRole.ORGANIZATION_READER].includes(sessionUserOrgRole?.role) + + if (!session.isSystemAdmin && !isOrgReader) + throw createError({ + statusCode: 403, + statusMessage: 'Forbidden: You do not have permission to access these items' + }) + + return { + solution, + sessionUser: sessionUserOrgRole?.appUser ?? (await em.findOne(AppUser, session.id))! + } +} \ No newline at end of file diff --git a/server/utils/validateEventBody.ts b/server/utils/validateEventBody.ts new file mode 100644 index 00000000..b43ab314 --- /dev/null +++ b/server/utils/validateEventBody.ts @@ -0,0 +1,15 @@ +import type { H3Event, EventHandlerRequest } from 'h3' +import type { ZodUnion, ZodObject, ZodEffects } from 'zod' + +export default async function validateEventBody | ZodUnion | ZodEffects>(event: H3Event, schema: Z): Promise { + const body = await readValidatedBody(event, (b) => schema.safeParse(b)) + + if (!body.success) + throw createError({ + statusCode: 400, + statusMessage: 'Bad Request: Invalid body', + message: JSON.stringify(body.error.errors) + }) + + return body.data +} \ No newline at end of file diff --git a/server/utils/validateEventParams.ts b/server/utils/validateEventParams.ts new file mode 100644 index 00000000..db1b8151 --- /dev/null +++ b/server/utils/validateEventParams.ts @@ -0,0 +1,15 @@ +import type { H3Event, EventHandlerRequest } from 'h3' +import type { ZodUnion, ZodObject, ZodEffects } from 'zod' + +export default async function validateEventParams | ZodUnion | ZodEffects>(event: H3Event, schema: Z): Promise { + const params = await getValidatedRouterParams(event, (q) => schema.safeParse(q)) + + if (!params.success) + throw createError({ + statusCode: 400, + statusMessage: 'Bad Request: Invalid route parameters', + message: JSON.stringify(params.error.errors) + }) + + return params.data +} \ No newline at end of file diff --git a/server/utils/validateEventQuery.ts b/server/utils/validateEventQuery.ts new file mode 100644 index 00000000..b7fa01a4 --- /dev/null +++ b/server/utils/validateEventQuery.ts @@ -0,0 +1,15 @@ +import type { H3Event, EventHandlerRequest } from 'h3' +import type { ZodUnion, ZodObject, ZodEffects } from 'zod' + +export default async function validateEventQuery | ZodUnion | ZodEffects>(event: H3Event, schema: Z): Promise { + const query = await getValidatedQuery(event, (q) => schema.safeParse(q)) + + if (!query.success) + throw createError({ + statusCode: 400, + statusMessage: 'Bad Request: Invalid query parameters', + message: JSON.stringify(query.error.errors) + }) + + return query.data +} \ No newline at end of file