mirror of
https://github.com/wkulhanek/bonob.git
synced 2025-12-21 17:33:29 +01:00
AlbumQuery and ArtistQuery types on MusicService
This commit is contained in:
@@ -38,31 +38,35 @@ export type Album = {
|
||||
};
|
||||
|
||||
export type Paging = {
|
||||
_index?: number;
|
||||
_count?: number;
|
||||
_index: number;
|
||||
_count: number;
|
||||
};
|
||||
|
||||
export type Result<T> = {
|
||||
results: T[],
|
||||
total: number
|
||||
}
|
||||
results: T[];
|
||||
total: number;
|
||||
};
|
||||
|
||||
export function slice2<T>({ _index, _count }: Paging) {
|
||||
const i0 = _index || 0;
|
||||
const i1 = _count ? i0 + _count : undefined;
|
||||
return (things: T[]): [T[], number] => [things.slice(i0, i1), things.length]
|
||||
return (things: T[]): [T[], number] => [
|
||||
things.slice(_index, _index + _count),
|
||||
things.length,
|
||||
];
|
||||
}
|
||||
|
||||
export const asResult = <T>([results, total]: [T[], number]) => ({ results, total })
|
||||
export const asResult = <T>([results, total]: [T[], number]) => ({
|
||||
results,
|
||||
total,
|
||||
});
|
||||
|
||||
export type ArtistQuery = Paging
|
||||
|
||||
export type AlbumQuery = Paging & {
|
||||
artistId?: string
|
||||
}
|
||||
|
||||
export interface MusicLibrary {
|
||||
artists({ _index, _count }: Paging): Promise<Result<Artist>>;
|
||||
artists(q: ArtistQuery): Promise<Result<Artist>>;
|
||||
artist(id: string): Artist;
|
||||
albums({
|
||||
artistId,
|
||||
_index,
|
||||
_count,
|
||||
}: {
|
||||
artistId?: string;
|
||||
} & Paging): Promise<Result<Album>>;
|
||||
albums(q: AlbumQuery): Promise<Result<Album>>;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Md5 } from "ts-md5/dist/md5";
|
||||
import { Credentials, MusicService, Paging, Album, Artist, Result, slice2, asResult } from "./music_service";
|
||||
import { Credentials, MusicService, Album, Artist, Result, slice2, asResult, AlbumQuery, ArtistQuery } from "./music_service";
|
||||
import X2JS from "x2js";
|
||||
|
||||
import axios from "axios";
|
||||
@@ -98,25 +98,20 @@ export class Navidrome implements MusicService {
|
||||
const navidrome = this;
|
||||
const credentials: Credentials = this.parseToken(token);
|
||||
return Promise.resolve({
|
||||
artists: (paging: Paging): Promise<Result<Artist>> =>
|
||||
artists: (q: ArtistQuery): Promise<Result<Artist>> =>
|
||||
navidrome
|
||||
.get<GetArtistsResponse>(credentials, "/rest/getArtists")
|
||||
.then((it) => it.artists.index.flatMap((it) => it.artist))
|
||||
.then((artists) =>
|
||||
artists.map((it) => ({ id: it._id, name: it._name }))
|
||||
)
|
||||
.then(slice2(paging))
|
||||
.then(slice2(q))
|
||||
.then(asResult),
|
||||
artist: (id: string) => ({
|
||||
id,
|
||||
name: id,
|
||||
}),
|
||||
albums: ({
|
||||
artistId,
|
||||
}: {
|
||||
artistId?: string;
|
||||
} & Paging): Promise<Result<Album>> => {
|
||||
console.log(artistId);
|
||||
albums: (_: AlbumQuery): Promise<Result<Album>> => {
|
||||
return Promise.resolve({ results: [], total: 0});
|
||||
},
|
||||
});
|
||||
|
||||
@@ -66,7 +66,7 @@ describe("InMemoryMusicService", () => {
|
||||
{ id: BLONDIE.id, name: BLONDIE.name },
|
||||
{ id: METALLICA.id, name: METALLICA.name },
|
||||
];
|
||||
expect(await musicLibrary.artists({})).toEqual({
|
||||
expect(await musicLibrary.artists({ _index: 0, _count: 100 })).toEqual({
|
||||
results: artists,
|
||||
total: 4,
|
||||
});
|
||||
@@ -126,7 +126,7 @@ describe("InMemoryMusicService", () => {
|
||||
describe("albums", () => {
|
||||
describe("fetching with no filtering", () => {
|
||||
it("should return all the albums for all the artists", async () => {
|
||||
expect(await musicLibrary.albums({})).toEqual({
|
||||
expect(await musicLibrary.albums({ _index: 0, _count: 100 })).toEqual({
|
||||
results: ALL_ALBUMS,
|
||||
total: ALL_ALBUMS.length,
|
||||
});
|
||||
@@ -135,56 +135,27 @@ describe("InMemoryMusicService", () => {
|
||||
|
||||
describe("fetching for a single artist", () => {
|
||||
it("should return them all if the artist has some", async () => {
|
||||
expect(await musicLibrary.albums({ artistId: BLONDIE.id })).toEqual({
|
||||
expect(await musicLibrary.albums({ artistId: BLONDIE.id, _index: 0, _count: 100 })).toEqual({
|
||||
results: BLONDIE.albums,
|
||||
total: BLONDIE.albums.length,
|
||||
});
|
||||
});
|
||||
|
||||
it("should return empty list of the artists does not have any", async () => {
|
||||
expect(await musicLibrary.albums({ artistId: MADONNA.id })).toEqual({
|
||||
expect(await musicLibrary.albums({ artistId: MADONNA.id, _index: 0, _count: 100 })).toEqual({
|
||||
results: [],
|
||||
total: 0,
|
||||
});
|
||||
});
|
||||
|
||||
it("should return empty list if the artist id is not valid", async () => {
|
||||
expect(await musicLibrary.albums({ artistId: uuid() })).toEqual({
|
||||
expect(await musicLibrary.albums({ artistId: uuid(), _index: 0, _count: 100 })).toEqual({
|
||||
results: [],
|
||||
total: 0,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("fetching with just index", () => {
|
||||
it("should return everything after", async () => {
|
||||
const albums = [
|
||||
BOB_MARLEY.albums[2],
|
||||
...BLONDIE.albums,
|
||||
...MADONNA.albums,
|
||||
...METALLICA.albums,
|
||||
];
|
||||
expect(await musicLibrary.albums({ _index: 2 })).toEqual({
|
||||
results: albums,
|
||||
total: ALL_ALBUMS.length,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("fetching with just count", () => {
|
||||
it("should return first n items", async () => {
|
||||
const albums = [
|
||||
BOB_MARLEY.albums[0],
|
||||
BOB_MARLEY.albums[1],
|
||||
BOB_MARLEY.albums[2],
|
||||
];
|
||||
expect(await musicLibrary.albums({ _count: 3 })).toEqual({
|
||||
results: albums,
|
||||
total: ALL_ALBUMS.length,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("fetching with index and count", () => {
|
||||
it("should be able to return the first page", async () => {
|
||||
const albums = [BOB_MARLEY.albums[0], BOB_MARLEY.albums[1]];
|
||||
|
||||
@@ -9,7 +9,8 @@ import {
|
||||
AuthFailure,
|
||||
Artist,
|
||||
MusicLibrary,
|
||||
Paging,
|
||||
ArtistQuery,
|
||||
AlbumQuery,
|
||||
slice2,
|
||||
asResult,
|
||||
} from "../src/music_service";
|
||||
@@ -57,9 +58,9 @@ export class InMemoryMusicService implements MusicService {
|
||||
if (this.users[credentials.username] != credentials.password)
|
||||
return Promise.reject("Invalid auth token");
|
||||
return Promise.resolve({
|
||||
artists: (paging: Paging) =>
|
||||
artists: (q: ArtistQuery) =>
|
||||
Promise.resolve(this.artists.map(artistWithAlbumsToArtist))
|
||||
.then(slice2(paging))
|
||||
.then(slice2(q))
|
||||
.then(asResult),
|
||||
artist: (id: string) =>
|
||||
pipe(
|
||||
@@ -68,26 +69,18 @@ export class InMemoryMusicService implements MusicService {
|
||||
O.map(artistWithAlbumsToArtist),
|
||||
getOrThrow(`No artist with id '${id}'`)
|
||||
),
|
||||
albums: ({
|
||||
artistId,
|
||||
_index,
|
||||
_count,
|
||||
}: {
|
||||
artistId?: string;
|
||||
_index?: number;
|
||||
_count?: number;
|
||||
}) =>
|
||||
albums: (q: AlbumQuery) =>
|
||||
Promise.resolve(
|
||||
this.artists.filter(
|
||||
pipe(
|
||||
O.fromNullable(artistId),
|
||||
O.fromNullable(q.artistId),
|
||||
O.map(artistWithId),
|
||||
O.getOrElse(() => all)
|
||||
)
|
||||
)
|
||||
)
|
||||
.then((artists) => artists.flatMap((it) => it.albums))
|
||||
.then(slice2({ _index, _count }))
|
||||
.then(slice2(q))
|
||||
.then(asResult),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -101,12 +101,12 @@ describe("navidrome", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("when no paging specified", () => {
|
||||
describe("when no paging is ineffect", () => {
|
||||
it("should return all the artists", async () => {
|
||||
const artists = await navidrome
|
||||
.generateToken({ username, password })
|
||||
.then((it) => navidrome.login(it.authToken))
|
||||
.then((it) => it.artists({}));
|
||||
.then((it) => it.artists({ _index: 0, _count: 100 }));
|
||||
|
||||
const expectedArtists = [
|
||||
{ id: "2911b2d67a6b11eb804dd360a6225680", name: "10 Planets" },
|
||||
|
||||
Reference in New Issue
Block a user