renamed attachment's parentId to more fitting ownerId

This commit is contained in:
zadam 2023-07-14 17:01:56 +02:00
parent ca41806bc2
commit d475346a09
25 changed files with 63 additions and 63 deletions

View file

@ -1,7 +1,7 @@
CREATE TABLE IF NOT EXISTS "attachments"
(
attachmentId TEXT not null primary key,
parentId TEXT not null,
ownerId TEXT not null,
role TEXT not null,
mime TEXT not null,
title TEXT not null,
@ -14,8 +14,8 @@ CREATE TABLE IF NOT EXISTS "attachments"
isDeleted INT not null,
deleteId TEXT DEFAULT NULL);
CREATE INDEX IDX_attachments_parentId_role
on attachments (parentId, role);
CREATE INDEX IDX_attachments_ownerId_role
on attachments (ownerId, role);
CREATE INDEX IDX_attachments_utcDateScheduledForErasureSince
on attachments (utcDateScheduledForErasureSince);

View file

@ -114,7 +114,7 @@ CREATE TABLE IF NOT EXISTS "blobs" (
CREATE TABLE IF NOT EXISTS "attachments"
(
attachmentId TEXT not null primary key,
parentId TEXT not null,
ownerId TEXT not null,
role TEXT not null,
mime TEXT not null,
title TEXT not null,
@ -126,5 +126,5 @@ CREATE TABLE IF NOT EXISTS "attachments"
utcDateScheduledForErasureSince TEXT DEFAULT NULL,
isDeleted INT not null,
deleteId TEXT DEFAULT NULL);
CREATE INDEX IDX_attachments_parentId_role
on attachments (parentId, role);
CREATE INDEX IDX_attachments_ownerId_role
on attachments (ownerId, role);

View file

@ -20,14 +20,14 @@ const attachmentRoleToNoteTypeMapping = {
class BAttachment extends AbstractBeccaEntity {
static get entityName() { return "attachments"; }
static get primaryKeyName() { return "attachmentId"; }
static get hashedProperties() { return ["attachmentId", "parentId", "role", "mime", "title", "blobId",
static get hashedProperties() { return ["attachmentId", "ownerId", "role", "mime", "title", "blobId",
"utcDateScheduledForErasureSince", "utcDateModified"]; }
constructor(row) {
super();
if (!row.parentId?.trim()) {
throw new Error("'parentId' must be given to initialize a Attachment entity");
if (!row.ownerId?.trim()) {
throw new Error("'ownerId' must be given to initialize a Attachment entity");
} else if (!row.role?.trim()) {
throw new Error("'role' must be given to initialize a Attachment entity");
} else if (!row.mime?.trim()) {
@ -39,7 +39,7 @@ class BAttachment extends AbstractBeccaEntity {
/** @type {string} */
this.attachmentId = row.attachmentId;
/** @type {string} either noteId or revisionId to which this attachment belongs */
this.parentId = row.parentId;
this.ownerId = row.ownerId;
/** @type {string} */
this.role = row.role;
/** @type {string} */
@ -68,7 +68,7 @@ class BAttachment extends AbstractBeccaEntity {
/** @returns {BAttachment} */
copy() {
return new BAttachment({
parentId: this.parentId,
ownerId: this.ownerId,
role: this.role,
mime: this.mime,
title: this.title,
@ -79,7 +79,7 @@ class BAttachment extends AbstractBeccaEntity {
/** @returns {BNote} */
getNote() {
return this.becca.notes[this.parentId];
return this.becca.notes[this.ownerId];
}
/** @returns {boolean} true if the note has string content (not binary) */
@ -148,7 +148,7 @@ class BAttachment extends AbstractBeccaEntity {
const noteService = require('../../services/notes');
const { note, branch } = noteService.createNewNote({
parentNoteId: this.parentId,
parentNoteId: this.ownerId,
title: this.title,
type: attachmentRoleToNoteTypeMapping[this.role],
mime: this.mime,
@ -189,7 +189,7 @@ class BAttachment extends AbstractBeccaEntity {
if (this.position === undefined || this.position === null) {
this.position = 10 + sql.getValue(`SELECT COALESCE(MAX(position), 0)
FROM attachments
WHERE parentId = ?`, [this.noteId]);
WHERE ownerId = ?`, [this.noteId]);
}
this.dateModified = dateUtils.localNowDateTime();
@ -199,7 +199,7 @@ class BAttachment extends AbstractBeccaEntity {
getPojo() {
return {
attachmentId: this.attachmentId,
parentId: this.parentId,
ownerId: this.ownerId,
role: this.role,
mime: this.mime,
title: this.title,

View file

@ -1101,9 +1101,9 @@ class BNote extends AbstractBeccaEntity {
? `SELECT attachments.*, LENGTH(blobs.content) AS contentLength
FROM attachments
JOIN blobs USING (blobId)
WHERE parentId = ? AND isDeleted = 0
WHERE ownerId = ? AND isDeleted = 0
ORDER BY position`
: `SELECT * FROM attachments WHERE parentId = ? AND isDeleted = 0 ORDER BY position`;
: `SELECT * FROM attachments WHERE ownerId = ? AND isDeleted = 0 ORDER BY position`;
return sql.getRows(query, [this.noteId])
.map(row => new BAttachment(row));
@ -1117,8 +1117,8 @@ class BNote extends AbstractBeccaEntity {
? `SELECT attachments.*, LENGTH(blobs.content) AS contentLength
FROM attachments
JOIN blobs USING (blobId)
WHERE parentId = ? AND attachmentId = ? AND isDeleted = 0`
: `SELECT * FROM attachments WHERE parentId = ? AND attachmentId = ? AND isDeleted = 0`;
WHERE ownerId = ? AND attachmentId = ? AND isDeleted = 0`
: `SELECT * FROM attachments WHERE ownerId = ? AND attachmentId = ? AND isDeleted = 0`;
return sql.getRows(query, [this.noteId, attachmentId])
.map(row => new BAttachment(row))[0];
@ -1129,7 +1129,7 @@ class BNote extends AbstractBeccaEntity {
return sql.getRows(`
SELECT attachments.*
FROM attachments
WHERE parentId = ?
WHERE ownerId = ?
AND role = ?
AND isDeleted = 0
ORDER BY position`, [this.noteId, role])
@ -1590,7 +1590,7 @@ class BNote extends AbstractBeccaEntity {
}
const revisionAttachment = noteAttachment.copy();
revisionAttachment.parentId = revision.revisionId;
revisionAttachment.ownerId = revision.revisionId;
revisionAttachment.setContent(noteAttachment.getContent(), {forceSave: true});
// content is rewritten to point to the revision attachments
@ -1618,7 +1618,7 @@ class BNote extends AbstractBeccaEntity {
attachment = this.becca.getAttachmentOrThrow(attachmentId);
} else {
attachment = new BAttachment({
parentId: this.noteId,
ownerId: this.noteId,
title,
role,
mime,

View file

@ -100,7 +100,7 @@ class BRevision extends AbstractBeccaEntity {
return sql.getRows(`
SELECT attachments.*
FROM attachments
WHERE parentId = ?
WHERE ownerId = ?
AND isDeleted = 0`, [this.revisionId])
.map(row => new BAttachment(row));
}

View file

@ -6,7 +6,7 @@ const utils = require("../services/utils");
function register(router) {
const ALLOWED_PROPERTIES_FOR_CREATE_ATTACHMENT = {
'parentId': [v.notNull, v.isNoteId],
'ownerId': [v.notNull, v.isNoteId],
'role': [v.notNull, v.isString],
'mime': [v.notNull, v.isString],
'title': [v.notNull, v.isString],
@ -20,7 +20,7 @@ function register(router) {
eu.validateAndPatch(params, req.body, ALLOWED_PROPERTIES_FOR_CREATE_ATTACHMENT);
try {
const note = becca.getNoteOrThrow(params.parentId);
const note = becca.getNoteOrThrow(params.ownerId);
const attachment = note.saveAttachment(params);
res.status(201).json(mappers.mapAttachmentToPojo(attachment));

View file

@ -50,7 +50,7 @@ function mapAttributeToPojo(attr) {
function mapAttachmentToPojo(attachment) {
return {
attachmentId: attachment.attachmentId,
parentId: attachment.parentId,
ownerId: attachment.ownerId,
role: attachment.role,
mime: attachment.mime,
title: attachment.title,

View file

@ -9,7 +9,7 @@ class FAttachment {
/** @type {string} */
this.attachmentId = row.attachmentId;
/** @type {string} */
this.parentId = row.parentId;
this.ownerId = row.ownerId;
/** @type {string} */
this.role = row.role;
/** @type {string} */
@ -31,7 +31,7 @@ class FAttachment {
/** @returns {FNote} */
getNote() {
return this.froca.notes[this.parentId];
return this.froca.notes[this.ownerId];
}
/**

View file

@ -280,7 +280,7 @@ function processAttachment(loadResults, ec) {
if (attachment) {
attachment.update(ec.entity);
} else {
const note = froca.notes[ec.entity.parentId];
const note = froca.notes[ec.entity.ownerId];
if (note?.attachments) {
note.attachments.push(new FAttachment(froca, ec.entity));

View file

@ -126,7 +126,7 @@ export default class AttachmentDetailWidget extends BasicWidget {
if (!this.isFullDetail) {
this.$wrapper.find('.attachment-title').append(
await linkService.createLink(this.attachment.parentId, {
await linkService.createLink(this.attachment.ownerId, {
title: this.attachment.title,
viewScope: {
viewMode: 'attachments',
@ -174,7 +174,7 @@ export default class AttachmentDetailWidget extends BasicWidget {
if (this.attachment.role === 'image') {
imageService.copyImageReferenceToClipboard(this.$wrapper.find('.attachment-content-wrapper'));
} else if (this.attachment.role === 'file') {
const $link = await linkService.createLink(this.attachment.parentId, {
const $link = await linkService.createLink(this.attachment.ownerId, {
referenceLink: true,
viewScope: {
viewMode: 'attachments',

View file

@ -121,7 +121,7 @@ export default class NoteActionsWidget extends NoteContextAwareWidget {
toastService.showMessage(`Note '${newAttachment.title}' has been converted to attachment.`);
await ws.waitForMaxKnownEntityChangeId();
await appContext.tabManager.getActiveContext().setNote(newAttachment.parentId, {
await appContext.tabManager.getActiveContext().setNote(newAttachment.ownerId, {
viewScope: {
viewMode: 'attachments',
attachmentId: newAttachment.attachmentId

View file

@ -60,7 +60,7 @@ export default class AbstractTextTypeWidget extends TypeWidget {
const attachment = await froca.getAttachment(attachmentId);
return {
noteId: attachment.parentId,
noteId: attachment.ownerId,
viewScope: {
viewMode: 'attachments',
attachmentId: attachmentId

View file

@ -112,7 +112,7 @@ function restoreRevision(req) {
for (const revisionAttachment of revision.getAttachments()) {
const noteAttachment = revisionAttachment.copy();
noteAttachment.parentId = note.noteId;
noteAttachment.ownerId = note.noteId;
noteAttachment.setContent(revisionAttachment.getContent(), { forceSave: true });
// content is rewritten to point to the restored revision attachments

View file

@ -8,7 +8,7 @@ function getNoteSize(req) {
SELECT blobs.blobId, LENGTH(content)
FROM blobs
LEFT JOIN notes ON notes.blobId = blobs.blobId AND notes.noteId = ? AND notes.isDeleted = 0
LEFT JOIN attachments ON attachments.blobId = blobs.blobId AND attachments.parentId = ? AND attachments.isDeleted = 0
LEFT JOIN attachments ON attachments.blobId = blobs.blobId AND attachments.ownerId = ? AND attachments.isDeleted = 0
LEFT JOIN revisions ON revisions.blobId = blobs.blobId AND revisions.noteId = ?
WHERE notes.noteId IS NOT NULL
OR attachments.attachmentId IS NOT NULL
@ -32,7 +32,7 @@ function getSubtreeSize(req) {
SELECT blobs.blobId, LENGTH(content)
FROM param_list
JOIN notes ON notes.noteId = param_list.paramId AND notes.isDeleted = 0
LEFT JOIN attachments ON attachments.parentId = param_list.paramId AND attachments.isDeleted = 0
LEFT JOIN attachments ON attachments.ownerId = param_list.paramId AND attachments.isDeleted = 0
LEFT JOIN revisions ON revisions.noteId = param_list.paramId
JOIN blobs ON blobs.blobId = notes.blobId OR blobs.blobId = attachments.blobId OR blobs.blobId = revisions.blobId`);

View file

@ -215,24 +215,24 @@ class ConsistencyChecks {
});
this.findAndFixIssues(`
SELECT attachmentId, attachments.parentId AS noteId
SELECT attachmentId, attachments.ownerId AS noteId
FROM attachments
WHERE attachments.parentId NOT IN (
WHERE attachments.ownerId NOT IN (
SELECT noteId FROM notes
UNION ALL
SELECT revisionId FROM revisions
)
AND attachments.isDeleted = 0`,
({attachmentId, parentId}) => {
({attachmentId, ownerId}) => {
if (this.autoFix) {
const attachment = becca.getAttachment(attachmentId);
attachment.markAsDeleted();
this.reloadNeeded = false;
logFix(`Attachment '${attachmentId}' has been deleted since it references missing note/revision '${parentId}'`);
logFix(`Attachment '${attachmentId}' has been deleted since it references missing note/revision '${ownerId}'`);
} else {
logError(`Attachment '${attachmentId}' references missing note/revision '${parentId}'`);
logError(`Attachment '${attachmentId}' references missing note/revision '${ownerId}'`);
}
});
}
@ -345,9 +345,9 @@ class ConsistencyChecks {
this.findAndFixIssues(`
SELECT attachmentId,
attachments.parentId AS noteId
attachments.ownerId AS noteId
FROM attachments
JOIN notes ON notes.noteId = attachments.parentId
JOIN notes ON notes.noteId = attachments.ownerId
WHERE notes.isDeleted = 1
AND attachments.isDeleted = 0`,
({attachmentId, noteId}) => {

View file

@ -452,7 +452,7 @@ async function importZip(taskContext, fileBuffer, importRootNote) {
if (attachmentMeta) {
const attachment = new BAttachment({
attachmentId: getNewAttachmentId(attachmentMeta.attachmentId),
parentId: noteId,
ownerId: noteId,
title: attachmentMeta.title,
role: attachmentMeta.role,
mime: attachmentMeta.mime,

View file

@ -378,21 +378,21 @@ function checkImageAttachments(note, content) {
localAttachment.save();
}
log.info(`Found equivalent attachment '${localAttachment.attachmentId}' of note '${note.noteId}' for the linked foreign attachment '${unknownAttachment.attachmentId}' of note '${unknownAttachment.parentId}'`);
log.info(`Found equivalent attachment '${localAttachment.attachmentId}' of note '${note.noteId}' for the linked foreign attachment '${unknownAttachment.attachmentId}' of note '${unknownAttachment.ownerId}'`);
} else {
localAttachment = unknownAttachment.copy();
localAttachment.parentId = note.noteId;
localAttachment.ownerId = note.noteId;
localAttachment.setContent(unknownAttachment.getContent(), {forceSave: true});
ws.sendMessageToAllClients({ type: 'toast', message: `Attachment '${localAttachment.title}' has been copied to note '${note.title}'.`});
log.info(`Copied attachment '${unknownAttachment.attachmentId}' of note '${unknownAttachment.parentId}' to new '${localAttachment.attachmentId}' of note '${note.noteId}'`);
log.info(`Copied attachment '${unknownAttachment.attachmentId}' of note '${unknownAttachment.ownerId}' to new '${localAttachment.attachmentId}' of note '${note.noteId}'`);
}
// replace image links
content = content.replace(`api/attachments/${unknownAttachment.attachmentId}/image`, `api/attachments/${localAttachment.attachmentId}/image`);
// replace reference links
content = content.replace(new RegExp(`href="[^"]+attachmentId=${unknownAttachment.attachmentId}[^"]*"`, "g"),
`href="#root/${localAttachment.parentId}?viewMode=attachments&attachmentId=${localAttachment.attachmentId}"`);
`href="#root/${localAttachment.ownerId}?viewMode=attachments&attachmentId=${localAttachment.attachmentId}"`);
}
return {

View file

@ -48,7 +48,7 @@ function checkAttachmentAccess(attachmentId, req, res) {
return false;
}
const note = checkNoteAccess(attachment.parentId, req, res);
const note = checkNoteAccess(attachment.ownerId, req, res);
// truthy note means the user has access, and we can return the attachment
return note ? attachment : false;

View file

@ -5,13 +5,13 @@ const utils = require('../../../services/utils');
const AbstractShacaEntity = require('./abstract_shaca_entity');
class SAttachment extends AbstractShacaEntity {
constructor([attachmentId, parentId, role, mime, title, blobId, utcDateModified]) {
constructor([attachmentId, ownerId, role, mime, title, blobId, utcDateModified]) {
super();
/** @param {string} */
this.attachmentId = attachmentId;
/** @param {string} */
this.parentId = parentId;
this.ownerId = ownerId;
/** @param {string} */
this.title = title;
/** @param {string} */
@ -24,12 +24,12 @@ class SAttachment extends AbstractShacaEntity {
this.utcDateModified = utcDateModified; // used for caching of images
this.shaca.attachments[this.attachmentId] = this;
this.shaca.notes[this.parentId].attachments.push(this);
this.shaca.notes[this.ownerId].attachments.push(this);
}
/** @returns {SNote} */
get note() {
return this.shaca.notes[this.parentId];
return this.shaca.notes[this.ownerId];
}
getContent(silentNotFoundError = false) {

View file

@ -67,10 +67,10 @@ function load() {
}
const rawAttachmentRows = sql.getRawRows(`
SELECT attachmentId, parentId, role, mime, title, blobId, utcDateModified
SELECT attachmentId, ownerId, role, mime, title, blobId, utcDateModified
FROM attachments
WHERE isDeleted = 0
AND parentId IN (${noteIdStr})`);
AND ownerId IN (${noteIdStr})`);
rawAttachmentRows.sort((a, b) => a.position < b.position ? -1 : 1);

View file

@ -127,7 +127,7 @@ Content-Type: application/json
Authorization: {{authToken}}
{
"parentId": "{{createdNoteId}}",
"ownerId": "{{createdNoteId}}",
"role": "file",
"mime": "plain/text",
"title": "my attachment",

View file

@ -18,7 +18,7 @@ Authorization: {{authToken}}
Content-Type: application/json
{
"parentId": "{{createdNoteId}}",
"ownerId": "{{createdNoteId}}",
"role": "file",
"mime": "text/plain",
"title": "my attachment",

View file

@ -18,7 +18,7 @@ Authorization: {{authToken}}
Content-Type: application/json
{
"parentId": "{{createdNoteId}}",
"ownerId": "{{createdNoteId}}",
"role": "file",
"mime": "text/plain",
"title": "my attachment",
@ -55,7 +55,7 @@ Authorization: {{authToken}}
Content-Type: application/json
{
"parentId": "root"
"ownerId": "root"
}
> {%
@ -76,4 +76,4 @@ Content-Type: application/json
> {%
client.assert(response.status === 400);
client.assert(response.body.code == "PROPERTY_VALIDATION_ERROR");
%}
%}

View file

@ -18,7 +18,7 @@ Authorization: {{authToken}}
Content-Type: application/json
{
"parentId": "{{createdNoteId}}",
"ownerId": "{{createdNoteId}}",
"role": "file",
"mime": "text/plain",
"title": "my attachment",
@ -36,4 +36,4 @@ Content-Transfer-Encoding: binary
< ../images/icon-color.png
> {% client.assert(response.status === 204); %}
> {% client.assert(response.status === 204); %}

View file

@ -18,7 +18,7 @@ Authorization: {{authToken}}
Content-Type: application/json
{
"parentId": "{{createdNoteId}}",
"ownerId": "{{createdNoteId}}",
"role": "file",
"mime": "text/plain",
"title": "my attachment",