Add simple error handling for when restaurant data cannot be fetched
This commit is contained in:
parent
400821bf82
commit
1ed1913197
5 changed files with 81 additions and 27 deletions
20
src/index.ts
20
src/index.ts
|
@ -1,7 +1,7 @@
|
|||
import { HTML_ID, RESTAURANTS, DAY_NAMES } from './config.js';
|
||||
import { getAllMenus } from './sodexo-api.js';
|
||||
import { getAllMenus, SodexoAPIError } from './sodexo-api.js';
|
||||
import { parseBlob } from './parser.js';
|
||||
import { renderMenu } from './view.js';
|
||||
import { renderMenu, renderError } from './view.js';
|
||||
import { el } from './dom.js';
|
||||
import { VERSION, SOURCE } from './version.js';
|
||||
|
||||
|
@ -14,7 +14,13 @@ async function init() {
|
|||
rootEl.innerText = 'Loading…';
|
||||
|
||||
const menus = await getAllMenus(RESTAURANTS);
|
||||
const parsedMenus = menus.map((m, idx) => parseBlob(m, RESTAURANTS[idx]));
|
||||
const parsedMenus = menus.map((m, idx) => {
|
||||
if (!(m instanceof SodexoAPIError)) {
|
||||
return parseBlob(m, RESTAURANTS[idx]);
|
||||
} else {
|
||||
return m;
|
||||
}
|
||||
});
|
||||
|
||||
rootEl.innerText = '';
|
||||
|
||||
|
@ -25,8 +31,11 @@ async function init() {
|
|||
);
|
||||
}
|
||||
|
||||
for (const menu of parsedMenus) {
|
||||
const [heading, ...elems] = renderMenu(menu);
|
||||
for (const data of parsedMenus) {
|
||||
if (data instanceof SodexoAPIError) {
|
||||
rootEl.appendChild(renderError(data));
|
||||
} else {
|
||||
const [heading, ...elems] = renderMenu(data);
|
||||
|
||||
rootEl.appendChild(heading);
|
||||
for (const day of DAY_NAMES) {
|
||||
|
@ -45,6 +54,7 @@ async function init() {
|
|||
}
|
||||
for (const elem of elems) { rootEl.appendChild(elem); }
|
||||
}
|
||||
}
|
||||
|
||||
const versionEl = el('div', { classes: ['version'], text: `Versio ${VERSION}. ` });
|
||||
versionEl.appendChild(
|
||||
|
|
|
@ -3,7 +3,14 @@ import { ServerBlob } from './types.js';
|
|||
|
||||
const API_URL = new URL('https://www.sodexo.fi/ruokalistat/output/weekly_json/');
|
||||
|
||||
export class SodexoAPIError extends Error { }
|
||||
export class SodexoAPIError extends Error {
|
||||
public restaurantId: RestaurantId;
|
||||
|
||||
constructor(message: string, restaurantId: RestaurantId) {
|
||||
super(message);
|
||||
this.restaurantId = restaurantId;
|
||||
}
|
||||
}
|
||||
|
||||
export async function getMenu(restaurantId: RestaurantId): Promise<ServerBlob> {
|
||||
const url = new URL(String(restaurantId), API_URL);
|
||||
|
@ -13,15 +20,28 @@ export async function getMenu(restaurantId: RestaurantId): Promise<ServerBlob> {
|
|||
return await resp.json();
|
||||
} else {
|
||||
console.error(resp);
|
||||
throw new SodexoAPIError(`Got invalid response: ${resp.status}.`);
|
||||
throw new SodexoAPIError(
|
||||
`Got invalid response: ${resp.status} ${resp.statusText}.`,
|
||||
restaurantId
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export async function getAllMenus(restaurants: readonly RestaurantId[]): Promise<ServerBlob[]> {
|
||||
const promises = [];
|
||||
export type MenuResult = ServerBlob | SodexoAPIError;
|
||||
|
||||
export async function getAllMenus(restaurants: readonly RestaurantId[]): Promise<MenuResult[]> {
|
||||
const promises: Promise<MenuResult>[] = [];
|
||||
|
||||
for (const restaurant of restaurants) {
|
||||
promises.push(getMenu(restaurant));
|
||||
promises.push(
|
||||
new Promise(async resolve => {
|
||||
try {
|
||||
resolve(await getMenu(restaurant));
|
||||
} catch (e) {
|
||||
resolve(e);
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
return await Promise.all(promises);
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
export const VERSION = '1.0.0';
|
||||
export const VERSION = '1.0.1';
|
||||
|
||||
export const SOURCE = 'https://gitlab.com/Nicd/sodexo-menu';
|
||||
|
|
12
src/view.ts
12
src/view.ts
|
@ -1,5 +1,7 @@
|
|||
import { MenuData, Course } from './types.js';
|
||||
import { el } from './dom.js';
|
||||
import { SodexoAPIError } from './sodexo-api.js';
|
||||
import { RESTAURANTS } from './config.js';
|
||||
|
||||
function renderCourse(course: Course): HTMLLIElement {
|
||||
const li = el('li');
|
||||
|
@ -44,3 +46,13 @@ export function renderMenu(menu: MenuData): HTMLElement[] {
|
|||
|
||||
return [heading, ...dayElements, metaElem];
|
||||
}
|
||||
|
||||
export function renderError(data: SodexoAPIError): HTMLElement {
|
||||
const errorDiv = el('div', { classes: ['error'] });
|
||||
errorDiv.appendChild(
|
||||
el('h2', { text: `Unable to fetch menu for "${RESTAURANTS[data.restaurantId]}":` })
|
||||
);
|
||||
errorDiv.appendChild(el('p', { text: data.message }));
|
||||
errorDiv.appendChild(el('p', { classes: ['stack'], text: data.stack }));
|
||||
return errorDiv;
|
||||
}
|
||||
|
|
14
style.css
14
style.css
|
@ -39,7 +39,7 @@ main {
|
|||
gap: 10px;
|
||||
}
|
||||
|
||||
h2, .meta {
|
||||
h2, .meta, .error {
|
||||
grid-column: span 5;
|
||||
}
|
||||
|
||||
|
@ -118,3 +118,15 @@ p.course-price {
|
|||
.version {
|
||||
font-size: 75%;
|
||||
}
|
||||
|
||||
.error h2 {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.error p {
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
.error .stack {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
|
Reference in a new issue