Clean-up after merge

This commit is contained in:
mProjectsCode 2022-09-22 13:55:16 +02:00
parent 391915eb09
commit da16b2af6a
15 changed files with 177 additions and 171 deletions

4
package-lock.json generated
View file

@ -1,12 +1,12 @@
{ {
"name": "obsidian-media-db-plugin", "name": "obsidian-media-db-plugin",
"version": "0.3.0", "version": "0.3.1",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "obsidian-media-db-plugin", "name": "obsidian-media-db-plugin",
"version": "0.3.0", "version": "0.3.1",
"license": "GPL-3.0", "license": "GPL-3.0",
"dependencies": { "dependencies": {
"ts-node": "^10.8.1", "ts-node": "^10.8.1",

View file

@ -1,6 +1,6 @@
{ {
"name": "obsidian-media-db-plugin", "name": "obsidian-media-db-plugin",
"version": "0.3.1", "version": "0.3.2",
"description": "A plugin that can query multiple APIs for movies, series, anime, games, music and wiki articles, and import them into your vault.", "description": "A plugin that can query multiple APIs for movies, series, anime, games, music and wiki articles, and import them into your vault.",
"main": "main.js", "main": "main.js",
"scripts": { "scripts": {

View file

@ -32,16 +32,16 @@ export class BoardGameGeekAPI extends APIModel {
} }
const data = fetchData.text; const data = fetchData.text;
const response = new window.DOMParser().parseFromString(data, "text/xml") const response = new window.DOMParser().parseFromString(data, 'text/xml');
debugLog(response); debugLog(response);
let ret: MediaTypeModel[] = []; let ret: MediaTypeModel[] = [];
for (const boardgame of Array.from(response.querySelectorAll("boardgame"))) { for (const boardgame of Array.from(response.querySelectorAll('boardgame'))) {
const id = boardgame.attributes.getNamedItem("objectid")!.value; const id = boardgame.attributes.getNamedItem('objectid')!.value;
const title = boardgame.querySelector("name")!.textContent!; const title = boardgame.querySelector('name')!.textContent!;
const year = boardgame.querySelector("yearpublished")?.textContent ?? ""; const year = boardgame.querySelector('yearpublished')?.textContent ?? '';
ret.push(new BoardGameModel({ ret.push(new BoardGameModel({
dataSource: this.apiName, dataSource: this.apiName,
@ -68,21 +68,21 @@ export class BoardGameGeekAPI extends APIModel {
} }
const data = fetchData.text; const data = fetchData.text;
const response = new window.DOMParser().parseFromString(data, "text/xml") const response = new window.DOMParser().parseFromString(data, 'text/xml');
debugLog(response); debugLog(response);
const boardgame = response.querySelector("boardgame")!; const boardgame = response.querySelector('boardgame')!;
const title = boardgame.querySelector("name")!.textContent!; const title = boardgame.querySelector('name')!.textContent!;
const year = boardgame.querySelector("yearpublished")?.textContent ?? ""; const year = boardgame.querySelector('yearpublished')?.textContent ?? '';
const image = boardgame.querySelector("image")?.textContent ?? undefined; const image = boardgame.querySelector('image')?.textContent ?? undefined;
const onlineRating = Number.parseFloat(boardgame.querySelector("statistics ratings average")?.textContent ?? ""); const onlineRating = Number.parseFloat(boardgame.querySelector('statistics ratings average')?.textContent ?? '');
const genres = Array.from(boardgame.querySelectorAll("boardgamecategory")).map(n => n!.textContent!); const genres = Array.from(boardgame.querySelectorAll('boardgamecategory')).map(n => n!.textContent!);
const model = new BoardGameModel({ const model = new BoardGameModel({
type: MediaType.BoardGame, type: MediaType.BoardGame,
title, title,
englishTitle: title, englishTitle: title,
year: year === "0" ? "" : year, year: year === '0' ? '' : year,
dataSource: this.apiName, dataSource: this.apiName,
url: `https://boardgamegeek.com/boardgame/${id}`, url: `https://boardgamegeek.com/boardgame/${id}`,
id, id,

View file

@ -305,7 +305,7 @@ export default class MediaDbPlugin extends Plugin {
const erroredFiles: { filePath: string, error: string }[] = []; const erroredFiles: { filePath: string, error: string }[] = [];
let canceled: boolean = false; let canceled: boolean = false;
const {selectedAPI, titleFieldName, appendContent} = await new Promise<{selectedAPI: string, titleFieldName: string, appendContent: boolean}>((resolve, reject) => { const {selectedAPI, titleFieldName, appendContent} = await new Promise<{ selectedAPI: string, titleFieldName: string, appendContent: boolean }>((resolve, reject) => {
new MediaDbFolderImportModal(this.app, this, ((selectedAPI: string, titleFieldName: string, appendContent: boolean) => { new MediaDbFolderImportModal(this.app, this, ((selectedAPI: string, titleFieldName: string, appendContent: boolean) => {
resolve({selectedAPI, titleFieldName, appendContent}); resolve({selectedAPI, titleFieldName, appendContent});
})).open(); })).open();
@ -402,7 +402,7 @@ export default class MediaDbPlugin extends Plugin {
if (err) { if (err) {
return reject(err); return reject(err);
} }
resolve(res) resolve(res);
}).open(); }).open();
}); });
} }
@ -413,7 +413,7 @@ export default class MediaDbPlugin extends Plugin {
if (err) { if (err) {
return reject(err); return reject(err);
} }
resolve(res) resolve(res);
}).open(); }).open();
}); });
} }
@ -426,7 +426,7 @@ export default class MediaDbPlugin extends Plugin {
} }
resolve(res); resolve(res);
}, () => { }, () => {
resolve([]) resolve([]);
}).open(); }).open();
}); });
} }

View file

@ -8,10 +8,10 @@ export class MediaDbAdvancedSearchModal extends Modal {
isBusy: boolean; isBusy: boolean;
plugin: MediaDbPlugin; plugin: MediaDbPlugin;
searchBtn: ButtonComponent; searchBtn: ButtonComponent;
selectedApis: {name: string, selected: boolean}[]; selectedApis: { name: string, selected: boolean }[];
onSubmit: (res: {query: string, apis: string[]}, err?: Error) => void; onSubmit: (res: { query: string, apis: string[] }, err?: Error) => void;
constructor(app: App, plugin: MediaDbPlugin, onSubmit?: (res: {query: string, apis: string[]}, err?: Error) => void) { constructor(app: App, plugin: MediaDbPlugin, onSubmit?: (res: { query: string, apis: string[] }, err?: Error) => void) {
super(app); super(app);
this.plugin = plugin; this.plugin = plugin;
this.onSubmit = onSubmit; this.onSubmit = onSubmit;

View file

@ -9,9 +9,9 @@ export class MediaDbIdSearchModal extends Modal {
plugin: MediaDbPlugin; plugin: MediaDbPlugin;
searchBtn: ButtonComponent; searchBtn: ButtonComponent;
selectedApi: string; selectedApi: string;
onSubmit: (res: {query: string, api: string}, err?: Error) => void; onSubmit: (res: { query: string, api: string }, err?: Error) => void;
constructor(app: App, plugin: MediaDbPlugin, onSubmit?: (res: {query: string, api: string}, err?: Error) => void) { constructor(app: App, plugin: MediaDbPlugin, onSubmit?: (res: { query: string, api: string }, err?: Error) => void) {
super(app); super(app);
this.plugin = plugin; this.plugin = plugin;
this.onSubmit = onSubmit; this.onSubmit = onSubmit;

View file

@ -39,8 +39,8 @@ export class MusicReleaseModel extends MediaTypeModel {
getSummary(): string { getSummary(): string {
var summary = this.title + ' (' + this.year + ')'; var summary = this.title + ' (' + this.year + ')';
if(this.artists.length > 0) if (this.artists.length > 0)
summary += ' - ' + this.artists.join(', ') summary += ' - ' + this.artists.join(', ');
return summary; return summary;
} }
} }

View file

@ -1,77 +1,77 @@
import fetchMock, { enableFetchMocks } from 'jest-fetch-mock'; import fetchMock, {enableFetchMocks} from 'jest-fetch-mock';
import { MediaDbPluginSettings } from 'src/settings/Settings'; import {MediaDbPluginSettings} from 'src/settings/Settings';
import { LocGovAPI } from '../api/apis/LocGovAPI'; import {LocGovAPI} from '../api/apis/LocGovAPI';
import { MALAPI } from '../api/apis/MALAPI'; import {MALAPI} from '../api/apis/MALAPI';
import { MusicBrainzAPI } from '../api/apis/MusicBrainzAPI'; import {MusicBrainzAPI} from '../api/apis/MusicBrainzAPI';
import { OMDbAPI } from '../api/apis/OMDbAPI'; import {OMDbAPI} from '../api/apis/OMDbAPI';
import { SteamAPI } from '../api/apis/SteamAPI'; import {SteamAPI} from '../api/apis/SteamAPI';
import { WikipediaAPI } from '../api/apis/WikipediaAPI'; import {WikipediaAPI} from '../api/apis/WikipediaAPI';
import MediaDbPlugin from '../main'; import MediaDbPlugin from '../main';
import { setMALResponseMock, setMusicBrainzResponseMock, setOMDbResponseMock, setSteamResponseMock, setWikipediaResponseMock } from "./mockHelpers"; import {setMALResponseMock, setMusicBrainzResponseMock, setOMDbResponseMock, setSteamResponseMock, setWikipediaResponseMock} from './mockHelpers';
import MALMockMovie from "./ResponseMocks/MALMockMovie.json"; import MALMockMovie from './ResponseMocks/MALMockMovie.json';
import MusicBrainzResponseMock from "./ResponseMocks/MusicBrainzMockResponse.json"; import MusicBrainzResponseMock from './ResponseMocks/MusicBrainzMockResponse.json';
import OMDBMockMovie from "./ResponseMocks/OMDBMockResponse.json"; import OMDBMockMovie from './ResponseMocks/OMDBMockResponse.json';
import SteamAPIResponseMock from "./ResponseMocks/SteamAPIMockResponse.json"; import SteamAPIResponseMock from './ResponseMocks/SteamAPIMockResponse.json';
import WikipediaMockResponse from "./ResponseMocks/WikipediaMockResponse.json"; import WikipediaMockResponse from './ResponseMocks/WikipediaMockResponse.json';
enableFetchMocks(); enableFetchMocks();
export let apiMock: OMDbAPI | MALAPI | LocGovAPI | MusicBrainzAPI | SteamAPI | WikipediaAPI; export let apiMock: OMDbAPI | MALAPI | LocGovAPI | MusicBrainzAPI | SteamAPI | WikipediaAPI;
describe.each( describe.each(
[ [
{ name: OMDbAPI }, {name: OMDbAPI},
{ name: MALAPI }, {name: MALAPI},
{ name: LocGovAPI }, {name: LocGovAPI},
{ name: MusicBrainzAPI }, {name: MusicBrainzAPI},
{ name: SteamAPI }, {name: SteamAPI},
{ name: WikipediaAPI } {name: WikipediaAPI},
] ],
)('$name.name', ({ name: parameterizedApi }) => { )('$name.name', ({name: parameterizedApi}) => {
beforeAll(() => { beforeAll(() => {
let settingsMock: MediaDbPluginSettings = {} as MediaDbPluginSettings; let settingsMock: MediaDbPluginSettings = {} as MediaDbPluginSettings;
let pluginMock = {} as MediaDbPlugin; let pluginMock = {} as MediaDbPlugin;
pluginMock.settings = settingsMock; pluginMock.settings = settingsMock;
// TODO: add fake API key? // TODO: add fake API key?
apiMock = new parameterizedApi(pluginMock); apiMock = new parameterizedApi(pluginMock);
}) });
beforeEach(() => { beforeEach(() => {
fetchMock.resetMocks(); fetchMock.resetMocks();
}) });
test("searchByTitle behavior when API returns garbage data", async () => { test('searchByTitle behavior when API returns garbage data', async () => {
const garbageResponse = JSON.stringify({ const garbageResponse = JSON.stringify({
data: "string" data: 'string',
}); });
fetchMock.mockResponseOnce(garbageResponse) fetchMock.mockResponseOnce(garbageResponse);
await expect(apiMock.searchByTitle("sample")).resolves.toEqual([]); await expect(apiMock.searchByTitle('sample')).resolves.toEqual([]);
// } // }
expect(fetch).toHaveBeenCalledTimes(1); expect(fetch).toHaveBeenCalledTimes(1);
}); });
test("searchByTitle behavior when requestUrl/fetch returns 401", async () => { test('searchByTitle behavior when requestUrl/fetch returns 401', async () => {
let sampleResponse = { let sampleResponse = {
data: "string" data: 'string',
}; };
fetchMock.mockResponse(JSON.stringify(sampleResponse), { status: 401 }); fetchMock.mockResponse(JSON.stringify(sampleResponse), {status: 401});
// TODO: Check API name and fix message // TODO: Check API name and fix message
// TODO: Externalize string // TODO: Externalize string
await expect(apiMock.searchByTitle("sample")).rejects.toThrow(`MDB | Received status code ${401} from an API.`); await expect(apiMock.searchByTitle('sample')).rejects.toThrow(`MDB | Received status code ${401} from an API.`);
expect(fetchMock).toHaveBeenCalledTimes(1); expect(fetchMock).toHaveBeenCalledTimes(1);
}); });
test("searchByTitle behavior when requestUrl/fetch returns 403", async () => { test('searchByTitle behavior when requestUrl/fetch returns 403', async () => {
let sampleResponse = { let sampleResponse = {
data: "string" data: 'string',
}; };
fetchMock.mockResponse(JSON.stringify(sampleResponse), { status: 403 }); fetchMock.mockResponse(JSON.stringify(sampleResponse), {status: 403});
// TODO: Check API name and fix message // TODO: Check API name and fix message
// TODO: Externalize string/import? // TODO: Externalize string/import?
await expect(apiMock.searchByTitle("sample")).rejects.toThrow(`MDB | Received status code ${403} from an API.`); await expect(apiMock.searchByTitle('sample')).rejects.toThrow(`MDB | Received status code ${403} from an API.`);
expect(fetchMock).toHaveBeenCalledTimes(1); expect(fetchMock).toHaveBeenCalledTimes(1);
}); });
test("searchByTitle behavior when requestUrl/fetch returns 200", async () => { test('searchByTitle behavior when requestUrl/fetch returns 200', async () => {
let sampleResponse; let sampleResponse;
let ret; let ret;
switch (parameterizedApi) { switch (parameterizedApi) {
@ -101,10 +101,10 @@ describe.each(
default: default:
throw Error(); throw Error();
} }
fetchMock.mockResponse(JSON.stringify(sampleResponse), { status: 200 }); fetchMock.mockResponse(JSON.stringify(sampleResponse), {status: 200});
// TODO: Check API name and fix message // TODO: Check API name and fix message
// TODO: Externalize string // TODO: Externalize string
await expect(apiMock.searchByTitle("Hooking Season Playtest")).resolves.toEqual(ret); await expect(apiMock.searchByTitle('Hooking Season Playtest')).resolves.toEqual(ret);
expect(fetchMock).toHaveBeenCalledTimes(1); expect(fetchMock).toHaveBeenCalledTimes(1);
}); });
}) });

View file

@ -1,25 +1,25 @@
{ {
"release-groups": [ "release-groups": [
{ {
"id": "9cf08bf9-1948-4087-abe1-783210ea1fae", "id": "9cf08bf9-1948-4087-abe1-783210ea1fae",
"primary-type-id": "f529b476-6e62-324f-b0aa-1f3e33d313fc", "primary-type-id": "f529b476-6e62-324f-b0aa-1f3e33d313fc",
"title": "Halo Halo", "title": "Halo Halo",
"first-release-date": "2013-07-08", "first-release-date": "2013-07-08",
"primary-type": "Album", "primary-type": "Album",
"artist-credit": [ "artist-credit": [
{ {
"name": "Halo Halo", "name": "Halo Halo",
"artist": { "artist": {
"name": "Halo Halo" "name": "Halo Halo"
} }
} }
], ],
"releases": [ "releases": [
{ {
"id": "58dd1d57-2201-472e-9e36-5d497dcedb6f", "id": "58dd1d57-2201-472e-9e36-5d497dcedb6f",
"title": "Halo Halo" "title": "Halo Halo"
} }
] ]
} }
] ]
} }

View file

@ -7,7 +7,7 @@
"Type": "movie", "Type": "movie",
"Poster": "https://m.media-amazon.com/images/M/MV5BMTAwMjU5OTgxNjZeQTJeQWpwZ15BbWU4MDUxNDYxODEx._V1_SX300.jpg" "Poster": "https://m.media-amazon.com/images/M/MV5BMTAwMjU5OTgxNjZeQTJeQWpwZ15BbWU4MDUxNDYxODEx._V1_SX300.jpg"
} }
], ],
"totalResults": "1", "totalResults": "1",
"Response": "True" "Response": "True"
} }

View file

@ -1,14 +1,14 @@
{ {
"applist": { "applist": {
"apps": [ "apps": [
{ {
"appid": 2076590, "appid": 2076590,
"name": "Hooking Season Playtest" "name": "Hooking Season Playtest"
}, },
{ {
"appid": 2076600, "appid": 2076600,
"name": "MonsterTamer" "name": "MonsterTamer"
} }
] ]
} }
} }

View file

@ -1,20 +1,20 @@
{ {
"query": { "query": {
"searchinfo": { "searchinfo": {
"totalhits": 1199001, "totalhits": 1199001,
"suggestion": "book", "suggestion": "book",
"suggestionsnippet": "book" "suggestionsnippet": "book"
}, },
"search": [ "search": [
{ {
"ns": 0, "ns": 0,
"title": "Book", "title": "Book",
"pageid": 3778, "pageid": 3778,
"size": 68829, "size": 68829,
"wordcount": 8821, "wordcount": 8821,
"snippet": "called <span class=\"searchmatch\">books</span> or chapters or parts, are parts. The intellectual content in a physical book need not be a composition, nor even be called a book. <span class=\"searchmatch\">Books</span> can", "snippet": "called <span class=\"searchmatch\">books</span> or chapters or parts, are parts. The intellectual content in a physical book need not be a composition, nor even be called a book. <span class=\"searchmatch\">Books</span> can",
"timestamp": "2022-08-19T19:13:56Z" "timestamp": "2022-08-19T19:13:56Z"
} }
] ]
} }
} }

View file

@ -1,14 +1,14 @@
import { GameModel } from "../models/GameModel"; import {GameModel} from '../models/GameModel';
import { MovieModel } from '../models/MovieModel'; import {MovieModel} from '../models/MovieModel';
import { MusicReleaseModel } from "../models/MusicReleaseModel"; import {MusicReleaseModel} from '../models/MusicReleaseModel';
import { WikiModel } from '../models/WikiModel'; import {WikiModel} from '../models/WikiModel';
import { MediaType } from "../utils/MediaType"; import {MediaType} from '../utils/MediaType';
import { apiMock } from "./ParameterizedAPI.test"; import {apiMock} from './ParameterizedAPI.test';
import MALMockMovie from "./ResponseMocks/MALMockMovie.json"; import MALMockMovie from './ResponseMocks/MALMockMovie.json';
import MusicBrainzResponseMock from "./ResponseMocks/MusicBrainzMockResponse.json"; import MusicBrainzResponseMock from './ResponseMocks/MusicBrainzMockResponse.json';
import OMDBMockMovie from "./ResponseMocks/OMDBMockResponse.json"; import OMDBMockMovie from './ResponseMocks/OMDBMockResponse.json';
import SteamAPIResponseMock from "./ResponseMocks/SteamAPIMockResponse.json"; import SteamAPIResponseMock from './ResponseMocks/SteamAPIMockResponse.json';
import WikipediaMockResponse from "./ResponseMocks/WikipediaMockResponse.json"; import WikipediaMockResponse from './ResponseMocks/WikipediaMockResponse.json';
export function setWikipediaResponseMock() { export function setWikipediaResponseMock() {
let ret = []; let ret = [];
@ -55,33 +55,31 @@ export function setMALResponseMock() {
export function setSteamResponseMock() { export function setSteamResponseMock() {
let ret = []; let ret = [];
let steamResponse = SteamAPIResponseMock.applist.apps[0]; let steamResponse = SteamAPIResponseMock.applist.apps[0];
ret.push( ret.push(new GameModel({
new GameModel({ type: MediaType.Game,
type: MediaType.Game, title: steamResponse.name,
title: steamResponse.name, englishTitle: steamResponse.name,
englishTitle: steamResponse.name, year: '',
year: '', dataSource: apiMock.apiName,
dataSource: apiMock.apiName, id: steamResponse.appid,
id: steamResponse.appid, }));
})
)
return ret; return ret;
} }
export function setMusicBrainzResponseMock() { export function setMusicBrainzResponseMock() {
let ret = []; let ret = [];
let result = MusicBrainzResponseMock["release-groups"][0]; let result = MusicBrainzResponseMock['release-groups'][0];
ret.push(new MusicReleaseModel({ ret.push(new MusicReleaseModel({
type: 'musicRelease', type: 'musicRelease',
title: result.title, title: result.title,
englishTitle: result.title, englishTitle: result.title,
year: (new Date(result['first-release-date'])).getFullYear().toString(), year: (new Date(result['first-release-date'])).getFullYear().toString(),
dataSource: apiMock.apiName, dataSource: apiMock.apiName,
url: '', url: '',
id: result.id, id: result.id,
artists: result['artist-credit'].map((a: any) => a.name), artists: result['artist-credit'].map((a: any) => a.name),
subType: result['primary-type'], subType: result['primary-type'],
} as MusicReleaseModel)); } as MusicReleaseModel));
return ret; return ret;
} }

View file

@ -1,24 +1,29 @@
import { wrapAround, containsOnlyLettersAndUnderscores, replaceIllegalFileNameCharactersInString} from '../utils/Utils'; import {containsOnlyLettersAndUnderscores, replaceIllegalFileNameCharactersInString, wrapAround} from '../utils/Utils';
test('If wrapAround wraps correctly', () => { test('If wrapAround wraps correctly', () => {
expect(wrapAround(100,5)).toBe(0); expect(wrapAround(100, 5)).toBe(0);
expect(wrapAround(100,7)).toBe(2); expect(wrapAround(100, 7)).toBe(2);
}); });
test('If wrapAround errors out when dividing by zero', () => { test('If wrapAround errors out when dividing by zero', () => {
expect(wrapAround(100,0)).toThrow(); expect(wrapAround(100, 0)).toThrow();
});
test('If wrapAround errors out when size is negative', () => {
expect(wrapAround(100, -5)).toThrow();
}); });
test('Letter and underscore string validity', () => { test('Letter and underscore string validity', () => {
expect(containsOnlyLettersAndUnderscores("asdkfj_")).toBe(true); expect(containsOnlyLettersAndUnderscores('asdkfj_')).toBe(true);
expect(containsOnlyLettersAndUnderscores("asdkfj0")).toBe(false); expect(containsOnlyLettersAndUnderscores('asdkfj0')).toBe(false);
}); });
test('Letter and underscore unicode char test', () =>{ // since this is used to check if a string is a valid name for an object property, unicode characters shouldn't be allowed, thus the name of the function is misleading
expect(containsOnlyLettersAndUnderscores("asdkaÈj")).toBe(true); test('Letter and underscore unicode char test', () => {
expect(containsOnlyLettersAndUnderscores("asdkaÈj0")).toBe(false); expect(containsOnlyLettersAndUnderscores('asdkaÈj')).toBe(true);
expect(containsOnlyLettersAndUnderscores('asdkaÈj0')).toBe(false);
}); });
test('Valid filename test', ()=>{ test('Valid filename test', () => {
expect(replaceIllegalFileNameCharactersInString("what?is\\this:")).toBe("whatisthis -"); expect(replaceIllegalFileNameCharactersInString('what?is\\this:')).toBe('whatisthis -');
}) });

View file

@ -8,7 +8,10 @@ export const mediaDbVersion: string = '0.3.2';
export const debug: boolean = true; export const debug: boolean = true;
export function wrapAround(value: number, size: number): number { export function wrapAround(value: number, size: number): number {
return ((value % size) + size) % size; if (size <= 0) {
throw Error('size may not be zero or negative');
}
return mod(value, size);
} }
export function debugLog(o: any): void { export function debugLog(o: any): void {