diff --git a/.idea/copyright/hedgedoc.xml b/.idea/copyright/hedgedoc.xml index 65ed1c6d5..0033252b9 100644 --- a/.idea/copyright/hedgedoc.xml +++ b/.idea/copyright/hedgedoc.xml @@ -1,6 +1,6 @@ - diff --git a/docs/content/history.md b/docs/content/history.md index cd12a3693..ec50ff717 100644 --- a/docs/content/history.md +++ b/docs/content/history.md @@ -1,5 +1,4 @@ -History of CodiMD -=== +# History of HedgeDoc ## It started with HackMD @@ -27,14 +26,29 @@ project), as people mistook it for an open core development model. ## CodiMD went independent -In March 2019, a discussion over licensing, governance and the future of CodiMD -lead to the formation of a distinct GitHub organization. Up to that point, the -community project resided in the organization of hackmdio but was for the most -part self-organized. +In March 2019, a discussion over licensing, governance and the future of CodiMD lead to the formation of a distinct +GitHub organization. Up to that point, the community project resided in the organization of hackmdio but was for the +most part self-organized. -During that debate, we did not reach an agreement that would have allowed us to -move the repository, so we simply forked it. We still welcome the HackMD team -as part of our community, especially since a large portion of this code base +During that debate, we did not reach an agreement that would have allowed us to move the repository, so we simply forked +it. We still welcome the HackMD team as part of our community, especially since a large portion of this code base originated with them. -*For the debate that lead to this step, please refer to the [governance debate](https://github.com/hackmdio/hackmd/issues/1170) and [the announcement of the new repository](https://github.com/codimd/server/issues/10).* +*For the debate that lead to this step, please refer to +the [governance debate](https://github.com/hackmdio/hackmd/issues/1170) +and [the announcement of the new repository](https://github.com/hedgedoc/hedgedoc/issues/10).* + +## CodiMD became HedgeDoc + +With two actively named forks sharing the same name, +both [insisting on being the original/actual owner of the name CodiMD](https://github.com/hackmdio/codimd/issues/1219), +people became rightfully confused about the projects sharing the same name. + +After roughly a year of being stuck on the name issue, the HedgeDoc community decided to take action and started +a [renaming process in April 2020](https://community.codimd.org/t/renaming-yet-another-time/102). With a good head start +in the amount of names, it was decided that an entire rebranding should take place and therefore, after +a [name was agreed on in July 2020](https://community.codimd.org/t/codimd-becomes-hedgedoc/170), the next step was +to [find a logo](https://community.codimd.org/t/time-to-find-the-hedgedoc-logo/171). + +In November of 2020, roughly 7 months after the initiative was started, a logo was found, the rebranding of the +application as well as all community pages took place and the time of name conflicts was over. (hopefully.) diff --git a/package.json b/package.json index 072f4ffde..d76f51257 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "rimraf": "3.0.2", "rxjs": "6.6.3", "shortid": "2.2.16", - "sqlite3": "5.0.0", + "sqlite3": "5.0.1", "swagger-ui-express": "4.1.6", "typeorm": "0.2.29" }, diff --git a/src/api/public/notes/notes.controller.ts b/src/api/public/notes/notes.controller.ts index 02e337af3..e50437867 100644 --- a/src/api/public/notes/notes.controller.ts +++ b/src/api/public/notes/notes.controller.ts @@ -10,12 +10,13 @@ import { Delete, Get, Header, + NotFoundException, Param, Post, Put, } from '@nestjs/common'; +import { NotInDBError } from '../../../errors/errors'; import { ConsoleLoggerService } from '../../../logger/console-logger.service'; -import { NoteMetadataUpdateDto } from '../../../notes/note-metadata.dto'; import { NotePermissionsUpdateDto } from '../../../notes/note-permissions.dto'; import { NotesService } from '../../../notes/notes.service'; import { RevisionsService } from '../../../revisions/revisions.service'; @@ -38,8 +39,15 @@ export class NotesController { } @Get(':noteIdOrAlias') - getNote(@Param('noteIdOrAlias') noteIdOrAlias: string) { - return this.noteService.getNoteDtoByIdOrAlias(noteIdOrAlias); + async getNote(@Param('noteIdOrAlias') noteIdOrAlias: string) { + try { + return await this.noteService.getNoteDtoByIdOrAlias(noteIdOrAlias); + } catch (e) { + if (e instanceof NotInDBError) { + throw new NotFoundException(e.message); + } + throw e; + } } @Post(':noteAlias') @@ -54,7 +62,14 @@ export class NotesController { @Delete(':noteIdOrAlias') async deleteNote(@Param('noteIdOrAlias') noteIdOrAlias: string) { this.logger.debug('Deleting note: ' + noteIdOrAlias); - await this.noteService.deleteNoteByIdOrAlias(noteIdOrAlias); + try { + await this.noteService.deleteNoteByIdOrAlias(noteIdOrAlias); + } catch (e) { + if (e instanceof NotInDBError) { + throw new NotFoundException(e.message); + } + throw e; + } this.logger.debug('Successfully deleted ' + noteIdOrAlias); return; } @@ -65,38 +80,88 @@ export class NotesController { @MarkdownBody() text: string, ) { this.logger.debug('Got raw markdown:\n' + text); - return this.noteService.updateNoteByIdOrAlias(noteIdOrAlias, text); + try { + return await this.noteService.updateNoteByIdOrAlias(noteIdOrAlias, text); + } catch (e) { + if (e instanceof NotInDBError) { + throw new NotFoundException(e.message); + } + throw e; + } } @Get(':noteIdOrAlias/content') @Header('content-type', 'text/markdown') - getNoteContent(@Param('noteIdOrAlias') noteIdOrAlias: string) { - return this.noteService.getNoteContent(noteIdOrAlias); + async getNoteContent(@Param('noteIdOrAlias') noteIdOrAlias: string) { + try { + return await this.noteService.getNoteContent(noteIdOrAlias); + } catch (e) { + if (e instanceof NotInDBError) { + throw new NotFoundException(e.message); + } + throw e; + } } @Get(':noteIdOrAlias/metadata') - getNoteMetadata(@Param('noteIdOrAlias') noteIdOrAlias: string) { - return this.noteService.getNoteMetadata(noteIdOrAlias); + async getNoteMetadata(@Param('noteIdOrAlias') noteIdOrAlias: string) { + try { + return await this.noteService.getNoteMetadata(noteIdOrAlias); + } catch (e) { + if (e instanceof NotInDBError) { + throw new NotFoundException(e.message); + } + throw e; + } } @Put(':noteIdOrAlias/permissions') - updateNotePermissions( + async updateNotePermissions( @Param('noteIdOrAlias') noteIdOrAlias: string, @Body() updateDto: NotePermissionsUpdateDto, ) { - return this.noteService.updateNotePermissions(noteIdOrAlias, updateDto); + try { + return await this.noteService.updateNotePermissions( + noteIdOrAlias, + updateDto, + ); + } catch (e) { + if (e instanceof NotInDBError) { + throw new NotFoundException(e.message); + } + throw e; + } } @Get(':noteIdOrAlias/revisions') - getNoteRevisions(@Param('noteIdOrAlias') noteIdOrAlias: string) { - return this.revisionsService.getNoteRevisionMetadatas(noteIdOrAlias); + async getNoteRevisions(@Param('noteIdOrAlias') noteIdOrAlias: string) { + try { + return await this.revisionsService.getNoteRevisionMetadatas( + noteIdOrAlias, + ); + } catch (e) { + if (e instanceof NotInDBError) { + throw new NotFoundException(e.message); + } + throw e; + } } @Get(':noteIdOrAlias/revisions/:revisionId') - getNoteRevision( + async getNoteRevision( @Param('noteIdOrAlias') noteIdOrAlias: string, @Param('revisionId') revisionId: number, ) { - return this.revisionsService.getNoteRevision(noteIdOrAlias, revisionId); + try { + return await this.revisionsService.getNoteRevision( + noteIdOrAlias, + revisionId, + ); + } catch (e) { + if (e instanceof NotInDBError) { + throw new NotFoundException(e.message); + } + throw e; + } } } diff --git a/test/public-api/notes.e2e-spec.ts b/test/public-api/notes.e2e-spec.ts index e308fccbb..2fcf1fe70 100644 --- a/test/public-api/notes.e2e-spec.ts +++ b/test/public-api/notes.e2e-spec.ts @@ -59,12 +59,19 @@ describe('Notes', () => { }); it(`GET /notes/{note}`, async () => { + // check if we can succefully get a note that exists await notesService.createNote('This is a test note.', 'test1'); const response = await request(app.getHttpServer()) .get('/notes/test1') .expect('Content-Type', /json/) .expect(200); expect(response.body.content).toEqual('This is a test note.'); + + // check if a missing note correctly returns 404 + await request(app.getHttpServer()) + .get('/notes/i_dont_exist') + .expect('Content-Type', /json/) + .expect(404); }); it(`POST /notes/{note}`, async () => { @@ -85,9 +92,13 @@ describe('Notes', () => { it(`DELETE /notes/{note}`, async () => { await notesService.createNote('This is a test note.', 'test3'); await request(app.getHttpServer()).delete('/notes/test3').expect(200); - return expect(notesService.getNoteByIdOrAlias('test3')).rejects.toEqual( + await expect(notesService.getNoteByIdOrAlias('test3')).rejects.toEqual( new NotInDBError("Note with id/alias 'test3' not found."), ); + // check if a missing note correctly returns 404 + await request(app.getHttpServer()) + .delete('/notes/i_dont_exist') + .expect(404); }); it(`PUT /notes/{note}`, async () => { @@ -97,9 +108,16 @@ describe('Notes', () => { .set('Content-Type', 'text/markdown') .send('New note text') .expect(200); - return expect( + await expect( (await notesService.getNoteDtoByIdOrAlias('test4')).content, ).toEqual('New note text'); + + // check if a missing note correctly returns 404 + await request(app.getHttpServer()) + .put('/notes/i_dont_exist') + .set('Content-Type', 'text/markdown') + .expect('Content-Type', /json/) + .expect(404); }); it(`GET /notes/{note}/metadata`, async () => { @@ -124,6 +142,12 @@ describe('Notes', () => { expect(typeof metadata.body.updateUser.photo).toEqual('string'); expect(typeof metadata.body.viewCount).toEqual('number'); expect(metadata.body.editedBy).toEqual([]); + + // check if a missing note correctly returns 404 + await request(app.getHttpServer()) + .get('/notes/i_dont_exist/metadata') + .expect('Content-Type', /json/) + .expect(404); }); it(`GET /notes/{note}/revisions`, async () => { @@ -133,6 +157,12 @@ describe('Notes', () => { .expect('Content-Type', /json/) .expect(200); expect(response.body).toHaveLength(1); + + // check if a missing note correctly returns 404 + await request(app.getHttpServer()) + .get('/notes/i_dont_exist/revisions') + .expect('Content-Type', /json/) + .expect(404); }); it(`GET /notes/{note}/revisions/{revision-id}`, async () => { @@ -143,6 +173,12 @@ describe('Notes', () => { .expect('Content-Type', /json/) .expect(200); expect(response.body.content).toEqual('This is a test note.'); + + // check if a missing note correctly returns 404 + await request(app.getHttpServer()) + .get('/notes/i_dont_exist/revisions/1') + .expect('Content-Type', /json/) + .expect(404); }); it(`GET /notes/{note}/content`, async () => { @@ -151,6 +187,11 @@ describe('Notes', () => { .get('/notes/test9/content') .expect(200); expect(response.text).toEqual('This is a test note.'); + + // check if a missing note correctly returns 404 + await request(app.getHttpServer()) + .get('/notes/i_dont_exist/content') + .expect(404); }); afterAll(async () => { diff --git a/yarn.lock b/yarn.lock index 4accb8ffc..041512fff 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4942,10 +4942,10 @@ nice-try@^1.0.4: resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== -node-addon-api@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.0.tgz#f9afb8d777a91525244b01775ea0ddbe1125483b" - integrity sha512-ASCL5U13as7HhOExbT6OlWJJUV/lLzL2voOSP1UVehpRD8FbSrSDjfScK/KwAvVTI5AS6r4VwbOMlIqtvRidnA== +node-addon-api@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.1.0.tgz#98b21931557466c6729e51cb77cd39c965f42239" + integrity sha512-flmrDNB06LIl5lywUz7YlNGZH/5p0M7W28k8hzd9Lshtdh1wshD2Y+U4h9LD6KObOy1f+fEVdgprPrEymjM5uw== node-emoji@1.10.0: version "1.10.0" @@ -6287,12 +6287,12 @@ sprintf-js@~1.0.2: resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= -sqlite3@5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/sqlite3/-/sqlite3-5.0.0.tgz#1bfef2151c6bc48a3ab1a6c126088bb8dd233566" - integrity sha512-rjvqHFUaSGnzxDy2AHCwhHy6Zp6MNJzCPGYju4kD8yi6bze4d1/zMTg6C7JI49b7/EM7jKMTvyfN/4ylBKdwfw== +sqlite3@5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/sqlite3/-/sqlite3-5.0.1.tgz#d5b58c8d1568bbaf13062eb9465982f36324f78a" + integrity sha512-kh2lTIcYNfmVcvhVJihsYuPj9U0xzBbh6bmqILO2hkryWSC9RRhzYmkIDtJkJ+d8Kg4wZRJ0T1reyHUEspICfg== dependencies: - node-addon-api "2.0.0" + node-addon-api "^3.0.0" node-pre-gyp "^0.11.0" optionalDependencies: node-gyp "3.x"