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

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

Ticket #57. Unified getStudy and getStudies.

Note: Could not get authorization working. This still needs to be tested for getStudies, getAssays, and getSubjects.

  • 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                return true
54        }
55
56        /**
57         * REST resource for data modules.
58         * Consumer and token should be supplied via URL parameters.
59         * Determines whether the given user/password combination is a valid GSCF account.
60         *
61         * @param       consumer        consumer name of the calling module
62         * @param       token           token for the authenticated user (e.g. session_id)
63         * @return bool {"authenticated":true} when user/password is a valid GSCF account, {"authenticated":false} otherwise.
64         */
65        def isUser = {
66                boolean isUser = AuthenticationService.isRemotelyLoggedIn( params.consumer, params.token )
67                def reply = ['authenticated':isUser]
68                render reply as JSON
69        }
70
71        /**
72         * REST resource for data modules.
73         * Consumer and token should be supplied via URL parameters.
74         * Provides the details of the user that has logged in
75         *
76         * @param       consumer        consumer name of the calling module
77         * @param       token           token for the authenticated user (e.g. session_id)
78         * @return bool {"username": "...", "id": ... } when user/password is logged in.
79         */
80        def getUser = {
81                SecUser user = AuthenticationService.getRemotelyLoggedInUser( params.consumer, params.token )
82                def reply = [username: user.username, id: user.id]
83                render reply as JSON
84        }
85
86
87        /**
88         * REST resource for data modules.
89         * Consumer and token should be supplied via URL parameters.
90         * Provide a list of all studies owned by the supplied user.
91         *
92         * @param       studyToken  optional parameter. If no studyToken is given, all studies available to user are returned.
93         *                      Otherwise, the studies for which the studyTokens are given are be returned.
94         * @param       consumer        consumer name of the calling module
95         * @param       token           token for the authenticated user (e.g. session_id)
96         * @return  JSON object list containing 'studyToken', and 'name' (title) for each study
97         *
98         *
99         * Example 1. REST call without studyToken.
100         *
101         * Call: http://localhost:8080/gscf/rest/getStudies/query
102         *
103         * Result: [{"title":"NuGO PPS3 mouse study leptin module","studyToken":"PPS3_leptin_module",
104         *                      "startDate":"2008-01-01T23:00:00Z","published":false,"Description":"C57Bl/6 mice were fed a high fat (45 en%)
105         *                      or low fat (10 en%) diet after a four week run-in on low fat diet.","Objectives":null,"Consortium":null,
106         *                      "Cohort name":null,"Lab id":null,"Institute":null,"Study protocol":null},
107         *                      {"title":"NuGO PPS human study","studyToken":"PPSH","startDate":"2008-01-13T23:00:00Z","published":false,
108         *                      "Description":"Human study performed at RRI; centres involved: RRI, IFR, TUM, Maastricht U.","Objectives":null,
109         *                      "Consortium":null,"Cohort name":null,"Lab id":null,"Institute":null,"Study protocol":null}]
110         *
111         *
112         * Example 2. REST call with one studyToken.
113         *
114         * Call: http://localhost:8080/gscf/rest/getStudies/query?studyToken=PPSH
115         *
116         * Result: [{"title":"NuGO PPS human study","studyToken":"PPSH","startDate":"2008-01-13T23:00:00Z",
117         *              "published":false,"Description":"Human study performed at RRI; centres involved: RRI, IFR, TUM, Maastricht U.",
118         *              "Objectives":null,"Consortium":null,"Cohort name":null,"Lab id":null,"Institute":null,"Study protocol":null}]
119         *
120         *
121         *
122         * Example 2. REST call with two studyTokens.
123         *
124         * http://localhost:8080/gscf/rest/getStudies/query?studyToken=PPSH&studyToken=PPS3_leptin_module
125         *
126         * Result: same as result of Example 1.
127         */
128        def getStudies = {
129
130                List returnStudies = []
131                List studies = []
132
133                if( !params.studyToken ) {
134                        studies = Study.findAll()
135                }
136                else if( params.studyToken instanceof String ) {
137                        studies.push Study.findByCode( params.studyToken )
138                }
139                else {
140                        params.studyToken.each{ studyToken ->
141                                studies.push Study.findByCode( studyToken )
142                        }
143                }
144
145                studies.each { study ->
146                        if(study) {
147                                // Check whether the person is allowed to read the data of this study
148                                if( !study.canRead(AuthenticationService.getRemotelyLoggedInUser( params.consumer, params.token ))) {
149                                        response.sendError(401)
150                                        return false
151                                }
152                                def items = [:]
153                                study.giveFields().each { field ->
154                                        def name = field.name
155                                        def value = study.getFieldValue( name )
156                                        if( name=='code' ) {
157                                                name = 'studyToken'
158                                        }
159                                        items[name] = value
160                                }
161                                returnStudies.push items
162                        }
163                }
164
165                render returnStudies as JSON
166        }
167
168
169        /**
170         * REST resource for data modules.
171         * Consumer and token should be supplied via URL parameters.
172         * Provide a list of all subjects belonging to a study.
173         *
174         * If the user is not allowed to read the study contents, a 401 error is given
175         *
176         * @param       studyToken      String The external study id (code) of the target GSCF Study object
177         * @param       consumer        consumer name of the calling module
178         * @param       token           token for the authenticated user (e.g. session_id)
179         * @return JSON object list of subject names
180         */
181        def getSubjects = {
182                List subjects = []
183                if( params.studyToken ) {
184                        def id = params.studyToken
185                        def study = Study.find( "from Study as s where s.code=?", [id])
186
187                        if(study) {
188                                // Check whether the person is allowed to read the data of this study
189                                if( !study.canRead(AuthenticationService.getRemotelyLoggedInUser( params.consumer, params.token ))) {
190                                        response.sendError(401)
191                                        return false
192                                }
193
194                                study.subjects.each { subjects.push it.name }
195                        }
196                }
197                render subjects as JSON
198        }
199
200
201        /**
202         * REST resource for data modules.
203         * Consumer and token should be supplied via URL parameters.
204         * Provide a list of all assays for a given study.
205         *
206         * If the user is not allowed to read the study contents, a 401 error is given
207         *
208         * @param       studyToken      String The external study id (code) of the target GSCF Study object
209         * @param       moduleURL       String The base URL of the calling dbNP module
210         * @param       consumer        consumer name of the calling module
211         * @return list of assays in the study as JSON object list, filtered to only contain assays
212         *         for the specified module, with 'assayToken' and 'name' for each assay
213         *
214         *
215         * Example 1. REST call without assayToken
216         *            http://localhost:8080/gscf/rest/getAssays/aas?studyToken=PPSH
217         *                              &moduleURL=http://localhost:8182/sam
218         *
219         * Result: [{"name":"Glucose assay after",
220         *                      "module":{"class":"dbnp.studycapturing.AssayModule","id":1,"name":"SAM module for clinical data",
221         *                              "platform":"clinical measurements","url":"http://localhost:8182/sam"},
222         *                      "externalAssayID":"PPSH-Glu-A", "Description":null,"parentStudyToken":"PPSH"},
223         *                      {"name":"Glucose assay before",
224         *                              "module":{"class":"dbnp.studycapturing.AssayModule","id":1,"name":"SAM module for clinical data",
225         *                              "platform":"clinical measurements","url":"http://localhost:8182/sam"},
226         *                              "externalAssayID":"PPSH-Glu-B","Description":null,"parentStudyToken":"PPSH"}]
227         *
228         *
229         * Example 2. REST call with one assayToken
230         *                        http://localhost:8080/gscf/rest/getAssays/queryOneTokenz?studyToken=PPSH
231         *                              &moduleURL=http://localhost:8182/sam&assayToken=PPSH-Glu-A
232         *
233         * Result: [{"name":"Glucose assay after","module":{"class":"dbnp.studycapturing.AssayModule","id":1,
234         *                      "name":"SAM module for clinical data","platform":"clinical measurements","url":"http://localhost:8182/sam"},
235         *                      "externalAssayID":"PPSH-Glu-A","Description":null,"parentStudyToken":"PPSH"}]
236         *
237         *
238         * Example 3. REST call with two assayTokens.
239         *
240         * Result: Same as result in Example 1.
241         */
242        def getAssays = {
243
244                List returnList = []    // return list of hashes each containing fields and values belonging to an assay
245
246                // Check if required parameters are present
247                def validCall = CommunicationManager.hasValidParams( params, "moduleURL", "studyToken" )
248                if( !validCall ) {
249                        render "Error. Wrong or insufficient parameters." as JSON
250                        return
251                }
252
253                if( params.studyToken ) {
254
255                        def id = params.studyToken
256                        def study = Study.find( "from Study as s where s.code=?", [id] )
257
258                        if(study) {
259                                // Check whether the person is allowed to read the data of this study
260                                if( !study.canRead(AuthenticationService.getRemotelyLoggedInUser( params.consumer, params.token ))) {
261                                        response.sendError(401)
262                                        return false
263                                }
264
265                                def assays = []
266                                if(params.assayToken==null) {
267                                        assays = study.assays
268                                }
269                                else if( params.assayToken instanceof String ) {
270                                        def assay = study.assays.find{ it.externalAssayID==params.assayToken }
271                                        if( assay ) {
272                                                 assays.push assay
273                                        }
274                                }
275                                else {                                                                                                  // there are multiple assayTokens instances
276                                        params.assayToken.each { assayToken ->
277                                                def assay = study.assays.find{ it.externalAssayID==assayToken }
278                                                if(assay) {
279                                                        assays.push assay
280                                                }
281                                        }
282                                }
283
284                                assays.each{ assay ->
285                                        if (assay.module.url.equals(params.moduleURL)) {
286                                                if(assay) {
287                                                        def map = [:]
288                                                        assay.giveFields().each { field ->
289                                                                def name = field.name
290                                                                def value = assay.getFieldValue( name )
291                                                                if(field.name=='externalAssayID') {
292                                                                        name = 'assayToken'
293                                                                }
294                                                                map[name] = value
295                                                        }
296                                                        map["parentStudyToken"] = assay.parent.getToken()
297                                                        returnList.push( map )
298                                                }
299                                        }
300                                }
301                }
302
303                }
304
305                render returnList as JSON
306        }
307
308
309
310
311
312
313
314        /**
315         * REST resource for data modules.
316         * Provide all samples of a given Assay. The result is an enriched list with additional information for each sample.
317         *
318         * @param       assayToken      String (assayToken of some Assay in GSCF)
319         * @param       sampleToken Optional parameter. One or more sampleTokens to specify what sample to give exectly.
320         *                      If not given, return all samples for specified assay.
321         * @param       consumer        consumer name of the calling module
322         * @param       token           token for the authenticated user (e.g. session_id)
323         * @return As a JSON object list, for each sample in that assay:
324         * @return 'name' (Sample name, which is unique)
325         * @return 'material' (Sample material)
326         * @return 'subject' (The name of the subject from which the sample was taken)
327         * @return 'event' (the name of the template of the SamplingEvent describing the sampling)
328         * @return 'startTime' (the time the sample was taken relative to the start of the study, as a string)
329         *
330         *
331         *
332         * Example 1: no sampleTokens given.
333         * Query:
334         * http://localhost:8080/gscf/rest/getSamples/query?assayToken=PPSH-Glu-A
335         *
336         * Result:
337         * [{"sampleToken":"5_A","material":"blood plasma","subject":"5","event":"Blood extraction","startTime":"4 days, 6 hours"},
338         * {"sampleToken":"6_A","material":"blood plasma","subject":"6","event":"Blood extraction","startTime":"4 days, 6 hours"},
339         * {"sampleToken":"10_A","material":"blood plasma","subject":"10","event":"Blood extraction","startTime":"4 days, 6 hours"},
340         * {"sampleToken":"2_A","material":"blood plasma","subject":"2","event":"Blood extraction","startTime":"4 days, 6 hours"},
341         * {"sampleToken":"11_A","material":"blood plasma","subject":"11","event":"Blood extraction","startTime":"4 days, 6 hours"},
342         * {"sampleToken":"1_A","material":"blood plasma","subject":"1","event":"Blood extraction","startTime":"4 days, 6 hours"},
343         * {"sampleToken":"9_A","material":"blood plasma","subject":"9","event":"Blood extraction","startTime":"4 days, 6 hours"},
344         * {"sampleToken":"4_A","material":"blood plasma","subject":"4","event":"Blood extraction","startTime":"4 days, 6 hours"},
345         * {"sampleToken":"8_A","material":"blood plasma","subject":"8","event":"Blood extraction","startTime":"4 days, 6 hours"},
346         * {"sampleToken":"7_A","material":"blood plasma","subject":"7","event":"Blood extraction","startTime":"4 days, 6 hours"},
347         * {"sampleToken":"3_A","material":"blood plasma","subject":"3","event":"Blood extraction","startTime":"4 days, 6 hours"}]
348         *
349         *
350         *
351         * Example 2: one sampleToken given.
352         * Query:
353         * http://localhost:8080/gscf/rest/getSamples/query?assayToken=PPSH-Glu-A&sampleToken=5_A
354         *
355         * Result:
356         * [{"sampleToken":"5_A","material":"blood plasma","subject":"5","event":"Blood extraction","startTime":"4 days, 6 hours"}]
357         *
358         *
359         *
360         * Example 3: two sampleTokens given.
361         * Query:
362         * http://localhost:8080/gscf/rest/getSamples/query?assayToken=PPSH-Glu-A&sampleToken=5_A
363         *
364         * Result:
365         * [{"sampleToken":"5_A","material":"blood plasma","subject":"5","event":"Blood extraction","startTime":"4 days, 6 hours"},
366         *  {"sampleToken":"6_A","material":"blood plasma","subject":"6","event":"Blood extraction","startTime":"4 days, 6 hours"}]
367         */
368        def getSamples = {
369                def items = []
370                if( params.assayToken ) {
371                        def assay = Assay.find( "from Assay as a where externalAssayID=?",[params.assayToken])
372                        if( assay )  {
373                                if( params.sampleToken ) {
374                                        def sampleTokens = (params.sampleToken instanceof String) ?
375                                                [params.sampleToken] : params.sampleToken
376                                                assay.getSamples().each { sample ->
377                                                if( sampleTokens.find{ it == sample.name } ) {
378                                                        def item = [
379                                                                'sampleToken' : sample.name,
380                                                                'material'        : sample.material?.name,
381                                                                'subject'         : sample.parentSubject?.name,
382                                                                'event'           : sample.parentEvent?.template?.name,
383                                                                'startTime'       : sample.parentEvent?.getStartTimeString()
384                                                        ]
385                                                        items.push item
386                                                }
387                                        }
388                                }
389                                else {
390                                        assay.getSamples().each { sample ->
391                                                def item = [
392                                                        'sampleToken' : sample.name,
393                                                        'material'        : sample.material?.name,
394                                                        'subject'         : sample.parentSubject?.name,
395                                                        'event'           : sample.parentEvent?.template?.name,
396                                                        'startTime'       : sample.parentEvent?.getStartTimeString()
397                                                ]
398                                                items.push item
399                                        }
400                                }
401                        }
402                }
403                render items as JSON
404        }
405
406
407
408        /**
409         * Returns the authorization level the user has for a given study.
410         *
411         * If no studyToken is given, a 400 (Bad Request) error is given.
412         * If the given study doesn't exist, a 404 (Not found) error is given.
413         *
414         * @param       consumer        consumer name of the calling module
415         * @param       token           token for the authenticated user (e.g. session_id)
416         * @return      JSON Object
417         * @return  { isOwner: true/false, 'canRead': true/false, 'canWrite': true/false }
418         */
419        def getAuthorizationLevel = {
420                if( params.studyToken ) {
421                        def id = params.studyToken
422                        def study = Study.find( "from Study as s where s.code=?", [id])
423
424                        if( !study ) {
425                                response.sendError(404)
426                                return false
427                        }
428
429                        def user = AuthenticationService.getRemotelyLoggedInUser( params.consumer, params.token );
430                        render( 'isOwner': study.isOwner(user), 'canRead': study.canRead(user), 'canWrite': study.canWrite(user) )
431                } else {
432                        response.sendError(400)
433                        return false
434                }
435    }
436}
Note: See TracBrowser for help on using the browser.