Merge branch 'develop' into develop

This commit is contained in:
David Mehren 2021-01-10 18:04:32 +01:00 committed by GitHub
commit e15fb2c8a3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 158 additions and 38 deletions

View file

@ -1,6 +1,6 @@
<component name="CopyrightManager"> <component name="CopyrightManager">
<copyright> <copyright>
<option name="notice" value="SPDX-FileCopyrightText: 2020 The HedgeDoc developers (see AUTHORS file)&#10;&#10;&#83;PDX-License-Identifier: AGPL-3.0-only" /> <option name="notice" value="SPDX-FileCopyrightText: $today.year The HedgeDoc developers (see AUTHORS file)&#10;&#10;&#83;PDX-License-Identifier: AGPL-3.0-only" />
<option name="myName" value="hedgedoc" /> <option name="myName" value="hedgedoc" />
</copyright> </copyright>
</component> </component>

View file

@ -1,5 +1,4 @@
History of CodiMD # History of HedgeDoc
===
## It started with HackMD ## It started with HackMD
@ -27,14 +26,29 @@ project), as people mistook it for an open core development model.
## CodiMD went independent ## CodiMD went independent
In March 2019, a discussion over licensing, governance and the future of CodiMD In March 2019, a discussion over licensing, governance and the future of CodiMD lead to the formation of a distinct
lead to the formation of a distinct GitHub organization. Up to that point, the GitHub organization. Up to that point, the community project resided in the organization of hackmdio but was for the
community project resided in the organization of hackmdio but was for the most most part self-organized.
part self-organized.
During that debate, we did not reach an agreement that would have allowed us to During that debate, we did not reach an agreement that would have allowed us to move the repository, so we simply forked
move the repository, so we simply forked it. We still welcome the HackMD team it. We still welcome the HackMD team as part of our community, especially since a large portion of this code base
as part of our community, especially since a large portion of this code base
originated with them. 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.)

View file

@ -39,7 +39,7 @@
"rimraf": "3.0.2", "rimraf": "3.0.2",
"rxjs": "6.6.3", "rxjs": "6.6.3",
"shortid": "2.2.16", "shortid": "2.2.16",
"sqlite3": "5.0.0", "sqlite3": "5.0.1",
"swagger-ui-express": "4.1.6", "swagger-ui-express": "4.1.6",
"typeorm": "0.2.29" "typeorm": "0.2.29"
}, },

View file

@ -10,12 +10,13 @@ import {
Delete, Delete,
Get, Get,
Header, Header,
NotFoundException,
Param, Param,
Post, Post,
Put, Put,
} from '@nestjs/common'; } from '@nestjs/common';
import { NotInDBError } from '../../../errors/errors';
import { ConsoleLoggerService } from '../../../logger/console-logger.service'; import { ConsoleLoggerService } from '../../../logger/console-logger.service';
import { NoteMetadataUpdateDto } from '../../../notes/note-metadata.dto';
import { NotePermissionsUpdateDto } from '../../../notes/note-permissions.dto'; import { NotePermissionsUpdateDto } from '../../../notes/note-permissions.dto';
import { NotesService } from '../../../notes/notes.service'; import { NotesService } from '../../../notes/notes.service';
import { RevisionsService } from '../../../revisions/revisions.service'; import { RevisionsService } from '../../../revisions/revisions.service';
@ -38,8 +39,15 @@ export class NotesController {
} }
@Get(':noteIdOrAlias') @Get(':noteIdOrAlias')
getNote(@Param('noteIdOrAlias') noteIdOrAlias: string) { async getNote(@Param('noteIdOrAlias') noteIdOrAlias: string) {
return this.noteService.getNoteDtoByIdOrAlias(noteIdOrAlias); try {
return await this.noteService.getNoteDtoByIdOrAlias(noteIdOrAlias);
} catch (e) {
if (e instanceof NotInDBError) {
throw new NotFoundException(e.message);
}
throw e;
}
} }
@Post(':noteAlias') @Post(':noteAlias')
@ -54,7 +62,14 @@ export class NotesController {
@Delete(':noteIdOrAlias') @Delete(':noteIdOrAlias')
async deleteNote(@Param('noteIdOrAlias') noteIdOrAlias: string) { async deleteNote(@Param('noteIdOrAlias') noteIdOrAlias: string) {
this.logger.debug('Deleting note: ' + noteIdOrAlias); 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); this.logger.debug('Successfully deleted ' + noteIdOrAlias);
return; return;
} }
@ -65,38 +80,88 @@ export class NotesController {
@MarkdownBody() text: string, @MarkdownBody() text: string,
) { ) {
this.logger.debug('Got raw markdown:\n' + text); 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') @Get(':noteIdOrAlias/content')
@Header('content-type', 'text/markdown') @Header('content-type', 'text/markdown')
getNoteContent(@Param('noteIdOrAlias') noteIdOrAlias: string) { async getNoteContent(@Param('noteIdOrAlias') noteIdOrAlias: string) {
return this.noteService.getNoteContent(noteIdOrAlias); try {
return await this.noteService.getNoteContent(noteIdOrAlias);
} catch (e) {
if (e instanceof NotInDBError) {
throw new NotFoundException(e.message);
}
throw e;
}
} }
@Get(':noteIdOrAlias/metadata') @Get(':noteIdOrAlias/metadata')
getNoteMetadata(@Param('noteIdOrAlias') noteIdOrAlias: string) { async getNoteMetadata(@Param('noteIdOrAlias') noteIdOrAlias: string) {
return this.noteService.getNoteMetadata(noteIdOrAlias); try {
return await this.noteService.getNoteMetadata(noteIdOrAlias);
} catch (e) {
if (e instanceof NotInDBError) {
throw new NotFoundException(e.message);
}
throw e;
}
} }
@Put(':noteIdOrAlias/permissions') @Put(':noteIdOrAlias/permissions')
updateNotePermissions( async updateNotePermissions(
@Param('noteIdOrAlias') noteIdOrAlias: string, @Param('noteIdOrAlias') noteIdOrAlias: string,
@Body() updateDto: NotePermissionsUpdateDto, @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') @Get(':noteIdOrAlias/revisions')
getNoteRevisions(@Param('noteIdOrAlias') noteIdOrAlias: string) { async getNoteRevisions(@Param('noteIdOrAlias') noteIdOrAlias: string) {
return this.revisionsService.getNoteRevisionMetadatas(noteIdOrAlias); try {
return await this.revisionsService.getNoteRevisionMetadatas(
noteIdOrAlias,
);
} catch (e) {
if (e instanceof NotInDBError) {
throw new NotFoundException(e.message);
}
throw e;
}
} }
@Get(':noteIdOrAlias/revisions/:revisionId') @Get(':noteIdOrAlias/revisions/:revisionId')
getNoteRevision( async getNoteRevision(
@Param('noteIdOrAlias') noteIdOrAlias: string, @Param('noteIdOrAlias') noteIdOrAlias: string,
@Param('revisionId') revisionId: number, @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;
}
} }
} }

View file

@ -59,12 +59,19 @@ describe('Notes', () => {
}); });
it(`GET /notes/{note}`, async () => { it(`GET /notes/{note}`, async () => {
// check if we can succefully get a note that exists
await notesService.createNote('This is a test note.', 'test1'); await notesService.createNote('This is a test note.', 'test1');
const response = await request(app.getHttpServer()) const response = await request(app.getHttpServer())
.get('/notes/test1') .get('/notes/test1')
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200); .expect(200);
expect(response.body.content).toEqual('This is a test note.'); 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 () => { it(`POST /notes/{note}`, async () => {
@ -85,9 +92,13 @@ describe('Notes', () => {
it(`DELETE /notes/{note}`, async () => { it(`DELETE /notes/{note}`, async () => {
await notesService.createNote('This is a test note.', 'test3'); await notesService.createNote('This is a test note.', 'test3');
await request(app.getHttpServer()).delete('/notes/test3').expect(200); 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."), 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 () => { it(`PUT /notes/{note}`, async () => {
@ -97,9 +108,16 @@ describe('Notes', () => {
.set('Content-Type', 'text/markdown') .set('Content-Type', 'text/markdown')
.send('New note text') .send('New note text')
.expect(200); .expect(200);
return expect( await expect(
(await notesService.getNoteDtoByIdOrAlias('test4')).content, (await notesService.getNoteDtoByIdOrAlias('test4')).content,
).toEqual('New note text'); ).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 () => { it(`GET /notes/{note}/metadata`, async () => {
@ -124,6 +142,12 @@ describe('Notes', () => {
expect(typeof metadata.body.updateUser.photo).toEqual('string'); expect(typeof metadata.body.updateUser.photo).toEqual('string');
expect(typeof metadata.body.viewCount).toEqual('number'); expect(typeof metadata.body.viewCount).toEqual('number');
expect(metadata.body.editedBy).toEqual([]); 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 () => { it(`GET /notes/{note}/revisions`, async () => {
@ -133,6 +157,12 @@ describe('Notes', () => {
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200); .expect(200);
expect(response.body).toHaveLength(1); 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 () => { it(`GET /notes/{note}/revisions/{revision-id}`, async () => {
@ -143,6 +173,12 @@ describe('Notes', () => {
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(200); .expect(200);
expect(response.body.content).toEqual('This is a test note.'); 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 () => { it(`GET /notes/{note}/content`, async () => {
@ -151,6 +187,11 @@ describe('Notes', () => {
.get('/notes/test9/content') .get('/notes/test9/content')
.expect(200); .expect(200);
expect(response.text).toEqual('This is a test note.'); 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 () => { afterAll(async () => {

View file

@ -4942,10 +4942,10 @@ nice-try@^1.0.4:
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
node-addon-api@2.0.0: node-addon-api@^3.0.0:
version "2.0.0" version "3.1.0"
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.0.tgz#f9afb8d777a91525244b01775ea0ddbe1125483b" resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.1.0.tgz#98b21931557466c6729e51cb77cd39c965f42239"
integrity sha512-ASCL5U13as7HhOExbT6OlWJJUV/lLzL2voOSP1UVehpRD8FbSrSDjfScK/KwAvVTI5AS6r4VwbOMlIqtvRidnA== integrity sha512-flmrDNB06LIl5lywUz7YlNGZH/5p0M7W28k8hzd9Lshtdh1wshD2Y+U4h9LD6KObOy1f+fEVdgprPrEymjM5uw==
node-emoji@1.10.0: node-emoji@1.10.0:
version "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" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=
sqlite3@5.0.0: sqlite3@5.0.1:
version "5.0.0" version "5.0.1"
resolved "https://registry.yarnpkg.com/sqlite3/-/sqlite3-5.0.0.tgz#1bfef2151c6bc48a3ab1a6c126088bb8dd233566" resolved "https://registry.yarnpkg.com/sqlite3/-/sqlite3-5.0.1.tgz#d5b58c8d1568bbaf13062eb9465982f36324f78a"
integrity sha512-rjvqHFUaSGnzxDy2AHCwhHy6Zp6MNJzCPGYju4kD8yi6bze4d1/zMTg6C7JI49b7/EM7jKMTvyfN/4ylBKdwfw== integrity sha512-kh2lTIcYNfmVcvhVJihsYuPj9U0xzBbh6bmqILO2hkryWSC9RRhzYmkIDtJkJ+d8Kg4wZRJ0T1reyHUEspICfg==
dependencies: dependencies:
node-addon-api "2.0.0" node-addon-api "^3.0.0"
node-pre-gyp "^0.11.0" node-pre-gyp "^0.11.0"
optionalDependencies: optionalDependencies:
node-gyp "3.x" node-gyp "3.x"