You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
460 lines
14 KiB
460 lines
14 KiB
import { AlbumWithRefs, AlbumWithRefsWithId, ArtistWithRefs, ArtistWithRefsWithId, TagWithRefs, TagWithRefsWithId, TrackWithRefs, TrackWithRefsWithId } from "../../../client/src/api/api"; |
|
import { userEndpoints } from "../../endpoints/User"; |
|
import { createAlbum, createArtist, createTag, createTrack, deleteTrack, ReferenceDatabase } from "./DBReferenceModel"; |
|
import * as helpers from '../integration/helpers'; |
|
import { DBErrorKind, isDBError } from "../../endpoints/types"; |
|
|
|
export enum DBActionType { |
|
CreateTrack = "CreateTrack", |
|
CreateAlbum = "CreateAlbum", |
|
CreateTag = "CreateTag", |
|
CreateArtist = "CreateArtist", |
|
DeleteTrack = "DeleteTrack", |
|
} |
|
|
|
export interface DBAction { |
|
type: DBActionType, |
|
userId: number, |
|
payload: any, |
|
} |
|
|
|
export type Distribution<T> = Map<T, number>; |
|
|
|
export interface RandomDBActionDistribution { |
|
type: Distribution<DBActionType>, |
|
userId: Distribution<number>, |
|
createTrackParams: RandomCreateTrackDistribution, |
|
createAlbumParams: RandomCreateAlbumDistribution, |
|
createArtistParams: RandomCreateArtistDistribution, |
|
createTagParams: RandomCreateTagDistribution, |
|
deleteTrackParams: RandomDeleteTrackDistribution, |
|
} |
|
|
|
export interface RandomCreateTrackDistribution { |
|
linkArtists: { |
|
numValid: Distribution<number>, |
|
numInvalid: Distribution<number>, |
|
} |
|
linkTags: { |
|
numValid: Distribution<number>, |
|
numInvalid: Distribution<number>, |
|
} |
|
linkAlbum: Distribution<boolean | 'nonexistent'>, |
|
} |
|
|
|
export interface RandomCreateAlbumDistribution { |
|
linkArtists: { |
|
numValid: Distribution<number>, |
|
numInvalid: Distribution<number>, |
|
} |
|
linkTags: { |
|
numValid: Distribution<number>, |
|
numInvalid: Distribution<number>, |
|
} |
|
linkTracks: { |
|
numValid: Distribution<number>, |
|
numInvalid: Distribution<number>, |
|
} |
|
} |
|
|
|
export interface RandomCreateArtistDistribution { |
|
linkAlbums: { |
|
numValid: Distribution<number>, |
|
numInvalid: Distribution<number>, |
|
} |
|
linkTags: { |
|
numValid: Distribution<number>, |
|
numInvalid: Distribution<number>, |
|
} |
|
linkTracks: { |
|
numValid: Distribution<number>, |
|
numInvalid: Distribution<number>, |
|
} |
|
} |
|
|
|
export interface RandomCreateTagDistribution { |
|
linkParent: Distribution<boolean | 'nonexistent'>, |
|
} |
|
|
|
export interface RandomDeleteTrackDistribution { |
|
validTrack: Distribution<boolean>, |
|
} |
|
|
|
export function applyDistribution<T>( |
|
dist: Map<T, number>, |
|
randomNumGen: any, |
|
): T { |
|
let n = randomNumGen(); |
|
let r: T | undefined = undefined; |
|
dist.forEach((value: number, key: T) => { |
|
if (r) { return; } |
|
if (n <= value) { r = key; } |
|
else { n -= value; } |
|
}) |
|
if (r === undefined) { |
|
throw new Error(`Invalid distribution: n=${n}, dist ${JSON.stringify(dist.entries())}`); |
|
} |
|
return r; |
|
} |
|
|
|
export function randomString(randomNumGen: any, length: number) { |
|
let chars = 'abcdefghijklmnopqrstuvwxyz'; |
|
let retval = ''; |
|
for (let i = 0; i < length; i++) { |
|
retval += chars[Math.floor(randomNumGen() * 26)]; |
|
} |
|
return retval; |
|
} |
|
|
|
export function pickNFromArray<T>( |
|
array: T[], |
|
randomNumGen: any, |
|
N: number) |
|
: T[] { |
|
let r: T[] = []; |
|
for (let i = 0; i < N; i++) { |
|
let idx = Math.floor(randomNumGen() * array.length); |
|
r.push(array[idx]); |
|
array.splice(idx); |
|
} |
|
return r; |
|
} |
|
|
|
export function applyReferenceDBAction( |
|
action: DBAction, |
|
db: ReferenceDatabase |
|
): { |
|
response: any, |
|
status: number, |
|
} { |
|
let response: any = undefined; |
|
let status: number = 0; |
|
|
|
try { |
|
switch (action.type) { |
|
case DBActionType.CreateTrack: { |
|
response = createTrack(action.userId, action.payload, db); |
|
status = 200; |
|
break; |
|
} |
|
case DBActionType.CreateAlbum: { |
|
response = createAlbum(action.userId, action.payload, db); |
|
status = 200; |
|
break; |
|
} |
|
case DBActionType.CreateArtist: { |
|
response = createArtist(action.userId, action.payload, db); |
|
status = 200; |
|
break; |
|
} |
|
case DBActionType.CreateTag: { |
|
response = createTag(action.userId, action.payload, db); |
|
status = 200; |
|
break; |
|
} |
|
case DBActionType.DeleteTrack: { |
|
deleteTrack(action.userId, action.payload, db); |
|
response = {}; |
|
status = 200; |
|
break; |
|
} |
|
} |
|
} catch (e) { |
|
if (isDBError(e)) { |
|
if (e.kind === DBErrorKind.ResourceNotFound) { |
|
status = 404; |
|
response = {}; |
|
} |
|
} else { |
|
throw e; |
|
} |
|
} |
|
|
|
return { response: response, status: status }; |
|
} |
|
|
|
export async function applyRealDBAction( |
|
action: DBAction, |
|
req: any, |
|
): Promise<{ |
|
response: any, |
|
status: number, |
|
}> { |
|
let response: any = undefined; |
|
let status: number = 0; |
|
|
|
switch (action.type) { |
|
case DBActionType.CreateTrack: { |
|
let res = await helpers.createTrack(req, action.payload); |
|
status = res.status; |
|
response = res.body; |
|
break; |
|
} |
|
case DBActionType.CreateAlbum: { |
|
let res = await helpers.createAlbum(req, action.payload); |
|
status = res.status; |
|
response = res.body; |
|
break; |
|
} |
|
case DBActionType.CreateArtist: { |
|
let res = await helpers.createArtist(req, action.payload); |
|
status = res.status; |
|
response = res.body; |
|
break; |
|
} |
|
case DBActionType.CreateTag: { |
|
let res = await helpers.createTag(req, action.payload); |
|
status = res.status; |
|
response = res.body; |
|
break; |
|
} |
|
case DBActionType.DeleteTrack: { |
|
let res = await helpers.deleteTrack(req, action.payload); |
|
status = res.status; |
|
response = res.body; |
|
break; |
|
} |
|
} |
|
|
|
return { response: response, status: status }; |
|
} |
|
|
|
export function randomDBAction( |
|
db: ReferenceDatabase, |
|
randomNumGen: any, |
|
distribution: RandomDBActionDistribution, |
|
): DBAction { |
|
let type = applyDistribution( |
|
distribution.type, |
|
randomNumGen |
|
); |
|
let userId = applyDistribution( |
|
distribution.userId, |
|
randomNumGen |
|
); |
|
|
|
switch (type) { |
|
case DBActionType.CreateTrack: { |
|
return { |
|
type: type, |
|
payload: createRandomTrack( |
|
db, |
|
userId, |
|
distribution.createTrackParams, |
|
randomNumGen |
|
), |
|
userId: userId, |
|
}; |
|
} |
|
case DBActionType.CreateArtist: { |
|
return { |
|
type: type, |
|
payload: createRandomArtist( |
|
db, |
|
userId, |
|
distribution.createArtistParams, |
|
randomNumGen |
|
), |
|
userId: userId, |
|
}; |
|
} |
|
case DBActionType.CreateAlbum: { |
|
return { |
|
type: type, |
|
payload: createRandomAlbum( |
|
db, |
|
userId, |
|
distribution.createAlbumParams, |
|
randomNumGen |
|
), |
|
userId: userId, |
|
}; |
|
} |
|
case DBActionType.CreateTag: { |
|
return { |
|
type: type, |
|
payload: createRandomTag( |
|
db, |
|
userId, |
|
distribution.createTagParams, |
|
randomNumGen |
|
), |
|
userId: userId, |
|
}; |
|
} |
|
case DBActionType.DeleteTrack: { |
|
return { |
|
type: type, |
|
payload: applyDistribution(distribution.deleteTrackParams.validTrack, randomNumGen) ? |
|
Math.floor(randomNumGen() * db[userId].tracks.length) + 1 : |
|
Math.floor(randomNumGen() * db[userId].tracks.length) + 1 + db[userId].tracks.length, |
|
userId: userId, |
|
} |
|
} |
|
} |
|
} |
|
|
|
function pickArtists(userId: number, nValid: number, nInvalid: number, rng: any, db: ReferenceDatabase) { |
|
let allValidArtistIds: number[] = db[userId] && db[userId].artists ? |
|
db[userId].artists.map((a: ArtistWithRefsWithId) => a.id) : []; |
|
let validArtists: number[] = pickNFromArray(allValidArtistIds, rng, nValid); |
|
let invalidArtists: number[] = []; |
|
for (let i = 0; i < nInvalid; i++) { |
|
invalidArtists.push(Math.round(rng() * 100) + allValidArtistIds.length); |
|
} |
|
return [...validArtists, ...invalidArtists]; |
|
} |
|
|
|
function pickAlbums(userId: number, nValid: number, nInvalid: number, rng: any, db: ReferenceDatabase) { |
|
let allValidAlbumIds: number[] = db[userId] && db[userId].albums ? |
|
db[userId].albums.map((a: AlbumWithRefsWithId) => a.id) : []; |
|
let validAlbums: number[] = pickNFromArray(allValidAlbumIds, rng, nValid); |
|
let invalidAlbums: number[] = []; |
|
for (let i = 0; i < nInvalid; i++) { |
|
invalidAlbums.push(Math.round(rng() * 100) + allValidAlbumIds.length); |
|
} |
|
return [...validAlbums, ...invalidAlbums]; |
|
} |
|
|
|
function pickTracks(userId: number, nValid: number, nInvalid: number, rng: any, db: ReferenceDatabase) { |
|
let allValidTrackIds: number[] = db[userId] && db[userId].tracks ? |
|
db[userId].tracks.map((a: TrackWithRefsWithId) => a.id) : []; |
|
let validTracks: number[] = pickNFromArray(allValidTrackIds, rng, nValid); |
|
let invalidTracks: number[] = []; |
|
for (let i = 0; i < nInvalid; i++) { |
|
invalidTracks.push(Math.round(rng() * 100) + allValidTrackIds.length); |
|
} |
|
return [...validTracks, ...invalidTracks]; |
|
} |
|
|
|
function pickTags(userId: number, nValid: number, nInvalid: number, rng: any, db: ReferenceDatabase) { |
|
let allValidTagIds: number[] = db[userId] && db[userId].tags ? |
|
db[userId].tags.map((a: TagWithRefsWithId) => a.id) : []; |
|
let validTags: number[] = pickNFromArray(allValidTagIds, rng, nValid); |
|
let invalidTags: number[] = []; |
|
for (let i = 0; i < nInvalid; i++) { |
|
invalidTags.push(Math.round(rng() * 100) + allValidTagIds.length); |
|
} |
|
return [...validTags, ...invalidTags]; |
|
} |
|
|
|
export function createRandomTrack( |
|
db: ReferenceDatabase, |
|
userId: number, |
|
dist: RandomCreateTrackDistribution, |
|
randomNumGen: any, |
|
): TrackWithRefs { |
|
let artists: number[] = pickArtists(userId, |
|
applyDistribution(dist.linkArtists.numValid, randomNumGen), |
|
applyDistribution(dist.linkArtists.numInvalid, randomNumGen), |
|
randomNumGen, |
|
db); |
|
|
|
let tags: number[] = pickTags(userId, |
|
applyDistribution(dist.linkTags.numValid, randomNumGen), |
|
applyDistribution(dist.linkTags.numInvalid, randomNumGen), |
|
randomNumGen, |
|
db); |
|
|
|
let maybeAlbum: number | null = (() => { |
|
let r: boolean | null | 'nonexistent' = applyDistribution(dist.linkAlbum, randomNumGen); |
|
let albums = pickAlbums(userId, r === true ? 1 : 0, r === 'nonexistent' ? 1 : 0, randomNumGen, db); |
|
return albums.length ? albums[0] : null; |
|
})(); |
|
|
|
return { |
|
mbApi_typename: 'track', |
|
albumId: maybeAlbum, |
|
artistIds: artists, |
|
tagIds: tags, |
|
name: randomString(randomNumGen, 20), |
|
storeLinks: [], // TODO |
|
} |
|
} |
|
|
|
export function createRandomArtist( |
|
db: ReferenceDatabase, |
|
userId: number, |
|
dist: RandomCreateArtistDistribution, |
|
randomNumGen: any, |
|
): ArtistWithRefs { |
|
let tracks: number[] = pickTracks(userId, |
|
applyDistribution(dist.linkTracks.numValid, randomNumGen), |
|
applyDistribution(dist.linkTracks.numInvalid, randomNumGen), |
|
randomNumGen, |
|
db); |
|
|
|
let tags: number[] = pickTags(userId, |
|
applyDistribution(dist.linkTags.numValid, randomNumGen), |
|
applyDistribution(dist.linkTags.numInvalid, randomNumGen), |
|
randomNumGen, |
|
db); |
|
|
|
let albums: number[] = pickAlbums(userId, |
|
applyDistribution(dist.linkAlbums.numValid, randomNumGen), |
|
applyDistribution(dist.linkAlbums.numInvalid, randomNumGen), |
|
randomNumGen, |
|
db); |
|
|
|
return { |
|
mbApi_typename: 'artist', |
|
albumIds: albums, |
|
trackIds: tracks, |
|
tagIds: tags, |
|
name: randomString(randomNumGen, 20), |
|
storeLinks: [], // TODO |
|
} |
|
} |
|
|
|
export function createRandomAlbum( |
|
db: ReferenceDatabase, |
|
userId: number, |
|
dist: RandomCreateAlbumDistribution, |
|
randomNumGen: any, |
|
): AlbumWithRefs { |
|
let tracks: number[] = pickTracks(userId, |
|
applyDistribution(dist.linkTracks.numValid, randomNumGen), |
|
applyDistribution(dist.linkTracks.numInvalid, randomNumGen), |
|
randomNumGen, |
|
db); |
|
|
|
let tags: number[] = pickTags(userId, |
|
applyDistribution(dist.linkTags.numValid, randomNumGen), |
|
applyDistribution(dist.linkTags.numInvalid, randomNumGen), |
|
randomNumGen, |
|
db); |
|
|
|
let artists: number[] = pickArtists(userId, |
|
applyDistribution(dist.linkArtists.numValid, randomNumGen), |
|
applyDistribution(dist.linkArtists.numInvalid, randomNumGen), |
|
randomNumGen, |
|
db); |
|
|
|
return { |
|
mbApi_typename: 'album', |
|
artistIds: artists, |
|
trackIds: tracks, |
|
tagIds: tags, |
|
name: randomString(randomNumGen, 20), |
|
storeLinks: [], // TODO |
|
} |
|
} |
|
|
|
export function createRandomTag( |
|
db: ReferenceDatabase, |
|
userId: number, |
|
dist: RandomCreateTagDistribution, |
|
randomNumGen: any, |
|
): TagWithRefs { |
|
let maybeParent: number | null = (() => { |
|
let r: boolean | null | 'nonexistent' = applyDistribution(dist.linkParent, randomNumGen); |
|
let tags = pickTags(userId, r === true ? 1 : 0, r === 'nonexistent' ? 1 : 0, randomNumGen, db); |
|
return tags.length ? tags[0] : null; |
|
})(); |
|
|
|
return { |
|
mbApi_typename: 'tag', |
|
name: randomString(randomNumGen, 20), |
|
parentId: maybeParent, |
|
} |
|
} |