Adds content column to documents and revisions as JSON snapshot (#6179)

This commit is contained in:
Tom Moor
2023-12-17 10:51:11 -05:00
committed by GitHub
parent 78b9322a28
commit 1840370e6f
18 changed files with 411 additions and 174 deletions

View File

@@ -35,7 +35,11 @@ import {
AllowNull,
} from "sequelize-typescript";
import isUUID from "validator/lib/isUUID";
import type { NavigationNode, SourceMetadata } from "@shared/types";
import type {
NavigationNode,
ProsemirrorData,
SourceMetadata,
} from "@shared/types";
import getTasks from "@shared/utils/getTasks";
import slugify from "@shared/utils/slugify";
import { SLUG_URL_REGEX } from "@shared/utils/urlHelpers";
@@ -206,15 +210,18 @@ class Document extends ParanoidModel {
@Column(DataType.SMALLINT)
version: number;
@Default(false)
@Column
template: boolean;
@Default(false)
@Column
fullWidth: boolean;
@Column
insightsEnabled: boolean;
/** The version of the editor last used to edit this document. */
@SimpleLength({
max: 255,
msg: `editorVersion must be 255 characters or less`,
@@ -222,6 +229,7 @@ class Document extends ParanoidModel {
@Column
editorVersion: string;
/** An emoji to use as the document icon. */
@Length({
max: 1,
msg: `Emoji must be a single character`,
@@ -229,9 +237,25 @@ class Document extends ParanoidModel {
@Column
emoji: string | null;
/**
* The content of the document as Markdown.
*
* @deprecated Use `content` instead, or `DocumentHelper.toMarkdown` if exporting lossy markdown.
* This column will be removed in a future migration.
*/
@Column(DataType.TEXT)
text: string;
/**
* The content of the document as JSON, this is a snapshot at the last time the state was saved.
*/
@Column(DataType.JSONB)
content: ProsemirrorData;
/**
* The content of the document as YJS collaborative state, this column can be quite large and
* should only be selected from the DB when the `content` snapshot cannot be used.
*/
@SimpleLength({
max: DocumentValidation.maxStateLength,
msg: `Document collaborative state is too large, you must create a new document`,
@@ -239,28 +263,38 @@ class Document extends ParanoidModel {
@Column(DataType.BLOB)
state: Uint8Array;
/** Whether this document is part of onboarding. */
@Default(false)
@Column
isWelcome: boolean;
/** How many versions there are in the history of this document. */
@IsNumeric
@Default(0)
@Column(DataType.INTEGER)
revisionCount: number;
/** Whether the document is archvied, and if so when. */
@IsDate
@Column
archivedAt: Date | null;
/** Whether the document is published, and if so when. */
@IsDate
@Column
publishedAt: Date | null;
/** An array of user IDs that have edited this document. */
@Column(DataType.ARRAY(DataType.UUID))
collaboratorIds: string[] = [];
// getters
/**
* The frontend path to this document.
*
* @deprecated Use `path` instead.
*/
get url() {
if (!this.title) {
return `/doc/untitled-${this.urlId}`;
@@ -269,6 +303,11 @@ class Document extends ParanoidModel {
return `/doc/${slugifiedTitle}-${this.urlId}`;
}
/** The frontend path to this document. */
get path() {
return this.url;
}
get tasks() {
return getTasks(this.text || "");
}
@@ -363,6 +402,11 @@ class Document extends ParanoidModel {
model.collaboratorIds = [];
}
// backfill content if it's missing
if (!model.content) {
model.content = DocumentHelper.toJSON(model);
}
// ensure the last modifying user is a collaborator
model.collaboratorIds = uniq(
model.collaboratorIds.concat(model.lastModifiedById)
@@ -589,6 +633,22 @@ class Document extends ParanoidModel {
return !!(this.importId && this.sourceMetadata?.trial);
}
/**
* Revert the state of the document to match the passed revision.
*
* @param revision The revision to revert to.
*/
restoreFromRevision = (revision: Revision) => {
if (revision.documentId !== this.id) {
throw new Error("Revision does not belong to this document");
}
this.content = revision.content;
this.text = revision.text;
this.title = revision.title;
this.emoji = revision.emoji;
};
/**
* Get a list of users that have collaborated on this document
*