Skip to content
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: add notes module #3

Merged
merged 3 commits into from
Nov 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions apps/api/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { AuthModule } from '@nx-next-nest-prisma-ory-template/auth';
import { DatabaseModule } from '@nx-next-nest-prisma-ory-template/database';
import { NotesModule } from '@nx-next-nest-prisma-ory-template/notes';
import { OpenTelemetryModule } from '@nx-next-nest-prisma-ory-template/opentelemetry';

@Module({
Expand All @@ -10,6 +11,7 @@ import { OpenTelemetryModule } from '@nx-next-nest-prisma-ory-template/opentelem
ConfigModule.forRoot(),
AuthModule,
DatabaseModule,
NotesModule,
],
controllers: [],
providers: [],
Expand Down
Empty file removed config/keto/namespaces/.gitkeep
Empty file.
4 changes: 4 additions & 0 deletions config/keto/namespaces/notes.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"id": 1,
"name": "notes"
}
1 change: 1 addition & 0 deletions config/oathkeeper/oathkeeper.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ access_rules:
- file:///etc/config/oathkeeper/rules/auth.yaml
- file:///etc/config/oathkeeper/rules/kratos.yaml
- file:///etc/config/oathkeeper/rules/dev.yaml
- file:///etc/config/oathkeeper/rules/api-notes.yaml

authenticators:
anonymous:
Expand Down
63 changes: 63 additions & 0 deletions config/oathkeeper/rules/api-notes.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#######################################
# Notes Access Rules #
#######################################
- id: "api:create-note:protected"
upstream:
preserve_host: true
url: "http://api:3100"
match:
url: http://api.nx-next-nest-prisma-ory-template.<127\.0\.0\.1\.sslip\.io|com>/notes
methods:
- POST
authenticators:
- handler: cookie_session
authorizer:
handler: allow
mutators:
- handler: id_token
errors:
- handler: redirect

- id: "api:note:protected"
upstream:
preserve_host: true
url: "http://api:3100"
match:
url: http://api.nx-next-nest-prisma-ory-template.<127\.0\.0\.1\.sslip\.io|com>/notes/<([0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})>
methods:
- GET
- PATCH
- DELETE
authenticators:
- handler: cookie_session
authorizer:
handler: remote_json
config:
payload: |
{
"namespace": "notes",
"object": "{{ printIndex .MatchContext.RegexpCaptureGroups 1 }}",
"relation": "owner",
"subject_id": "{{ print .Subject }}"
}
mutators:
- handler: id_token
errors:
- handler: redirect

- id: "api:list-note:protected"
upstream:
preserve_host: true
url: "http://api:3100"
match:
url: http://api.nx-next-nest-prisma-ory-template.<127\.0\.0\.1\.sslip\.io|com>/notes
methods:
- GET
authenticators:
- handler: cookie_session
authorizer:
handler: allow
mutators:
- handler: id_token
errors:
- handler: redirect
1 change: 1 addition & 0 deletions docker-compose.base.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ services:
- ./config/oathkeeper/id_token.jwks.json:/etc/config/oathkeeper/id_token.jwks.json
- ./config/oathkeeper/rules/auth.yaml:/etc/config/oathkeeper/rules/auth.yaml
- ./config/oathkeeper/rules/kratos.yaml:/etc/config/oathkeeper/rules/kratos.yaml
- ./config/oathkeeper/rules/api-notes.yaml:/etc/config/oathkeeper/rules/api-notes.yaml
depends_on:
- kratos

Expand Down
18 changes: 18 additions & 0 deletions packages/notes/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"extends": ["../../.eslintrc.json"],
"ignorePatterns": ["!**/*"],
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
"rules": {}
},
{
"files": ["*.ts", "*.tsx"],
"rules": {}
},
{
"files": ["*.js", "*.jsx"],
"rules": {}
}
]
}
11 changes: 11 additions & 0 deletions packages/notes/jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/* eslint-disable */
export default {
displayName: 'notes',
preset: '../../jest.preset.js',
testEnvironment: 'node',
transform: {
'^.+\\.[tj]s$': ['ts-jest', { tsconfig: '<rootDir>/tsconfig.spec.json' }],
},
moduleFileExtensions: ['ts', 'js', 'html'],
coverageDirectory: '../../coverage/packages/notes',
};
23 changes: 23 additions & 0 deletions packages/notes/project.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"name": "notes",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "packages/notes/src",
"projectType": "library",
"targets": {
"lint": {
"executor": "@nx/eslint:lint",
"outputs": ["{options.outputFile}"],
"options": {
"lintFilePatterns": ["packages/notes/**/*.ts"]
}
},
"test": {
"executor": "@nx/jest:jest",
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
"options": {
"jestConfig": "packages/notes/jest.config.ts"
}
}
},
"tags": []
}
1 change: 1 addition & 0 deletions packages/notes/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './lib/notes.module';
159 changes: 159 additions & 0 deletions packages/notes/src/lib/notes.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
import {
Body,
Controller,
NotFoundException,
Param,
Req,
RequestMethod,
} from '@nestjs/common';
import { ApiTags } from '@nestjs/swagger';
import { ApiRouteAuthenticated } from '@nx-next-nest-prisma-ory-template/utils';
import { NotesService } from './notes.service';
import { FastifyRequest } from 'fastify';
import {
CreateNoteDto,
Note,
NoteDto,
UpdateNoteDto,
} from '@nx-next-nest-prisma-ory-template/types';

@ApiTags('Notes')
@Controller('notes')
export class NotesController {
constructor(private notesService: NotesService) {}

@ApiRouteAuthenticated({
method: RequestMethod.POST,
operation: {
summary: 'Create a note',
description: 'Create a new note with the given name and type',
},
response: {
status: 201,
type: NoteDto,
},
})
create(
@Body() body: CreateNoteDto,
@Req() request: FastifyRequest
): Promise<NoteDto> {
return this.notesService.create(body, request.user.sub);
}

@ApiRouteAuthenticated({
method: RequestMethod.GET,
operation: {
summary: 'List all notes',
description: 'Returns the list of all notes (limited to 1000)',
},
response: {
status: 200,
schema: {
type: 'array',
items: {
type: 'object',
properties: {
id: {
type: 'string',
description: 'The note id',
example: 'b1774d2f-05f7-4ea4-b427-0d808bdca583',
},
title: {
type: 'string',
description: 'The note title',
example: 'My note',
},
body: {
type: 'string',
description: 'The note body',
example: 'This is the body of my note',
},
createdAt: {
type: 'string',
description: 'The note creation date',
example: '2021-03-11T20:43:06.000Z',
},
},
},
},
},
})
async list(@Req() request: FastifyRequest): Promise<Note[]> {
const notes = await this.notesService.list(request.user.sub);

if (!notes) {
throw new NotFoundException();
}

return notes;
}

@ApiRouteAuthenticated({
method: RequestMethod.GET,
path: ':noteId',
operation: {
summary: 'Get a specific note',
description: 'Returns the note with the given id',
},
response: {
status: 200,
type: NoteDto,
},
})
async get(@Param('noteId') noteId: string): Promise<NoteDto> {
const note = await this.notesService.get(noteId);

if (!note) {
throw new NotFoundException();
}

return note;
}

@ApiRouteAuthenticated({
method: RequestMethod.PATCH,
path: ':noteId',
operation: {
summary: 'Update a specific note',
description: 'Updates the note with the given id',
},
response: {
status: 200,
type: NoteDto,
},
})
async patch(
@Body() body: UpdateNoteDto,
@Param('noteId') noteId: string
): Promise<NoteDto> {
const note = await this.notesService.patch(noteId, body);

if (!note) {
throw new NotFoundException();
}

return note;
}

@ApiRouteAuthenticated({
method: RequestMethod.DELETE,
path: ':noteId',
operation: {
summary: 'Delete a specific note',
description: 'Deletes the note with the given id',
},
response: {
status: 200,
type: NoteDto,
},
})
async delete(@Param('noteId') noteId: string): Promise<NoteDto> {
const note = await this.notesService.delete(noteId);

if (!note) {
throw new NotFoundException();
}

return note;
}
}
13 changes: 13 additions & 0 deletions packages/notes/src/lib/notes.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Module } from '@nestjs/common';
import { AuthModule } from '@nx-next-nest-prisma-ory-template/auth';
import { DatabaseModule } from '@nx-next-nest-prisma-ory-template/database';
import { NotesController } from './notes.controller';
import { NotesService } from './notes.service';

@Module({
imports: [AuthModule, DatabaseModule],
controllers: [NotesController],
providers: [NotesService],
exports: [],
})
export class NotesModule {}
Loading
Loading