Compare commits
5 commits
fbaed2c1d6
...
02c2f37379
| Author | SHA1 | Date | |
|---|---|---|---|
| 02c2f37379 | |||
| a2b747a823 | |||
| fd2f2091ee | |||
| ab5bb50d7b | |||
| 98f4e451a9 |
6 changed files with 84 additions and 15 deletions
|
|
@ -14,7 +14,7 @@
|
||||||
};
|
};
|
||||||
packages = rec {
|
packages = rec {
|
||||||
default = shows-api;
|
default = shows-api;
|
||||||
shows-api = pkgs.callPackage ./package.nix {};
|
shows-api = pkgs.callPackage ./package.nix { };
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
||||||
10
package.nix
10
package.nix
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{ lib
|
||||||
lib,
|
, buildNpmPackage
|
||||||
buildNpmPackage,
|
, nodejs
|
||||||
nodejs,
|
,
|
||||||
}:
|
}:
|
||||||
buildNpmPackage {
|
buildNpmPackage {
|
||||||
name = "shows-api";
|
name = "shows-api";
|
||||||
|
|
@ -28,7 +28,7 @@ buildNpmPackage {
|
||||||
meta = {
|
meta = {
|
||||||
description = "NestJS API to store info about shows on a MongoDB database";
|
description = "NestJS API to store info about shows on a MongoDB database";
|
||||||
homepage = "https://git.everest.tailscale/Toast/shows-api";
|
homepage = "https://git.everest.tailscale/Toast/shows-api";
|
||||||
sourceProvenance = [lib.sourceTypes.fromSource];
|
sourceProvenance = [ lib.sourceTypes.fromSource ];
|
||||||
mainProgram = "shows-api";
|
mainProgram = "shows-api";
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
28
src/main.ts
28
src/main.ts
|
|
@ -1,6 +1,7 @@
|
||||||
import { NestFactory } from '@nestjs/core';
|
import { NestFactory } from '@nestjs/core';
|
||||||
import { AppModule } from './app.module';
|
import { AppModule } from './app.module';
|
||||||
import { Logger } from '@nestjs/common';
|
import { BadRequestException, Logger, ValidationPipe } from '@nestjs/common';
|
||||||
|
import { ValidationError } from 'class-validator';
|
||||||
|
|
||||||
async function bootstrap() {
|
async function bootstrap() {
|
||||||
const app = await NestFactory.create(AppModule);
|
const app = await NestFactory.create(AppModule);
|
||||||
|
|
@ -17,6 +18,31 @@ async function bootstrap() {
|
||||||
credentials: true,
|
credentials: true,
|
||||||
origin,
|
origin,
|
||||||
});
|
});
|
||||||
|
app.useGlobalPipes(
|
||||||
|
new ValidationPipe({
|
||||||
|
transform: true,
|
||||||
|
// https://stackoverflow.com/questions/75581669/customize-error-message-in-nest-js-using-class-validator
|
||||||
|
exceptionFactory: (validationErrors: ValidationError[] = []) => {
|
||||||
|
const errors = validationErrors.map((error) => ({
|
||||||
|
error: Object.values(error.constraints).join(', '),
|
||||||
|
}));
|
||||||
|
|
||||||
|
let errorMessage: string;
|
||||||
|
errors.forEach((value, index) => {
|
||||||
|
if (index == 0) {
|
||||||
|
errorMessage = value.error;
|
||||||
|
} else {
|
||||||
|
errorMessage = errorMessage.concat(', ', value.error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return new BadRequestException({
|
||||||
|
status: 'Validation Error',
|
||||||
|
message: errorMessage,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
await app.listen(process.env.PORT);
|
await app.listen(process.env.PORT);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
15
src/pagination.dto.ts
Normal file
15
src/pagination.dto.ts
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
import { Type } from "class-transformer";
|
||||||
|
import { IsNumber, IsOptional, Min } from "class-validator";
|
||||||
|
|
||||||
|
export class PaginationDto {
|
||||||
|
@IsOptional()
|
||||||
|
@IsNumber()
|
||||||
|
@Type(() => Number)
|
||||||
|
@Min(1)
|
||||||
|
page?: number = 1;
|
||||||
|
@IsOptional()
|
||||||
|
@IsNumber()
|
||||||
|
@Type(() => Number)
|
||||||
|
@Min(1)
|
||||||
|
limit?: number = 1;
|
||||||
|
}
|
||||||
|
|
@ -13,6 +13,14 @@ import {
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { ShowsService } from './shows.service';
|
import { ShowsService } from './shows.service';
|
||||||
import { ShowDto } from './dto/show.dto';
|
import { ShowDto } from './dto/show.dto';
|
||||||
|
import { PaginationDto } from 'src/pagination.dto';
|
||||||
|
import { IsNotEmpty, IsString } from 'class-validator';
|
||||||
|
|
||||||
|
export class SearchDto {
|
||||||
|
@IsString()
|
||||||
|
@IsNotEmpty()
|
||||||
|
query: string;
|
||||||
|
}
|
||||||
|
|
||||||
@Controller('shows')
|
@Controller('shows')
|
||||||
export class ShowsController {
|
export class ShowsController {
|
||||||
|
|
@ -36,10 +44,15 @@ export class ShowsController {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get()
|
@Get()
|
||||||
async findAll() {
|
async findAll(@Query() pagination: PaginationDto) {
|
||||||
try {
|
try {
|
||||||
const shows = await this.showsService.findAll();
|
const { page, limit } = pagination;
|
||||||
return { status: 'Ok', shows, totalShows: shows.length };
|
const serviceResponse = await this.showsService.findAll(page, limit);
|
||||||
|
return {
|
||||||
|
status: 'Ok',
|
||||||
|
shows: serviceResponse.shows,
|
||||||
|
showCount: serviceResponse.showCount,
|
||||||
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new InternalServerErrorException({
|
throw new InternalServerErrorException({
|
||||||
status: error.name,
|
status: error.name,
|
||||||
|
|
@ -72,15 +85,16 @@ export class ShowsController {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get('search')
|
@Get('search')
|
||||||
async search(@Query('query') name: string) {
|
async search(@Query() search: SearchDto) {
|
||||||
try {
|
try {
|
||||||
const shows = await this.showsService.search(name);
|
const { query } = search;
|
||||||
|
const shows = await this.showsService.search(query);
|
||||||
if (shows.length > 0) {
|
if (shows.length > 0) {
|
||||||
return { status: 'Ok', show: shows, test: shows.length };
|
return { status: 'Ok', show: shows, test: shows.length };
|
||||||
} else {
|
} else {
|
||||||
throw new NotFoundException({
|
throw new NotFoundException({
|
||||||
status: 'Error',
|
status: 'Error',
|
||||||
message: `Can't find show matching ${name}`,
|
message: `Can't find show matching ${query}`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,22 @@ export class ShowsService {
|
||||||
return show.save();
|
return show.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
async findAll(): Promise<Show[]> {
|
async findAll(page: number, showsPerPage: number): Promise<any> {
|
||||||
return this.showModel.find();
|
// Default value is 1 and I can't think any reason why you would want
|
||||||
|
// a single item per page, so if showsPerPage is 1 then just don't do
|
||||||
|
// any pagination at all
|
||||||
|
let shows;
|
||||||
|
if (showsPerPage != 1) {
|
||||||
|
const skip = (page - 1) * showsPerPage;
|
||||||
|
shows = await this.showModel.find().skip(skip).limit(showsPerPage);
|
||||||
|
} else {
|
||||||
|
shows = await this.showModel.find();
|
||||||
|
}
|
||||||
|
const total = await this.showModel.countDocuments();
|
||||||
|
return {
|
||||||
|
shows: shows,
|
||||||
|
showCount: total,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async findId(id: string): Promise<any> {
|
async findId(id: string): Promise<any> {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue