2018-08-03 04:48:21 +08:00
"use strict" ;
const sql = require ( '../../services/sql' ) ;
2020-06-05 23:25:14 +08:00
const log = require ( '../../services/log' ) ;
2018-08-03 04:48:21 +08:00
const attributeService = require ( '../../services/attributes' ) ;
const repository = require ( '../../services/repository' ) ;
const Attribute = require ( '../../entities/attribute' ) ;
2018-08-06 14:59:26 +08:00
async function getEffectiveNoteAttributes ( req ) {
2018-08-07 19:33:10 +08:00
const note = await repository . getNote ( req . params . noteId ) ;
return await note . getAttributes ( ) ;
2018-08-03 04:48:21 +08:00
}
2018-08-06 20:43:42 +08:00
async function updateNoteAttribute ( req ) {
const noteId = req . params . noteId ;
const body = req . body ;
let attribute ;
if ( body . attributeId ) {
attribute = await repository . getAttribute ( body . attributeId ) ;
2020-01-29 05:37:06 +08:00
if ( attribute . noteId !== noteId ) {
return [ 400 , ` Attribute ${ body . attributeId } is not owned by ${ noteId } ` ] ;
}
if ( body . type !== attribute . type
|| body . name !== attribute . name
|| ( body . type === 'relation' && body . value !== attribute . value ) ) {
if ( body . type !== 'relation' || ! ! body . value . trim ( ) ) {
const newAttribute = attribute . createClone ( body . type , body . name , body . value ) ;
await newAttribute . save ( ) ;
}
attribute . isDeleted = true ;
await attribute . save ( ) ;
return {
attributeId : attribute . attributeId
} ;
}
2018-08-06 20:43:42 +08:00
}
else {
2018-11-13 06:34:22 +08:00
if ( body . type === 'relation' && ! body . value . trim ( ) ) {
return { } ;
}
2018-08-06 20:43:42 +08:00
attribute = new Attribute ( ) ;
attribute . noteId = noteId ;
attribute . name = body . name ;
attribute . type = body . type ;
}
2018-11-13 06:34:22 +08:00
if ( body . value . trim ( ) ) {
attribute . value = body . value ;
}
else {
// relations should never have empty target
attribute . isDeleted = true ;
}
2018-08-06 20:43:42 +08:00
await attribute . save ( ) ;
2018-08-06 23:24:35 +08:00
return {
attributeId : attribute . attributeId
} ;
}
async function deleteNoteAttribute ( req ) {
2018-08-07 17:38:00 +08:00
const noteId = req . params . noteId ;
2018-08-06 23:24:35 +08:00
const attributeId = req . params . attributeId ;
const attribute = await repository . getAttribute ( attributeId ) ;
if ( attribute ) {
2018-08-07 17:38:00 +08:00
if ( attribute . noteId !== noteId ) {
return [ 400 , ` Attribute ${ attributeId } is not owned by ${ noteId } ` ] ;
}
attribute . isDeleted = true ;
2018-08-06 23:24:35 +08:00
await attribute . save ( ) ;
}
2018-08-06 20:43:42 +08:00
}
2020-06-05 23:25:14 +08:00
async function updateNoteAttributes2 ( req ) {
const noteId = req . params . noteId ;
const incomingAttributes = req . body ;
const note = await repository . getNote ( noteId ) ;
let existingAttrs = await note . getAttributes ( ) ;
let position = 0 ;
for ( const incAttr of incomingAttributes ) {
position += 10 ;
const perfectMatchAttr = existingAttrs . find ( attr =>
attr . type === incAttr . type &&
attr . name === incAttr . name &&
attr . isInheritable === incAttr . isInheritable &&
attr . value === incAttr . value ) ;
if ( perfectMatchAttr ) {
existingAttrs = existingAttrs . filter ( attr => attr . attributeId !== perfectMatchAttr . attributeId ) ;
if ( perfectMatchAttr . position !== position ) {
perfectMatchAttr . position = position ;
await perfectMatchAttr . save ( ) ;
}
continue ; // nothing to update
}
if ( incAttr . type === 'relation' ) {
const targetNote = await repository . getNote ( incAttr . value ) ;
if ( ! targetNote || targetNote . isDeleted ) {
log . error ( ` Target note of relation ${ JSON . stringify ( incAttr ) } does not exist or is deleted ` ) ;
continue ;
}
}
const matchedAttr = existingAttrs . find ( attr =>
attr . type === incAttr . type &&
attr . name === incAttr . name &&
attr . isInheritable === incAttr . isInheritable ) ;
if ( matchedAttr ) {
matchedAttr . value = incAttr . value ;
matchedAttr . position = position ;
await matchedAttr . save ( ) ;
existingAttrs = existingAttrs . filter ( attr => attr . attributeId !== matchedAttr . attributeId ) ;
continue ;
}
// no existing attribute has been matched so we need to create a new one
// 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
await note . addAttribute ( incAttr . type , incAttr . name , incAttr . value , incAttr . isInheritable , position ) ;
}
// all the remaining existing attributes are not defined anymore and should be deleted
for ( const toDeleteAttr of existingAttrs ) {
toDeleteAttr . isDeleted = true ;
await toDeleteAttr . save ( ) ;
}
}
2018-08-03 04:48:21 +08:00
async function updateNoteAttributes ( req ) {
const noteId = req . params . noteId ;
const attributes = req . body ;
for ( const attribute of attributes ) {
let attributeEntity ;
if ( attribute . attributeId ) {
attributeEntity = await repository . getAttribute ( attribute . attributeId ) ;
2018-08-07 17:38:00 +08:00
if ( attributeEntity . noteId !== noteId ) {
return [ 400 , ` Attribute ${ attributeEntity . noteId } is not owned by ${ noteId } ` ] ;
}
2020-01-29 05:37:06 +08:00
if ( attribute . type !== attributeEntity . type
|| attribute . name !== attributeEntity . name
2020-05-13 20:42:16 +08:00
|| ( attribute . type === 'relation' && attribute . value !== attributeEntity . value )
|| attribute . isInheritable !== attributeEntity . isInheritable ) {
2020-01-29 05:37:06 +08:00
if ( attribute . type !== 'relation' || ! ! attribute . value . trim ( ) ) {
2020-05-13 20:42:16 +08:00
const newAttribute = attributeEntity . createClone ( attribute . type , attribute . name , attribute . value , attribute . isInheritable ) ;
2020-01-29 05:37:06 +08:00
await newAttribute . save ( ) ;
}
attributeEntity . isDeleted = true ;
await attributeEntity . save ( ) ;
continue ;
}
2018-08-03 04:48:21 +08:00
}
else {
// if it was "created" and then immediatelly deleted, we just don't create it at all
if ( attribute . isDeleted ) {
continue ;
}
attributeEntity = new Attribute ( ) ;
attributeEntity . noteId = noteId ;
}
2018-08-06 02:08:56 +08:00
attributeEntity . type = attribute . type ;
2018-08-03 04:48:21 +08:00
attributeEntity . name = attribute . name ;
attributeEntity . position = attribute . position ;
2018-08-06 02:08:56 +08:00
attributeEntity . isInheritable = attribute . isInheritable ;
2018-08-03 04:48:21 +08:00
attributeEntity . isDeleted = attribute . isDeleted ;
2018-11-19 06:36:21 +08:00
if ( attributeEntity . type === 'relation' && ! attribute . value . trim ( ) ) {
2018-11-13 06:34:22 +08:00
// relation should never have empty target
attributeEntity . isDeleted = true ;
}
else {
attributeEntity . value = attribute . value ;
}
2018-08-03 04:48:21 +08:00
await attributeEntity . save ( ) ;
}
2018-08-07 19:33:10 +08:00
const note = await repository . getNote ( noteId ) ;
2020-05-07 03:41:14 +08:00
note . invalidateAttributeCache ( ) ;
2018-08-07 19:33:10 +08:00
return await note . getAttributes ( ) ;
2018-08-03 04:48:21 +08:00
}
2018-08-03 17:11:57 +08:00
async function getAttributeNames ( req ) {
const type = req . query . type ;
const query = req . query . query ;
2018-08-03 04:48:21 +08:00
2018-08-03 17:11:57 +08:00
return attributeService . getAttributeNames ( type , query ) ;
2018-08-03 04:48:21 +08:00
}
async function getValuesForAttribute ( req ) {
const attributeName = req . params . attributeName ;
2018-08-07 04:52:49 +08:00
return await sql . getColumn ( "SELECT DISTINCT value FROM attributes WHERE isDeleted = 0 AND name = ? AND type = 'label' AND value != '' ORDER BY value" , [ attributeName ] ) ;
2018-08-03 04:48:21 +08:00
}
2018-10-30 05:38:51 +08:00
async function createRelation ( req ) {
const sourceNoteId = req . params . noteId ;
const targetNoteId = req . params . targetNoteId ;
const name = req . params . name ;
let attribute = await repository . getEntity ( ` SELECT * FROM attributes WHERE isDeleted = 0 AND noteId = ? AND type = 'relation' AND name = ? AND value = ? ` , [ sourceNoteId , name , targetNoteId ] ) ;
if ( ! attribute ) {
attribute = new Attribute ( ) ;
attribute . noteId = sourceNoteId ;
attribute . name = name ;
attribute . type = 'relation' ;
attribute . value = targetNoteId ;
await attribute . save ( ) ;
}
2018-10-30 17:36:19 +08:00
return attribute ;
}
async function deleteRelation ( req ) {
const sourceNoteId = req . params . noteId ;
const targetNoteId = req . params . targetNoteId ;
const name = req . params . name ;
let attribute = await repository . getEntity ( ` SELECT * FROM attributes WHERE isDeleted = 0 AND noteId = ? AND type = 'relation' AND name = ? AND value = ? ` , [ sourceNoteId , name , targetNoteId ] ) ;
2018-10-30 18:28:48 +08:00
if ( attribute ) {
attribute . isDeleted = true ;
2018-10-30 17:36:19 +08:00
await attribute . save ( ) ;
}
2018-10-30 05:38:51 +08:00
}
2020-06-18 18:52:16 +08:00
async function getNotesWithAttribute ( req ) {
const { type , name , value } = req . body ;
const notes = await attributeService . getNotesWithAttribute ( type , name , value ) ;
return notes . map ( note => note . noteId ) ;
}
2018-08-03 04:48:21 +08:00
module . exports = {
updateNoteAttributes ,
2020-06-05 23:25:14 +08:00
updateNoteAttributes2 ,
2018-08-06 20:43:42 +08:00
updateNoteAttribute ,
2018-08-06 23:24:35 +08:00
deleteNoteAttribute ,
2018-08-03 17:11:57 +08:00
getAttributeNames ,
2018-08-06 14:59:26 +08:00
getValuesForAttribute ,
2018-10-30 05:38:51 +08:00
getEffectiveNoteAttributes ,
2018-10-30 17:36:19 +08:00
createRelation ,
2020-06-18 18:52:16 +08:00
deleteRelation ,
getNotesWithAttribute
2020-05-12 05:57:39 +08:00
} ;