Catch and report API errors to user

This commit is contained in:
Edward Betts 2021-10-22 11:40:10 +01:00
parent e7f8884b7b
commit 20a95c73f2
1 changed files with 88 additions and 24 deletions

View File

@ -1,6 +1,29 @@
<template> <template>
<div> <div>
<div class="modal fade" ref="error_modal" tabindex="-1">
<div class="modal-dialog modal-dialog-scrollable modal-xl">
<div class="modal-content">
<div class="modal-header bg-danger text-white">
<h5 class="modal-title">
<span v-if="api_call_error_message">{{ api_call_error_message }}</span>
<span v-else>Error from server</span>
</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<div v-if="api_call_error_traceback">
<div>error message: {{ api_call_error_message }}</div>
<div class="mt-2"><pre>{{ api_call_error_traceback }}</pre></div>
</div>
</div>
<div class="modal-footer bg-danger">
<button type="button" class="btn btn-primary" data-bs-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<div id="map"> <div id="map">
</div> </div>
@ -740,6 +763,10 @@ export default {
bounds_done: [], bounds_done: [],
zoom_on_open: false, zoom_on_open: false,
selected_marker: undefined, selected_marker: undefined,
debug_info: [],
map_update_number: 0,
api_call_error_message: undefined,
api_call_error_traceback: undefined,
}; };
}, },
computed: { computed: {
@ -905,6 +932,10 @@ export default {
} }
}, },
methods: { methods: {
api_call(endpoint, options) {
var url = `${this.api_base_url}/api/1/${endpoint}`;
return axios.get(url, options).catch(this.show_api_error_modal);
},
update_unload_warning(edit_list) { update_unload_warning(edit_list) {
if (edit_list.length) { if (edit_list.length) {
addEventListener("beforeunload", beforeUnloadListener, {capture: true}); addEventListener("beforeunload", beforeUnloadListener, {capture: true});
@ -935,6 +966,22 @@ export default {
bounds_param() { bounds_param() {
return 'bounds=' + this.map.getBounds().toBBoxString(); return 'bounds=' + this.map.getBounds().toBBoxString();
}, },
show_api_error_modal(error) {
var reply = error.data;
this.api_call_error_message = reply.error;
this.api_call_error_traceback = reply.traceback;
var error_modal = new bootstrap.Modal(this.$refs.error_modal);
this.$refs.error_modal.addEventListener('hidden.bs.modal', function (event) {
this.api_call_error_message = undefined;
this.api_call_error_traceback = undefined;
})
error_modal.show();
},
test_api_error() {
this.api_call("error");
},
close_edit_list() { close_edit_list() {
this.view_edits = false; this.view_edits = false;
if (this.upload_state != 'done') return; if (this.upload_state != 'done') return;
@ -1223,15 +1270,13 @@ export default {
if (item.detail_requested !== undefined) return; if (item.detail_requested !== undefined) return;
item.detail_requested = true; item.detail_requested = true;
var item_tags_url = `${this.api_base_url}/api/1/item/${qid}/tags`; this.api_call(`item/${qid}/tags`).then((response) => {
axios.get(item_tags_url).then((response) => {
var qid = response.data.qid; var qid = response.data.qid;
this.items[qid].tag_or_key_list = response.data.tag_or_key_list; this.items[qid].tag_or_key_list = response.data.tag_or_key_list;
this.items[qid].tag_src = response.data.tag_src;
}); });
var item_osm_candidates_url = `${this.api_base_url}/api/1/item/${qid}/candidates`; this.api_call(`item/${qid}/candidates`).then((response) => {
axios.get(item_osm_candidates_url).then((response) => {
var qid = response.data.qid; var qid = response.data.qid;
var item = this.items[qid]; var item = this.items[qid];
var osm_identifiers = item.osm ? Object.keys(item.osm) : []; var osm_identifiers = item.osm ? Object.keys(item.osm) : [];
@ -1388,18 +1433,18 @@ export default {
this.wikidata_loading = true; this.wikidata_loading = true;
this.osm_loading = true; this.osm_loading = true;
bounds ||= this.map.getBounds(); var params = { bounds: this.bbox_string(bounds) };
var items_url = this.api_base_url + "/api/1/items";
var osm_objects_url = this.api_base_url + "/api/1/osm";
var params = { bounds: bounds.toBBoxString() };
if (this.item_type_filters.length) { if (this.item_type_filters.length) {
params["isa"] = this.item_type_filters.map(isa => isa.qid).join(","); params["isa"] = this.item_type_filters.map(isa => isa.qid).join(",");
} }
axios.get(items_url, { params: params }).then((response) => { this.debug_info.push(`${this_update}: get items ` + this.bbox_string(bounds));
this.api_call("items", { params: params }).then((response) => {
if (this.map_update_number > this_update) {
this.debug_info.push(`${this_update}: got items, took ${response.data.duration.toFixed(4)} seconds. ignoring, ${this.map_update_number} is newer`)
return;
}
this.clear_isa(); this.clear_isa();
this.isa_list = response.data.isa_count; this.isa_list = response.data.isa_count;
this.isa_list.forEach(isa => { this.isa_list.forEach(isa => {
@ -1410,15 +1455,26 @@ export default {
this.isa_labels[isa.qid] = isa.label; this.isa_labels[isa.qid] = isa.label;
this.isa_lookup[isa.qid] = isa; this.isa_lookup[isa.qid] = isa;
}); });
this.debug_info.push(`${this_update} got items, took ${response.data.duration.toFixed(4)} seconds`)
this.process_wikidata_items(response.data.items); this.process_wikidata_items(response.data.items);
this.wikidata_loaded = true; this.wikidata_loaded = true;
this.wikidata_loading = false;
this.check_for_missing(); this.check_for_missing();
this.hits = []; this.hits = [];
this.wikidata_loading = false;
}); });
axios.get(osm_objects_url, { params: params }).then((response) => { this.debug_info.push(`${this_update}: get OSM objects ` + this.bbox_string(bounds));
this.api_call("osm", { params: params }).then((response) => {
var osm_count = response.data.objects.length;
if (this.map_update_number > this_update) {
this.debug_info.push(`${this_update}: got OSM: ${osm_count} objects. took ${response.data.duration.toFixed(4)} seconds, ignoring, ${this.map_update_number} is newer`)
return;
}
this.debug_info.push(`${this_update}: got OSM: ${osm_count} objects. took ${response.data.duration.toFixed(4)} seconds`)
response.data.objects.forEach((osm) => { response.data.objects.forEach((osm) => {
var qid = osm.wikidata; var qid = osm.wikidata;
var item = this.items[qid] ||= {'qid': qid}; var item = this.items[qid] ||= {'qid': qid};
@ -1432,14 +1488,21 @@ export default {
}); });
this.osm_loaded = true; this.osm_loaded = true;
this.osm_loading = false;
this.check_for_missing(); this.check_for_missing();
this.hits = []; this.hits = [];
this.osm_loading = false;
}); });
}, },
bbox_string(bounds) {
const dp = 4;
return [bounds.getWest().toFixed(dp),
bounds.getSouth().toFixed(dp),
bounds.getEast().toFixed(dp),
bounds.getNorth().toFixed(dp)].join(',');
},
auto_load(bounds) { auto_load(bounds) {
var count_url = this.api_base_url + "/api/1/count";
bounds ||= this.map.getBounds(); bounds ||= this.map.getBounds();
this.map_area = this.bounds_area(bounds); this.map_area = this.bounds_area(bounds);
if (this.area_too_big) { if (this.area_too_big) {
@ -1447,17 +1510,17 @@ export default {
if (this.items) this.clear_items(); if (this.items) this.clear_items();
return; return;
} }
var params = { bounds: bounds.toBBoxString() }; var params = { bounds: this.bbox_string(bounds) };
console.log(this.item_type_filters.length);
if (this.item_type_filters.length) { if (this.item_type_filters.length) {
params["isa"] = this.item_type_filters.map(isa => isa.qid).join(","); params["isa"] = this.item_type_filters.map(isa => isa.qid).join(",");
console.log(params.isa); console.log(params.isa);
} }
axios.get(count_url, { params: params }).then((response) => { this.debug_info.push("count " + this.bbox_string(bounds));
this.api_call("count", { params: params }).then((response) => {
this.item_count = response.data.count; this.item_count = response.data.count;
this.debug_info.push(`item count: ${this.item_count}, took ${response.data.duration.toFixed(4)} seconds`)
if (!this.too_many_items) this.load_wikidata_items(bounds); if (!this.too_many_items) this.load_wikidata_items(bounds);
}); });
}, },
@ -1471,8 +1534,7 @@ export default {
if (!this.search_text) return; if (!this.search_text) return;
this.current_hit = undefined; this.current_hit = undefined;
var params = { q: this.search_text }; var params = { q: this.search_text };
var api_search_url = this.api_base_url + "/api/1/search"; this.api_call("search", { params: params }).then((response) => {
axios.get(api_search_url, { params: params }).then((response) => {
this.hits = response.data.hits; this.hits = response.data.hits;
if (!this.hits.length) return; if (!this.hits.length) return;
@ -1510,8 +1572,10 @@ export default {
lat: c.lat.toFixed(5), lat: c.lat.toFixed(5),
lon: c.lng.toFixed(5), lon: c.lng.toFixed(5),
}; };
var missing_url = this.api_base_url + "/api/1/missing"; this.debug_info.push("missing: " + params.qids);
axios.get(missing_url, { params: params }).then((response) => { this.api_call("missing", { params: params }).then((response) => {
var item_count = response.data.items.length;
this.debug_info.push(`missing done: ${item_count} items, took ${response.data.duration.toFixed(2)} seconds`);
response.data.isa_count.forEach((isa) => { response.data.isa_count.forEach((isa) => {
this.isa_labels[isa.qid] = isa.label; this.isa_labels[isa.qid] = isa.label;
if (this.isa_lookup[isa.qid] === undefined) { if (this.isa_lookup[isa.qid] === undefined) {