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

Last change on this file since 835 was 835, checked in by keesvb, 10 years ago

added metabolomics assay examples, cleaned up RestController?, changed assay show link for SAM

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/showByExternalID', ['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.