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.
238 lines
7.1 KiB
238 lines
7.1 KiB
import { AlbumWithRefsWithId, ArtistWithRefsWithId, TagWithRefsWithId, TrackWithRefs } from "../../../client/src/api/api"; |
|
import { userEndpoints } from "../../endpoints/User"; |
|
import { createTrack, deleteTrack, ReferenceDatabase } from "./DBReferenceModel"; |
|
import * as helpers from '../integration/helpers'; |
|
import { DBErrorKind, isDBError } from "../../endpoints/types"; |
|
|
|
export enum DBActionType { |
|
CreateTrack = 0, |
|
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, |
|
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 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.DeleteTrack: { |
|
deleteTrack(action.userId, action.payload, db); |
|
response = {}; |
|
status = 200; |
|
break; |
|
} |
|
} |
|
} catch(e) { |
|
if(isDBError(e)) { |
|
if(e.kind === DBErrorKind.ResourceNotFound) { |
|
status = 404; |
|
response = {}; |
|
} |
|
} |
|
} |
|
|
|
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.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.DeleteTrack: { |
|
return { |
|
type: type, |
|
payload: applyDistribution(distribution.deleteTrackParams.validTrack, randomNumGen) ? |
|
Math.floor(Math.random() * db[userId].tracks.length) + 1 : |
|
Math.floor(Math.random() * db[userId].tracks.length) + 1 + db[userId].tracks.length, |
|
userId: userId, |
|
} |
|
} |
|
} |
|
} |
|
|
|
export function createRandomTrack( |
|
db: ReferenceDatabase, |
|
userId: number, |
|
trackDist: RandomCreateTrackDistribution, |
|
randomNumGen: any, |
|
): TrackWithRefs { |
|
let allValidArtistIds: number[] = db[userId] && db[userId].artists ? |
|
db[userId].artists.map((a: ArtistWithRefsWithId) => a.id) : []; |
|
let allValidTagIds: number[] = db[userId] && db[userId].tags ? |
|
db[userId].tags.map((a: TagWithRefsWithId) => a.id) : []; |
|
let allValidAlbumIds: number[] = db[userId] && db[userId].albums ? |
|
db[userId].albums.map((a: AlbumWithRefsWithId) => a.id) : []; |
|
|
|
let artists: number[] = (() => { |
|
let validArtists: number[] = pickNFromArray(allValidArtistIds, randomNumGen, applyDistribution(trackDist.linkArtists.numValid, randomNumGen)); |
|
let invalidArtists: number[] = []; |
|
for (let i = 0; i < applyDistribution(trackDist.linkArtists.numInvalid, randomNumGen); i++) { |
|
invalidArtists.push(Math.round(Math.random() * 100) + allValidArtistIds.length); |
|
} |
|
return [...validArtists, ...invalidArtists]; |
|
})(); |
|
|
|
let tags: number[] = (() => { |
|
let validTags: number[] = pickNFromArray(allValidTagIds, randomNumGen, applyDistribution(trackDist.linkTags.numValid, randomNumGen)); |
|
let invalidTags: number[] = []; |
|
for (let i = 0; i < applyDistribution(trackDist.linkTags.numInvalid, randomNumGen); i++) { |
|
invalidTags.push(Math.round(Math.random() * 100) + allValidTagIds.length); |
|
} |
|
return [...validTags, ...invalidTags]; |
|
})(); |
|
|
|
let maybeAlbum: number | null = (() => { |
|
let r: boolean | null | 'nonexistent' = applyDistribution(trackDist.linkAlbum, randomNumGen); |
|
let maybeValidAlbum: number | null = |
|
r === true && |
|
allValidAlbumIds.length ? |
|
pickNFromArray(allValidAlbumIds, randomNumGen, 1)[0] : |
|
null; |
|
let maybeInvalidAlbum: number | null = |
|
r === 'nonexistent' ? |
|
allValidAlbumIds.length + 1 : null; |
|
return maybeValidAlbum || maybeInvalidAlbum; |
|
})(); |
|
|
|
return { |
|
mbApi_typename: 'track', |
|
albumId: maybeAlbum, |
|
artistIds: artists, |
|
tagIds: tags, |
|
name: randomString(randomNumGen, 20), |
|
storeLinks: [], // TODO |
|
} |
|
} |