source: trunk/grails-app/controllers/RestController.groovy @ 1001

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

Ticket 57. Merged getSample and getSamples into one REST resource.
getSamples can now be called with zero, one, or more than one sampleTokens
to be returned.

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