root/src/groovy/dbnp/rest/common/CommunicationManager.groovy @ 175

Revision 175, 11.9 KB (checked in by j.saito@…, 3 years ago)

Debugging with cont. integration.

Line 
1package dbnp.rest.common
2
3import dbnp.rest.*
4import grails.converters.JSON
5import java.net.URLEncoder
6import org.codehaus.groovy.grails.web.json.*
7
8/**  CommunicationManager
9 *
10 *   @author Jahn
11 *
12 *   This class manages communication between dbNP modules such as GSCF and SAM.
13 *   By communication we mean two ways of exchanging information: (1) via Rest resources,
14 *   and (2) via Grails views that a module can make available to another module.
15 *
16 *   For Rest communication this class implements a Rest client that fetches data
17 *   from other modules' Rest resources. The Rest implementation transfers data in JSON.
18 *
19 *   Note: Do not use this class directly to fetch data. Instead use your module's
20 *   rest wrapper methods. Use this module, to create these rest wrapper methods.
21 *   For instance, use dbnp.rest.sam.registerRestWrapperMethodsGSCFtoSAM to register new methods
22 *   for accessing GSCF's Rest service in SAM; your new method shoud then use this class.
23 */
24
25
26class CommunicationManager {
27
28    def        static Encoding      = "UTF-8"
29    def public static SAMServerURL  = "http://configuration.seems.wrong:8182/sam"
30    def public static GSCFServerURL = "http://configuration.seems.wrong:8080/gscf"
31
32    static def username = "user"
33        static def password = "useR123!"
34
35    /**
36     * Get the results of provided by a rest Rest resource.
37     *
38     * @params String resource The name of the resource, e.g. importer/pages
39     * @params Map params      A Map of parmater names and values., e.g. ['externalAssayID':12]
40     * @return String url   
41     */
42    public static Object getRestResource( RestServerURL, resource, params ) {
43                if(!RestServerURL) { throw new Exception("SAMServerURL is null") }
44
45                def url = getRestURL( RestServerURL, resource, params )
46            def authString = "${username}:${password}".getBytes().encodeBase64().toString()
47            def conn = url.openConnection()
48            conn.setRequestProperty("Authorization", "Basic ${authString}")
49            return JSON.parse( conn.content.text )
50    }
51
52
53    /**
54     * Convenience method for constructing URLs for SAM that need parameters.
55     * Note that parameters are first convereted to strings by calling their toString() method
56     * and then Encoded to protect special characters.
57     *
58     * @params String resource The name of the resource, e.g. importer/pages
59     * @params Map params      A Map of parmater names and values., e.g. ['externalAssayID':12]
60     * @return String url   
61     */
62    public static URL getRestURL( RestServerURL, resource, params ) {
63
64                println ""
65                println "RestServerURL: ${RestServerURL}"
66                println "resource: ${resource}"
67                println "params: ${params}"
68                println ""
69               
70                if(!SAMServerURL) { throw new Exception("SAMServerURL is null") }
71
72        def url = RestServerURL + '/' + resource
73                def first = true
74                params['consumer']=SAMServerURL
75                params.each { name, value ->
76                        if(first) {
77                                first = false
78                                url += '/nil?' + name + "=" + URLEncoder.encode( value.toString(), Encoding )
79                        }
80                        else {
81                                url += '&' + name + "=" + URLEncoder.encode( value.toString(), Encoding  )
82                        }
83                }
84                return new URL( url )
85    }
86
87
88
89    /**
90     * This method dynamically adds a static method to the CommunicationManager.
91     * 
92     * @params String serverURL          A rest server URL.
93     * @params String restName           The name of a rest resource on the server.     
94     * @params Map params (optional) A list of parameter names to be passed to this resource.
95     * @params Colosure (optional)   A closure that acts on the result of the Rest call in the new method.
96     * 
97     * Given a rest resource at serverURL called resourceName, we register a static method
98     * for the CommunicationManager. The new method has the same name and arity as the resource.
99     * 
100     * Example: Suppopse http://localhost:8080/gscf/rest/getSamples is a Rest resource.
101     * 
102     * In our grails app, we would like to connect to this service. We want to have a
103     * method getSamples() that fetches the result from the service. We do this by calling
104     * 
105     *          CommunicationManager.addRestWrapper( 'http://localhost:8080/gscf/rest', 'getSamples', ['externalStudyID'] )
106     * 
107     * This registers a new method:
108     * 
109         *               public static Object CommunicationManager.getSamples( Object arg )
110     * 
111     * This method has arrity 1 and expects to be given a map. The map is the parameter map
112     * of the rest service getSamples. It maps parameter called "externalStudyID" to some object
113     * that is passed. So, it can be called like as follows:
114     * 
115     *      def sampleList = CommunicationManager.getSamples( [externalStudyID:4711] )
116     * 
117     *  The call will deliver the results of the parameterized rest resource given at:
118     * 
119     *          http://localhost:8080/gscf/rest/nil?externalStudyID=4711
120     *
121     */
122
123    public static addRestWrapper( serverURL, restName, params = [], closure = { return it } ) {
124                if(!serverURL) { throw new Exception("addRestWrapper: REST serverURL is null") }
125                def result
126                try {
127                        CommunicationManager.metaClass.registerStaticMethod( restName ) { Object [] strangeGroovyArgs ->
128                                def map = [:]
129                            def args = strangeGroovyArgs[0]        // groovy nests the parameters of the methods in some other array
130                                if(params.size > 0 )
131                                {
132                                        for( i in 0..(params.size-1) ) {
133                                                def param = params[i]
134                                                map[param] = args[i]
135                                        }
136                                }
137                                result = closure( getRestResource( serverURL, restName, map ) )
138                        }
139                } catch ( Exception e ) { result = RestException.getErrorObject()  }
140                return result
141        }
142
143
144
145
146
147    /**
148     * This method dynamically registers a static method to the CommunicationManager. The new method
149     * gives url for a Grails view on some server and takes as arguments the arguments required
150     * as params by the view.
151     * 
152     * @params String methodname        The name for method to be registered.
153     * @params String serverURL         The server's URL.
154     * @params String viewName          The view's name, e.g., '/Assay/show'
155     * @params Map params               The parameter list required by this view.
156     * @return String URL
157     * 
158     */ 
159    public static addViewWrapper( methodName, serverURL, viewName, params = [] ) {
160
161                CommunicationManager.metaClass.registerStaticMethod( methodName ) { Object [] strangeGroovyArgs ->
162                        def map = [:]
163                    def args = strangeGroovyArgs[0]        // groovy nests the parameters of the methods in some other array
164                        for( i in 0..(params.size-1) ) {
165                                def param = params[i]
166                            map[param] = args[i]
167                        }
168                        return getRestURL( serverURL, viewName, map )
169                }
170    }
171
172
173    /**
174     *  This creates on run time new methods for accessing Rest resources that GSCF provides for SAM.
175     *  This method should be called in grails-app/conf/BootStrap.groovy in the SAM module.
176     */
177    public static registerRestWrapperMethodsGSCFtoSAM() {
178        def url = GSCFServerURL + '/rest'
179                addRestWrapper( url , 'getStudies', ['token'] )
180                addRestWrapper( url , 'getSubjects', ['token','studyToken'] )
181                addRestWrapper( url , 'getAssays',   ['token','studyToken','url'] )
182                addRestWrapper( url , 'getSamples',  ['token','assayToken'] )
183    }
184
185
186    /**
187     *  This method creates on run time new methods for accessing Grails views that SAM provides for GSCF.
188     *  This method should be called in grails-app/conf/BootStrap.groovy in the GSCF module.
189     */
190    public static registerRestWrapperMethodsSAMtoGSCF() {
191                def url = SAMServerURL
192
193                // register method that links to the SAM view for importing a SimpleAssay.
194        // parameters: externalAssayID, an externalAssayID
195                addViewWrapper( 'getAssayImportURL', url, 'importer/pages', ['externalAssayID', 'externalStudyID'] )
196
197                // register method that links to the SAM view for showing a SimpleAssay
198        // parameters: externalAssayID
199                addViewWrapper( 'getAssayShowURL', url, 'simpleAssay/show', ['externalAssayID'] )
200
201                // register method that links to the SAM view for editing a SimpleAssay
202        // parameters: externalAssayID
203                addViewWrapper( 'getAssayEditURL', url, 'simpleAssay/show', ['externalAssayID'] )
204
205                // register method that links to the SAM view for editing a SimpleAssay
206        // parameters: externalAssayID
207                addViewWrapper( 'getMeasurementTypesURL', url, 'simpleAssayMeasurementType/list', ['externalStudyID'] )
208
209                // register rest resource that returns the results of a full text query on SAM
210        // parameters:   query. A string for fulltext search on SAM
211        // return value: results map. It contains two keys 'studyIds', and 'assays'. 'studyIds'
212                //               key maps to a list of Study domain objects of GSCF. 'assays' map to a
213                //               list of pairs. Each pair consists of an Assay domain object of GSCF and
214                //               additional assay information from SAM provided as a map.
215                // Example of a returned map:
216                //               [studyIds:[PPSH],
217                //                               assays:[[isIntake:false, isDrug:false, correctionMethod:test Correction Method 1,
218                //                               detectableLimit:1, isNew:false, class:data.SimpleAssay, externalAssayID:1, id:1,
219                //                               measurements:null, unit:Insulin, inSerum:false, name:test Simple Assay 1,
220                //                               referenceValues:test Reference Values 1]]]
221                def closure = { map ->
222                    def studies = []   
223                    def assays  = []   
224                        def studiesHQ = "from dbnp.studycapturing.Study as s where s.code=?"
225                        map['studyIds'].each { studies.add( dbnp.studycapturing.Study.find(studiesHQ,[it]) ) }
226                        map['assays'].each { samAssay ->
227                                def assayID = samAssay['externalAssayID']
228                            def assayHQ = "from dbnp.studycapturing.Assay as a where a.externalAssayID='${assayID}'"
229                                def assay = dbnp.studycapturing.Assay.find(assayHQ)
230                                assays.add( [samAssay,assay] )
231                        }
232                        return [studies:studies, assays:assays]
233                }
234
235                addRestWrapper( url+'/rest', 'getQueryResult',  ['query'], closure )
236
237
238                // Rest resource: getQueryResultWithOperator
239                //
240                // register rest resource that returns the results of a simple query with measurement value on SAM
241        // parameters:   query. A keyword to match a measurement type on SAM.
242        //               operator. One of '=','<', or '>' that serves for selecting measurements.
243        //               value. A double value for the measurement.
244                //
245        // return value: results list of maps. each map contains an assay, a sample, the value found,
246        //               a unit (as String), and a type (as String).
247                //
248                // Example of a returned list of maps:
249                //                               [["type":"Glucose", "unit":"g", "value":"201.0", "assay":Lipid profiling,
250                //                                 "sample":A10_B], ["type":"Glucose", "unit":"g", "value":"101.0",
251                //                                 "assay":Lipid profiling, "sample":A1_B], ["type":"Insulin", "unit":"g", "value":"202.0",
252                //                                 "assay":Lipid profiling, "sample":A10_B], ["type":"Insulin", "unit":"g", "value":"102.0",
253                //                                 "assay":Lipid profiling, "sample":A1_B]]
254                //             
255                def closure2 = { listOfMaps ->
256                        def results = []
257                        listOfMaps.each{ map ->
258                                def result = [ type:map['type'], unit:map['unit'], value:map['value'] ]
259                                def assayId = map['externalAssayId'].toLong()
260                                def assay  = dbnp.studycapturing.Assay.find( "from dbnp.studycapturing.Assay as a where a.externalAssayID= ?", [assayId] )
261                                def sample = dbnp.studycapturing.Sample.find( "from dbnp.studycapturing.Sample as s where s.name = ?", [map['externalSampleId']] )
262                                result['assay']=assay
263                                result['sample']=sample
264                                results.add( result )
265                        }
266                        return results
267                }
268
269                addRestWrapper( url+'/rest', 'getQueryResultWithOperator',  ['query','operator','value'], closure2 )
270
271        }
272       
273
274
275    /**
276     * Give list of missing parameters for a parameter call in a RestController.
277     * 
278     * @params params Map params        The parameter list required by this view.
279     * @params requiredParamers                 List of parameter names that must be provided
280     * @return true, if params has all required parameters, an error hash otherwise
281     */ 
282        static String hasValidParams( params, Object [] requiredParams ) {
283                def list = []
284                requiredParams.each { p ->
285                        if( !params[p] ) list.push p
286                }
287                if(list.size()<=0) { return true }
288                return false  // return error object
289        }
290
291
292}
Note: See TracBrowser for help on using the browser.