]> git.openstreetmap.org Git - nominatim-ui.git/blob - src/lib/api_utils.js
replace httpbin with beeceptor for mocking HTTP error codes
[nominatim-ui.git] / src / lib / api_utils.js
1 import { last_api_request_url_store, error_store } from './stores.js';
2
3 function api_request_progress(status) {
4   var loading_el = document.getElementById('loading');
5   if (!loading_el) return; // might not be on page yet
6
7   loading_el.style.display = (status === 'start') ? 'block' : null;
8 }
9
10 export async function fetch_from_api(endpoint_name, params, callback) {
11   var api_url = generate_nominatim_api_url(endpoint_name, params);
12
13   // For the test suite:
14   // If mock_http_status URL parameter is set we call an external webservice. First
15   // https://httpbin.org/#/Status_codes but we saw timeouts. Now beeceptor.com
16   // If that turns out unreliable or expensive (only 50/day free) we might have to
17   // start running a local webserver for the test suite
18   var tmp_params = new URLSearchParams(window.location.search);
19   if (tmp_params && tmp_params.get('mock_http_status')) {
20     api_url = 'https://nominatim-ui.free.beeceptor.com/status/' + parseInt(tmp_params.get('mock_http_status'), 10);
21   }
22
23   api_request_progress('start');
24   if (endpoint_name !== 'status') last_api_request_url_store.set(null);
25
26   try {
27     await fetch(api_url, { headers: Nominatim_Config.Nominatim_API_Endpoint_Headers || {} })
28       .then(async (response) => {
29         if (!((response.status >= 200 && response.status < 300) || response.status === 404)) {
30           error_store.set(`Error fetching data from ${api_url} (${response.statusText})`);
31           return undefined;
32         }
33
34         // Parse JSON here instead of returning a promise so we can catch possible
35         // errors.
36         var data;
37         try {
38           data = await response.json();
39         } catch (err) {
40           // e.g. 'JSON.parse: unexpected non-whitespace character after JSON data at line 1'
41           error_store.set(`Error parsing JSON data from ${api_url} (${err})`);
42           return undefined;
43         }
44         return data;
45       })
46       .then((data) => {
47         if (data) {
48           if (data.error) {
49             error_store.set(data.error.message);
50           }
51           callback(data);
52         }
53         api_request_progress('finish');
54       });
55   } catch (error) {
56     error_store.set(`Error fetching data from ${api_url} (${error})`);
57     api_request_progress('finish');
58   }
59
60   if (endpoint_name !== 'status') last_api_request_url_store.set(api_url);
61 }
62
63 var fetch_content_cache = {};
64 export async function fetch_content_into_element(url, dom_element) {
65   if (!window.location.protocol.match(/^http/)) {
66     dom_element.innerHTML = `Cannot display data from ${url} here. `
67       + 'Browser security prevents loading content from file:// URLs.';
68     return;
69   }
70
71   if (fetch_content_cache[url]) {
72     dom_element.innerHTML = fetch_content_cache[url];
73     return;
74   }
75   try {
76     await fetch(url)
77       .then(response => response.text())
78       .then(html => {
79         html = html.replace('Nominatim_API_Endpoint', generate_nominatim_endpoint_url());
80         dom_element.innerHTML = html;
81         fetch_content_cache[url] = html;
82       });
83   } catch (error) {
84     dom_element.innerHTML = `Error fetching content from ${url} (${error})`;
85   }
86 }
87
88 function generate_nominatim_endpoint_url(endpoint_name) {
89   var conf_endpoint = Nominatim_Config.Nominatim_API_Endpoint;
90
91   if (typeof conf_endpoint === 'function') {
92     return conf_endpoint(endpoint_name);
93   }
94
95   if (!endpoint_name) return conf_endpoint;
96
97   return conf_endpoint + endpoint_name + '.php';
98 }
99
100 function generate_nominatim_api_url(endpoint_name, params) {
101   // default value for /search
102   if (params.dedupe === 1) delete params.dedupe;
103
104   extend_parameters(params, Nominatim_Config.Nominatim_API_Endpoint_Params);
105   return generate_nominatim_endpoint_url(endpoint_name)
106          + '?'
107          + Object.keys(clean_up_parameters(params)).map((k) => {
108            return encodeURIComponent(k) + '=' + encodeURIComponent(params[k]);
109          }).join('&');
110 }
111
112 function extend_parameters(params, params2) {
113   var param_names = Object.keys(params2);
114   for (var i = 0; i < param_names.length; i += 1) {
115     params[param_names[i]] = params2[param_names[i]];
116   }
117 }
118
119 function clean_up_parameters(params) {
120   // `&a=&b=&c=1` => '&c=1'
121   var param_names = Object.keys(params);
122   for (var i = 0; i < param_names.length; i += 1) {
123     var val = params[param_names[i]];
124     if (typeof (val) === 'undefined' || val === '' || val === null) {
125       delete params[param_names[i]];
126     }
127   }
128   return params;
129 }
130
131 export function update_html_title(title) {
132   document.title = [title, Nominatim_Config.Page_Title]
133     .filter((val) => val && val.length > 1)
134     .join(' | ');
135 }
136