Skip to content

Commit 292dcf5

Browse files
committed
update deps; make apis more type safe
1 parent 9518ede commit 292dcf5

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+26911
-550
lines changed

automation/fetchSchemas.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { $ } from 'utils/shellUtils';
2+
3+
async function fetchSchema() {
4+
// https://docs.api.jikan.moe/
5+
await $('bun openapi-typescript https://raw.githubusercontent.com/jikan-me/jikan-rest/master/storage/api-docs/api-docs.json -o ./src/api/schemas/MALAPI.ts');
6+
7+
// https://www.giantbomb.com/forums/api-developers-3017/giant-bomb-openapi-specification-1901269/
8+
await $('bun openapi-typescript ./src/api/schemas/GiantBomb.json -o ./src/api/schemas/GiantBomb.ts');
9+
10+
// https://www.omdbapi.com/swagger.json
11+
await $('bun openapi-typescript ./src/api/schemas/OMDb.json -o ./src/api/schemas/OMDb.ts');
12+
13+
// https://github.com/internetarchive/openlibrary-api/blob/main/swagger.yaml
14+
await $('bun openapi-typescript ./src/api/schemas/OpenLibrary.json -o ./src/api/schemas/OpenLibrary.ts');
15+
}
16+
17+
await fetchSchema();

bun.lockb

29.8 KB
Binary file not shown.

eslint.config.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import * as plugin_import from 'eslint-plugin-import';
77

88
export default tseslint.config(
99
{
10-
ignores: ['npm/', 'node_modules/', 'exampleVault/', 'automation/', 'main.js', '*.svelte'],
10+
ignores: ['npm/', 'node_modules/', 'exampleVault/', 'automation/', 'main.js', '*.svelte', 'src/api/schemas/'],
1111
},
1212
{
1313
files: ['src/**/*.ts'],

package.json

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,35 +14,37 @@
1414
"lint": "eslint --max-warnings=0 src/**",
1515
"lint:fix": "eslint --max-warnings=0 --fix src/**",
1616
"svelte-check": "svelte-check --compiler-warnings \"unused-export-let:ignore\"",
17-
"check": "bun run format:check && bun run tsc && bun run test",
18-
"check:fix": "bun run format && bun run tsc && bun run test",
17+
"check": "bun run format:check && bun run tsc && bun run lint && bun run svelte-check",
18+
"check:fix": "bun run format && bun run tsc && bun run lint:fix && bun run svelte-check",
1919
"release": "bun run automation/release.ts",
2020
"stats": "bun run automation/stats.ts"
2121
},
2222
"keywords": [],
2323
"author": "Moritz Jung",
2424
"license": "GPL-3.0",
2525
"devDependencies": {
26-
"@popperjs/core": "^2.11.8",
26+
"@happy-dom/global-registrator": "^18.0.1",
2727
"@lemons_dev/parsinom": "^0.0.12",
28-
"@happy-dom/global-registrator": "^14.12.3",
29-
"@types/bun": "^1.1.16",
30-
"builtin-modules": "^4.0.0",
31-
"esbuild": "^0.24.2",
28+
"@popperjs/core": "^2.11.8",
29+
"@types/bun": "^1.2.19",
30+
"builtin-modules": "^5.0.0",
31+
"esbuild": "^0.25.8",
3232
"esbuild-plugin-copy-watch": "^2.3.1",
33-
"esbuild-svelte": "^0.8.2",
34-
"eslint": "^9.18.0",
35-
"eslint-plugin-import": "^2.31.0",
33+
"esbuild-svelte": "^0.9.3",
34+
"eslint": "^9.32.0",
35+
"eslint-plugin-import": "^2.32.0",
3636
"eslint-plugin-only-warn": "^1.1.0",
3737
"obsidian": "latest",
38-
"prettier": "^3.4.2",
39-
"prettier-plugin-svelte": "^3.3.3",
38+
"openapi-fetch": "^0.14.0",
39+
"openapi-typescript": "^7.8.0",
40+
"prettier": "^3.6.2",
41+
"prettier-plugin-svelte": "^3.4.0",
4042
"string-argv": "^0.3.2",
41-
"svelte": "^5.17.5",
42-
"svelte-check": "^4.1.4",
43+
"svelte": "^5.38.0",
44+
"svelte-check": "^4.3.1",
4345
"svelte-preprocess": "^6.0.3",
4446
"tslib": "^2.8.1",
45-
"typescript": "^5.7.3",
46-
"typescript-eslint": "^8.20.0"
47+
"typescript": "^5.9.2",
48+
"typescript-eslint": "^8.39.0"
4749
}
4850
}

src/api/apis/BoardGameGeekAPI.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import type { MediaTypeModel } from '../../models/MediaTypeModel';
55
import { MediaType } from '../../utils/MediaType';
66
import { APIModel } from '../APIModel';
77

8+
// sadly no open api schema available
9+
810
export class BoardGameGeekAPI extends APIModel {
911
plugin: MediaDbPlugin;
1012

@@ -118,6 +120,6 @@ export class BoardGameGeekAPI extends APIModel {
118120
});
119121
}
120122
getDisabledMediaTypes(): MediaType[] {
121-
return this.plugin.settings.BoardgameGeekAPI_disabledMediaTypes as MediaType[];
123+
return this.plugin.settings.BoardgameGeekAPI_disabledMediaTypes;
122124
}
123125
}

src/api/apis/ComicVineAPI.ts

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
1+
/* eslint-disable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access */
2+
13
import { requestUrl } from 'obsidian';
24
import { ComicMangaModel } from 'src/models/ComicMangaModel';
35
import type MediaDbPlugin from '../../main';
46
import type { MediaTypeModel } from '../../models/MediaTypeModel';
57
import { MediaType } from '../../utils/MediaType';
68
import { APIModel } from '../APIModel';
79

10+
// sadly no open api schema available
11+
812
export class ComicVineAPI extends APIModel {
913
plugin: MediaDbPlugin;
1014

@@ -41,7 +45,7 @@ export class ComicVineAPI extends APIModel {
4145
year: result.start_year,
4246
dataSource: this.apiName,
4347
id: `4050-${result.id}`,
44-
publishers: result.publisher?.name ?? [],
48+
publishers: result.publisher?.name,
4549
}),
4650
);
4751
}
@@ -64,28 +68,32 @@ export class ComicVineAPI extends APIModel {
6468
}
6569

6670
const data = await fetchData.json;
67-
// console.debug(data);
6871
const result = data.results;
6972

73+
const authors = result.people as
74+
| {
75+
name: string;
76+
}[]
77+
| undefined;
78+
7079
return new ComicMangaModel({
7180
type: MediaType.ComicManga,
7281
title: result.name,
7382
englishTitle: result.name,
7483
alternateTitles: result.aliases,
7584
plot: result.deck,
76-
year: result.start_year ?? '',
85+
year: result.start_year,
7786
dataSource: this.apiName,
7887
url: result.site_detail_url,
7988
id: `4050-${result.id}`,
8089

81-
authors: result.people?.map((x: any) => x.name) ?? [],
90+
authors: authors?.map(x => x.name),
8291
chapters: result.count_of_issues,
83-
image: result.image?.original_url ?? '',
92+
image: result.image?.original_url,
8493

8594
released: true,
86-
publishers: result.publisher?.name ?? [],
87-
publishedFrom: result.start_year ?? 'unknown',
88-
publishedTo: 'unknown',
95+
publishers: result.publisher?.name,
96+
publishedFrom: result.start_year,
8997
status: result.status,
9098

9199
userData: {
@@ -96,6 +104,6 @@ export class ComicVineAPI extends APIModel {
96104
});
97105
}
98106
getDisabledMediaTypes(): MediaType[] {
99-
return this.plugin.settings.ComicVineAPI_disabledMediaTypes as MediaType[];
107+
return this.plugin.settings.ComicVineAPI_disabledMediaTypes;
100108
}
101109
}

src/api/apis/GiantBombAPI.ts

Lines changed: 86 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1-
import { requestUrl } from 'obsidian';
1+
import createClient from 'openapi-fetch';
2+
import { obsidianFetch } from 'src/utils/Utils';
23
import type MediaDbPlugin from '../../main';
34
import { GameModel } from '../../models/GameModel';
45
import type { MediaTypeModel } from '../../models/MediaTypeModel';
56
import { MediaType } from '../../utils/MediaType';
67
import { APIModel } from '../APIModel';
8+
import type { paths } from '../schemas/GiantBomb';
79

810
export class GiantBombAPI extends APIModel {
911
plugin: MediaDbPlugin;
@@ -18,42 +20,50 @@ export class GiantBombAPI extends APIModel {
1820
this.apiUrl = 'https://www.giantbomb.com/api';
1921
this.types = [MediaType.Game];
2022
}
23+
2124
async searchByTitle(title: string): Promise<MediaTypeModel[]> {
2225
console.log(`MDB | api "${this.apiName}" queried by Title`);
2326

2427
if (!this.plugin.settings.GiantBombKey) {
2528
throw Error(`MDB | API key for ${this.apiName} missing.`);
2629
}
2730

28-
const searchUrl = `${this.apiUrl}/games?api_key=${this.plugin.settings.GiantBombKey}&filter=name:${encodeURIComponent(title)}&format=json`;
29-
const fetchData = await requestUrl({
30-
url: searchUrl,
31+
const client = createClient<paths>({ baseUrl: 'https://www.giantbomb.com/api/' });
32+
const response = await client.GET('/games', {
33+
params: {
34+
query: {
35+
api_key: this.plugin.settings.GiantBombKey,
36+
filter: `name:${encodeURIComponent(title)}`,
37+
format: 'json',
38+
limit: 20,
39+
},
40+
},
41+
fetch: obsidianFetch,
3142
});
3243

33-
// console.debug(fetchData);
34-
35-
if (fetchData.status === 401) {
44+
if (response.response.status === 401) {
3645
throw Error(`MDB | Authentication for ${this.apiName} failed. Check the API key.`);
3746
}
38-
if (fetchData.status === 429) {
47+
if (response.response.status === 429) {
3948
throw Error(`MDB | Too many requests for ${this.apiName}, you've exceeded your API quota.`);
4049
}
41-
if (fetchData.status !== 200) {
42-
throw Error(`MDB | Received status code ${fetchData.status} from ${this.apiName}.`);
50+
if (response.response.status !== 200) {
51+
throw Error(`MDB | Received status code ${response.response.status} from ${this.apiName}.`);
4352
}
4453

45-
const data = await fetchData.json;
46-
// console.debug(data);
54+
const data = response.data?.results;
55+
4756
const ret: MediaTypeModel[] = [];
48-
for (const result of data.results) {
57+
for (const result of data ?? []) {
58+
const year = result.original_release_date ? new Date(result.original_release_date).getFullYear().toString() : undefined;
59+
4960
ret.push(
5061
new GameModel({
51-
type: MediaType.Game,
5262
title: result.name,
5363
englishTitle: result.name,
54-
year: new Date(result.original_release_date).getFullYear().toString(),
64+
year: year,
5565
dataSource: this.apiName,
56-
id: result.guid,
66+
id: result.guid?.toString(),
5767
}),
5868
);
5969
}
@@ -68,36 +78,79 @@ export class GiantBombAPI extends APIModel {
6878
throw Error(`MDB | API key for ${this.apiName} missing.`);
6979
}
7080

71-
const searchUrl = `${this.apiUrl}/game/${encodeURIComponent(id)}/?api_key=${this.plugin.settings.GiantBombKey}&format=json`;
72-
const fetchData = await requestUrl({
73-
url: searchUrl,
81+
const client = createClient<paths>({ baseUrl: 'https://www.giantbomb.com/api/' });
82+
const response = await client.GET('/game/{guid}', {
83+
params: {
84+
path: {
85+
guid: id,
86+
},
87+
query: {
88+
api_key: this.plugin.settings.GiantBombKey,
89+
format: 'json',
90+
},
91+
},
92+
fetch: obsidianFetch,
7493
});
75-
console.debug(fetchData);
7694

77-
if (fetchData.status !== 200) {
78-
throw Error(`MDB | Received status code ${fetchData.status} from ${this.apiName}.`);
95+
if (response.response.status === 401) {
96+
throw Error(`MDB | Authentication for ${this.apiName} failed. Check the API key.`);
97+
}
98+
if (response.response.status === 429) {
99+
throw Error(`MDB | Too many requests for ${this.apiName}, you've exceeded your API quota.`);
100+
}
101+
if (response.response.status !== 200) {
102+
throw Error(`MDB | Received status code ${response.response.status} from ${this.apiName}.`);
103+
}
104+
105+
const result = response.data?.results;
106+
107+
if (!result) {
108+
throw Error(`MDB | No results found for ID ${id} in ${this.apiName}.`);
79109
}
80110

81-
const data = await fetchData.json;
82-
// console.debug(data);
83-
const result = data.results;
111+
console.log(result);
112+
113+
// sadly the only OpenAPI definition I could find doesn't have the right types
114+
const year = result.original_release_date ? new Date(result.original_release_date).getFullYear().toString() : undefined;
115+
const developers = result.developers as
116+
| {
117+
name: string;
118+
}[]
119+
| undefined;
120+
const publishers = result.publishers as
121+
| {
122+
name: string;
123+
}[]
124+
| undefined;
125+
const genres = result.genres as
126+
| {
127+
name: string;
128+
}[]
129+
| undefined;
130+
const image = result.image as
131+
| {
132+
small_url: string;
133+
medium_url: string;
134+
super_url: string;
135+
}
136+
| undefined;
84137

85138
return new GameModel({
86139
type: MediaType.Game,
87140
title: result.name,
88141
englishTitle: result.name,
89-
year: new Date(result.original_release_date).getFullYear().toString(),
142+
year: year,
90143
dataSource: this.apiName,
91144
url: result.site_detail_url,
92-
id: result.guid,
93-
developers: result.developers?.map((x: any) => x.name) ?? [],
94-
publishers: result.publishers?.map((x: any) => x.name) ?? [],
95-
genres: result.genres?.map((x: any) => x.name) ?? [],
145+
id: result.guid?.toString(),
146+
developers: developers?.map(x => x.name),
147+
publishers: publishers?.map(x => x.name),
148+
genres: genres?.map(x => x.name),
96149
onlineRating: 0,
97-
image: result.image?.super_url ?? '',
150+
image: image?.super_url,
98151

99152
released: true,
100-
releaseDate: result.original_release_date ?? 'unknown',
153+
releaseDate: result.original_release_date,
101154

102155
userData: {
103156
played: false,
@@ -107,6 +160,6 @@ export class GiantBombAPI extends APIModel {
107160
});
108161
}
109162
getDisabledMediaTypes(): MediaType[] {
110-
return this.plugin.settings.GiantBombAPI_disabledMediaTypes as MediaType[];
163+
return this.plugin.settings.GiantBombAPI_disabledMediaTypes;
111164
}
112165
}

0 commit comments

Comments
 (0)