-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(parent structure): add method that return note parent structure #276
base: main
Are you sure you want to change the base?
Changes from all commits
df44b32
0551b9f
a5adcc2
bccf7b1
42cb421
bb6caff
a11d1f8
0e3e8f0
3adff28
d6c9ded
6bdc56a
6cf3d4c
1809ca1
ca7a9a2
e14d948
5c6cfc0
5bb6be5
fee9bbf
d65c589
14234a1
c09ad3a
9872d5e
5a9fad2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
version: "3.2" | ||
version: '3.2' | ||
|
||
services: | ||
api: | ||
build: | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,6 +9,7 @@ import type User from '@domain/entities/user.js'; | |
import type { NoteList } from '@domain/entities/noteList.js'; | ||
import type NoteHistoryRepository from '@repository/noteHistory.repository.js'; | ||
import type { NoteHistoryMeta, NoteHistoryRecord, NoteHistoryPublic } from '@domain/entities/noteHistory.js'; | ||
import { definePublicNote, type NotePublic } from '@domain/entities/notePublic.js'; | ||
|
||
/** | ||
* Note service | ||
|
@@ -441,4 +442,20 @@ export default class NoteService { | |
|
||
return noteHistoryPublic; | ||
} | ||
|
||
/** | ||
* Get note parent structure recursively by note id and user id | ||
* and check if user has access to the parent note. | ||
* @param noteId - id of the note to get parent structure | ||
* @returns - array of notes that are parent structure of the note | ||
*/ | ||
public async getNoteParents(noteId: NoteInternalId): Promise<NotePublic[]> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Domain level should return Note[]. The Presentation level is responsible for converting it to NotePublic[] |
||
const noteIds: NoteInternalId[] = await this.noteRelationsRepository.getNoteParentsIds(noteId); | ||
const noteParents = await this.noteRepository.getNotesByIds(noteIds); | ||
const noteParentsPublic: NotePublic[] = noteParents.map((note) => { | ||
return definePublicNote(note); | ||
}); | ||
|
||
return noteParentsPublic; | ||
} | ||
} |
neSpecc marked this conversation as resolved.
Show resolved
Hide resolved
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -518,6 +518,221 @@ describe('Note API', () => { | |||||
|
||||||
expect(response?.json().message).toStrictEqual(expectedMessage); | ||||||
}); | ||||||
|
||||||
e11sy marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
test('Returns one parents note in case when note has one parents', async () => { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
/** Create test user */ | ||||||
const user = await global.db.insertUser(); | ||||||
|
||||||
/** Create acces token for the user */ | ||||||
const accessToken = global.auth(user.id); | ||||||
|
||||||
/** Create test note - a parent note */ | ||||||
const parentNote = await global.db.insertNote({ | ||||||
creatorId: user.id, | ||||||
}); | ||||||
|
||||||
/** Create test note - a child note */ | ||||||
const childNote = await global.db.insertNote({ | ||||||
creatorId: user.id, | ||||||
}); | ||||||
|
||||||
/** Create test note settings */ | ||||||
await global.db.insertNoteSetting({ | ||||||
noteId: childNote.id, | ||||||
isPublic: true, | ||||||
}); | ||||||
|
||||||
/** Create test note relation */ | ||||||
await global.db.insertNoteRelation({ | ||||||
parentId: parentNote.id, | ||||||
noteId: childNote.id, | ||||||
}); | ||||||
|
||||||
const response = await global.api?.fakeRequest({ | ||||||
method: 'GET', | ||||||
headers: { | ||||||
authorization: `Bearer ${accessToken}`, | ||||||
}, | ||||||
url: `/note/${childNote.publicId}`, | ||||||
}); | ||||||
|
||||||
expect(response?.statusCode).toBe(200); | ||||||
|
||||||
expect(response?.json()).toMatchObject({ | ||||||
parents: [ | ||||||
{ | ||||||
id: parentNote.publicId, | ||||||
content: parentNote.content, | ||||||
}, | ||||||
{ | ||||||
id: childNote.publicId, | ||||||
content: childNote.content, | ||||||
}, | ||||||
Comment on lines
+563
to
+570
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
so why there are two notes? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||
], | ||||||
}); | ||||||
}); | ||||||
|
||||||
test('Returns two parents note in case when note has two parents', async () => { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this test repeats the previous one? or its name is not clear. What the exact case you are testing? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is almost similar, just adding more than one note. |
||||||
/** Create test user */ | ||||||
const user = await global.db.insertUser(); | ||||||
|
||||||
/** Create acces token for the user */ | ||||||
const accessToken = global.auth(user.id); | ||||||
|
||||||
/** Create test note - a grand parent note */ | ||||||
const grandParentNote = await global.db.insertNote({ | ||||||
creatorId: user.id, | ||||||
}); | ||||||
|
||||||
/** Create test note - a parent note */ | ||||||
const parentNote = await global.db.insertNote({ | ||||||
creatorId: user.id, | ||||||
}); | ||||||
|
||||||
/** Create test note - a child note */ | ||||||
const childNote = await global.db.insertNote({ | ||||||
creatorId: user.id, | ||||||
}); | ||||||
|
||||||
/** Create test note settings */ | ||||||
await global.db.insertNoteSetting({ | ||||||
noteId: childNote.id, | ||||||
isPublic: true, | ||||||
}); | ||||||
|
||||||
/** Create note relation between parent and grandParentNote */ | ||||||
await global.db.insertNoteRelation({ | ||||||
parentId: grandParentNote.id, | ||||||
noteId: parentNote.id, | ||||||
}); | ||||||
|
||||||
/** Create test note relation */ | ||||||
await global.db.insertNoteRelation({ | ||||||
parentId: parentNote.id, | ||||||
noteId: childNote.id, | ||||||
}); | ||||||
|
||||||
const response = await global.api?.fakeRequest({ | ||||||
method: 'GET', | ||||||
headers: { | ||||||
authorization: `Bearer ${accessToken}`, | ||||||
}, | ||||||
url: `/note/${childNote.publicId}`, | ||||||
}); | ||||||
|
||||||
expect(response?.statusCode).toBe(200); | ||||||
|
||||||
expect(response?.json()).toMatchObject({ | ||||||
parents: [ | ||||||
{ | ||||||
id: grandParentNote.publicId, | ||||||
content: grandParentNote.content, | ||||||
}, | ||||||
{ | ||||||
id: parentNote.publicId, | ||||||
content: parentNote.content, | ||||||
}, | ||||||
{ | ||||||
id: childNote.publicId, | ||||||
content: childNote.content, | ||||||
}, | ||||||
], | ||||||
}); | ||||||
}); | ||||||
|
||||||
test('Returns one parent\'s note when the user is not in the parent note\'s team but shares a relation', async () => { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. also if note has parent, that already means that relation exists, so There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. also, i did not get the case, if one parent note is returned, then overall two notes would be returned? (actual note and parent) or only just actual note? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In this case, the note is not created by the same user, at the same time the main user does not exist in the team of the parent note. Just to prove that the notes are returned based on the note relation, not on team members of the note. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
unclear statement |
||||||
/** Create test user */ | ||||||
const user = await global.db.insertUser(); | ||||||
|
||||||
/** Create another user */ | ||||||
const anotherUser = await global.db.insertUser(); | ||||||
|
||||||
/** Create acces token for the user */ | ||||||
const accessToken = global.auth(user.id); | ||||||
|
||||||
/** Create test note - a parent note */ | ||||||
const parentNote = await global.db.insertNote({ | ||||||
creatorId: anotherUser.id, | ||||||
}); | ||||||
|
||||||
/** Create test note - a child note */ | ||||||
const childNote = await global.db.insertNote({ | ||||||
creatorId: user.id, | ||||||
}); | ||||||
|
||||||
/** Create test note settings */ | ||||||
await global.db.insertNoteSetting({ | ||||||
noteId: childNote.id, | ||||||
isPublic: true, | ||||||
}); | ||||||
|
||||||
/** Create test note relation */ | ||||||
await global.db.insertNoteRelation({ | ||||||
parentId: parentNote.id, | ||||||
noteId: childNote.id, | ||||||
}); | ||||||
|
||||||
const response = await global.api?.fakeRequest({ | ||||||
method: 'GET', | ||||||
headers: { | ||||||
authorization: `Bearer ${accessToken}`, | ||||||
}, | ||||||
url: `/note/${childNote.publicId}`, | ||||||
}); | ||||||
|
||||||
expect(response?.statusCode).toBe(200); | ||||||
|
||||||
expect(response?.json()).toMatchObject({ | ||||||
parents: [ | ||||||
{ | ||||||
id: parentNote.publicId, | ||||||
content: parentNote.content, | ||||||
}, | ||||||
{ | ||||||
id: childNote.publicId, | ||||||
content: childNote.content, | ||||||
}, | ||||||
], | ||||||
}); | ||||||
}); | ||||||
|
||||||
test('Returns one note in case where there is no relation exist for the note with status 200', async () => { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. case is also unclear for me. If note has no relation, why do you return a some note? |
||||||
/** Create test user */ | ||||||
const user = await global.db.insertUser(); | ||||||
|
||||||
/** Create acces token for the user */ | ||||||
const accessToken = global.auth(user.id); | ||||||
|
||||||
/** Create test note - a child note */ | ||||||
const note = await global.db.insertNote({ | ||||||
creatorId: user.id, | ||||||
}); | ||||||
|
||||||
/** Create test note settings */ | ||||||
await global.db.insertNoteSetting({ | ||||||
noteId: note.id, | ||||||
isPublic: true, | ||||||
}); | ||||||
|
||||||
const response = await global.api?.fakeRequest({ | ||||||
method: 'GET', | ||||||
headers: { | ||||||
authorization: `Bearer ${accessToken}`, | ||||||
}, | ||||||
url: `/note/${note.publicId}`, | ||||||
}); | ||||||
|
||||||
expect(response?.statusCode).toBe(200); | ||||||
|
||||||
expect(response?.json()).toMatchObject({ | ||||||
parents: [ | ||||||
{ | ||||||
id: note.publicId, | ||||||
content: note.content, | ||||||
}, | ||||||
], | ||||||
}); | ||||||
}); | ||||||
}); | ||||||
|
||||||
describe('PATCH note/:notePublicId ', () => { | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -67,4 +67,13 @@ export default class NoteRelationsRepository { | |
public async hasRelation(noteId: NoteInternalId): Promise<boolean> { | ||
return await this.storage.hasRelation(noteId); | ||
} | ||
|
||
/** | ||
* Get all note parents based on note id | ||
* @param noteId : note id to get all its parents | ||
* @returns an array of note parents ids | ||
*/ | ||
public async getNoteParentsIds(noteId: NoteInternalId): Promise<NoteInternalId[]> { | ||
return await this.storage.getAllNoteParentsIds(noteId); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what is the meaning of "All" here? is it something different between regular |
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,8 +5,8 @@ import type { Note, NoteCreationAttributes, NoteInternalId, NotePublicId } from | |
import { UserModel } from '@repository/storage/postgres/orm/sequelize/user.js'; | ||
import type { NoteSettingsModel } from './noteSettings.js'; | ||
import type { NoteVisitsModel } from './noteVisits.js'; | ||
import { DomainError } from '@domain/entities/DomainError.js'; | ||
import type { NoteHistoryModel } from './noteHistory.js'; | ||
import { notEmpty } from '@infrastructure/utils/empty.js'; | ||
|
||
/* eslint-disable @typescript-eslint/naming-convention */ | ||
|
||
|
@@ -233,11 +233,11 @@ export default class NoteSequelizeStorage { | |
*/ | ||
public async getNoteListByUserId(userId: number, offset: number, limit: number): Promise<Note[]> { | ||
if (this.visitsModel === null) { | ||
throw new DomainError('NoteVisit model should be defined'); | ||
throw new Error('NoteStorage: NoteVisit model should be defined'); | ||
} | ||
|
||
if (!this.settingsModel) { | ||
throw new Error('Note settings model not initialized'); | ||
throw new Error('NoteStorage: Note settings model not initialized'); | ||
} | ||
|
||
const reply = await this.model.findAll({ | ||
|
@@ -293,7 +293,7 @@ export default class NoteSequelizeStorage { | |
*/ | ||
public async getNoteByHostname(hostname: string): Promise<Note | null> { | ||
if (!this.settingsModel) { | ||
throw new Error('Note settings model not initialized'); | ||
throw new Error('NoteStorage: Note settings model not initialized'); | ||
} | ||
|
||
/** | ||
|
@@ -324,4 +324,24 @@ export default class NoteSequelizeStorage { | |
}, | ||
}); | ||
}; | ||
|
||
/** | ||
* Get all notes based on their ids | ||
* @param noteIds - list of note ids | ||
*/ | ||
public async getNotesByIds(noteIds: NoteInternalId[]): Promise<Note[]> { | ||
const notes: Note[] = []; | ||
|
||
for (const noteId of noteIds) { | ||
const note: Note | null = await this.model.findOne({ | ||
where: { id: noteId }, | ||
}); | ||
|
||
if (notEmpty(note)) { | ||
notes.push(note); | ||
} | ||
} | ||
Comment on lines
+335
to
+343
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. its better to use the single sql query instead of looping over them |
||
|
||
return notes; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unexpected change