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

Last change on this file since 773 was 773, checked in by jahn, 10 years ago

Added rest method for querying measurement values.

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                        for( i in 0..(params.size-1) ) {
112                                def param = params[i]
113                            map[param] = args[i]
114                        }
115                        return closure( getRestResource( serverURL, restName, map ) )
116                }
117        }
118
119
120
121
122
123    /**
124     * This method dynamically registers a static method to the CommunicationManager. The new method
125     * gives url for a Grails view on some server and takes as arguments the arguments required
126     * as params by the view.
127     * 
128     * @params String methodname        The name for method to be registered.
129     * @params String serverURL         The server's URL.
130     * @params String viewName          The view's name, e.g., '/Assay/show'
131     * @params Map params               The parameter list required by this view.
132     * @return String URL
133     * 
134     */ 
135    public static addViewWrapper( methodName, serverURL, viewName, params = [] ) {
136
137                CommunicationManager.metaClass.registerStaticMethod( methodName ) { Object [] strangeGroovyArgs ->
138                        def map = [:]
139                    def args = strangeGroovyArgs[0]        // groovy nests the parameters of the methods in some other array
140                        for( i in 0..(params.size-1) ) {
141                                def param = params[i]
142                            map[param] = args[i]
143                        }
144                        return getRestURL( serverURL, viewName, map )
145                }
146    }
147
148
149    /**
150     *  This creates on run time new methods for accessing Rest resources that GSCF provides for SAM.
151     *  This method should be called in grails-app/conf/BootStrap.groovy in the SAM module.
152     */ 
153    public static registerRestWrapperMethodsGSCFtoSAM() {
154        def url = GSCFServerURL + '/rest'
155                addRestWrapper( url , 'getStudies' )
156                addRestWrapper( url , 'getSubjects', ['externalStudyID'] )
157                addRestWrapper( url , 'getAssays',   ['externalStudyID'] )
158                addRestWrapper( url , 'getSamples',  ['externalAssayID'] )
159    }
160
161
162    /**
163     *  This method creates on run time new methods for accessing Grails views that SAM provides for GSCF.
164     *  This method should be called in grails-app/conf/BootStrap.groovy in the GSCF module.
165     */ 
166    public static registerRestWrapperMethodsSAMtoGSCF() {
167                def url = SAMServerURL
168
169                // register method that links to the SAM view for importing a SimpleAssay.
170        // parameters: externalAssayID, an externalAssayID
171                addViewWrapper( 'getAssayImportURL', url, 'importer/pages', ['externalAssayID', 'externalStudyID'] )
172
173                // register method that links to the SAM view for showing a SimpleAssay
174        // parameters: externalAssayID
175                addViewWrapper( 'getAssayShowURL', url, 'simpleAssay/show', ['externalAssayID'] )
176
177                // register method that links to the SAM view for editing a SimpleAssay
178        // parameters: externalAssayID
179                addViewWrapper( 'getAssayEditURL', url, 'simpleAssay/show', ['externalAssayID'] )
180
181                // register method that links to the SAM view for editing a SimpleAssay
182        // parameters: externalAssayID
183                addViewWrapper( 'getMeasurementTypesURL', url, 'simpleAssayMeasurementType/list', ['externalStudyID'] )
184
185                // register rest resource that returns the results of a full text query on SAM
186        // parameters:   query. A string for fulltext search on SAM
187        // return value: results map. It contains two keys 'studyIds', and 'samples'. 'studyIds'
188                //               key maps to a list of Study domain objects of GSCF. 'samples' maps to a
189                //               list of pairs. Each pair consists of a Sample domain object of GSCF and
190                //               additional sample information from SAM provided as a map.
191                // Example of a returned map:
192                //                               ["studies":[NuGO PPS human study],
193                //               "samples":[[ [...], dbnp.studycapturing.Sample: 1]]]
194                def closure1 = { map ->
195                    def studies = []
196                    def samples = []
197                        def studiesHQ = "from dbnp.studycapturing.Study as s where s.code=?"
198                        map['studyIds'].each { studies.add( dbnp.studycapturing.Study.find(studiesHQ,[it]) ) }
199                        map['Samples'].each { samSample ->
200                                def sampleID = samSample['externalSampleID']
201                            def sampleHQ = "from dbnp.studycapturing.Sample as a where a.externalSampleID='${sampleID}'"
202                                def sample = dbnp.studycapturing.Sample.find(sampleHQ)
203                                samples.add( [samSample,sample] )
204                        }
205                        return [studies:studies, samples:samples]
206                }
207
208                addRestWrapper( url+'/rest', 'getQueryResult',  ['query'], closure1 )
209
210
211                // Rest resource: getQueryResultWithOperator
212                //
213                // register rest resource that returns the results of a simple query with measurement value on SAM
214        // parameters:   query. A keyword to match a measurement type on SAM.
215        //               operator. One of '=','<', or '>' that serves for selecting measurements.
216        //               value. A double value for the measurement.
217                //
218        // return value: results list of maps. each map contains an assay, a sample, the value found,
219        //               a unit (as String), and a type (as String).
220                //
221                // Example of a returned list of maps:
222                //                               [["type":"Glucose", "unit":"g", "value":"201.0", "assay":Lipid profiling,
223                //                                 "sample":A10_B], ["type":"Glucose", "unit":"g", "value":"101.0",
224                //                                 "assay":Lipid profiling, "sample":A1_B], ["type":"Insulin", "unit":"g", "value":"202.0",
225                //                                 "assay":Lipid profiling, "sample":A10_B], ["type":"Insulin", "unit":"g", "value":"102.0",
226                //                                 "assay":Lipid profiling, "sample":A1_B]]
227                //             
228                def closure2 = { listOfMaps ->
229                        def results = []
230                        listOfMaps.each{ map -> 
231                                def result = [ type:map['type'], unit:map['unit'], value:map['value'] ]
232                                def assayId = map['externalAssayId'].toLong()
233                                def assay  = dbnp.studycapturing.Assay.find( "from dbnp.studycapturing.Assay as a where a.externalAssayID= ?", [assayId] ) 
234                                def sample = dbnp.studycapturing.Sample.find( "from dbnp.studycapturing.Sample as s where s.name = ?", [map['externalSampleId']] ) 
235                                result['assay']=assay
236                                result['sample']=sample
237                                results.add( result )
238                        }
239                        return results
240                }
241
242                addRestWrapper( url+'/rest', 'getQueryResultWithOperator',  ['query','operator','value'], closure2 )
243    }
244
245
246    /**
247     *  This method creates on run time new methods for accessing Grails views that SAM provides for GSCF.
248     *  This method should be called in grails-app/conf/BootStrap.groovy in the GSCF module.
249     */ 
250    public static registerRestWrapperMethodsGSCFtoDSP() {
251                def url = DSPServerURL
252                addRestWrapper( url, 'isUser',  ['username','password'] )
253                addRestWrapper( url, 'listStudies',  ['username','password'] )
254                addRestWrapper( url, 'listStudySamples',  ['username','password','study_token'] )
255                addRestWrapper( url, 'getStudy',  ['username','password','study_token'] )
256                addRestWrapper( url, 'getStudySample',  ['username','password','study_token','sample_token'] )
257                addRestWrapper( url, 'isUser',  ['username','password'] )
258    }
259
260
261
262}
Note: See TracBrowser for help on using the repository browser.