2018-08-02 22:48:21 +02:00
"use strict" ;
2024-07-18 21:35:17 +03:00
import sql from "../../services/sql.js" ;
import log from "../../services/log.js" ;
import attributeService from "../../services/attributes.js" ;
import BAttribute from "../../becca/entities/battribute.js" ;
import becca from "../../becca/becca.js" ;
import ValidationError from "../../errors/validation_error.js" ;
2025-01-09 18:36:24 +02:00
import type { Request } from "express" ;
2024-04-05 20:22:10 +03:00
function getEffectiveNoteAttributes ( req : Request ) {
2021-05-02 11:23:58 +02:00
const note = becca . getNote ( req . params . noteId ) ;
2018-08-07 13:33:10 +02:00
2024-04-05 20:22:10 +03:00
return note ? . getAttributes ( ) ;
2018-08-02 22:48:21 +02:00
}
2024-04-05 20:22:10 +03:00
function updateNoteAttribute ( req : Request ) {
2018-08-06 14:43:42 +02:00
const noteId = req . params . noteId ;
const body = req . body ;
let attribute ;
if ( body . attributeId ) {
2023-05-08 00:02:08 +02:00
attribute = becca . getAttributeOrThrow ( body . attributeId ) ;
2022-11-26 14:57:39 +01:00
2020-01-28 22:37:06 +01:00
if ( attribute . noteId !== noteId ) {
2022-12-09 16:04:13 +01:00
throw new ValidationError ( ` Attribute ' ${ body . attributeId } ' is not owned by ${ noteId } ` ) ;
2020-01-28 22:37:06 +01:00
}
2025-01-09 18:07:02 +02:00
if ( body . type !== attribute . type || body . name !== attribute . name || ( body . type === "relation" && body . value !== attribute . value ) ) {
2020-11-11 23:02:14 +01:00
let newAttribute ;
2025-01-09 18:07:02 +02:00
if ( body . type !== "relation" || ! ! body . value . trim ( ) ) {
2020-11-11 23:02:14 +01:00
newAttribute = attribute . createClone ( body . type , body . name , body . value ) ;
2020-06-20 12:31:38 +02:00
newAttribute . save ( ) ;
2020-01-28 22:37:06 +01:00
}
2021-05-02 20:32:50 +02:00
attribute . markAsDeleted ( ) ;
2020-01-28 22:37:06 +01:00
return {
2020-11-11 23:02:14 +01:00
attributeId : newAttribute ? newAttribute.attributeId : null
2020-01-28 22:37:06 +01:00
} ;
}
2025-01-09 18:07:02 +02:00
} else {
if ( body . type === "relation" && ! body . value ? . trim ( ) ) {
2018-11-12 23:34:22 +01:00
return { } ;
}
2023-01-03 13:52:37 +01:00
attribute = new BAttribute ( {
2021-05-18 20:56:49 +02:00
noteId : noteId ,
name : body.name ,
type : body . type
} ) ;
2018-08-06 14:43:42 +02:00
}
2025-01-09 18:07:02 +02:00
if ( attribute . type === "label" || body . value . trim ( ) ) {
2018-11-12 23:34:22 +01:00
attribute . value = body . value ;
2025-01-09 18:07:02 +02:00
} else {
2018-11-12 23:34:22 +01:00
// relations should never have empty target
2021-05-02 20:32:50 +02:00
attribute . markAsDeleted ( ) ;
2018-11-12 23:34:22 +01:00
}
2018-08-06 14:43:42 +02:00
2020-06-20 12:31:38 +02:00
attribute . save ( ) ;
2018-08-06 17:24:35 +02:00
return {
attributeId : attribute.attributeId
} ;
}
2024-04-05 20:22:10 +03:00
function setNoteAttribute ( req : Request ) {
2020-10-24 23:50:32 +02:00
const noteId = req . params . noteId ;
const body = req . body ;
2024-04-05 20:22:10 +03:00
const attributeId = sql . getValue < string | null > ( ` SELECT attributeId FROM attributes WHERE isDeleted = 0 AND noteId = ? AND type = ? AND name = ? ` , [ noteId , body . type , body . name ] ) ;
2020-10-24 23:50:32 +02:00
2021-05-17 22:05:35 +02:00
if ( attributeId ) {
const attr = becca . getAttribute ( attributeId ) ;
2024-04-05 20:22:10 +03:00
if ( ! attr ) {
throw new ValidationError ( ` Missing attribute with ID ${ attributeId } . ` ) ;
}
2020-10-24 23:50:32 +02:00
attr . value = body . value ;
2021-05-17 22:05:35 +02:00
attr . save ( ) ;
2020-10-24 23:50:32 +02:00
} else {
2025-01-09 18:07:02 +02:00
const params = { . . . body } ;
2021-09-29 13:33:05 +02:00
params . noteId = noteId ; // noteId must be set before calling constructor for proper initialization
2023-01-03 13:52:37 +01:00
new BAttribute ( params ) . save ( ) ;
2020-10-24 23:50:32 +02:00
}
}
2024-04-05 20:22:10 +03:00
function addNoteAttribute ( req : Request ) {
2021-01-17 23:01:01 +01:00
const noteId = req . params . noteId ;
const body = req . body ;
2025-01-09 18:07:02 +02:00
new BAttribute ( { . . . body , noteId } ) . save ( ) ;
2021-01-17 23:01:01 +01:00
}
2024-04-05 20:22:10 +03:00
function deleteNoteAttribute ( req : Request ) {
2018-08-07 11:38:00 +02:00
const noteId = req . params . noteId ;
2018-08-06 17:24:35 +02:00
const attributeId = req . params . attributeId ;
2021-04-26 22:24:55 +02:00
const attribute = becca . getAttribute ( attributeId ) ;
2018-08-06 17:24:35 +02:00
if ( attribute ) {
2018-08-07 11:38:00 +02:00
if ( attribute . noteId !== noteId ) {
2022-12-09 16:04:13 +01:00
throw new ValidationError ( ` Attribute ${ attributeId } is not owned by ${ noteId } ` ) ;
2018-08-07 11:38:00 +02:00
}
2021-05-02 20:32:50 +02:00
attribute . markAsDeleted ( ) ;
2018-08-06 17:24:35 +02:00
}
2018-08-06 14:43:42 +02:00
}
2024-04-05 20:22:10 +03:00
function updateNoteAttributes ( req : Request ) {
2020-06-05 17:25:14 +02:00
const noteId = req . params . noteId ;
const incomingAttributes = req . body ;
2021-05-02 11:23:58 +02:00
const note = becca . getNote ( noteId ) ;
2024-04-05 20:22:10 +03:00
if ( ! note ) {
throw new ValidationError ( ` Cannot find note with ID ${ noteId } . ` ) ;
}
2020-06-05 17:25:14 +02:00
2023-07-17 22:53:54 +02:00
let existingAttrs = note . getOwnedAttributes ( ) . slice ( ) ;
2020-06-05 17:25:14 +02:00
let position = 0 ;
for ( const incAttr of incomingAttributes ) {
position += 10 ;
2021-10-26 20:57:45 +02:00
const value = incAttr . value || "" ;
2025-01-09 18:07:02 +02:00
const perfectMatchAttr = existingAttrs . find ( ( attr ) = > attr . type === incAttr . type && attr . name === incAttr . name && attr . isInheritable === incAttr . isInheritable && attr . value === value ) ;
2020-06-05 17:25:14 +02:00
if ( perfectMatchAttr ) {
2025-01-09 18:07:02 +02:00
existingAttrs = existingAttrs . filter ( ( attr ) = > attr . attributeId !== perfectMatchAttr . attributeId ) ;
2020-06-05 17:25:14 +02:00
if ( perfectMatchAttr . position !== position ) {
perfectMatchAttr . position = position ;
2020-06-20 12:31:38 +02:00
perfectMatchAttr . save ( ) ;
2020-06-05 17:25:14 +02:00
}
continue ; // nothing to update
}
2025-01-09 18:07:02 +02:00
if ( incAttr . type === "relation" ) {
2021-05-02 11:23:58 +02:00
const targetNote = becca . getNote ( incAttr . value ) ;
2020-06-05 17:25:14 +02:00
2021-10-31 21:03:26 +01:00
if ( ! targetNote ) {
2020-06-05 17:25:14 +02:00
log . error ( ` Target note of relation ${ JSON . stringify ( incAttr ) } does not exist or is deleted ` ) ;
continue ;
}
}
2025-01-09 18:07:02 +02:00
const matchedAttr = existingAttrs . find ( ( attr ) = > attr . type === incAttr . type && attr . name === incAttr . name && attr . isInheritable === incAttr . isInheritable ) ;
2020-06-05 17:25:14 +02:00
if ( matchedAttr ) {
matchedAttr . value = incAttr . value ;
matchedAttr . position = position ;
2020-06-20 12:31:38 +02:00
matchedAttr . save ( ) ;
2020-06-05 17:25:14 +02:00
2025-01-09 18:07:02 +02:00
existingAttrs = existingAttrs . filter ( ( attr ) = > attr . attributeId !== matchedAttr . attributeId ) ;
2020-06-05 17:25:14 +02:00
continue ;
}
2022-12-27 14:44:28 +01:00
// no existing attribute has been matched, so we need to create a new one
2020-06-05 17:25:14 +02:00
// type, name and isInheritable are immutable so even if there is an attribute with matching type & name, we need to create a new one and delete the former one
2020-06-20 12:31:38 +02:00
note . addAttribute ( incAttr . type , incAttr . name , incAttr . value , incAttr . isInheritable , position ) ;
2020-06-05 17:25:14 +02:00
}
// all the remaining existing attributes are not defined anymore and should be deleted
for ( const toDeleteAttr of existingAttrs ) {
2020-11-11 23:15:48 +01:00
if ( ! toDeleteAttr . isAutoLink ( ) ) {
2021-05-02 20:32:50 +02:00
toDeleteAttr . markAsDeleted ( ) ;
2020-11-11 23:15:48 +01:00
}
2020-06-05 17:25:14 +02:00
}
}
2024-04-05 20:22:10 +03:00
function getAttributeNames ( req : Request ) {
2018-08-03 11:11:57 +02:00
const type = req . query . type ;
const query = req . query . query ;
2018-08-02 22:48:21 +02:00
2024-04-05 20:22:10 +03:00
if ( typeof type !== "string" || typeof query !== "string" ) {
throw new ValidationError ( "Invalid data type." ) ;
}
2018-08-03 11:11:57 +02:00
return attributeService . getAttributeNames ( type , query ) ;
2018-08-02 22:48:21 +02:00
}
2024-04-05 20:22:10 +03:00
function getValuesForAttribute ( req : Request ) {
2018-08-02 22:48:21 +02:00
const attributeName = req . params . attributeName ;
2020-06-20 12:31:38 +02:00
return sql . getColumn ( "SELECT DISTINCT value FROM attributes WHERE isDeleted = 0 AND name = ? AND type = 'label' AND value != '' ORDER BY value" , [ attributeName ] ) ;
2018-08-02 22:48:21 +02:00
}
2024-04-05 20:22:10 +03:00
function createRelation ( req : Request ) {
2018-10-29 22:38:51 +01:00
const sourceNoteId = req . params . noteId ;
const targetNoteId = req . params . targetNoteId ;
const name = req . params . name ;
2025-01-09 18:07:02 +02:00
const attributeId = sql . getValue < string > ( ` SELECT attributeId FROM attributes WHERE isDeleted = 0 AND noteId = ? AND type = 'relation' AND name = ? AND value = ? ` , [
sourceNoteId ,
name ,
targetNoteId
] ) ;
2021-05-17 22:05:35 +02:00
let attribute = becca . getAttribute ( attributeId ) ;
2018-10-29 22:38:51 +01:00
if ( ! attribute ) {
2023-01-03 13:52:37 +01:00
attribute = new BAttribute ( {
2021-05-17 22:05:35 +02:00
noteId : sourceNoteId ,
name : name ,
2025-01-09 18:07:02 +02:00
type : "relation" ,
2021-05-17 22:05:35 +02:00
value : targetNoteId
} ) . save ( ) ;
2018-10-29 22:38:51 +01:00
}
2018-10-30 10:36:19 +01:00
return attribute ;
}
2024-04-05 20:22:10 +03:00
function deleteRelation ( req : Request ) {
2018-10-30 10:36:19 +01:00
const sourceNoteId = req . params . noteId ;
const targetNoteId = req . params . targetNoteId ;
const name = req . params . name ;
2025-01-09 18:07:02 +02:00
const attributeId = sql . getValue < string | null > ( ` SELECT attributeId FROM attributes WHERE isDeleted = 0 AND noteId = ? AND type = 'relation' AND name = ? AND value = ? ` , [
sourceNoteId ,
name ,
targetNoteId
] ) ;
2018-10-30 10:36:19 +01:00
2021-05-17 22:05:35 +02:00
if ( attributeId ) {
const attribute = becca . getAttribute ( attributeId ) ;
2024-04-05 20:22:10 +03:00
if ( attribute ) {
attribute . markAsDeleted ( ) ;
}
2018-10-30 10:36:19 +01:00
}
2018-10-29 22:38:51 +01:00
}
2024-07-18 21:47:30 +03:00
export default {
2018-08-02 22:48:21 +02:00
updateNoteAttributes ,
2018-08-06 14:43:42 +02:00
updateNoteAttribute ,
2020-10-24 23:50:32 +02:00
setNoteAttribute ,
2021-01-17 23:01:01 +01:00
addNoteAttribute ,
2018-08-06 17:24:35 +02:00
deleteNoteAttribute ,
2018-08-03 11:11:57 +02:00
getAttributeNames ,
2018-08-06 08:59:26 +02:00
getValuesForAttribute ,
2018-10-29 22:38:51 +01:00
getEffectiveNoteAttributes ,
2018-10-30 10:36:19 +01:00
createRelation ,
2020-06-18 22:28:18 +02:00
deleteRelation
2020-05-11 23:57:39 +02:00
} ;