Add deletions.

editsong
Sander Vocke 5 years ago
parent 299342824c
commit cd26f79e4b
  1. 2
      .vscode/launch.json
  2. 6
      client/src/api/endpoints/data.ts
  3. 24
      server/db/Album.ts
  4. 2
      server/endpoints/Tag.ts
  5. 1
      server/migrations/20200828124218_init_db.ts
  6. 66
      server/test/integration/flows/ResourceFlow.ts
  7. 42
      server/test/integration/helpers.ts
  8. 129
      server/test/reference_model/DBReferenceModel.ts
  9. 233
      server/test/reference_model/randomGen.ts

@ -10,7 +10,7 @@
"name": "Jasmine Tests with SQLite",
"env": {
"MUDBASE_DB_CONFIG": "{\"client\": \"sqlite3\", \"connection\": \":memory:\"}",
"TEST_RANDOM_SEED": "0.61658"
"TEST_RANDOM_SEED": "0.77715"
},
"program": "${workspaceFolder}/server/node_modules/jasmine-ts/lib/index",
"args": [

@ -5,10 +5,14 @@
// referencing between objects. They do not correspond to IDs in the actual
// database.
// Upon import, they might be replaced, and upon export, they might be randomly
// generated.
import { AlbumWithRefsWithId, ArtistWithRefsWithId, isAlbumWithRefs, isArtistWithRefs, isTagWithRefs, isTrackBaseWithRefs, isTrackWithRefs, TagWithRefsWithId, TrackWithRefsWithId } from "../types/resources";
// generated.
// The import/export DB format is just a set of lists of objects.
// Each object has an ID and references others by ID.
// Any object referenced by ID also has a reverse reference.
// In other words, if A references B, B must also reference A.
export interface DBDataFormat {
tracks: TrackWithRefsWithId[],
albums: AlbumWithRefsWithId[],

@ -73,7 +73,7 @@ export async function getAlbum(id: number, userId: number, knex: Knex):
// Returns the id of the created album.
export async function createAlbum(userId: number, album: AlbumWithRefs, knex: Knex): Promise<number> {
return await knex.transaction(async (trx) => {
return await knex.transaction(async (trx) => {
// Start retrieving artists.
const artistIdsPromise: Promise<number[]> =
trx.select('id')
@ -181,10 +181,10 @@ export async function modifyAlbum(userId: number, albumId: number, album: AlbumB
// Start retrieving tracks if we are modifying those.
const trackIdsPromise: Promise<number[] | undefined> =
album.trackIds ?
trx.select('artistId')
.from('tracks_albums')
trx.select('id')
.from('tracks')
.whereIn('albumId', album.trackIds)
.then((as: any) => as.map((a: any) => a['trackId']))
.then((as: any) => as.map((a: any) => a['id']))
: (async () => undefined)();
// Start retrieving tags if we are modifying those.
@ -229,11 +229,11 @@ export async function modifyAlbum(userId: number, albumId: number, album: AlbumB
.whereNotIn('tagId', album.tagIds || [])
.delete() : undefined;
// Remove unlinked tracks.
const removeUnlinkedTracks = tracks ? trx('tracks_albums')
// Remove unlinked tracks by setting their references to null.
const removeUnlinkedTracks = tracks ? trx('tracks')
.where({ 'albumId': albumId })
.whereNotIn('trackId', album.trackIds || [])
.delete() : undefined;
.whereNotIn('id', album.trackIds || [])
.update({ 'albumId': null }) : undefined;
// Link new artists.
const addArtists = artists ? trx('artists_albums')
@ -356,11 +356,11 @@ export async function deleteAlbum(userId: number, albumId: number, knex: Knex):
.from('albums_tags')
.where({ 'albumId': albumId });
// Start deleting track associations with the album.
// Start deleting track associations with the album by setting their references to null.
const deleteTracksPromise: Promise<any> =
trx.delete()
.from('tracks_albums')
.where({ 'albumId': albumId });
trx.update({ 'album': null })
.from('tracks')
.where({ 'album': albumId });
// Start deleting the album.
const deleteAlbumPromise: Promise<any> =

@ -37,7 +37,7 @@ export const DeleteTag: EndpointHandler = async (req: any, res: any, knex: Knex)
try {
deleteTag(userId, req.params.id, knex);
await deleteTag(userId, req.params.id, knex);
res.status(200).send();
} catch (e) {

@ -204,7 +204,6 @@ export async function down(knex: Knex): Promise<void> {
await knex.schema.dropTable('albums');
await knex.schema.dropTable('tags');
await knex.schema.dropTable('tracks_artists');
await knex.schema.dropTable('tracks_albums');
await knex.schema.dropTable('tracks_tags');
await knex.schema.dropTable('artists_tags');
await knex.schema.dropTable('albums_tags');

@ -144,10 +144,20 @@ function transformActionIDs(action: DBAction, mappings: IDMappings, rng: any) {
tag.parentId = tag.parentId ? doMap(tag.parentId, mappings.tags) : null;
break;
}
case DBActionType.DeleteTrack: {
r.payload = mappings.tracks[r.payload];
break;
}
case DBActionType.DeleteTrack:
case DBActionType.DeleteArtist:
case DBActionType.DeleteAlbum:
case DBActionType.DeleteTag:
{
let keys = {
[DBActionType.DeleteTrack]: 'tracks',
[DBActionType.DeleteArtist]: 'artists',
[DBActionType.DeleteAlbum]: 'albums',
[DBActionType.DeleteTag]: 'tags',
}
r.payload = (mappings as any)[keys[r.type]][r.payload];
break;
}
}
return r;
}
@ -208,11 +218,14 @@ describe('Randomized model-based DB back-end tests', () => {
// be generated.
let dist: RandomDBActionDistribution = {
type: new Map([
[DBActionType.CreateTrack, 0.2],
[DBActionType.CreateArtist, 0.2],
[DBActionType.CreateAlbum, 0.2],
[DBActionType.CreateTag, 0.2],
[DBActionType.DeleteTrack, 0.2]
[DBActionType.CreateTrack, 0.125],
[DBActionType.CreateArtist, 0.125],
[DBActionType.CreateAlbum, 0.125],
[DBActionType.CreateTag, 0.125],
[DBActionType.DeleteTrack, 0.125],
[DBActionType.DeleteArtist, 0.125],
[DBActionType.DeleteAlbum, 0.125],
[DBActionType.DeleteTag, 0.125],
]),
userId: new Map([[1, 1.0]]),
createTrackParams: {
@ -259,8 +272,17 @@ describe('Randomized model-based DB back-end tests', () => {
},
deleteTrackParams: {
validTrack: new Map([[false, 0.2], [true, 0.8]])
}
validId: new Map([[false, 0.2], [true, 0.8]])
},
deleteArtistParams: {
validId: new Map([[false, 0.2], [true, 0.8]])
},
deleteAlbumParams: {
validId: new Map([[false, 0.2], [true, 0.8]])
},
deleteTagParams: {
validId: new Map([[false, 0.2], [true, 0.8]])
},
}
// Loop to generate and execute a bunch of random actions.
@ -284,14 +306,12 @@ describe('Randomized model-based DB back-end tests', () => {
// If this was an object creation action, we need to update the mappings.
if (refStatus === 200 && realStatus === 200) {
if (refAction.type === DBActionType.CreateTrack) {
idMappingsRefToReal.tracks[refResponse.id] = realResponse.id;
} else if (refAction.type === DBActionType.CreateAlbum) {
idMappingsRefToReal.albums[refResponse.id] = realResponse.id;
} else if (refAction.type === DBActionType.CreateArtist) {
idMappingsRefToReal.artists[refResponse.id] = realResponse.id;
} else if (refAction.type === DBActionType.CreateTag) {
idMappingsRefToReal.tags[refResponse.id] = realResponse.id;
switch(refAction.type) {
case DBActionType.CreateTrack: { idMappingsRefToReal.tracks[refResponse.id] = realResponse.id; break; }
case DBActionType.CreateArtist: { idMappingsRefToReal.artists[refResponse.id] = realResponse.id; break; }
case DBActionType.CreateAlbum: { idMappingsRefToReal.albums[refResponse.id] = realResponse.id; break; }
case DBActionType.CreateTag: { idMappingsRefToReal.tags[refResponse.id] = realResponse.id; break; }
default: { break; }
}
}
@ -300,7 +320,7 @@ describe('Randomized model-based DB back-end tests', () => {
expect(normalizeResponse(realResponse)).to.deep.equal(normalizeResponse(refResponse));
// Compare the database state after the action.
let newRefState = refDB;
let newRefState = _.cloneDeep(refDB);
let newRealState = {
[1]: (await helpers.getExport(req)).body,
};
@ -318,11 +338,15 @@ describe('Randomized model-based DB back-end tests', () => {
e.idMappingsRefToReal = idMappingsRefToReal;
if (e.actual && e.expected) {
let basename = tmp.tmpNameSync();
e.actualDump = basename + "_actual";
e.expectedDump = basename + "_expected";
e.actionTraceDump = basename + "_actiontrace";
e.startingDBDump = basename + "_startingDB";
e.lastRefDBDump = basename + "_lastRefDB";
e.lastRealDBDump = basename + "_lastRealDB";
e.idMappingsRefToRealDump = basename + "_idMappings";
fs.writeFileSync(e.actualDump, stringify(e.actual, { space: ' ' }));
fs.writeFileSync(e.expectedDump, stringify(e.expected, { space: ' ' }));
fs.writeFileSync(e.actionTraceDump, stringify(e.actionTrace, { space: ' ' }));
fs.writeFileSync(e.startingDBDump, stringify(e.startingDB, { space: ' ' }));
fs.writeFileSync(e.lastRefDBDump, stringify(e.lastRefDB, { space: ' ' }));
@ -331,6 +355,8 @@ describe('Randomized model-based DB back-end tests', () => {
console.log(
"A comparison error occurred. Wrote compared values to temporary files for debugging:\n"
+ ` Actual value: ${e.actualDump}\n`
+ ` Expected value: ${e.actualDump}\n`
+ ` DB action trace: ${e.actionTraceDump}\n`
+ ` Starting DB: ${e.startingDBDump}\n`
+ ` Reference DB before last action: ${e.lastRefDBDump}\n`

@ -128,6 +128,20 @@ export async function checkArtist(
})
}
export async function deleteArtist(
req: any,
id = 1,
expectStatus: number | undefined = undefined,
) {
return await req
.delete('/artist/' + id)
.send()
.then((res: any) => {
expectStatus && expect(res).to.have.status(expectStatus);
return res;
});
}
export async function createTag(
req: any,
props = { name: "Tag" },
@ -174,6 +188,20 @@ export async function checkTag(
})
}
export async function deleteTag(
req: any,
id = 1,
expectStatus: number | undefined = undefined,
) {
return await req
.delete('/tag/' + id)
.send()
.then((res: any) => {
expectStatus && expect(res).to.have.status(expectStatus);
return res;
});
}
export async function createAlbum(
req: any,
props = { name: "Album" },
@ -220,6 +248,20 @@ export async function checkAlbum(
})
}
export async function deleteAlbum(
req: any,
id = 1,
expectStatus: number | undefined = undefined,
) {
return await req
.delete('/album/' + id)
.send()
.then((res: any) => {
expectStatus && expect(res).to.have.status(expectStatus);
return res;
});
}
export async function createUser(
req: any,
email: string,

@ -1,9 +1,10 @@
import { AlbumWithRefsWithId, ArtistWithRefsWithId, DBDataFormat, PostAlbumRequest, PostArtistRequest, PostTagRequest, PostTrackRequest, TrackWithDetails, TrackWithRefsWithId } from "../../../client/src/api/api";
import { AlbumWithRefsWithId, ArtistWithRefsWithId, DBDataFormat, PostAlbumRequest, PostArtistRequest, PostTagRequest, PostTrackRequest, TagWithRefsWithId, TrackWithDetails, TrackWithRefsWithId } from "../../../client/src/api/api";
import { makeNotFoundError } from "../../db/common";
import filterInPlace from "../../lib/filterInPlace";
// The mock reference database is in the same format as
// the JSON import/export format, for multiple users.
export type ReferenceDatabase = Record<number, DBDataFormat>
type ObjectsType = "tracks" | "artists" | "tags" | "albums";
@ -42,23 +43,27 @@ function ensureLinked(fromObjects: number[], fromObjectsType: ObjectsType,
}
// Create a new object.
export interface LinkField { field: string, otherObjectType: ObjectsType };
// The general procedure for this is:
// - check that any existing objects referenced in the new object actually exist
// - generate a new ID and insert the object
// - add reverse references into any existing object referenced by the new object.
export interface ReferencingField { field: string, otherObjectType: ObjectsType };
export function createObject(
userId: number,
object: any,
objectType: ObjectsType,
singularLinkFields: LinkField[],
pluralLinkFields: LinkField[],
singularReverseRefs: ReferencingField[],
pluralReverseRefs: ReferencingField[],
db: ReferenceDatabase
): { id: number } {
// Existence checks
if (!(userId in db)) { throw makeNotFoundError() }
singularLinkFields.forEach((f: LinkField) => {
singularReverseRefs.forEach((f: ReferencingField) => {
if (!checkExists(db[userId][f.otherObjectType], object[f.field] ? [object[f.field]] : [])) {
throw makeNotFoundError();
}
});
pluralLinkFields.forEach((f: LinkField) => {
pluralReverseRefs.forEach((f: ReferencingField) => {
if (!checkExists(db[userId][f.otherObjectType], object[f.field] || [])) {
throw makeNotFoundError();
}
@ -72,16 +77,51 @@ export function createObject(
})
// reverse links
singularLinkFields.forEach((f: LinkField) => {
singularReverseRefs.forEach((f: ReferencingField) => {
ensureLinked(object[f.field] ? [object[f.field]] : [], f.otherObjectType, id, objectType, db[userId]);
});
pluralLinkFields.forEach((f: LinkField) => {
pluralReverseRefs.forEach((f: ReferencingField) => {
ensureLinked(object[f.field] || [], f.otherObjectType, id, objectType, db[userId]);
});
return { id: id };
}
// Delete an object.
// The general procedure for this is:
// - check that the to-be-deleted object exists
// - remove any references that exist to the object in other objects
// - delete the object
export function deleteObject(
userId: number,
objectId: number,
objectType: ObjectsType,
singularRefsToThisObject: ReferencingField[],
pluralRefsToThisObject: ReferencingField[],
db: ReferenceDatabase
): void {
// Existence checks
if (!(userId in db)) { throw makeNotFoundError() }
// Find the object to delete.
let idx = db[userId][objectType].findIndex((o: any) => 'id' in o && o.id === objectId);
if (idx < 0) {
// Not found
throw makeNotFoundError();
}
// Remove references to this object
pluralRefsToThisObject.forEach((f: ReferencingField) => {
db[userId][f.otherObjectType].forEach((other: any) => { filterInPlace(other[f.field], (oid: number) => oid !== objectId) })
});
singularRefsToThisObject.forEach((f: ReferencingField) => {
db[userId][f.otherObjectType].forEach((other: any) => { if (other[f.field] === objectId) { other[f.field] = null; } })
});
// Delete the object
db[userId][objectType].splice(idx, 1);
}
// Create a new track.
export function createTrack(userId: number, track: PostTrackRequest, db: ReferenceDatabase): { id: number } {
return createObject(
@ -143,20 +183,69 @@ export function createTag(userId: number, tag: PostTagRequest, db: ReferenceData
// Delete a track.
export function deleteTrack(userId: number, id: number, db: ReferenceDatabase): void {
// Existence checks
if (!(userId in db)) { throw makeNotFoundError() }
return deleteObject(
userId,
id,
'tracks',
[],
[
{ field: 'trackIds', otherObjectType: 'albums' },
{ field: 'trackIds', otherObjectType: 'artists' },
],
db
);
}
// Find the object to delete.
let idx = db[userId].tracks.findIndex((track: TrackWithRefsWithId) => track.id === id);
if (idx < 0) {
// Not found
// Delete an artist.
export function deleteArtist(userId: number, id: number, db: ReferenceDatabase): void {
return deleteObject(
userId,
id,
'artists',
[],
[
{ field: 'artistIds', otherObjectType: 'tracks' },
{ field: 'artistIds', otherObjectType: 'albums' },
],
db
);
}
// Delete a track.
export function deleteAlbum(userId: number, id: number, db: ReferenceDatabase): void {
return deleteObject(
userId,
id,
'albums',
[{ field: 'albumId', otherObjectType: 'tracks' }],
[{ field: 'albumIds', otherObjectType: 'artists' },],
db
);
}
// Delete a tag.
export function deleteTag(userId: number, id: number, db: ReferenceDatabase): void {
// Tags are special in that deleting them also deletes their children. Implement that here
// with recursive calls.
if (!(userId in db)) { throw makeNotFoundError() }
let tag = db[userId].tags.find((o: any) => 'id' in o && o.id === id);
if (!tag) {
throw makeNotFoundError();
}
let children = db[userId].tags.filter((t: TagWithRefsWithId) => t.parentId === id);
children.forEach((child: TagWithRefsWithId) => { deleteTag(userId, child.id, db) })
// Remove references
db[userId].albums.forEach((x: AlbumWithRefsWithId) => { filterInPlace(x.trackIds, (tid: number) => tid !== id); })
db[userId].artists.forEach((x: ArtistWithRefsWithId) => { filterInPlace(x.trackIds, (tid: number) => tid !== id); })
// Delete the object
db[userId].tracks.splice(idx, 1);
// Do the actual deletion of this tag.
return deleteObject(
userId,
id,
'tags',
[],
[
{ field: 'tagIds', otherObjectType: 'albums' },
{ field: 'tagIds', otherObjectType: 'artists' },
{ field: 'tagIds', otherObjectType: 'tracks' },
],
db
);
}

@ -1,6 +1,6 @@
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 { createAlbum, createArtist, createTag, createTrack, deleteAlbum, deleteArtist, deleteTag, deleteTrack, ReferenceDatabase } from "./DBReferenceModel";
import * as helpers from '../integration/helpers';
import { DBErrorKind, isDBError } from "../../endpoints/types";
@ -10,6 +10,9 @@ export enum DBActionType {
CreateTag = "CreateTag",
CreateArtist = "CreateArtist",
DeleteTrack = "DeleteTrack",
DeleteAlbum = "DeleteAlbum",
DeleteArtist = "DeleteArtist",
DeleteTag = "DeleteTag",
}
export interface DBAction {
@ -27,7 +30,10 @@ export interface RandomDBActionDistribution {
createAlbumParams: RandomCreateAlbumDistribution,
createArtistParams: RandomCreateArtistDistribution,
createTagParams: RandomCreateTagDistribution,
deleteTrackParams: RandomDeleteTrackDistribution,
deleteTrackParams: RandomDeleteObjectDistribution,
deleteArtistParams: RandomDeleteObjectDistribution,
deleteAlbumParams: RandomDeleteObjectDistribution,
deleteTagParams: RandomDeleteObjectDistribution,
}
export interface RandomCreateTrackDistribution {
@ -76,8 +82,8 @@ export interface RandomCreateTagDistribution {
linkParent: Distribution<boolean | 'nonexistent'>,
}
export interface RandomDeleteTrackDistribution {
validTrack: Distribution<boolean>,
export interface RandomDeleteObjectDistribution {
validId: Distribution<boolean>,
}
export function applyDistribution<T>(
@ -132,32 +138,37 @@ export function applyReferenceDBAction(
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;
}
case DBActionType.CreateTrack:
case DBActionType.CreateAlbum:
case DBActionType.CreateArtist:
case DBActionType.CreateTag:
{
let funcs = {
[DBActionType.CreateTrack]: createTrack,
[DBActionType.CreateAlbum]: createAlbum,
[DBActionType.CreateArtist]: createArtist,
[DBActionType.CreateTag]: createTag,
}
response = funcs[action.type](action.userId, action.payload, db);
status = 200;
break;
}
case DBActionType.DeleteTrack:
case DBActionType.DeleteAlbum:
case DBActionType.DeleteArtist:
case DBActionType.DeleteTag:
{
let funcs = {
[DBActionType.DeleteTrack]: deleteTrack,
[DBActionType.DeleteAlbum]: deleteAlbum,
[DBActionType.DeleteArtist]: deleteArtist,
[DBActionType.DeleteTag]: deleteTag,
}
funcs[action.type](action.userId, action.payload, db);
response = {};
status = 200;
break;
}
}
} catch (e) {
if (isDBError(e)) {
@ -184,36 +195,39 @@ export async function applyRealDBAction(
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;
}
case DBActionType.CreateTrack:
case DBActionType.CreateAlbum:
case DBActionType.CreateArtist:
case DBActionType.CreateTag:
{
let funcs = {
[DBActionType.CreateTrack]: helpers.createTrack,
[DBActionType.CreateAlbum]: helpers.createAlbum,
[DBActionType.CreateArtist]: helpers.createArtist,
[DBActionType.CreateTag]: helpers.createTag,
}
let res = await funcs[action.type](req, action.payload);
status = res.status;
response = res.body;
break;
}
case DBActionType.DeleteTrack:
case DBActionType.DeleteAlbum:
case DBActionType.DeleteArtist:
case DBActionType.DeleteTag:
{
let funcs = {
[DBActionType.DeleteTrack]: helpers.deleteTrack,
[DBActionType.DeleteAlbum]: helpers.deleteAlbum,
[DBActionType.DeleteArtist]: helpers.deleteArtist,
[DBActionType.DeleteTag]: helpers.deleteTag,
}
let res = await funcs[action.type](req, action.payload);
status = res.status;
response = res.body;
break;
}
}
return { response: response, status: status };
@ -234,60 +248,55 @@ export function randomDBAction(
);
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: {
case DBActionType.CreateTrack:
case DBActionType.CreateArtist:
case DBActionType.CreateAlbum:
case DBActionType.CreateTag:
{
let funcs = {
[DBActionType.CreateTrack]: createRandomTrack,
[DBActionType.CreateArtist]: createRandomArtist,
[DBActionType.CreateAlbum]: createRandomAlbum,
[DBActionType.CreateTag]: createRandomTag,
}
let params = {
[DBActionType.CreateTrack]: distribution.createTrackParams,
[DBActionType.CreateArtist]: distribution.createArtistParams,
[DBActionType.CreateAlbum]: distribution.createAlbumParams,
[DBActionType.CreateTag]: distribution.createTagParams,
}
return {
type: type,
payload: funcs[type](
db,
userId,
params[type] as any,
randomNumGen
),
userId: userId,
};
}
case DBActionType.DeleteTrack:
case DBActionType.DeleteArtist:
case DBActionType.DeleteAlbum:
case DBActionType.DeleteTag: {
let params = {
[DBActionType.DeleteTrack]: distribution.deleteTrackParams,
[DBActionType.DeleteArtist]: distribution.deleteArtistParams,
[DBActionType.DeleteAlbum]: distribution.deleteAlbumParams,
[DBActionType.DeleteTag]: distribution.deleteTagParams,
}
let objectArrays = {
[DBActionType.DeleteTrack]: db[userId].tracks,
[DBActionType.DeleteArtist]: db[userId].artists,
[DBActionType.DeleteAlbum]: db[userId].albums,
[DBActionType.DeleteTag]: db[userId].tags,
}
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,
payload: applyDistribution(params[type].validId, randomNumGen) ?
Math.floor(randomNumGen() * objectArrays[type].length) + 1 :
Math.floor(randomNumGen() * objectArrays[type].length) + 1 + objectArrays[type].length,
userId: userId,
}
}

Loading…
Cancel
Save