Add slice2 and asResult functions

This commit is contained in:
simojenki
2021-03-02 10:16:56 +11:00
parent 3aa1056aa5
commit bdee01d2c7
6 changed files with 86 additions and 74 deletions

View File

@@ -42,8 +42,21 @@ export type Paging = {
_count?: number; _count?: number;
}; };
export type Result<T> = {
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]
}
export const asResult = <T>([results, total]: [T[], number]) => ({ results, total })
export interface MusicLibrary { export interface MusicLibrary {
artists({ _index, _count }: Paging): Promise<[Artist[], number]>; artists({ _index, _count }: Paging): Promise<Result<Artist>>;
artist(id: string): Artist; artist(id: string): Artist;
albums({ albums({
artistId, artistId,
@@ -51,5 +64,5 @@ export interface MusicLibrary {
_count, _count,
}: { }: {
artistId?: string; artistId?: string;
} & Paging): Promise<[Album[], number]>; } & Paging): Promise<Result<Album>>;
} }

View File

@@ -1,5 +1,5 @@
import { Md5 } from "ts-md5/dist/md5"; import { Md5 } from "ts-md5/dist/md5";
import { Credentials, MusicService, Paging, Album, Artist } from "./music_service"; import { Credentials, MusicService, Paging, Album, Artist, Result, slice2, asResult } from "./music_service";
import X2JS from "x2js"; import X2JS from "x2js";
import axios from "axios"; import axios from "axios";
@@ -98,18 +98,15 @@ export class Navidrome implements MusicService {
const navidrome = this; const navidrome = this;
const credentials: Credentials = this.parseToken(token); const credentials: Credentials = this.parseToken(token);
return Promise.resolve({ return Promise.resolve({
artists: ({ _index, _count }: Paging): Promise<[Artist[], number]> => artists: (paging: Paging): Promise<Result<Artist>> =>
navidrome navidrome
.get<GetArtistsResponse>(credentials, "/rest/getArtists") .get<GetArtistsResponse>(credentials, "/rest/getArtists")
.then((it) => it.artists.index.flatMap((it) => it.artist)) .then((it) => it.artists.index.flatMap((it) => it.artist))
.then((artists) => .then((artists) =>
artists.map((it) => ({ id: it._id, name: it._name })) artists.map((it) => ({ id: it._id, name: it._name }))
) )
.then((artists) => { .then(slice2(paging))
const i0 = _index || 0; .then(asResult),
const i1 = _count ? i0 + _count : undefined;
return [artists.slice(i0, i1), artists.length];
}),
artist: (id: string) => ({ artist: (id: string) => ({
id, id,
name: id, name: id,
@@ -118,9 +115,9 @@ export class Navidrome implements MusicService {
artistId, artistId,
}: { }: {
artistId?: string; artistId?: string;
} & Paging): Promise<[Album[], number]> => { } & Paging): Promise<Result<Album>> => {
console.log(artistId); console.log(artistId);
return Promise.resolve([[], 0]); return Promise.resolve({ results: [], total: 0});
}, },
}); });
} }

View File

@@ -6,7 +6,7 @@ import path from "path";
import logger from "./logger"; import logger from "./logger";
import { LinkCodes } from "./link_codes"; import { LinkCodes } from "./link_codes";
import { MusicLibrary, MusicService } from "./music_service"; import { Artist, MusicLibrary, MusicService } from "./music_service";
export const LOGIN_ROUTE = "/login"; export const LOGIN_ROUTE = "/login";
export const SOAP_PATH = "/ws/sonos"; export const SOAP_PATH = "/ws/sonos";
@@ -227,9 +227,9 @@ function bindSmapiSoapServiceToExpress(
case "artists": case "artists":
return await musicLibrary return await musicLibrary
.artists(paging) .artists(paging)
.then(([artists, total]) => .then(({ results, total }: { results: Artist[], total: number}) =>
getMetadataResult({ getMetadataResult({
mediaCollection: artists.map((it) => mediaCollection: results.map((it) =>
container({ container({
id: `artist:${it.id}`, id: `artist:${it.id}`,
title: it.name, title: it.name,
@@ -242,9 +242,9 @@ function bindSmapiSoapServiceToExpress(
case "albums": case "albums":
return await musicLibrary return await musicLibrary
.albums(paging) .albums(paging)
.then(([albums, total]) => .then(({ results, total }: { results: Artist[], total: number}) =>
getMetadataResult({ getMetadataResult({
mediaCollection: albums.map((it) => mediaCollection: results.map((it) =>
container({ container({
id: `album:${it.id}`, id: `album:${it.id}`,
title: it.name, title: it.name,

View File

@@ -66,7 +66,10 @@ describe("InMemoryMusicService", () => {
{ id: BLONDIE.id, name: BLONDIE.name }, { id: BLONDIE.id, name: BLONDIE.name },
{ id: METALLICA.id, name: METALLICA.name }, { id: METALLICA.id, name: METALLICA.name },
]; ];
expect(await musicLibrary.artists({})).toEqual([artists, 4]); expect(await musicLibrary.artists({})).toEqual({
results: artists,
total: 4,
});
}); });
}); });
@@ -76,10 +79,10 @@ describe("InMemoryMusicService", () => {
{ id: BLONDIE.id, name: BLONDIE.name }, { id: BLONDIE.id, name: BLONDIE.name },
{ id: METALLICA.id, name: METALLICA.name }, { id: METALLICA.id, name: METALLICA.name },
]; ];
expect(await musicLibrary.artists({ _index: 2, _count: 2 })).toEqual([ expect(await musicLibrary.artists({ _index: 2, _count: 2 })).toEqual({
artists, results: artists,
4, total: 4,
]); });
}); });
}); });
@@ -92,7 +95,7 @@ describe("InMemoryMusicService", () => {
]; ];
expect( expect(
await musicLibrary.artists({ _index: 1, _count: 50 }) await musicLibrary.artists({ _index: 1, _count: 50 })
).toEqual([artists, 4]); ).toEqual({ results: artists, total: 4 });
}); });
}); });
}); });
@@ -123,33 +126,33 @@ describe("InMemoryMusicService", () => {
describe("albums", () => { describe("albums", () => {
describe("fetching with no filtering", () => { describe("fetching with no filtering", () => {
it("should return all the albums for all the artists", async () => { it("should return all the albums for all the artists", async () => {
expect(await musicLibrary.albums({})).toEqual([ expect(await musicLibrary.albums({})).toEqual({
ALL_ALBUMS, results: ALL_ALBUMS,
ALL_ALBUMS.length, total: ALL_ALBUMS.length,
]); });
}); });
}); });
describe("fetching for a single artist", () => { describe("fetching for a single artist", () => {
it("should return them all if the artist has some", async () => { 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 })).toEqual({
BLONDIE.albums, results: BLONDIE.albums,
BLONDIE.albums.length, total: BLONDIE.albums.length,
]); });
}); });
it("should return empty list of the artists does not have any", async () => { 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 })).toEqual({
[], results: [],
0, total: 0,
]); });
}); });
it("should return empty list if the artist id is not valid", async () => { 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() })).toEqual({
[], results: [],
0, total: 0,
]); });
}); });
}); });
@@ -161,10 +164,10 @@ describe("InMemoryMusicService", () => {
...MADONNA.albums, ...MADONNA.albums,
...METALLICA.albums, ...METALLICA.albums,
]; ];
expect(await musicLibrary.albums({ _index: 2 })).toEqual([ expect(await musicLibrary.albums({ _index: 2 })).toEqual({
albums, results: albums,
ALL_ALBUMS.length, total: ALL_ALBUMS.length,
]); });
}); });
}); });
@@ -175,33 +178,33 @@ describe("InMemoryMusicService", () => {
BOB_MARLEY.albums[1], BOB_MARLEY.albums[1],
BOB_MARLEY.albums[2], BOB_MARLEY.albums[2],
]; ];
expect(await musicLibrary.albums({ _count: 3 })).toEqual([ expect(await musicLibrary.albums({ _count: 3 })).toEqual({
albums, results: albums,
ALL_ALBUMS.length, total: ALL_ALBUMS.length,
]); });
}); });
}); });
describe("fetching with index and count", () => { describe("fetching with index and count", () => {
it("should be able to return the first page", async () => { it("should be able to return the first page", async () => {
const albums = [BOB_MARLEY.albums[0], BOB_MARLEY.albums[1]]; const albums = [BOB_MARLEY.albums[0], BOB_MARLEY.albums[1]];
expect(await musicLibrary.albums({ _index: 0, _count: 2 })).toEqual([ expect(await musicLibrary.albums({ _index: 0, _count: 2 })).toEqual({
albums, results: albums,
ALL_ALBUMS.length, total: ALL_ALBUMS.length,
]); });
}); });
it("should be able to return the second page", async () => { it("should be able to return the second page", async () => {
const albums = [BOB_MARLEY.albums[2], BLONDIE.albums[0]]; const albums = [BOB_MARLEY.albums[2], BLONDIE.albums[0]];
expect(await musicLibrary.albums({ _index: 2, _count: 2 })).toEqual([ expect(await musicLibrary.albums({ _index: 2, _count: 2 })).toEqual({
albums, results: albums,
ALL_ALBUMS.length, total: ALL_ALBUMS.length,
]); });
}); });
it("should be able to return the last page", async () => { it("should be able to return the last page", async () => {
expect(await musicLibrary.albums({ _index: 5, _count: 2 })).toEqual([ expect(await musicLibrary.albums({ _index: 5, _count: 2 })).toEqual({
METALLICA.albums, results: METALLICA.albums,
ALL_ALBUMS.length, total: ALL_ALBUMS.length,
]); });
}); });
}); });
}); });

View File

@@ -10,6 +10,8 @@ import {
Artist, Artist,
MusicLibrary, MusicLibrary,
Paging, Paging,
slice2,
asResult,
} from "../src/music_service"; } from "../src/music_service";
const artistWithAlbumsToArtist = (it: ArtistWithAlbums): Artist => ({ const artistWithAlbumsToArtist = (it: ArtistWithAlbums): Artist => ({
@@ -55,12 +57,10 @@ export class InMemoryMusicService implements MusicService {
if (this.users[credentials.username] != credentials.password) if (this.users[credentials.username] != credentials.password)
return Promise.reject("Invalid auth token"); return Promise.reject("Invalid auth token");
return Promise.resolve({ return Promise.resolve({
artists: ({ _index, _count }: Paging) => { artists: (paging: Paging) =>
const i0 = _index || 0; Promise.resolve(this.artists.map(artistWithAlbumsToArtist))
const i1 = _count ? i0 + _count : undefined; .then(slice2(paging))
const artists = this.artists.map(artistWithAlbumsToArtist); .then(asResult),
return Promise.resolve([artists.slice(i0, i1), artists.length]);
},
artist: (id: string) => artist: (id: string) =>
pipe( pipe(
this.artists.find((it) => it.id === id), this.artists.find((it) => it.id === id),
@@ -76,20 +76,19 @@ export class InMemoryMusicService implements MusicService {
artistId?: string; artistId?: string;
_index?: number; _index?: number;
_count?: number; _count?: number;
}) => { }) =>
const i0 = _index || 0; Promise.resolve(
const i1 = _count ? i0 + _count : undefined; this.artists.filter(
const albums = this.artists
.filter(
pipe( pipe(
O.fromNullable(artistId), O.fromNullable(artistId),
O.map(artistWithId), O.map(artistWithId),
O.getOrElse(() => all) O.getOrElse(() => all)
) )
) )
.flatMap((it) => it.albums); )
return Promise.resolve([albums.slice(i0, i1), albums.length]); .then((artists) => artists.flatMap((it) => it.albums))
}, .then(slice2({ _index, _count }))
.then(asResult),
}); });
} }

View File

@@ -114,7 +114,7 @@ describe("navidrome", () => {
{ id: "3c5113007a6b11eb87173bfb9b07f9b1", name: "AAAB" }, { id: "3c5113007a6b11eb87173bfb9b07f9b1", name: "AAAB" },
{ id: "3ca781c27a6b11eb897ebbb5773603ad", name: "BAAB" }, { id: "3ca781c27a6b11eb897ebbb5773603ad", name: "BAAB" },
]; ];
expect(artists).toEqual([expectedArtists, 4]); expect(artists).toEqual({ results: expectedArtists, total: 4 });
expect(axios.get).toHaveBeenCalledWith(`${url}/rest/getArtists`, { expect(axios.get).toHaveBeenCalledWith(`${url}/rest/getArtists`, {
params: authParams, params: authParams,
@@ -133,7 +133,7 @@ describe("navidrome", () => {
{ id: "3c0b9d7a7a6b11eb9773f398e6236ad6", name: "1200 Ounces" }, { id: "3c0b9d7a7a6b11eb9773f398e6236ad6", name: "1200 Ounces" },
{ id: "3c5113007a6b11eb87173bfb9b07f9b1", name: "AAAB" }, { id: "3c5113007a6b11eb87173bfb9b07f9b1", name: "AAAB" },
]; ];
expect(artists).toEqual([expectedArtists, 4]); expect(artists).toEqual({ results: expectedArtists, total: 4 });
expect(axios.get).toHaveBeenCalledWith(`${url}/rest/getArtists`, { expect(axios.get).toHaveBeenCalledWith(`${url}/rest/getArtists`, {
params: authParams, params: authParams,