source: trunk/src/groovy/dbnp/rest/common/CommunicationManager.groovy @ 934

Last change on this file since 934 was 934, checked in by j.saito@…, 10 years ago

Adjusted CommunicationManager? to match closer with CommunicationManager? in SAM.
Added support for "token" notation of REST services directly in Study and Assay domain classes.
Cleaned up and tested communication further.

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