JAL-3829 mocking for PDBFTS for related query to UniprotFTS mock.
[jalview.git] / src / jalview / fts / service / threedbeacons / TDBeaconsFTSRestClient.java
1 package jalview.fts.service.threedbeacons;
2
3 import java.net.URI;
4 import java.util.ArrayList;
5 import java.util.Collection;
6 import java.util.Iterator;
7 import java.util.List;
8 import java.util.Map;
9 import java.util.Objects;
10
11 import javax.ws.rs.core.MediaType;
12
13 import org.json.simple.parser.ParseException;
14
15 import com.sun.jersey.api.client.Client;
16 import com.sun.jersey.api.client.ClientResponse;
17 import com.sun.jersey.api.client.WebResource;
18 import com.sun.jersey.api.client.config.DefaultClientConfig;
19
20 import jalview.datamodel.SequenceI;
21 import jalview.fts.api.FTSData;
22 import jalview.fts.api.FTSDataColumnI;
23 import jalview.fts.api.FTSRestClientI;
24 import jalview.fts.api.StructureFTSRestClientI;
25 import jalview.fts.core.FTSRestClient;
26 import jalview.fts.core.FTSRestRequest;
27 import jalview.fts.core.FTSRestResponse;
28 import jalview.fts.core.FTSDataColumnPreferences.PreferenceSource;
29 import jalview.fts.service.pdb.PDBFTSRestClient;
30 import jalview.util.JSONUtils;
31 import jalview.util.MessageManager;
32 import jalview.util.Platform;
33
34 public class TDBeaconsFTSRestClient extends FTSRestClient
35         implements StructureFTSRestClientI
36 {
37   private static final String DEFAULT_THREEDBEACONS_DOMAIN = "https://wwwdev.ebi.ac.uk/pdbe/pdbe-kb/3dbeacons/api/uniprot/summary/";
38
39   public static FTSRestClientI instance = null;
40
41   protected TDBeaconsFTSRestClient()
42   {
43   }
44   @SuppressWarnings("unchecked")
45   @Override
46   public FTSRestResponse executeRequest(FTSRestRequest tdbRestRequest)
47           throws Exception
48   {
49     try
50     {
51       String query = tdbRestRequest.getSearchTerm();
52       Client client;
53       Class<ClientResponse> clientResponseClass;
54       if (Platform.isJS())
55       {
56         // JavaScript only
57         client = (Client) (Object) new jalview.javascript.web.Client();
58         clientResponseClass = (Class<ClientResponse>) (Object) jalview.javascript.web.ClientResponse.class;
59       }
60       else
61       /**
62        * Java only
63        * 
64        * @j2sIgnore
65        */
66       {
67         client = Client.create(new DefaultClientConfig());
68         clientResponseClass = ClientResponse.class;
69       }
70       WebResource webResource;
71       webResource = client.resource(DEFAULT_THREEDBEACONS_DOMAIN)
72               .path(query);
73       URI uri = webResource.getURI();
74       System.out.println(uri.toString());
75
76       // Execute the REST request
77       ClientResponse clientResponse;
78       if (isMocked()) { 
79         clientResponse = null;
80       }
81       else
82       {
83         clientResponse = webResource.accept(MediaType.APPLICATION_JSON)
84                 .get(clientResponseClass);
85       }
86
87       // Get the JSON string from the response object or directly from the
88       // client (JavaScript)
89       Map<String, Object> jsonObj = null;
90       String responseString = null;
91
92       // Check the response status and report exception if one occurs
93       int responseStatus = isMocked() ? (mockQuery.equals(query) ? 200 : 404) : clientResponse.getStatus();
94       switch (responseStatus)
95       {
96       // if success
97       case 200:
98         if (Platform.isJS())
99         {
100           jsonObj = clientResponse.getEntity(Map.class);
101         }
102         else
103         {
104           responseString = isMocked() ? mockResponse: clientResponse.getEntity(String.class);
105         }
106         break;
107       case 400:
108         throw new Exception(parseJsonExceptionString(responseString));
109       case 404:
110         return emptyTDBeaconsJsonResponse();
111       default:
112         throw new Exception(
113                 getMessageByHTTPStatusCode(responseStatus, "3DBeacons"));
114       }
115       // Process the response and return the result to the caller.
116       return parseTDBeaconsJsonResponse(responseString, jsonObj,
117               tdbRestRequest);
118     } catch (Exception e)
119     {
120       String exceptionMsg = e.getMessage();
121       if (exceptionMsg.contains("SocketException"))
122       {
123         // No internet connection
124         throw new Exception(MessageManager.getString(
125                 "exception.unable_to_detect_internet_connection"));
126       }
127       else if (exceptionMsg.contains("UnknownHostException"))
128       {
129         // The server is unreachable
130         throw new Exception(MessageManager.formatMessage(
131                 "exception.fts_server_unreachable", "3DB Hub"));
132       }
133       else
134       {
135         throw e;
136       }
137     }
138
139   }
140
141   /**
142    * returns response for when the 3D-Beacons service doesn't have a record for
143    * the given query - in 2.11.2 this triggers a failover to the PDBe FTS 
144    * 
145    * @return null
146    */
147   private FTSRestResponse emptyTDBeaconsJsonResponse()
148   {
149     return null;
150   }
151
152   public String setSearchTerm(String term)
153   {
154     return term;
155   }
156
157   public static FTSRestResponse parseTDBeaconsJsonResponse(
158           String tdbJsonResponseString, FTSRestRequest tdbRestRequest)
159   {
160     return parseTDBeaconsJsonResponse(tdbJsonResponseString,
161             (Map<String, Object>) null, tdbRestRequest);
162   }
163
164   @SuppressWarnings("unchecked")
165   public static FTSRestResponse parseTDBeaconsJsonResponse(
166           String tdbJsonResponseString, Map<String, Object> jsonObj,
167           FTSRestRequest tdbRestRequest)
168   {
169     FTSRestResponse searchResult = new FTSRestResponse();
170     List<FTSData> result = null;
171
172     try
173     {
174       if (jsonObj == null)
175       {
176         jsonObj = (Map<String, Object>) JSONUtils
177                 .parse(tdbJsonResponseString);
178       }
179
180       Object uniprot_entry = jsonObj.get("uniprot_entry");
181       // TODO: decide if anything from uniprot_entry needs to be reported via
182       // the FTSRestResponse object
183       // Arnaud added seqLength = (Long) ((Map<String, Object>)
184       // jsonObj.get("uniprot_entry")).get("sequence_length");
185
186       List<Object> structures = (List<Object>) jsonObj.get("structures");
187       result = new ArrayList<>();
188
189       int numFound = 0;
190       for (Iterator<Object> strucIter = structures.iterator(); strucIter
191               .hasNext();)
192       {
193         Map<String, Object> structure = (Map<String, Object>) strucIter
194                 .next();
195         result.add(getFTSData(structure, tdbRestRequest));
196         numFound++;
197       }
198
199       searchResult.setNumberOfItemsFound(numFound);
200       searchResult.setSearchSummary(result);
201
202     } catch (ParseException e)
203     {
204       e.printStackTrace();
205     }
206     return searchResult;
207   }
208
209   private static FTSData getFTSData(Map<String, Object> tdbJsonStructure,
210           FTSRestRequest tdbRequest)
211   {
212     // TODO: consider reusing PDBFTSRestClient.getFTSData ?
213
214     String primaryKey = null;
215     Object[] summaryRowData;
216
217     SequenceI associatedSequence;
218
219     Collection<FTSDataColumnI> displayFields = tdbRequest.getWantedFields();
220     SequenceI associatedSeq = tdbRequest.getAssociatedSequence();
221     int colCounter = 0;
222     summaryRowData = new Object[(associatedSeq != null)
223                                 ? displayFields.size() + 1
224                                 : displayFields.size()];
225                         if (associatedSeq != null)
226                         {
227                           associatedSequence = associatedSeq;
228                           summaryRowData[0] = associatedSequence;
229                           colCounter = 1;
230                         }
231
232     for (FTSDataColumnI field : displayFields)
233     {
234       String fieldData = (tdbJsonStructure.get(field.getCode()) == null)
235               ? " "
236               : tdbJsonStructure.get(field.getCode()).toString();
237       // System.out.println("Field : " + field + " Data : " + fieldData);
238       if (field.isPrimaryKeyColumn())
239       {
240         primaryKey = fieldData;
241         summaryRowData[colCounter++] = primaryKey;
242       }
243       else if (fieldData == null || fieldData.trim().isEmpty())
244       {
245         summaryRowData[colCounter++] = null;
246       }
247       else
248       {
249         try
250         {
251           summaryRowData[colCounter++] = (field.getDataType()
252                   .getDataTypeClass() == Integer.class)
253                           ? Integer.valueOf(fieldData)
254                           : (field.getDataType()
255                                   .getDataTypeClass() == Double.class)
256                                           ? Double.valueOf(fieldData)
257                                           : fieldData;
258         } catch (Exception e)
259         {
260           // e.printStackTrace();
261           System.out.println("offending value:" + fieldData + fieldData);
262         }
263       }
264     }
265     final String primaryKey1 = primaryKey;
266     final Object[] summaryRowData1 = summaryRowData;
267
268     return new FTSData()
269     {
270
271       @Override
272       public Object[] getSummaryData()
273       {
274         return summaryRowData1;
275       }
276
277       @Override
278       public Object getPrimaryKey()
279       {
280         return primaryKey1;
281       }
282
283       /**
284        * Returns a string representation of this object;
285        */
286       @Override
287       public String toString()
288       {
289         StringBuilder summaryFieldValues = new StringBuilder();
290         for (Object summaryField : summaryRowData1)
291         {
292           summaryFieldValues.append(
293                   summaryField == null ? " " : summaryField.toString())
294                   .append("\t");
295         }
296         return summaryFieldValues.toString();
297       }
298
299       /**
300        * Returns hash code value for this object
301        */
302       @Override
303       public int hashCode()
304       {
305         return Objects.hash(primaryKey1, this.toString());
306       }
307
308       @Override
309       public boolean equals(Object that)
310       {
311         return this.toString().equals(that.toString());
312       }
313     };
314   }
315
316   // private static FTSData getFTSData(Map<String, Object> doc,
317   // FTSRestRequest tdbRestRequest)
318   // {
319   // String primaryKey = null;
320   //
321   // Object[] summaryRowData;
322   //
323   // Collection<FTSDataColumnI> displayFields =
324   // tdbRestRequest.getWantedFields();
325   // int colCounter = 0;
326   // summaryRowData = new Object[displayFields.size() + 1];
327   //
328   // return null;
329   // }
330
331   private String parseJsonExceptionString(String jsonErrorString)
332   {
333     // TODO Auto-generated method stub
334     return null;
335   }
336
337   @Override
338   public String getColumnDataConfigFileName()
339   {
340     return "/fts/tdbeacons_data_columns.txt";
341   }
342
343   public static FTSRestClientI getInstance()
344   {
345     if (instance == null)
346     {
347       instance = new TDBeaconsFTSRestClient();
348     }
349     return instance;
350   }
351
352   private Collection<FTSDataColumnI> allDefaultDisplayedStructureDataColumns;
353
354   public Collection<FTSDataColumnI> getAllDefaultDisplayedStructureDataColumns()
355   {
356     if (allDefaultDisplayedStructureDataColumns == null
357             || allDefaultDisplayedStructureDataColumns.isEmpty())
358     {
359       allDefaultDisplayedStructureDataColumns = new ArrayList<>();
360       allDefaultDisplayedStructureDataColumns
361               .addAll(super.getAllDefaultDisplayedFTSDataColumns());
362     }
363     return allDefaultDisplayedStructureDataColumns;
364   }
365
366   @Override
367   public String[] getPreferencesColumnsFor(PreferenceSource source)
368   {
369     String[] columnNames = null;
370     switch (source)
371     {
372     case SEARCH_SUMMARY:
373       columnNames = new String[] { "", "Display", "Group" };
374       break;
375     case STRUCTURE_CHOOSER:
376       columnNames = new String[] { "", "Display", "Group" };
377       break;
378     case PREFERENCES:
379       columnNames = new String[] { "3DB Beacons Field", "Show in search summary",
380           "Show in structure summary" };
381       break;
382     default:
383       break;
384     }
385     return columnNames;
386   }
387 }