Skip to content

Commit

Permalink
Merge pull request #56 from codex-team/feat/update-note
Browse files Browse the repository at this point in the history
feat(note): update note content logic added
  • Loading branch information
neSpecc authored Oct 12, 2023
2 parents cc90ee2 + 464f141 commit bef9dfa
Show file tree
Hide file tree
Showing 8 changed files with 128 additions and 4 deletions.
3 changes: 2 additions & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"match": false
}
}
]
],
"jsdoc/require-returns-type": "off"
}
}
14 changes: 12 additions & 2 deletions src/domain/entities/note.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,20 @@ export interface Note {
* Note creator
*/
creatorId: number;

/**
* When note was created
*/
createdAt: string;

/**
* Last time note was updated
*/
updatedAt: string;
}


/**
* Notes creation attributes, omitting id, because it's generated by database
* Part of note entity used to create new note
*/
export type NoteCreationAttributes = Omit<Note, 'id'>;
export type NoteCreationAttributes = Pick<Note, 'publicId' | 'content' | 'creatorId'>;
18 changes: 18 additions & 0 deletions src/domain/service/note.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,24 @@ export default class NoteService {
});
}

/**
* Updates note
*
* @param id - note id
* @param content - new content
* @returns updated note
* @throws { Error } if note was not updated
*/
public async updateNoteContentByPublicId(id: NotePublicId, content: Note['content']): Promise<Note> {
const updatedNote = await this.repository.updateNoteContentByPublicId(id, content);

if (updatedNote === null) {
throw new Error(`Note with id ${id} was not updated`);
}

return updatedNote;
}

/**
* Gets note by id
*
Expand Down
2 changes: 2 additions & 0 deletions src/presentation/http/middlewares/authRequired.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ export default (authService: AuthService): preHandlerHookHandler => {
await authService.verifyAccessToken(token);

done();

return reply;
} catch (error) {
return reply
.code(StatusCodes.UNAUTHORIZED)
Expand Down
4 changes: 3 additions & 1 deletion src/presentation/http/middlewares/withUser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export default (authService: AuthService): preHandlerHookHandler => {
if (!notEmpty(authorizationHeader)) {
done();

return;
return reply;
}

/**
Expand All @@ -44,6 +44,8 @@ export default (authService: AuthService): preHandlerHookHandler => {
};
} finally {
done();

return reply;
}
};
};
44 changes: 44 additions & 0 deletions src/presentation/http/router/note.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,22 @@ interface AddNoteOptions {
content: JSON;
}

/**
* Payload for update note request
*/
interface UpdateNoteOptions {
/**
* Note public id
*/
id: NotePublicId;

/**
* New content
*/
content: JSON;
}


/**
* Interface for the note router.
*/
Expand Down Expand Up @@ -185,6 +201,34 @@ const NoteRouter: FastifyPluginCallback<NoteRouterOptions> = (fastify, opts, don
});
});

/**
* Updates note by id.
*/
fastify.patch<{
Body: UpdateNoteOptions,
Reply: {
updatedAt: Note['updatedAt'],
}
}>('/', {
preHandler: [
opts.middlewares.authRequired,
opts.middlewares.withUser,
],
}, async (request, reply) => {
/**
* @todo Validate request params
* @todo Check user access right
*/
const { id, content } = request.body;

const note = await noteService.updateNoteContentByPublicId(id, content);

return reply.send({
updatedAt: note.updatedAt,
});
});


/**
* Get note by custom hostname
*/
Expand Down
11 changes: 11 additions & 0 deletions src/repository/note.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,17 @@ export default class NoteRepository {
return await this.storage.createNote(options);
}

/**
* Update note content in a store using note public id
*
* @param publicId - note public id
* @param content - new content
* @returns Note on success, null on failure
*/
public async updateNoteContentByPublicId(publicId: NotePublicId, content: Note['content'] ): Promise<Note | null> {
return await this.storage.updateNoteContentByPublicId(publicId, content);
}

/**
* Gets note by id
*
Expand Down
36 changes: 36 additions & 0 deletions src/repository/storage/postgres/orm/sequelize/note.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,16 @@ export class NoteModel extends Model<InferAttributes<NoteModel>, InferCreationAt
* Note creator, user identifier, who created this note
*/
public declare creatorId: Note['creatorId'];

/**
* Time when note was created
*/
public declare createdAt: CreationOptional<Note['createdAt']>;

/**
* Last time when note was updated
*/
public declare updatedAt: CreationOptional<Note['updatedAt']>;
}


Expand Down Expand Up @@ -95,6 +105,8 @@ export default class NoteSequelizeStorage {
key: 'id',
},
},
createdAt: DataTypes.DATE,
updatedAt: DataTypes.DATE,
}, {
tableName: this.tableName,
sequelize: this.database,
Expand Down Expand Up @@ -154,6 +166,30 @@ export default class NoteSequelizeStorage {
return createdNote;
}

/**
* Update note content by public id
*
* @param publicId - note public id
* @param content - new content
* @returns Note on success, null on failure
*/
public async updateNoteContentByPublicId(publicId: NotePublicId, content: Note['content']): Promise<Note | null> {
const [affectedRowsCount, affectedRows] = await this.model.update({
content,
}, {
where: {
publicId,
},
returning: true,
});

if (affectedRowsCount !== 1) {
return null;
}

return affectedRows[0];
}

/**
* Gets note by id
*
Expand Down

0 comments on commit bef9dfa

Please sign in to comment.