root/trunk/grails-app/controllers/RestController.groovy @ 1010

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

Ticket #57 - Unified getAssays and getAssay.

Also, added convenience method for checking the params object in RestControllers? in CommunicationManager?.

  • Property svn:keywords set to Author Date Rev
Line 
1/**
2 * RestController
3 *
4 * This controler provides a REST service.
5 * The names of the RESET resources are the same as the names of this
6 * controller's actions. E.g., the resources called getStudies simply
7 * corresponds to the action getStudies. Some of the resources are parameterized.
8 * The parameters are passed as parameters in the url and are available in the
9 * params respecting Grails' conventions. In this file, we adher to the javadoc 
10 * convention for describing parameters ("@param"), but actually we mean
11 * key-value pairs in the params object of each Grails action we comment on.
12 *
13 * @author      Jahn-Takeshi Saito
14 * @since       20100601
15 *
16 */
17
18import dbnp.studycapturing.Study
19import dbnp.studycapturing.Assay
20import dbnp.authentication.SecUser
21import grails.converters.*
22import nl.metabolomicscentre.dsp.http.BasicAuthentication
23import dbnp.rest.common.CommunicationManager
24
25
26class RestController {
27
28       /**************************************************/
29      /** Rest resources for Simple Assay Module (SAM) **/
30     /**************************************************/
31
32        def AuthenticationService       
33        def beforeInterceptor = [action:this.&auth,except:["isUser"]]
34        def credentials
35        def requestUser //= SecUser.findByUsername( "user" )
36
37        /**
38         * Authorization closure, which is run before executing any of the REST resource actions
39         * It fetches a consumer/token combination from the url and checks whether
40         * that is a correct and known combination
41         *
42         * @param       consumer        consumer name of the calling module
43         * @param       token           token for the authenticated user (e.g. session_id)
44         * @return true if the user is remotely logged in, false otherwise
45         */
46        private def auth() {
47                if( !AuthenticationService.isRemotelyLoggedIn( params.consumer, params.token ) ) {
48                        response.sendError(403)
49                        return false
50                } else {
51                        return true
52                }
53        }
54
55        /**
56         * REST resource for data modules.
57         * Consumer and token should be supplied via URL parameters.
58         * Determines whether the given user/password combination is a valid GSCF account.
59         *
60         * @param       consumer        consumer name of the calling module
61         * @param       token           token for the authenticated user (e.g. session_id)
62         * @return bool {"authenticated":true} when user/password is a valid GSCF account, {"authenticated":false} otherwise.
63         */
64        def isUser = {
65                boolean isUser = AuthenticationService.isRemotelyLoggedIn( params.consumer, params.token )
66                def reply = ['authenticated':isUser]
67                render reply as JSON
68        }
69
70        /**
71         * REST resource for data modules.
72         * Consumer and token should be supplied via URL parameters.
73         * Provides the details of the user that has logged in
74         *
75         * @param       consumer        consumer name of the calling module
76         * @param       token           token for the authenticated user (e.g. session_id)
77         * @return bool {"username": "...", "id": ... } when user/password is logged in.
78         */
79        def getUser = {
80                SecUser user = AuthenticationService.getRemotelyLoggedInUser( params.consumer, params.token )
81                def reply = [username: user.username, id: user.id]
82                render reply as JSON
83        }
84
85        /**
86         * REST resource for data modules.
87         * Consumer and token should be supplied via URL parameters.
88         * Provide a list of all studies owned by the supplied user.
89         *
90         * @param       consumer        consumer name of the calling module
91         * @param       token           token for the authenticated user (e.g. session_id)
92         * @return JSON object list containing 'studyToken', and 'name' (title) for each study
93         */
94        def getStudies = {
95                List studies = []
96                Study.findAllByOwner(AuthenticationService.getRemotelyLoggedInUser( params.consumer, params.token )).each { study ->
97                        studies.push( [ 'title':study.title, 'studyToken':study.getToken()] )
98                }
99                render studies as JSON
100        }
101
102
103        /**
104         * REST resource for data modules.
105         * Consumer and token should be supplied via URL parameters.
106         * Provide a list of all subjects belonging to a study.
107         *
108         * If the user is not allowed to read the study contents, a 401 error is given
109         *
110         * @param       studyToken      String The external study id (code) of the target GSCF Study object
111         * @param       consumer        consumer name of the calling module
112         * @param       token           token for the authenticated user (e.g. session_id)
113         * @return JSON object list of subject names
114         */
115        def getSubjects = {
116                List subjects = []
117                if( params.studyToken ) {
118                        def id = params.studyToken
119                        def study = Study.find( "from Study as s where s.code=?", [id])
120
121                        if(study) {
122                                // Check whether the person is allowed to read the data of this study
123                                if( !study.canRead(AuthenticationService.getRemotelyLoggedInUser( params.consumer, params.token ))) {
124                                        response.sendError(401)
125                                        return false
126                                }
127
128                                study.subjects.each { subjects.push it.name }
129                        }
130                }
131                render subjects as JSON
132        }
133
134
135        /**
136         * REST resource for data modules.
137         * Consumer and token should be supplied via URL parameters.
138         * Provide a list of all assays for a given study.
139         *
140         * If the user is not allowed to read the study contents, a 401 error is given
141         *
142         * @param       studyToken      String The external study id (code) of the target GSCF Study object
143         * @param       moduleURL       String The base URL of the calling dbNP module
144         * @param       consumer        consumer name of the calling module
145         * @return list of assays in the study as JSON object list, filtered to only contain assays
146         *         for the specified module, with 'assayToken' and 'name' for each assay
147         *
148         *
149         * Example 1. REST call without assayToken
150         *            http://localhost:8080/gscf/rest/getAssays/aas?studyToken=PPSH
151         *                              &moduleURL=http://localhost:8182/sam
152         *
153         * Result: [{"name":"Glucose assay after",
154         *                      "module":{"class":"dbnp.studycapturing.AssayModule","id":1,"name":"SAM module for clinical data",
155         *                              "platform":"clinical measurements","url":"http://localhost:8182/sam"},
156         *                      "externalAssayID":"PPSH-Glu-A", "Description":null,"parentStudyToken":"PPSH"},
157         *                      {"name":"Glucose assay before",
158         *                              "module":{"class":"dbnp.studycapturing.AssayModule","id":1,"name":"SAM module for clinical data",
159         *                              "platform":"clinical measurements","url":"http://localhost:8182/sam"},
160         *                              "externalAssayID":"PPSH-Glu-B","Description":null,"parentStudyToken":"PPSH"}]
161         *
162         *
163         * Example 2. REST call with one assayToken
164         *                        http://localhost:8080/gscf/rest/getAssays/queryOneTokenz?studyToken=PPSH
165         *                              &moduleURL=http://localhost:8182/sam&assayToken=PPSH-Glu-A
166         *
167         * Result: [{"name":"Glucose assay after","module":{"class":"dbnp.studycapturing.AssayModule","id":1,
168         *                      "name":"SAM module for clinical data","platform":"clinical measurements","url":"http://localhost:8182/sam"},
169         *                      "externalAssayID":"PPSH-Glu-A","Description":null,"parentStudyToken":"PPSH"}]
170         *
171         *
172         * Example 3. REST call with two assayTokens.
173         *
174         * Result: Same as result in Example 1.
175         */
176        def getAssays = {
177
178                List returnList = []    // return list of hashes each containing fields and values belonging to an assay
179
180                // Check if required parameters are present
181                def validCall = CommunicationManager.hasValidParams( params, "moduleURL", "studyToken" )
182                if( !validCall ) {
183                        render "Error. Wrong or insufficient parameters." as JSON
184                        return
185                }
186
187                if( params.studyToken ) {
188
189                        def id = params.studyToken
190                        def study = Study.find( "from Study as s where s.code=?", [id] )
191
192                        if(study) {
193                                // Check whether the person is allowed to read the data of this study
194                                if( !study.canRead(AuthenticationService.getRemotelyLoggedInUser( params.consumer, params.token ))) {
195                                        response.sendError(401)
196                                        return false
197                                }
198
199                                def assays = []
200                                if(params.assayToken==null) {
201                                        assays = study.assays
202                                }
203                                else if( params.assayToken instanceof String ) {
204                                        def assay = study.assays.find{ it.externalAssayID==params.assayToken }
205                                        if( assay ) {
206                                                 assays.push assay
207                                        }
208                                }
209                                else {                                                                                                  // there are multiple assayTokens instances
210                                        params.assayToken.each { assayToken ->
211                                                def assay = study.assays.find{ it.externalAssayID==assayToken }
212                                                if(assay) {
213                                                        assays.push assay
214                                                }
215                                        }
216                                }
217
218                                assays.each{ assay ->
219                                        if (assay.module.url.equals(params.moduleURL)) {
220                                                if(assay) {
221                                                        def map = [:]
222                                                        assay.giveFields().each { field ->
223                                                                def name = field.name
224                                                                def value = assay.getFieldValue( name )
225                                                                if(field.name=='externalAssayID') {
226                                                                        name = 'assayToken'
227                                                                }
228                                                                map[name] = value
229                                                        }
230                                                        map["parentStudyToken"] = assay.parent.getToken()
231                                                        returnList.push( map )
232                                                }
233                                        }
234                                }
235                }
236
237                }
238
239                render returnList as JSON
240        }
241
242
243
244
245
246
247
248        /**
249         * REST resource for data modules.
250         * Provide all samples of a given Assay. The result is an enriched list with additional information for each sample.
251         *
252         * @param       assayToken      String (assayToken of some Assay in GSCF)
253         * @param       sampleToken Optional parameter. One or more sampleTokens to specify what sample to give exectly.
254         *                      If not given, return all samples for specified assay.
255         * @param       consumer        consumer name of the calling module
256         * @param       token           token for the authenticated user (e.g. session_id)
257         * @return As a JSON object list, for each sample in that assay:
258         * @return 'name' (Sample name, which is unique)
259         * @return 'material' (Sample material)
260         * @return 'subject' (The name of the subject from which the sample was taken)
261         * @return 'event' (the name of the template of the SamplingEvent describing the sampling)
262         * @return 'startTime' (the time the sample was taken relative to the start of the study, as a string)
263         *
264         *
265         *
266         * Example 1: no sampleTokens given.
267         * Query:
268         * http://localhost:8080/gscf/rest/getSamples/query?assayToken=PPSH-Glu-A
269         *
270         * Result:
271         * [{"sampleToken":"5_A","material":"blood plasma","subject":"5","event":"Blood extraction","startTime":"4 days, 6 hours"},
272         * {"sampleToken":"6_A","material":"blood plasma","subject":"6","event":"Blood extraction","startTime":"4 days, 6 hours"},
273         * {"sampleToken":"10_A","material":"blood plasma","subject":"10","event":"Blood extraction","startTime":"4 days, 6 hours"},
274         * {"sampleToken":"2_A","material":"blood plasma","subject":"2","event":"Blood extraction","startTime":"4 days, 6 hours"},
275         * {"sampleToken":"11_A","material":"blood plasma","subject":"11","event":"Blood extraction","startTime":"4 days, 6 hours"},
276         * {"sampleToken":"1_A","material":"blood plasma","subject":"1","event":"Blood extraction","startTime":"4 days, 6 hours"},
277         * {"sampleToken":"9_A","material":"blood plasma","subject":"9","event":"Blood extraction","startTime":"4 days, 6 hours"},
278         * {"sampleToken":"4_A","material":"blood plasma","subject":"4","event":"Blood extraction","startTime":"4 days, 6 hours"},
279         * {"sampleToken":"8_A","material":"blood plasma","subject":"8","event":"Blood extraction","startTime":"4 days, 6 hours"},
280         * {"sampleToken":"7_A","material":"blood plasma","subject":"7","event":"Blood extraction","startTime":"4 days, 6 hours"},
281         * {"sampleToken":"3_A","material":"blood plasma","subject":"3","event":"Blood extraction","startTime":"4 days, 6 hours"}]
282         *
283         *
284         *
285         * Example 2: one sampleToken given.
286         * Query:
287         * http://localhost:8080/gscf/rest/getSamples/query?assayToken=PPSH-Glu-A&sampleToken=5_A
288         *
289         * Result:
290         * [{"sampleToken":"5_A","material":"blood plasma","subject":"5","event":"Blood extraction","startTime":"4 days, 6 hours"}]
291         *
292         *
293         *
294         * Example 3: two sampleTokens given.
295         * Query:
296         * http://localhost:8080/gscf/rest/getSamples/query?assayToken=PPSH-Glu-A&sampleToken=5_A
297         *
298         * Result:
299         * [{"sampleToken":"5_A","material":"blood plasma","subject":"5","event":"Blood extraction","startTime":"4 days, 6 hours"},
300         *  {"sampleToken":"6_A","material":"blood plasma","subject":"6","event":"Blood extraction","startTime":"4 days, 6 hours"}]
301         */
302        def getSamples = {
303                def items = []
304                if( params.assayToken ) {
305                        def assay = Assay.find( "from Assay as a where externalAssayID=?",[params.assayToken])
306                        if( assay )  {
307                                if( params.sampleToken ) {
308                                        def sampleTokens = (params.sampleToken instanceof String) ?
309                                                [params.sampleToken] : params.sampleToken
310                                                assay.getSamples().each { sample ->
311                                                if( sampleTokens.find{ it == sample.name } ) {
312                                                        println "adding"
313                                                        def item = [
314                                                                'sampleToken' : sample.name,
315                                                                'material'        : sample.material?.name,
316                                                                'subject'         : sample.parentSubject?.name,
317                                                                'event'           : sample.parentEvent?.template?.name,
318                                                                'startTime'       : sample.parentEvent?.getStartTimeString()
319                                                        ]
320                                                        items.push item
321                                                }
322                                        }
323                                }
324                                else {
325                                        assay.getSamples().each { sample ->
326                                                def item = [
327                                                        'sampleToken' : sample.name,
328                                                        'material'        : sample.material?.name,
329                                                        'subject'         : sample.parentSubject?.name,
330                                                        'event'           : sample.parentEvent?.template?.name,
331                                                        'startTime'       : sample.parentEvent?.getStartTimeString()
332                                                ]
333                                                items.push item
334                                        }
335                                }
336                        }
337                }
338                render items as JSON
339        }
340
341
342        /**
343         * REST resource for dbNP modules.
344         *
345         * @param       studyToken String, the external identifier of the study
346         * @param       consumer        consumer name of the calling module
347         * @param       token           token for the authenticated user (e.g. session_id)
348         * @return List of all fields of this study
349         * @return
350         *
351         * If the user is not allowed to read this study, a 401 error is given
352         *
353         * Example REST call (without authentication):
354     * http://localhost:8080/gscf/rest/getStudy/study?studyToken=PPSH
355     *
356         * Returns the JSON object:
357         * {"title":"NuGO PPS human study","studyToken":"PPSH","startDate":"2008-01-13T23:00:00Z",
358         * "Description":"Human study performed at RRI; centres involved: RRI, IFR, TUM, Maastricht U.",
359         * "Objectives":null,"Consortium":null,"Cohort name":null,"Lab id":null,"Institute":null,
360         * "Study protocol":null}
361        */
362        def getStudy = {
363                def items = [:]
364                if( params.studyToken ) {
365                        def study = Study.find( "from Study as s where code=?",[params.studyToken])
366                        if(study) {
367                                // Check whether the person is allowed to read the data of this study
368                                if( !study.canRead(AuthenticationService.getRemotelyLoggedInUser( params.consumer, params.token ))) {
369                                        response.sendError(401)
370                                        return false
371                                }
372                               
373                                study.giveFields().each { field ->
374                                        def name = field.name
375                                        def value = study.getFieldValue( name )
376                                        items[name] = value
377                                }
378                        }
379        }
380                render items as JSON
381        }
382
383
384
385
386        /**
387         * Returns the authorization level the user has for a given study.
388         *
389         * If no studyToken is given, a 400 (Bad Request) error is given.
390         * If the given study doesn't exist, a 404 (Not found) error is given.
391         *
392         * @param       consumer        consumer name of the calling module
393         * @param       token           token for the authenticated user (e.g. session_id)
394         * @return      JSON Object
395         * @return  { isOwner: true/false, 'canRead': true/false, 'canWrite': true/false }
396         */
397        def getAuthorizationLevel = {
398                if( params.studyToken ) {
399                        def id = params.studyToken
400                        def study = Study.find( "from Study as s where s.code=?", [id])
401
402                        if( !study ) {
403                                response.sendError(404)
404                                return false
405                        }
406
407                        def user = AuthenticationService.getRemotelyLoggedInUser( params.consumer, params.token );
408                        render( 'isOwner': study.isOwner(user), 'canRead': study.canRead(user), 'canWrite': study.canWrite(user) )
409                } else {
410                        response.sendError(400)
411                        return false
412                }
413    }
414}
Note: See TracBrowser for help on using the browser.