Commit 50dd7220400406a831c1c0e846c6727321cc8b23
1 parent
07382ff2
Exists in
master
and in
112 other branches
epntap.php works now as an API; improve error handling.
Showing
3 changed files
with
154 additions
and
100 deletions
Show diff stats
js/app/controllers/EpnTapModule.js
... | ... | @@ -130,7 +130,7 @@ Ext.define('amdaDesktop.EpnTapModule', { |
130 | 130 | |
131 | 131 | loadMask.show(); |
132 | 132 | this.servicesStore.each(function(record) { |
133 | - // TODO: use store.load() method instead. | |
133 | + // TODO: use store.load() method instead and add 'success' and 'enable' columns in the store | |
134 | 134 | Ext.Ajax.request({ |
135 | 135 | url: 'php/epntap.php', |
136 | 136 | method: 'GET', |
... | ... | @@ -146,8 +146,18 @@ Ext.define('amdaDesktop.EpnTapModule', { |
146 | 146 | 'timeMax': timeMax |
147 | 147 | }, |
148 | 148 | // timeout: 3000, |
149 | - success: this.updateNbResults, | |
150 | - failure: this.updateNbResultsFail, | |
149 | + success: function(response) { | |
150 | + var record = this.servicesStore.getById(response.request.options.params['serviceId']); | |
151 | + var responseObj = Ext.decode(response.responseText); | |
152 | + if(responseObj['success']) { | |
153 | + this.updateNbResults(responseObj, record); | |
154 | + } else { | |
155 | + this.updateNbResultsFail(response, responseObj['msg']); | |
156 | + } | |
157 | + }, | |
158 | + failure: function(response) { | |
159 | + this.updateNbResultsFail(response, response.statusText); | |
160 | + }, | |
151 | 161 | scope: this |
152 | 162 | }); |
153 | 163 | }, this); |
... | ... | @@ -156,25 +166,21 @@ Ext.define('amdaDesktop.EpnTapModule', { |
156 | 166 | /** |
157 | 167 | Update the nb_result field of the services store (see `EpnTapUI.servicesStore`), according to the field values in `serviceFilterPanel`. |
158 | 168 | */ |
159 | - updateNbResults: function(response) { | |
160 | - if(response.status !== 200 || isNaN(response.responseText)) { | |
161 | - this.updateNbResultsFail(response); | |
162 | - } else { | |
163 | - var record = this.servicesStore.getById(response.request.options.params['serviceId']); | |
164 | - var nbRes = Number(response.responseText); | |
165 | - record.set('nb_results', response.responseText); | |
166 | - record.set('error', ''); | |
167 | - record.set('info', 'The service returned ' + (nbRes == 0 ? 'any' : nbRes) + ' result' + (nbRes > 1 ? 's' : '') + ' for the given query.'); | |
168 | - this.servicesStore.sort(); | |
169 | - } | |
169 | + updateNbResults: function(responseObj, record) { | |
170 | + record.set('nb_results', responseObj['data']); | |
171 | + record.set('info', responseObj['msg']); | |
172 | + record.set('error', ''); | |
173 | + this.servicesStore.sort(); | |
170 | 174 | loadMask.hide(); |
171 | 175 | }, |
172 | 176 | |
173 | - updateNbResultsFail: function(response) { | |
174 | - var record = this.servicesStore.getById(response.request.options.params['serviceId']); | |
175 | - var reason = response.status === 200 ? response.responseText : response.statusText; | |
176 | - record.set('error', reason); | |
177 | + updateNbResultsFail: function(response, reason) { | |
178 | + var serviceId = response.request.options.params['serviceId']; | |
179 | + var record = this.servicesStore.getById(serviceId); | |
180 | + console.log('Can not get nb results for service ' + serviceId + ': ' + reason, response); | |
177 | 181 | record.set('info', ''); |
182 | + record.set('error', reason); | |
183 | + this.servicesStore.sort(); | |
178 | 184 | loadMask.hide(); |
179 | 185 | }, |
180 | 186 | |
... | ... | @@ -203,9 +209,11 @@ Ext.define('amdaDesktop.EpnTapModule', { |
203 | 209 | // Ext.Ajax.abortAll(); |
204 | 210 | this.selectedService = record; |
205 | 211 | var nbRes = this.selectedService.get('nb_results'); |
206 | - if(nbRes > 0 && !isNaN(nbRes)) { | |
212 | + | |
213 | + if(nbRes > 0 && !isNaN(nbRes)) { // TODO replace !isNaN(nbRes) by this.selectedService.get('success') | |
207 | 214 | this.granulesStore.load({ |
208 | 215 | params: this.getGranuleParams(), |
216 | + // callback: function (records, operation) { console.log(Ext.decode(operation.response.responseText)); }, | |
209 | 217 | start: 0, |
210 | 218 | limit: this.granulesStore.pageSize, |
211 | 219 | scope: this |
... | ... |
js/app/views/EpnTapUI.js
... | ... | @@ -48,6 +48,8 @@ Ext.create('Ext.data.Store', { |
48 | 48 | ] |
49 | 49 | }); |
50 | 50 | |
51 | +// TODO: Create a generic epntap stop from which all other stores inherits. | |
52 | + | |
51 | 53 | /** |
52 | 54 | `targetNamesStore`: An ExtJS Store containing the list of the different target names defined on all granules, on |
53 | 55 | all available EPN-TAP services (defined in `generic_data/EpnTapData/metadata.json`, updated periodically with a cron |
... | ... | @@ -65,7 +67,12 @@ Ext.create('Ext.data.Store', { |
65 | 67 | proxy: { |
66 | 68 | type: 'ajax', |
67 | 69 | url: 'php/epntap.php', |
68 | - extraParams : { action: 'resolver' } | |
70 | + extraParams: { action: 'resolver' } | |
71 | + // listeners: { | |
72 | + // exception: function(proxy, response, operation) { | |
73 | + // console.log('Error ', response); //TODO: Use ExtJs alert instead | |
74 | + // } | |
75 | + // } | |
69 | 76 | } |
70 | 77 | }); |
71 | 78 | |
... | ... | @@ -140,7 +147,7 @@ selected. |
140 | 147 | */ |
141 | 148 | Ext.create('Ext.data.Store', { |
142 | 149 | id: 'granulesStore', |
143 | - model: 'granulesModel', | |
150 | + model: 'granulesModel', // Created dynamically | |
144 | 151 | autoload: false, |
145 | 152 | pageSize: 25, |
146 | 153 | proxy: { |
... | ... |
php/epntap.php
1 | 1 | <?php |
2 | 2 | |
3 | 3 | include(realpath(dirname(__FILE__) . "/config.php")); |
4 | -// include(CLASSPATH . "EpnTapMgr.php"); | |
5 | 4 | include(CLASSPATH . "VOTableMgr.php"); |
6 | 5 | |
7 | 6 | $action = preg_replace("/[^a-zA-Z]+/", "", filter_var($_GET['action'], FILTER_SANITIZE_STRING)); |
8 | 7 | |
9 | 8 | switch ($action) { |
10 | 9 | case 'resolver': |
11 | - $response = json_encode(resolver()); | |
10 | + $response = resolver(); | |
12 | 11 | break; |
13 | 12 | case 'getServices': |
14 | - $response = json_encode(getServices()); | |
13 | + $response = getServices(); | |
15 | 14 | break; |
16 | 15 | case 'getNbResults': |
17 | 16 | $response = getNbResults(); |
18 | 17 | break; |
19 | 18 | case 'getGranules': |
20 | - $response = json_encode(getGranules()); | |
19 | + $response = getGranules(); | |
21 | 20 | break; |
22 | 21 | default: |
23 | - $response = 'unknown action'; | |
22 | + $response = ['success' => false, 'msg' => 'Unknown action: ' . $action]; | |
24 | 23 | break; |
25 | 24 | } |
26 | -// error_log('epntap response: ' . $response); | |
27 | -echo $response; | |
25 | +echo json_encode($response); | |
28 | 26 | |
29 | 27 | function resolver() { |
30 | 28 | $input = filter_var($_GET['input'], FILTER_SANITIZE_URL); |
31 | 29 | $resolver_url = "http://voparis-registry.obspm.fr/ssodnet/1/autocomplete?q=%22$input%22"; |
32 | - $result = json_decode(file_get_contents($resolver_url), true); | |
33 | 30 | |
34 | - $targets = array(); | |
35 | - foreach($result['hits'] as $e) { | |
36 | - $aliases = '<li>' . join('</li><li>', $e['aliases']) . '</li>'; | |
37 | - $target = array('name' => $e['name'], 'type' => $e['type'], 'parent' => $e['parent'], 'aliases' => $aliases); | |
38 | - array_push($targets, $target); | |
31 | + $response = ['success' => true, 'metaData' => ['root' => 'data', 'messageProperty' => 'msg']]; | |
32 | + try { | |
33 | + $content = file_get_contents($resolver_url); | |
34 | + } catch (Exception $e) { | |
35 | + error_log('Resolver access error: ' . $e); | |
36 | + $response['success'] = false; | |
37 | + $response['msg'] = "Resolver unreachable on $resolver_url."; | |
39 | 38 | } |
40 | - return $targets; | |
39 | + try { | |
40 | + $result = json_decode($content, true); | |
41 | + $targets = array(); | |
42 | + foreach($result['hits'] as $e) { | |
43 | + $aliases = '<li>' . join('</li><li>', $e['aliases']) . '</li>'; | |
44 | + $target = array('name' => $e['name'], 'type' => $e['type'], 'parent' => $e['parent'], 'aliases' => $aliases); | |
45 | + array_push($targets, $target); | |
46 | + } | |
47 | + $response['data'] = $targets; | |
48 | + } catch (Exception $e) { | |
49 | + error_log('Resolver type error: ' . $e); | |
50 | + $response['success'] = false; | |
51 | + $response['msg'] = 'The resolver returned a bad result.'; | |
52 | + } | |
53 | + return $response; | |
54 | +} | |
55 | + | |
56 | +function request($access_url, $query) { | |
57 | + $votMgr = new VOTableMgr; | |
58 | + $params = 'FORMAT=votable&LANG=ADQL&REQUEST=doQuery'; | |
59 | + $url = $access_url . '/sync?' . $params . '&QUERY=' . urlencode(preg_replace('/\s+/', ' ', $query)); // remove also multiple whitespaces | |
60 | + | |
61 | + $votMgr->load($url); | |
62 | + $data = $votMgr->parseStream(); | |
63 | + $error = $votMgr->getVotableError(); | |
64 | + | |
65 | + $response = ['query' => $query, 'metaData' => ['root' => 'data', 'messageProperty' => 'msg']]; | |
66 | + if($error) { | |
67 | + $response['success'] = false; | |
68 | + $response['msg'] = $error; | |
69 | + } else { | |
70 | + $response['success'] = true; | |
71 | + $response['data'] = $data; | |
72 | + } | |
73 | + return $response; | |
41 | 74 | } |
42 | 75 | |
43 | 76 | /* Return the list of available services by querying some usual registries. */ |
... | ... | @@ -48,29 +81,33 @@ function getServices() { |
48 | 81 | NATURAL JOIN rr.res_schema NATURAL JOIN rr.res_table NATURAL JOIN rr.interface NATURAL JOIN rr.res_detail NATURAL JOIN rr.capability |
49 | 82 | WHERE standard_id='ivo://ivoa.net/std/tap' AND intf_type='vs:paramhttp' AND detail_xpath='/capability/dataModel/@ivo-id' |
50 | 83 | AND 1=ivo_nocasematch(detail_value, 'ivo://vopdc.obspm/std/EpnCore%') AND table_name LIKE '%.epn_core' ORDER BY short_name, table_name"; |
51 | - // error_log('getServices query: ' . $query); | |
52 | - | |
53 | - for($i=0; $i<count($registriesURL); $i++) { | |
54 | - $services = request($registriesURL[$i], $query); | |
55 | - if(! array_key_exists("error", $services)) { | |
56 | - for($j=0; $j<count($services); $j++) { | |
57 | - $services[$j]['id'] = generateServiceId($services[$j]); | |
58 | - $services[$j]['nb_results'] = -1; | |
59 | - $services[$j]['info'] = 'Please make a query first.'; | |
60 | - if($services[$j]['id'] == 'cdpp/amda/amdadb') { | |
61 | - array_splice($services, $j, 1); | |
84 | + | |
85 | + $regNumber = 0; | |
86 | + for(; $regNumber<count($registriesURL) ; $regNumber++) { | |
87 | + $response = request($registriesURL[$regNumber], $query); | |
88 | + if($response['success']) { | |
89 | + // Add several other parameters and remove AMDA | |
90 | + for($j=0 ; $j<count($response['data']) ; $j++) { | |
91 | + $response['data'][$j]['id'] = generateServiceId($response['data'][$j]); | |
92 | + $response['data'][$j]['nb_results'] = -1; | |
93 | + $response['data'][$j]['info'] = 'Please make a query.'; | |
94 | + if($response['data'][$j]['id'] == 'cdpp/amda/amdadb') { | |
95 | + array_splice($response['data'], $j, 1); | |
62 | 96 | $j-=1; |
63 | 97 | } |
64 | 98 | } |
65 | - return $services; | |
66 | - } else { | |
67 | - error_log('getServices error: ' . $services['error']); | |
68 | - if($i === count($registriesURL)-1) { | |
69 | - error_log("Can not access any of these registries : " . implode(', ', $registriesURL) . ", check the internet connexion."); | |
70 | - return; | |
99 | + if(isset($lastErrorMesage)) { | |
100 | + $response['msg'] = $lastErrorMesage; | |
71 | 101 | } |
102 | + break; | |
103 | + } else { | |
104 | + $lastErrorMesage = 'Last tried registry (' . $registriesURL[$regNumber] . ') returned this error: ' . $response['msg'] . '.'; | |
72 | 105 | } |
73 | 106 | } |
107 | + if(!$response['success']) { | |
108 | + $response['msg'] = 'Can not access any of these registries: ' . implode(', ', $registriesURL) . ', last error message is ' . $lastErrorMesage; | |
109 | + } | |
110 | + return $response; | |
74 | 111 | } |
75 | 112 | |
76 | 113 | function getNbResults() { |
... | ... | @@ -82,27 +119,33 @@ function getNbResults() { |
82 | 119 | $timeMax = filter_var($_GET['timeMax'], FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW); |
83 | 120 | |
84 | 121 | $query = "SELECT COUNT(*) AS nb_rows FROM $tableName" . createFilter($targetName, $productTypes, $timeMin, $timeMax); |
85 | - // error_log('getNbResults query: ' . $query); | |
86 | - $result = request($url, $query); | |
87 | - if(count($result) < 1) { | |
88 | - return 'Too few returned raws.'; | |
89 | - } else if(count($result) > 1) { | |
90 | - return 'Too many returned raws.'; | |
91 | - } else if(!array_key_exists(0, $result)) { | |
92 | - return 'cant find raw item 0'; | |
93 | - } else if(is_null($result[0])) { | |
94 | - return 'The returned raw is null.'; | |
95 | - } else if(!array_key_exists("nb_rows", $result[0])) { | |
96 | - return 'cant find nb_rows.'; | |
97 | - } else if(!is_numeric($result[0]['nb_rows'])) { | |
98 | - return 'The returned value is not a number.'; | |
99 | - } else { | |
100 | - return (int)($result[0]['nb_rows']); | |
122 | + $response = request($url, $query); | |
123 | + if($response['success']) { | |
124 | + $response['success'] = false; | |
125 | + $response['msg'] = 'The service returned a bad value, can not get the number of results.'; | |
126 | + if(count($response['data']) < 1) { | |
127 | + error_log('getNbResults error: Too few returned raws.'); | |
128 | + } else if(count($response['data']) > 1) { | |
129 | + error_log('getNbResults error: Too many returned raws.'); | |
130 | + } else if(!array_key_exists(0, $response['data'])) { | |
131 | + error_log('getNbResults error: cant find raw item 0'); | |
132 | + } else if(is_null($response['data'][0])) { | |
133 | + error_log('getNbResults error: The returned raw is null.'); | |
134 | + } else if(!array_key_exists("nb_rows", $response['data'][0])) { | |
135 | + error_log('getNbResults error: cant find nb_rows.'); | |
136 | + } else if(!is_numeric($response['data'][0]['nb_rows'])) { | |
137 | + error_log('getNbResults error: The returned value is not a number.'); | |
138 | + } else { | |
139 | + $response['success'] = true; | |
140 | + $response['data'] = (int)($response['data'][0]['nb_rows']); | |
141 | + $response['msg'] = 'The service returned ' . ($response['data'] == 0 ? 'no' : $response['data']) . ' result' . ($response['data'] > 1 ? 's' : '') . ' for the given query.'; | |
142 | + } | |
101 | 143 | } |
144 | + return $response; | |
102 | 145 | } |
103 | 146 | |
104 | 147 | function getGranules() { |
105 | - // error_log('getGranules GET: ' . json_encode($_GET)); | |
148 | + // TODO: simplify this | |
106 | 149 | $url = filter_var($_GET['url'], FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW); |
107 | 150 | $tableName = filter_var($_GET['tableName'], FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW); |
108 | 151 | $targetName = filter_var($_GET['targetName'], FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW); |
... | ... | @@ -116,42 +159,38 @@ function getGranules() { |
116 | 159 | $filter = createFilter($targetName, $productTypes, $timeMin, $timeMax); |
117 | 160 | $query = "SELECT TOP $limit * FROM $tableName $filter OFFSET $start"; |
118 | 161 | // error_log('getGranules query: ' . $query); |
119 | - $rows = request($url, $query); | |
120 | - | |
121 | - $visibleColumns = ['granule_uid', 'dataproduct_type', 'time_min', 'time_max', 'access_estsize', 'thumbnail_url', 'access_url']; | |
122 | - $names = ['dataproduct_type' => 'Type', 'access_estsize' => 'Size']; | |
123 | - $renderers = ['dataproduct_type' => 'type', 'time_min' => 'date', 'time_max' => 'date', 'access_estsize' => 'size', 'thumbnail_url' => 'img', 'access_url' => 'link', 'access_format' => 'format']; | |
124 | - $flexs = ['granule_uid' => 2]; | |
125 | - | |
126 | - $fields = array(); | |
127 | - $columns = array(); | |
128 | - foreach($rows[0] as $key => $value) { | |
129 | - $fields[] = ['name' => $key, 'type' => 'string']; | |
130 | - $columns[] = [ | |
131 | - 'dataIndex' => $key, | |
132 | - 'text' => array_key_exists($key, $names) ? $names[$key] : ucfirst(str_replace('_', ' ', $key)), | |
133 | - 'flex' => array_key_exists($key, $flexs) ? $flexs[$key] : 1, | |
134 | - 'hidden' => !in_array($key, $visibleColumns), | |
135 | - 'renderer' => array_key_exists($key, $renderers) ? $renderers[$key] : 'text' | |
136 | - ]; | |
137 | - } | |
162 | + $response = request($url, $query); | |
163 | + if($response['success']) { | |
164 | + $visibleColumns = ['granule_uid', 'dataproduct_type', 'time_min', 'time_max', 'access_estsize', 'thumbnail_url', 'access_url']; // rest are hidden | |
165 | + $names = ['dataproduct_type' => 'Type', 'access_estsize' => 'Size']; // default: pretty printed key name | |
166 | + $renderers = ['dataproduct_type' => 'type', 'time_min' => 'date', 'time_max' => 'date', 'access_estsize' => 'size', 'thumbnail_url' => 'img', 'access_url' => 'link', 'access_format' => 'format']; // default: text | |
167 | + $flexs = ['granule_uid' => 2]; // default: 1 | |
168 | + // $types = ['boolean' => , 'integer']; // TODO see http://php.net/manual/fr/function.gettype.php | |
169 | + | |
170 | + $fields = array(); | |
171 | + $columns = array(); | |
172 | + foreach($response['data'][0] as $key => $value) { | |
173 | + error_log('Granule ' . $key . ' is ' . gettype($value)); | |
174 | + $fields[] = ['name' => $key, 'type' => 'string']; | |
175 | + $columns[] = [ | |
176 | + 'dataIndex' => $key, | |
177 | + 'text' => array_key_exists($key, $names) ? $names[$key] : ucfirst(str_replace('_', ' ', $key)), | |
178 | + 'flex' => array_key_exists($key, $flexs) ? $flexs[$key] : 1, | |
179 | + 'hidden' => !in_array($key, $visibleColumns), | |
180 | + // 'type' => array_key_exists(gettype(), $types), | |
181 | + 'renderer' => array_key_exists($key, $renderers) ? $renderers[$key] : 'text' | |
182 | + ]; | |
183 | + } | |
138 | 184 | |
139 | - $metadata = ['fields' => $fields, 'columns' => $columns, 'root' => 'data']; | |
140 | - return ['data' => $rows, 'total' => $nbRes, 'metaData' => $metadata]; | |
185 | + $response['total'] = $nbRes; | |
186 | + $response['metaData']['fields'] = $fields; | |
187 | + $response['metaData']['columns'] = $columns; | |
188 | + } | |
189 | + return $response; | |
141 | 190 | } |
142 | 191 | |
143 | 192 | // ----- utils ----- |
144 | 193 | |
145 | -function request($access_url, $query) { | |
146 | - $votMgr = new VOTableMgr; | |
147 | - $params = 'FORMAT=votable&LANG=ADQL&REQUEST=doQuery'; | |
148 | - $url = $access_url . '/sync?' . $params . '&QUERY=' . urlencode(preg_replace('/\s+/', ' ', $query)); // remove also multiple whitespaces | |
149 | - | |
150 | - $votMgr->load($url); | |
151 | - $result = $votMgr->parseStream(); | |
152 | - return $votMgr->getVotableError() ? array('error' => $votMgr->getVotableError()) : $result; | |
153 | -} | |
154 | - | |
155 | 194 | function createFilter($targetName, $productTypes, $timeMin, $timeMax) { |
156 | 195 | $filter = array(); |
157 | 196 | if($targetName) { |
... | ... |