Skip to content

Commit

Permalink
Song Controller: Get Song from id or slug
Browse files Browse the repository at this point in the history
  • Loading branch information
Arthi-chaud committed Jul 15, 2022
2 parents f4fb03d + 27ca86b commit 8ff0231
Show file tree
Hide file tree
Showing 9 changed files with 95 additions and 40 deletions.
13 changes: 7 additions & 6 deletions src/album/album.pipe.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,25 @@
import type { ArgumentMetadata, PipeTransform } from "@nestjs/common";
import { InvalidRequestException } from "src/exceptions/meelo-exception";
import { ParseIdPipe } from "src/identifier/id.pipe";
import ParseMultipleSlugPipe from "src/identifier/identifier.parse-slugs";
import { SlugSeparator } from "src/identifier/identifier.slug-separator";
import Slug from "src/slug/slug";
import compilationAlbumArtistKeyword from "src/utils/compilation";
import type AlbumQueryParameters from "./models/album.query-parameters";

export default class ParseAlbumIdentifierPipe implements PipeTransform {
transform<T extends { idOrSlug: string; }>(value: T, _metadata: ArgumentMetadata): AlbumQueryParameters.WhereInput {
try {
return { byId: { id: new ParseIdPipe().transform(value.idOrSlug, _metadata) }};
} catch {
const slugs = value.idOrSlug.split(SlugSeparator);
const slugs = new ParseMultipleSlugPipe().transform(value.idOrSlug, _metadata);
if (slugs.length != 2)
throw new InvalidRequestException(`Expected the following string format: 'artist-slug${SlugSeparator}album-slug'`);
return {
bySlug: {
slug: new Slug(slugs[1]),
artist: {
slug: new Slug(slugs[0]),
}
slug: slugs[1],
artist: slugs[0].toString() == compilationAlbumArtistKeyword
? undefined
: { slug: slugs[0] }
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/artist/artist.pipe.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import type { ArgumentMetadata } from "@nestjs/common";
import ParseResourceIdentifierPipe from "src/identifier/identifier.pipe";
import ParseBaseIdentifierPipe from "src/identifier/identifier.base-pipe";
import compilationAlbumArtistKeyword from "src/utils/compilation";
import type ArtistQueryParameters from "./models/artist.query-parameters";

class ParseArtistIdentifierPipe extends ParseResourceIdentifierPipe<ArtistQueryParameters.WhereInput> {
class ParseArtistIdentifierPipe extends ParseBaseIdentifierPipe<ArtistQueryParameters.WhereInput> {
transform<T extends { idOrSlug: any; }>(value: T, _metadata: ArgumentMetadata): ArtistQueryParameters.WhereInput {
const transformedIdentifier = super.transform(value, _metadata);
if (transformedIdentifier.slug?.toString() == compilationAlbumArtistKeyword) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { ArgumentMetadata, PipeTransform } from "@nestjs/common";
import { ParseIdPipe } from "src/identifier/id.pipe";
import Slug from "src/slug/slug";

export default class ParseResourceIdentifierPipe<W extends Partial<{ id: number, slug: Slug}>> implements PipeTransform {
export default class ParseBaseIdentifierPipe<W extends Partial<{ id: number, slug: Slug }>> implements PipeTransform {
transform<T extends { idOrSlug: any }>(value: T, _metadata: ArgumentMetadata): W {
try {
const id = new ParseIdPipe().transform(value.idOrSlug, _metadata);
Expand Down
10 changes: 10 additions & 0 deletions src/identifier/identifier.parse-slugs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import type { ArgumentMetadata, PipeTransform } from "@nestjs/common";
import Slug from "src/slug/slug";
import { SlugSeparator } from "./identifier.slug-separator";

export default class ParseMultipleSlugPipe implements PipeTransform {
transform(value: any, _metadata: ArgumentMetadata): Slug[] {
const slugs = value.split(SlugSeparator).map((slugString: string) => new Slug(slugString));
return slugs;
}
};
4 changes: 2 additions & 2 deletions src/library/library.pipe.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import ParseResourceIdentifierPipe from "src/identifier/identifier.pipe";
import ParseBaseIdentifierPipe from "src/identifier/identifier.base-pipe";
import type LibraryQueryParameters from "./models/library.query-parameters";

class ParseLibraryIdentifierPipe extends ParseResourceIdentifierPipe<LibraryQueryParameters.WhereInput> {};
class ParseLibraryIdentifierPipe extends ParseBaseIdentifierPipe<LibraryQueryParameters.WhereInput> {};
export default ParseLibraryIdentifierPipe;
24 changes: 24 additions & 0 deletions src/song/song.controller.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,18 @@ describe('Song Controller', () => {
});
});
});
it("should return song (w/ slug)", () => {
return request(app.getHttpServer())
.get(`/songs/${artist.slug}+${song1.slug}`)
.expect(200)
.expect((res) => {
let song: Song = res.body
expect(song).toStrictEqual({
...song1,
illustration: `http://meelo.com/songs/${song1.id}/illustration`,
});
});
});
it("should return song w/ artist", () => {
return request(app.getHttpServer())
.get(`/songs/${song1.id}?with=artist`)
Expand Down Expand Up @@ -359,6 +371,18 @@ describe('Song Controller', () => {
});
});
});
it("should return artist", () => {
return request(app.getHttpServer())
.get(`/songs/${artist.slug}+${song2.slug}/artist`)
.expect(200)
.expect((res) => {
let fetchedArtist : Artist = res.body
expect(fetchedArtist).toStrictEqual({
...artist,
illustration: `http://meelo.com/artists/${artist.id}/illustration`
});
});
});
it("should return artist w/ songs & albums", () => {
return request(app.getHttpServer())
.get(`/songs/${song2.id}/artist?with=songs,albums`)
Expand Down
50 changes: 22 additions & 28 deletions src/song/song.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ import { Controller, forwardRef, Get, Inject, Param, Query, Redirect } from '@ne
import { UrlGeneratorService } from 'nestjs-url-generator';
import ArtistService from 'src/artist/artist.service';
import ArtistQueryParameters from 'src/artist/models/artist.query-parameters';
import { ParseIdPipe } from 'src/identifier/id.pipe';
import type { PaginationParameters } from 'src/pagination/models/pagination-parameters';
import ParsePaginationParameterPipe from 'src/pagination/pagination.pipe';
import TrackQueryParameters from 'src/track/models/track.query-parameters';
import { TrackController } from 'src/track/track.controller';
import TrackService from 'src/track/track.service';
import SongQueryParameters from './models/song.query-params';
import ParseSongIdentifierPipe from './song.pipe';
import SongService from './song.service';


Expand Down Expand Up @@ -36,70 +36,64 @@ export class SongController {
return songs.map((song) => this.songService.buildSongResponse(song));
}

@Get(':id')
@Get(':idOrSlug')
async getSong(
@Query('with', SongQueryParameters.ParseRelationIncludePipe)
include: SongQueryParameters.RelationInclude,
@Param('id', ParseIdPipe)
songId: number
@Param(ParseSongIdentifierPipe)
where: SongQueryParameters.WhereInput
) {
let song = await this.songService.getSong({ byId: { id: songId } }, include);
let song = await this.songService.getSong(where, include);
return this.songService.buildSongResponse(song);
}

@Get(':id/artist')
@Get(':idOrSlug/artist')
async getSongArtist(
@Query('with', ArtistQueryParameters.ParseRelationIncludePipe)
include: ArtistQueryParameters.RelationInclude,
@Param('id', ParseIdPipe)
songId: number
@Param(ParseSongIdentifierPipe)
where: SongQueryParameters.WhereInput
) {
let song = await this.songService.getSong({ byId: { id: songId } });
let song = await this.songService.getSong(where);
let artist = await this.artistService.getArtist({
id: song.artistId
}, include);
return this.artistService.buildArtistResponse(artist);
}

@Get(':id/master')
@Get(':idOrSlug/master')
async getSongMaster(
@Query('with', TrackQueryParameters.ParseRelationIncludePipe)
include: TrackQueryParameters.RelationInclude,
@Param('id', ParseIdPipe)
songId: number
@Param(ParseSongIdentifierPipe)
where: SongQueryParameters.WhereInput
) {
let master = await this.trackService.getMasterTrack({
byId: { id: songId }
}, include);
let master = await this.trackService.getMasterTrack(where, include);
return this.trackService.buildTrackResponse(master);
}

@Get(':id/tracks')
@Get(':idOrSlug/tracks')
async getSongTracks(
@Query(ParsePaginationParameterPipe)
paginationParameters: PaginationParameters,
@Query('with', TrackQueryParameters.ParseRelationIncludePipe)
include: TrackQueryParameters.RelationInclude,
@Param('id', ParseIdPipe)
songId: number
@Param(ParseSongIdentifierPipe)
where: SongQueryParameters.WhereInput
) {
let tracks = await this.trackService.getSongTracks({
byId: { id: songId }
}, paginationParameters, include);
let tracks = await this.trackService.getSongTracks(where, paginationParameters, include);
if (tracks.length == 0)
await this.songService.getSong({ byId: { id: songId }});
await this.songService.getSong(where);
return tracks.map((track) => this.trackService.buildTrackResponse(track));
}

@Get(':id/illustration')
@Get(':idOrSlug/illustration')
@Redirect()
async getSongIllustration(
@Param('id', ParseIdPipe)
songId: number
@Param(ParseSongIdentifierPipe)
where: SongQueryParameters.WhereInput
) {
let master = await this.trackService.getMasterTrack({
byId: { id: songId }
});
let master = await this.trackService.getMasterTrack(where);
const illustrationRedirectUrl = this.urlGeneratorService.generateUrlFromController({
controller: TrackController,
controllerMethod: TrackController.prototype.getTrackIllustration,
Expand Down
26 changes: 26 additions & 0 deletions src/song/song.pipe.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import type { PipeTransform, ArgumentMetadata } from "@nestjs/common";
import { InvalidRequestException } from "src/exceptions/meelo-exception";
import { ParseIdPipe } from "src/identifier/id.pipe";
import ParseMultipleSlugPipe from "src/identifier/identifier.parse-slugs";
import { SlugSeparator } from "src/identifier/identifier.slug-separator";
import type SongQueryParameters from "./models/song.query-params";

export default class ParseSongIdentifierPipe implements PipeTransform {
transform<T extends { idOrSlug: string; }>(value: T, _metadata: ArgumentMetadata): SongQueryParameters.WhereInput {
try {
return { byId: { id: new ParseIdPipe().transform(value.idOrSlug, _metadata) }};
} catch {
const slugs = new ParseMultipleSlugPipe().transform(value.idOrSlug, _metadata);
if (slugs.length != 2)
throw new InvalidRequestException(`Expected the following string format: 'artist-slug${SlugSeparator}song-slug'`);
return {
bySlug: {
slug: slugs[1],
artist: {
slug: slugs[0],
}
}
}
}
}
};
2 changes: 1 addition & 1 deletion src/song/song.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ export default class SongService {
controller: SongController,
controllerMethod: SongController.prototype.getSongIllustration,
params: {
id: song.id.toString()
idOrSlug: song.id.toString()
}
})
};
Expand Down

0 comments on commit 8ff0231

Please sign in to comment.