mirror of
https://github.com/wkulhanek/bonob.git
synced 2025-12-21 17:33:29 +01:00
Add slice2 and asResult functions
This commit is contained in:
@@ -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>>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
10
src/smapi.ts
10
src/smapi.ts
@@ -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,
|
||||||
|
|||||||
@@ -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,
|
||||||
]);
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user