2018-12-23 05:16:32 +08:00
import server from '../services/server.js' ;
2019-08-20 02:12:00 +08:00
import Attribute from './attribute.js' ;
2019-10-26 15:51:08 +08:00
import branches from "../services/branches.js" ;
2018-12-23 05:16:32 +08:00
const LABEL = 'label' ;
const LABEL _DEFINITION = 'label-definition' ;
const RELATION = 'relation' ;
const RELATION _DEFINITION = 'relation-definition' ;
2018-08-23 18:55:45 +08:00
/ * *
2019-09-08 15:40:29 +08:00
* FIXME : rethink how attributes are cached in Note entities since they are long lived inside the cache .
* Attribute cache should be limited to "transaction" .
*
2018-08-23 18:55:45 +08:00
* This note ' s representation is used in note tree and is kept in TreeCache .
* /
2018-03-26 00:29:00 +08:00
class NoteShort {
2019-10-26 15:51:08 +08:00
/ * *
* @ param { TreeCache } treeCache
* @ param { Object . < string , Object > } row
* @ param { Branch [ ] } branches - all relevant branches , i . e . where this note is either child or parent
* /
constructor ( treeCache , row , branches ) {
2018-03-26 00:29:00 +08:00
this . treeCache = treeCache ;
2018-08-23 18:55:45 +08:00
/** @param {string} */
2018-03-26 00:29:00 +08:00
this . noteId = row . noteId ;
2018-08-23 18:55:45 +08:00
/** @param {string} */
2018-03-26 00:29:00 +08:00
this . title = row . title ;
2018-08-23 18:55:45 +08:00
/** @param {boolean} */
2018-03-26 00:29:00 +08:00
this . isProtected = row . isProtected ;
2018-08-23 18:55:45 +08:00
/** @param {string} one of 'text', 'code', 'file' or 'render' */
2018-03-26 00:29:00 +08:00
this . type = row . type ;
2018-08-23 18:55:45 +08:00
/** @param {string} content-type, e.g. "application/json" */
2018-03-26 00:29:00 +08:00
this . mime = row . mime ;
2018-08-23 18:55:45 +08:00
/** @param {boolean} */
2018-06-08 07:26:28 +08:00
this . archived = row . archived ;
2019-09-07 05:36:08 +08:00
/** @param {string} */
2018-08-13 16:59:31 +08:00
this . cssClass = row . cssClass ;
2019-10-26 15:51:08 +08:00
/** @type {string[]} */
this . parents = [ ] ;
/** @type {string[]} */
this . children = [ ] ;
/** @type {Object.<string, string>} */
this . parentToBranch = { } ;
/** @type {Object.<string, string>} */
this . childToBranch = { } ;
for ( const branch of branches ) {
if ( this . noteId === branch . noteId ) {
this . parents . push ( branch . parentNoteId ) ;
this . parentToBranch [ branch . parentNoteId ] = branch . branchId ;
}
else if ( this . noteId === branch . parentNoteId ) {
this . children . push ( branch . noteId ) ;
this . childToBranch [ branch . noteId ] = branch . branchId ;
}
else {
throw new Error ( ` Unknown branch ${ branch . branchId } for note ${ this . noteId } ` ) ;
}
}
}
addParent ( parentNoteId , branchId ) {
if ( ! this . parents . includes ( parentNoteId ) ) {
this . parents . push ( parentNoteId ) ;
}
this . parentToBranch [ parentNoteId ] = branchId ;
}
addChild ( childNoteId , branchId ) {
if ( ! this . children . includes ( childNoteId ) ) {
this . children . push ( childNoteId ) ;
}
this . childToBranch [ childNoteId ] = branchId ;
const branchIdPos = { } ;
for ( const branchId of Object . values ( this . childToBranch ) ) {
branchIdPos [ branchId ] = this . treeCache . branches [ branchId ] . notePosition ;
}
this . children . sort ( ( a , b ) => branchIdPos [ this . childToBranch [ a ] ] < branchIdPos [ this . childToBranch [ b ] ] ? - 1 : 1 ) ;
2018-03-26 00:29:00 +08:00
}
2018-08-23 18:55:45 +08:00
/** @returns {boolean} */
2018-03-26 11:25:17 +08:00
isJson ( ) {
return this . mime === "application/json" ;
}
2019-09-07 05:36:08 +08:00
async getContent ( ) {
// we're not caching content since these objects are in treeCache and as such pretty long lived
const note = await server . get ( "notes/" + this . noteId ) ;
return note . content ;
}
async getJsonContent ( ) {
const content = await this . getContent ( ) ;
try {
return JSON . parse ( content ) ;
}
catch ( e ) {
console . log ( ` Cannot parse content of note ${ this . noteId } : ` , e . message ) ;
return null ;
}
}
2018-08-23 21:33:19 +08:00
/** @returns {Promise<Branch[]>} */
2018-03-26 00:29:00 +08:00
async getBranches ( ) {
2019-10-26 15:51:08 +08:00
const branchIds = Object . values ( this . parentToBranch ) ;
2018-03-26 00:29:00 +08:00
2018-04-17 11:13:33 +08:00
return this . treeCache . getBranches ( branchIds ) ;
2018-03-26 00:29:00 +08:00
}
2018-08-23 18:55:45 +08:00
/** @returns {boolean} */
2018-04-17 08:40:18 +08:00
hasChildren ( ) {
2019-10-26 15:51:08 +08:00
return this . children . length > 0 ;
2018-04-17 08:40:18 +08:00
}
2018-08-23 21:33:19 +08:00
/** @returns {Promise<Branch[]>} */
2018-03-26 00:29:00 +08:00
async getChildBranches ( ) {
2019-10-26 15:51:08 +08:00
const branchIds = Object . values ( this . childToBranch ) ;
2018-03-26 00:29:00 +08:00
2019-10-26 15:58:00 +08:00
return this . treeCache . getBranches ( branchIds ) ;
2018-03-26 00:29:00 +08:00
}
2018-08-23 21:33:19 +08:00
/** @returns {string[]} */
2018-04-17 11:34:56 +08:00
getParentNoteIds ( ) {
2019-10-26 15:51:08 +08:00
return this . parents ;
2018-04-17 08:40:18 +08:00
}
2018-08-23 21:33:19 +08:00
/** @returns {Promise<NoteShort[]>} */
2018-03-26 00:29:00 +08:00
async getParentNotes ( ) {
2019-10-26 15:51:08 +08:00
return await this . treeCache . getNotes ( this . parents ) ;
2018-04-17 11:34:56 +08:00
}
2018-08-23 21:33:19 +08:00
/** @returns {string[]} */
2018-04-17 11:34:56 +08:00
getChildNoteIds ( ) {
2019-10-26 15:51:08 +08:00
return this . children ;
2018-03-26 00:29:00 +08:00
}
2018-08-23 21:33:19 +08:00
/** @returns {Promise<NoteShort[]>} */
2018-03-26 00:29:00 +08:00
async getChildNotes ( ) {
2019-10-26 15:51:08 +08:00
return await this . treeCache . getNotes ( this . children ) ;
2018-03-26 00:29:00 +08:00
}
2018-12-23 05:16:32 +08:00
/ * *
* @ param { string } [ name ] - attribute name to filter
* @ returns { Promise < Attribute [ ] > }
* /
async getAttributes ( name ) {
if ( ! this . attributeCache ) {
2019-08-20 02:12:00 +08:00
this . attributeCache = ( await server . get ( 'notes/' + this . noteId + '/attributes' ) )
. map ( attrRow => new Attribute ( this . treeCache , attrRow ) ) ;
2018-12-23 05:16:32 +08:00
}
if ( name ) {
return this . attributeCache . filter ( attr => attr . name === name ) ;
}
else {
return this . attributeCache ;
}
}
/ * *
* @ param { string } [ name ] - label name to filter
* @ returns { Promise < Attribute [ ] > } all note ' s labels ( attributes with type label ) , including inherited ones
* /
async getLabels ( name ) {
return ( await this . getAttributes ( name ) ) . filter ( attr => attr . type === LABEL ) ;
}
/ * *
* @ param { string } [ name ] - label name to filter
* @ returns { Promise < Attribute [ ] > } all note ' s label definitions , including inherited ones
* /
async getLabelDefinitions ( name ) {
return ( await this . getAttributes ( name ) ) . filter ( attr => attr . type === LABEL _DEFINITION ) ;
}
/ * *
* @ param { string } [ name ] - relation name to filter
* @ returns { Promise < Attribute [ ] > } all note ' s relations ( attributes with type relation ) , including inherited ones
* /
async getRelations ( name ) {
return ( await this . getAttributes ( name ) ) . filter ( attr => attr . type === RELATION ) ;
}
/ * *
* @ param { string } [ name ] - relation name to filter
* @ returns { Promise < Attribute [ ] > } all note ' s relation definitions including inherited ones
* /
async getRelationDefinitions ( name ) {
return ( await this . getAttributes ( name ) ) . filter ( attr => attr . type === RELATION _DEFINITION ) ;
}
/ * *
* @ param { string } type - attribute type ( label , relation , etc . )
* @ param { string } name - attribute name
* @ returns { Promise < boolean > } true if note has an attribute with given type and name ( including inherited )
* /
async hasAttribute ( type , name ) {
return ! ! await this . getAttribute ( type , name ) ;
}
/ * *
* @ param { string } type - attribute type ( label , relation , etc . )
* @ param { string } name - attribute name
* @ returns { Promise < Attribute > } attribute of given type and name . If there 's more such attributes, first is returned. Returns null if there' s no such attribute belonging to this note .
* /
async getAttribute ( type , name ) {
const attributes = await this . getAttributes ( ) ;
return attributes . find ( attr => attr . type === type && attr . name === name ) ;
}
/ * *
* @ param { string } type - attribute type ( label , relation , etc . )
* @ param { string } name - attribute name
* @ returns { Promise < string > } attribute value of given type and name or null if no such attribute exists .
* /
async getAttributeValue ( type , name ) {
const attr = await this . getAttribute ( type , name ) ;
return attr ? attr . value : null ;
}
/ * *
* @ param { string } name - label name
* @ returns { Promise < boolean > } true if label exists ( including inherited )
* /
async hasLabel ( name ) { return await this . hasAttribute ( LABEL , name ) ; }
/ * *
* @ param { string } name - relation name
* @ returns { Promise < boolean > } true if relation exists ( including inherited )
* /
async hasRelation ( name ) { return await this . hasAttribute ( RELATION , name ) ; }
/ * *
* @ param { string } name - label name
* @ returns { Promise < Attribute > } label if it exists , null otherwise
* /
async getLabel ( name ) { return await this . getAttribute ( LABEL , name ) ; }
/ * *
* @ param { string } name - relation name
* @ returns { Promise < Attribute > } relation if it exists , null otherwise
* /
async getRelation ( name ) { return await this . getAttribute ( RELATION , name ) ; }
/ * *
* @ param { string } name - label name
* @ returns { Promise < string > } label value if label exists , null otherwise
* /
async getLabelValue ( name ) { return await this . getAttributeValue ( LABEL , name ) ; }
/ * *
* @ param { string } name - relation name
* @ returns { Promise < string > } relation value if relation exists , null otherwise
* /
async getRelationValue ( name ) { return await this . getAttributeValue ( RELATION , name ) ; }
/ * *
* @ param { string } name
2019-08-17 17:28:36 +08:00
* @ returns { Promise < NoteShort > | null } target note of the relation or null ( if target is empty or note was not found )
2018-12-23 05:16:32 +08:00
* /
async getRelationTarget ( name ) {
2019-08-17 17:28:36 +08:00
const targets = await this . getRelationTargets ( name ) ;
2018-12-23 05:16:32 +08:00
2019-08-17 17:28:36 +08:00
return targets . length > 0 ? targets [ 0 ] : null ;
}
/ * *
* @ param { string } [ name ] - relation name to filter
* @ returns { Promise < NoteShort [ ] > }
* /
async getRelationTargets ( name ) {
const relations = await this . getRelations ( name ) ;
const targets = [ ] ;
for ( const relation of relations ) {
targets . push ( await this . treeCache . getNote ( relation . value ) ) ;
}
return targets ;
2018-12-23 05:16:32 +08:00
}
/ * *
* Clear note ' s attributes cache to force fresh reload for next attribute request .
* Cache is note instance scoped .
* /
invalidateAttributeCache ( ) {
this . attributeCache = null ;
}
2019-08-20 02:12:00 +08:00
/ * *
2019-08-20 02:59:40 +08:00
* Get relations which target this note
*
* @ returns { Promise < Attribute [ ] > }
2019-08-20 02:12:00 +08:00
* /
2019-08-20 02:59:40 +08:00
async getTargetRelations ( ) {
return ( await server . get ( 'notes/' + this . noteId + '/target-relations' ) )
. map ( attrRow => new Attribute ( this . treeCache , attrRow ) ) ;
2019-08-20 02:12:00 +08:00
}
2018-03-26 00:29:00 +08:00
get toString ( ) {
return ` Note(noteId= ${ this . noteId } , title= ${ this . title } ) ` ;
}
2018-04-08 20:21:49 +08:00
get dto ( ) {
const dto = Object . assign ( { } , this ) ;
delete dto . treeCache ;
2018-06-08 07:26:28 +08:00
delete dto . archived ;
2018-12-23 05:16:32 +08:00
delete dto . attributeCache ;
2018-04-08 20:21:49 +08:00
return dto ;
}
2018-03-26 00:29:00 +08:00
}
export default NoteShort ;