From e034605e7c1c431c3da79dda1a9915ac598b1140 Mon Sep 17 00:00:00 2001 From: vm2052 Date: Sat, 7 Mar 2026 13:00:32 +0300 Subject: [PATCH 1/2] added resources table, updated tags_assigned, added enum tables for audience, resource type --- docs/db/schema.dbml | 30 ++++++- .../20260307092657_create_resources_table.js | 78 +++++++++++++++++++ .../20260307092839_update_tags_assigned.js | 29 +++++++ 3 files changed, 134 insertions(+), 3 deletions(-) create mode 100644 knex/migrations/20260307092657_create_resources_table.js create mode 100644 knex/migrations/20260307092839_update_tags_assigned.js diff --git a/docs/db/schema.dbml b/docs/db/schema.dbml index fe46939..5d2d7a8 100644 --- a/docs/db/schema.dbml +++ b/docs/db/schema.dbml @@ -118,7 +118,7 @@ Table tags { Table tags_assigned { tag_id integer [primary key] assigned_id integer [primary key] - assigned_to enum [note: 'mentor, project, study_buddy'] + assigned_to enum [note: 'mentor, project, study_buddy, resource'] } Table links { @@ -129,8 +129,31 @@ Table links { entity_id integer } +Table resources { + id integer [primary key] + title varchar(255) [not null] + type resource_type [not null] + audience resource_audience [not null] + description text + attachment_filename varchar(255) + link varchar(500) + created_by integer [ref: > users.id] + created_at timestamp + updated_at timestamp +} +Enum resource_type { + post + video + picture + link + file +} - +Enum resource_audience { + public + member + mentor +} //Ref: applications.last_modified_by > users.id // many-to-one Ref: users.id <> user_roles.user_id // many-to-many @@ -151,4 +174,5 @@ Ref: study_buddy.id <> users_learnings.learning_entity_id // links relationships: other resources to be added Ref: links.entity_id > projects.id -Ref: "study_buddy"."id" < "study_buddy"."created_by" \ No newline at end of file +Ref: "study_buddy"."id" < "study_buddy"."created_by" +Ref: resources.id <> tags_assigned.assigned_id \ No newline at end of file diff --git a/knex/migrations/20260307092657_create_resources_table.js b/knex/migrations/20260307092657_create_resources_table.js new file mode 100644 index 0000000..e891f2b --- /dev/null +++ b/knex/migrations/20260307092657_create_resources_table.js @@ -0,0 +1,78 @@ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ +exports.up = async function(knex) { + return knex.schema + // First create ENUM types if they don't exist + .raw(`CREATE TYPE resource_type AS ENUM ('post', 'video', 'picture', 'link', 'file');`) + .raw(`CREATE TYPE resource_audience AS ENUM ('public', 'member', 'mentor');`) + + // Then create table using these ENUMs + .createTable('resources', function(table) { + table.increments('id').primary(); + table.string('title', 255).notNullable(); + table.specificType('type', 'resource_type').notNullable(); + table.specificType('audience', 'resource_audience').notNullable(); + table.text('description'); + table.string('attachment_filename', 255); + table.string('link', 500); + table.integer('created_by').unsigned().references('id').inTable('users'); + table.timestamp('created_at').defaultTo(knex.fn.now()); + table.timestamp('updated_at').defaultTo(knex.fn.now()); + + // Base constraint for at least one field + table.check('?? IS NOT NULL OR ?? IS NOT NULL OR ?? IS NOT NULL', + ['description', 'attachment_filename', 'link']); + }) + .then(() => { + // Add type-specific constraints + return knex.raw(` + ALTER TABLE resources + ADD CONSTRAINT resources_type_description_check + CHECK ( + (type = 'post' AND description IS NOT NULL) OR + (type != 'post') + ); + + ALTER TABLE resources + ADD CONSTRAINT resources_type_video_check + CHECK ( + (type = 'video' AND link IS NOT NULL) OR + (type != 'video') + ); + + ALTER TABLE resources + ADD CONSTRAINT resources_type_picture_check + CHECK ( + (type = 'picture' AND (attachment_filename IS NOT NULL OR link IS NOT NULL)) OR + (type != 'picture') + ); + + ALTER TABLE resources + ADD CONSTRAINT resources_type_link_check + CHECK ( + (type = 'link' AND link IS NOT NULL) OR + (type != 'link') + ); + + ALTER TABLE resources + ADD CONSTRAINT resources_type_file_check + CHECK ( + (type = 'file' AND attachment_filename IS NOT NULL) OR + (type != 'file') + ); + `); + }); +}; + +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ +exports.down = async function(knex) { + return knex.schema + .dropTableIfExists('resources') + .raw('DROP TYPE IF EXISTS resource_type;') + .raw('DROP TYPE IF EXISTS resource_audience;'); +}; \ No newline at end of file diff --git a/knex/migrations/20260307092839_update_tags_assigned.js b/knex/migrations/20260307092839_update_tags_assigned.js new file mode 100644 index 0000000..1ad816d --- /dev/null +++ b/knex/migrations/20260307092839_update_tags_assigned.js @@ -0,0 +1,29 @@ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ +exports.up = async function(knex) { + return knex.raw(` + ALTER TABLE tags_assigned + DROP CONSTRAINT IF EXISTS tags_assigned_assigned_to_check; + + ALTER TABLE tags_assigned + ADD CONSTRAINT tags_assigned_assigned_to_check + CHECK (assigned_to IN ('mentor', 'project', 'study_buddy', 'resource')); + `); +}; + +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ +exports.down = async function(knex) { + return knex.raw(` + ALTER TABLE tags_assigned + DROP CONSTRAINT IF EXISTS tags_assigned_assigned_to_check; + + ALTER TABLE tags_assigned + ADD CONSTRAINT tags_assigned_assigned_to_check + CHECK (assigned_to IN ('mentor', 'project', 'study_buddy')); + `); +}; From 2c058abf818f1eaf89824b496c2ff1ca5a4b5017 Mon Sep 17 00:00:00 2001 From: vm2052 Date: Sun, 22 Mar 2026 07:58:50 +0300 Subject: [PATCH 2/2] fixed as per comments --- docs/db/schema.dbml | 6 +- .../20260307092657_create_resources_table.js | 134 +++++++++--------- .../20260307092839_update_tags_assigned.js | 6 +- 3 files changed, 76 insertions(+), 70 deletions(-) diff --git a/docs/db/schema.dbml b/docs/db/schema.dbml index 5d2d7a8..0b9816b 100644 --- a/docs/db/schema.dbml +++ b/docs/db/schema.dbml @@ -133,10 +133,10 @@ Table resources { id integer [primary key] title varchar(255) [not null] type resource_type [not null] - audience resource_audience [not null] + audience audience [not null] description text attachment_filename varchar(255) - link varchar(500) + link varchar(255) created_by integer [ref: > users.id] created_at timestamp updated_at timestamp @@ -149,7 +149,7 @@ Enum resource_type { file } -Enum resource_audience { +Enum audience { public member mentor diff --git a/knex/migrations/20260307092657_create_resources_table.js b/knex/migrations/20260307092657_create_resources_table.js index e891f2b..5e20272 100644 --- a/knex/migrations/20260307092657_create_resources_table.js +++ b/knex/migrations/20260307092657_create_resources_table.js @@ -3,67 +3,74 @@ * @returns { Promise } */ exports.up = async function(knex) { - return knex.schema - // First create ENUM types if they don't exist - .raw(`CREATE TYPE resource_type AS ENUM ('post', 'video', 'picture', 'link', 'file');`) - .raw(`CREATE TYPE resource_audience AS ENUM ('public', 'member', 'mentor');`) - - // Then create table using these ENUMs - .createTable('resources', function(table) { - table.increments('id').primary(); - table.string('title', 255).notNullable(); - table.specificType('type', 'resource_type').notNullable(); - table.specificType('audience', 'resource_audience').notNullable(); - table.text('description'); - table.string('attachment_filename', 255); - table.string('link', 500); - table.integer('created_by').unsigned().references('id').inTable('users'); - table.timestamp('created_at').defaultTo(knex.fn.now()); - table.timestamp('updated_at').defaultTo(knex.fn.now()); - - // Base constraint for at least one field - table.check('?? IS NOT NULL OR ?? IS NOT NULL OR ?? IS NOT NULL', - ['description', 'attachment_filename', 'link']); - }) - .then(() => { - // Add type-specific constraints - return knex.raw(` - ALTER TABLE resources - ADD CONSTRAINT resources_type_description_check - CHECK ( - (type = 'post' AND description IS NOT NULL) OR - (type != 'post') - ); - - ALTER TABLE resources - ADD CONSTRAINT resources_type_video_check - CHECK ( - (type = 'video' AND link IS NOT NULL) OR - (type != 'video') - ); - - ALTER TABLE resources - ADD CONSTRAINT resources_type_picture_check - CHECK ( - (type = 'picture' AND (attachment_filename IS NOT NULL OR link IS NOT NULL)) OR - (type != 'picture') - ); + await knex.raw(` + DO $$ + BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'resource_type') THEN + CREATE TYPE resource_type AS ENUM ('post', 'video', 'picture', 'link', 'file'); + END IF; - ALTER TABLE resources - ADD CONSTRAINT resources_type_link_check - CHECK ( - (type = 'link' AND link IS NOT NULL) OR - (type != 'link') - ); - - ALTER TABLE resources - ADD CONSTRAINT resources_type_file_check - CHECK ( - (type = 'file' AND attachment_filename IS NOT NULL) OR - (type != 'file') - ); - `); - }); + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'audience') THEN + CREATE TYPE audience AS ENUM ('public', 'member', 'mentor'); + END IF; + END $$; + `); + + // create resources + await knex.schema.createTable('resources', function(table) { + table.increments('id').primary(); + table.string('title', 255).notNullable(); + table.specificType('type', 'resource_type').notNullable(); + table.specificType('audience', 'audience').notNullable(); + table.text('description'); + table.string('attachment_filename', 255); + table.string('link', 255); + table.integer('created_by').references('id').inTable('users'); + table.timestamp('created_at').defaultTo(knex.fn.now()); + table.timestamp('updated_at').defaultTo(knex.fn.now()); + + //checks for certain fields + table.check('?? IS NOT NULL OR ?? IS NOT NULL OR ?? IS NOT NULL', + ['description', 'attachment_filename', 'link']); + }); + + //adding contraints to resources + await knex.raw(` + ALTER TABLE resources + ADD CONSTRAINT resources_type_description_check + CHECK ( + (type = 'post' AND description IS NOT NULL) OR + (type != 'post') + ); + + ALTER TABLE resources + ADD CONSTRAINT resources_type_video_check + CHECK ( + (type = 'video' AND link IS NOT NULL) OR + (type != 'video') + ); + + ALTER TABLE resources + ADD CONSTRAINT resources_type_picture_check + CHECK ( + (type = 'picture' AND (attachment_filename IS NOT NULL OR link IS NOT NULL)) OR + (type != 'picture') + ); + + ALTER TABLE resources + ADD CONSTRAINT resources_type_link_check + CHECK ( + (type = 'link' AND link IS NOT NULL) OR + (type != 'link') + ); + + ALTER TABLE resources + ADD CONSTRAINT resources_type_file_check + CHECK ( + (type = 'file' AND attachment_filename IS NOT NULL) OR + (type != 'file') + ); + `); }; /** @@ -71,8 +78,7 @@ exports.up = async function(knex) { * @returns { Promise } */ exports.down = async function(knex) { - return knex.schema - .dropTableIfExists('resources') - .raw('DROP TYPE IF EXISTS resource_type;') - .raw('DROP TYPE IF EXISTS resource_audience;'); + await knex.schema.dropTableIfExists('resources'); + await knex.raw('DROP TYPE IF EXISTS resource_type;'); + await knex.raw('DROP TYPE IF EXISTS audience;'); }; \ No newline at end of file diff --git a/knex/migrations/20260307092839_update_tags_assigned.js b/knex/migrations/20260307092839_update_tags_assigned.js index 1ad816d..83eba3e 100644 --- a/knex/migrations/20260307092839_update_tags_assigned.js +++ b/knex/migrations/20260307092839_update_tags_assigned.js @@ -3,7 +3,7 @@ * @returns { Promise } */ exports.up = async function(knex) { - return knex.raw(` + await knex.raw(` ALTER TABLE tags_assigned DROP CONSTRAINT IF EXISTS tags_assigned_assigned_to_check; @@ -18,7 +18,7 @@ exports.up = async function(knex) { * @returns { Promise } */ exports.down = async function(knex) { - return knex.raw(` + await knex.raw(` ALTER TABLE tags_assigned DROP CONSTRAINT IF EXISTS tags_assigned_assigned_to_check; @@ -26,4 +26,4 @@ exports.down = async function(knex) { ADD CONSTRAINT tags_assigned_assigned_to_check CHECK (assigned_to IN ('mentor', 'project', 'study_buddy')); `); -}; +}; \ No newline at end of file