This repository has been archived by the owner on Nov 2, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
✨ Add separate authors table, add support for "custom authors" (#1248)
* Rebuild how authors are handled for news * Fix how database articles are seeded by quering database * Fix various issues for author * Fix resolvers for author * Frontend changes required for authors * Fix and comment migration * Remove temporary disabling of meilisearch * Fix lint * Drop enum type on migration rollback * Use getAuthorImage on nolla article * Refactor links without href * Remove commented out code * Add check constraint to enforce type column on authors * gen grapghl * Change authors to not have duplicate rows * Change fromMember to fromAuthor on notifications * Fix frontend for new notification author system * Fix migration * Fix lint * Change datetime > check to >= check
- Loading branch information
Showing
57 changed files
with
2,636 additions
and
1,717 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
119 changes: 119 additions & 0 deletions
119
backend/services/core/migrations/20230822094308_add_custom_author_type_to_articles.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
import { Knex } from 'knex'; | ||
|
||
export async function up(knex: Knex): Promise<void> { | ||
// Add custom authors table | ||
await knex.schema.createTable('custom_authors', (table) => { | ||
table.uuid('id').primary().defaultTo(knex.raw('gen_random_uuid()')); | ||
table.string('name').notNullable().comment('Swedish name of custom author'); | ||
table.string('name_en').comment('English name of custom author, possibly null'); | ||
table.string('image_url').comment('Image url of custom author, possibly null'); | ||
table.timestamps(true, true); | ||
}); | ||
await knex.schema.createTable('custom_author_roles', (table) => { | ||
table.uuid('id').primary().defaultTo(knex.raw('gen_random_uuid()')); | ||
table.uuid('custom_author_id').unsigned().notNullable().references('custom_authors.id') | ||
.onDelete('CASCADE'); | ||
table.string('role').unsigned().notNullable().comment('should use role syntax, such as dsek.styr or dsek.stab.noll'); | ||
table.timestamps(true, true); | ||
}); | ||
await knex.schema; | ||
// Add new "authors" table | ||
await knex.schema.createTable('authors', (table) => { | ||
table.uuid('id').primary().defaultTo(knex.raw('gen_random_uuid()')); | ||
table.uuid('member_id').notNullable().references('members.id').onDelete('CASCADE'); | ||
table.uuid('mandate_id').unsigned().nullable().references('mandates.id') | ||
.onDelete('SET NULL'); | ||
table.uuid('custom_id').unsigned().nullable().references('custom_authors.id') | ||
.onDelete('SET NULL'); | ||
table.timestamps(true, true); | ||
|
||
table.check(`mandate_id IS NULL AND custom_id IS NULL | ||
OR (mandate_id IS NOT NULL AND custom_id IS NULL) | ||
OR (mandate_id IS NULL AND custom_id IS NOT NULL)`, undefined, 'enforce_author_type'); | ||
table.unique(['member_id', 'mandate_id', 'custom_id']).comment('Only one author per member, mandate or custom author'); | ||
}); | ||
// add "type column" which is generated based on the other ones | ||
await knex.raw(` | ||
ALTER TABLE authors ADD COLUMN type VARCHAR GENERATED ALWAYS AS ( | ||
CASE | ||
WHEN mandate_id IS NULL AND custom_id IS NULL THEN 'Member' | ||
WHEN mandate_id IS NOT NULL AND custom_id IS NULL THEN 'Mandate' | ||
WHEN mandate_id IS NULL AND custom_id IS NOT NULL THEN 'Custom' | ||
END | ||
) STORED`); | ||
// Get all articles posted by a member, not as a mandate | ||
const memberAuthors = await knex('articles').distinct('author_id') | ||
.where({ author_type: 'Member' }) | ||
.distinct('author_id'); | ||
// Get all articles posted as a mandate | ||
const mandateAuthors = await knex('articles').distinct('author_id as mandate_id', 'mandates.member_id') | ||
.join('mandates', 'mandates.id', '=', 'articles.author_id') | ||
.where({ author_type: 'Mandate' }); | ||
// Create rows in authors table for each article posted as a member | ||
if (memberAuthors.length > 0) { | ||
await knex('authors') | ||
.insert(memberAuthors.map((author) => ({ | ||
member_id: author.author_id, | ||
}))); | ||
} | ||
// Create rows in authors table for each article posted as a mandate | ||
if (mandateAuthors.length > 0) { | ||
await knex('authors') | ||
.insert(mandateAuthors.map((author) => ({ | ||
member_id: author.member_id, | ||
mandate_id: author.mandate_id, | ||
}))); | ||
} | ||
// Update all articles to point to the newly created author rows, | ||
// using the temporary article_id column | ||
await knex.raw(` | ||
UPDATE articles | ||
SET author_id = authors.id | ||
FROM authors | ||
WHERE articles.author_id IS NOT NULL | ||
AND articles.author_type = 'Member' | ||
AND authors.member_id = articles.author_id | ||
AND authors.type = 'Member' | ||
`); | ||
await knex.raw(` | ||
UPDATE articles | ||
SET author_id = authors.id | ||
FROM authors | ||
WHERE articles.author_id IS NOT NULL | ||
AND articles.author_type = 'Mandate' | ||
AND authors.mandate_id = articles.author_id | ||
AND authors.type = 'Mandate' | ||
`); | ||
|
||
// remove old author columns from articles | ||
await knex.schema.alterTable('articles', (table) => { | ||
table.foreign('author_id').references('authors.id').onDelete('SET NULL'); | ||
table.dropColumn('author_type'); | ||
}); | ||
} | ||
|
||
export async function down(knex: Knex): Promise<void> { | ||
await knex.schema.alterTable('articles', (table) => { | ||
table.dropForeign('author_id'); | ||
table.string('author_type').notNullable().defaultTo('Member').comment('What type the author is (e.g. Member and Mandate)'); | ||
}); | ||
await knex.raw(` | ||
UPDATE articles | ||
SET author_id = authors.member_id, | ||
author_type = 'Member' | ||
FROM authors | ||
WHERE authors.id = articles.author_id | ||
AND authors.type = 'Member' | ||
`); | ||
await knex.raw(` | ||
UPDATE articles | ||
SET author_id = authors.mandate_id, | ||
author_type = 'Mandate' | ||
FROM authors | ||
WHERE authors.id = articles.author_id | ||
AND authors.type = 'Mandate' | ||
`); | ||
await knex.schema.dropTable('authors'); | ||
await knex.schema.dropTable('custom_author_roles'); | ||
await knex.schema.dropTable('custom_authors'); | ||
} |
53 changes: 53 additions & 0 deletions
53
backend/services/core/migrations/20230906092300_add_author_to_notifications.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
import { Knex } from 'knex'; | ||
|
||
export async function up(knex: Knex): Promise<void> { | ||
await knex.schema.alterTable('notifications', (table) => { | ||
table.uuid('from_author_id') | ||
.unsigned() | ||
.references('authors.id') | ||
.onDelete('SET NULL') | ||
.comment('The author which took the action that initiated the notification. Null if not relevant.'); | ||
}); | ||
const membersWithoutAuthor = await knex('notifications') | ||
.distinct('notifications.from_member_id') | ||
.leftJoin('authors', 'authors.member_id', '=', 'notifications.from_member_id') | ||
.whereNotNull('notifications.from_member_id') | ||
.andWhere('authors.type', 'Member') | ||
.whereNull('authors.id'); | ||
if (membersWithoutAuthor.length > 0) { | ||
await knex.insert(membersWithoutAuthor.map((member) => ({ | ||
member_id: member.from_member_id, | ||
type: 'Member', | ||
}))).into('authors'); | ||
} | ||
await knex.raw(` | ||
UPDATE notifications | ||
SET from_author_id = authors.id | ||
FROM authors | ||
WHERE notifications.from_member_id = authors.member_id | ||
AND authors.type = 'Member' | ||
`); | ||
await knex.schema.alterTable('notifications', (table) => { | ||
table.dropColumn('from_member_id'); | ||
}); | ||
} | ||
|
||
export async function down(knex: Knex): Promise<void> { | ||
await knex.schema.alterTable('notifications', (table) => { | ||
table.uuid('from_member_id') | ||
.unsigned() | ||
.references('members.id') | ||
.onDelete('SET NULL') | ||
.comment('The author which took the action that initiated the notification. Null if not relevant.'); | ||
}); | ||
await knex.raw(` | ||
UPDATE notifications | ||
SET from_member_id = authors.member_id | ||
FROM authors | ||
WHERE notifications.from_author_id = authors.id | ||
AND authors.type = 'Member' | ||
`); | ||
await knex.schema.alterTable('notifications', (table) => { | ||
table.dropColumn('from_author_id'); | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.