root/trunk/grails-app/controllers/api/ApiController.groovy @ 2202

Revision 2202, 13.3 KB (checked in by work@…, 2 years ago)

- updated api specification
- added in-code documentation
- added try/catch block to getMeasurementDataForAssay API call to check for valid module REST implementation

Line 
1/**
2 * Api Controler
3 *
4 * API for third party applications to interact
5 * with GSCF
6 *
7 * @author  your email (+name?)
8 * @since       20120328ma
9 *
10 * Revision information:
11 * $Rev$
12 * $Author$
13 * $Date$
14 */
15package api
16
17import grails.plugins.springsecurity.Secured
18import grails.converters.JSON
19import dbnp.studycapturing.Study
20import dbnp.studycapturing.Assay
21
22class ApiController {
23    def authenticationService
24    def apiService
25
26        /**
27         * index closure
28         */
29    def index = {
30        render(view:'index')
31    }
32
33    /**
34     * authenticate with the api using HTTP_BASIC authentication
35     *
36     * This means
37     * 1. the client should send the HTTP_BASIC authentication header
38     *    which is an md5 hash of the username + password concatenated:
39     *
40     *    Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
41     *
42     * 2. the user used to authenticate with the API should have
43     *    the ROLE_CLIENT role
44     *
45     * @param string deviceID
46     */
47    @Secured(['ROLE_CLIENT', 'ROLE_ADMIN'])
48    def authenticate = {
49        println "api::authenticate: ${params}"
50
51        // see if we already have a token on file for this device id
52        String deviceID = (params.containsKey('deviceID')) ? params.deviceID : ''
53        def token = Token.findByDeviceID(deviceID)
54       
55        // generate a new token if we don't have a token on file
56        def result = [:]
57        try {
58            // TODO - check if token belongs to current user?
59            if (!token) {
60                // generate a token for this device
61                token = new Token(
62                        deviceID    : params.deviceID,
63                        deviceToken : UUID.randomUUID().toString(),
64                        user        : authenticationService.getLoggedInUser(),
65                        sequence    : 0
66                ).save(failOnError: true)
67            }
68           
69            result = ['token':token.deviceToken,'sequence':token.sequence]
70
71            // set output headers
72            response.status = 200
73        } catch (Exception e) {
74            // caught an error
75            response.status = 500
76            result = ['error':e.getMessage()]
77        }
78
79        response.contentType = 'application/json;charset=UTF-8'
80
81        if (params.containsKey('callback')) {
82            render "${params.callback}(${result as JSON})"
83        } else {
84            render result as JSON
85        }
86    }
87
88    /**
89     * get all readable studies
90     *
91     * @param string deviceID
92     * @param string validation md5 sum
93     */
94    def getStudies = {
95        println "api::getStudies: ${params}"
96
97        String deviceID = (params.containsKey('deviceID')) ? params.deviceID : ''
98        String validation = (params.containsKey('validation')) ? params.validation : ''
99
100        // check
101        if (!apiService.validateRequest(deviceID,validation)) {
102            response.sendError(401, 'Unauthorized')
103        } else {
104            def user = Token.findByDeviceID(deviceID)?.user
105            def readableStudies = Study.giveReadableStudies(user)
106            def studies = []
107           
108            // iterate through studies and define resultset
109            readableStudies.each { study ->
110                // get result data
111                studies[ studies.size() ] = [
112                        'token'                 : study.getToken(),
113                        'title'                 : study.title,
114                        'description'           : study.description,
115                        'subjects'              : study.subjects.size(),
116                        'species'               : study.subjects.species.collect { it.name }.unique(),
117                        'assays'                : study.assays.collect { it.name }.unique(),
118                        'modules'               : study.assays.collect { it.module.name }.unique(),
119                        'events'                : study.events.size(),
120                        'uniqueEvents'          : study.events.collect { it.toString() }.unique(),
121                        'samplingEvents'        : study.samplingEvents.size(),
122                        'uniqueSamplingEvents'  : study.samplingEvents.collect { it.toString() }.unique(),
123                        'eventGroups'           : study.eventGroups.size(),
124                        'uniqueEventGroups'     : study.eventGroups.collect { it.name }.unique(),
125                        'samples'               : study.samples.size()
126                ]
127            }
128
129            def result = [
130                    'count'     : studies.size(),
131                    'studies'   : studies
132            ]
133
134            // set output headers
135            response.status = 200
136            response.contentType = 'application/json;charset=UTF-8'
137
138            if (params.containsKey('callback')) {
139                render "${params.callback}(${result as JSON})"
140            } else {
141                render result as JSON
142            }
143        }
144    }
145
146    /**
147     * get all subjects for a study
148     *
149     * @param string deviceID
150     * @param string studyToken
151     * @param string validation md5 sum
152     */
153    def getSubjectsForStudy = {
154        println "api::getSubjectsForStudy: ${params}"
155
156        String deviceID     = (params.containsKey('deviceID')) ? params.deviceID : ''
157        String validation   = (params.containsKey('validation')) ? params.validation : ''
158        String studyToken   = (params.containsKey('studyToken')) ? params.studyToken : ''
159
160        // fetch user and study
161        def user    = Token.findByDeviceID(deviceID)?.user
162        def study   = Study.findByStudyUUID(studyToken)
163       
164        // check
165        if (!apiService.validateRequest(deviceID,validation)) {
166            response.sendError(401, 'Unauthorized')
167        } else if (!study) {
168            response.sendError(400, 'No such study')
169        } else if (!study.canRead(user)) {
170            response.sendError(401, 'Unauthorized')
171        } else {
172            def subjects = apiService.flattenDomainData( study.subjects )
173
174            // define result
175            def result = [
176                    'count'     : subjects.size(),
177                    'subjects'  : subjects
178            ]
179
180            // set output headers
181            response.status = 200
182            response.contentType = 'application/json;charset=UTF-8'
183
184            if (params.containsKey('callback')) {
185                render "${params.callback}(${result as JSON})"
186            } else {
187                render result as JSON
188            }
189        }
190    }
191
192    /**
193     * get all assays for a study
194     *
195     * @param string deviceID
196     * @param string studyToken
197     * @param string validation md5 sum
198     */
199    def getAssaysForStudy = {
200        println "api::getAssaysForStudy: ${params}"
201
202        String deviceID     = (params.containsKey('deviceID')) ? params.deviceID : ''
203        String validation   = (params.containsKey('validation')) ? params.validation : ''
204        String studyToken   = (params.containsKey('studyToken')) ? params.studyToken : ''
205
206        // fetch user and study
207        def user    = Token.findByDeviceID(deviceID)?.user
208        def study   = Study.findByStudyUUID(studyToken)
209
210        // check
211        if (!apiService.validateRequest(deviceID,validation)) {
212            response.sendError(401, 'Unauthorized')
213        } else if (!study) {
214            response.sendError(400, 'No such study')
215        } else if (!study.canRead(user)) {
216            response.sendError(401, 'Unauthorized')
217        } else {
218            def assays = apiService.flattenDomainData( study.assays )
219
220            // define result
221            def result = [
222                    'count'     : assays.size(),
223                    'assays'    : assays
224            ]
225
226            // set output headers
227            response.status = 200
228            response.contentType = 'application/json;charset=UTF-8'
229
230            if (params.containsKey('callback')) {
231                render "${params.callback}(${result as JSON})"
232            } else {
233                render result as JSON
234            }
235        }
236    }
237
238    /**
239     * get all samples for an assay
240     *
241     * @param string deviceID
242     * @param string assayToken
243     * @param string validation md5 sum
244     */
245    def getSamplesForAssay = {
246        println "api::getSamplesForAssay: ${params}"
247
248        String deviceID     = (params.containsKey('deviceID')) ? params.deviceID : ''
249        String validation   = (params.containsKey('validation')) ? params.validation : ''
250        String assayToken   = (params.containsKey('assayToken')) ? params.assayToken : ''
251
252        // fetch user and study
253        def user    = Token.findByDeviceID(deviceID)?.user
254        def assay   = Assay.findByAssayUUID(assayToken)
255
256        // check
257        if (!apiService.validateRequest(deviceID,validation)) {
258            response.sendError(401, 'Unauthorized')
259        } else if (!assay) {
260            response.sendError(400, 'No such assay')
261        } else if (!assay.parent.canRead(user)) {
262            response.sendError(401, 'Unauthorized')
263        } else {
264            def samples = apiService.flattenDomainData( assay.samples )
265
266            // define result
267            def result = [
268                    'count'     : samples.size(),
269                    'samples'   : samples
270            ]
271
272            // set output headers
273            response.status = 200
274            response.contentType = 'application/json;charset=UTF-8'
275
276            if (params.containsKey('callback')) {
277                render "${params.callback}(${result as JSON})"
278            } else {
279                render result as JSON
280            }
281        }
282
283    }
284
285    /**
286     * get all measurement data from a linked module for an assay
287     *
288     * @param string deviceID
289     * @param string assayToken
290     * @param string validation md5 sum
291     */
292    def getMeasurementDataForAssay = {
293        println "api:getMeasurementDataForAssay: ${params}"
294
295        String deviceID     = (params.containsKey('deviceID')) ? params.deviceID : ''
296        String validation   = (params.containsKey('validation')) ? params.validation : ''
297        String assayToken   = (params.containsKey('assayToken')) ? params.assayToken : ''
298
299        // fetch user and study
300        def user    = Token.findByDeviceID(deviceID)?.user
301        def assay   = Assay.findByAssayUUID(assayToken)
302
303        // check
304        if (!apiService.validateRequest(deviceID,validation)) {
305            response.sendError(401, 'Unauthorized')
306        } else if (!assay) {
307            response.sendError(400, 'No such assay')
308        } else if (!assay.parent.canRead(user)) {
309            response.sendError(401, 'Unauthorized')
310        } else {
311            // define sample measurement data matrix
312            def matrix = [:]
313            def measurementData     = apiService.getMeasurementData(assay, user)
314            def measurementMetaData = apiService.getMeasurementData(assay, user)
315
316            // iterate through measurementData and build data matrix
317            try {
318                measurementData.each { data ->
319                    if (!matrix.containsKey(data.sampleToken)) matrix[data.sampleToken] = [:]
320                    matrix[data.sampleToken][data.measurementToken] = data.value
321                }
322
323                // define result
324                def result = [
325                        'count'         : matrix.size(),
326                        'measurements'  : matrix
327                ]
328
329                // set output headers
330                response.status = 200
331                response.contentType = 'application/json;charset=UTF-8'
332
333                if (params.containsKey('callback')) {
334                    render "${params.callback}(${result as JSON})"
335                } else {
336                    render result as JSON
337                }
338            } catch (Exception e) {
339                response.sendError(500, "module '${assay.module}' does not properly implement getMeasurementData REST specification (${e.getMessage()})")
340            }
341        }
342    }
343
344    // ---- debugging -----
345
346    def debugModuleDataForAssay = {
347        println "api:debugModuleDataForAssay: ${params}"
348
349        String deviceID     = (params.containsKey('deviceID')) ? params.deviceID : ''
350        String validation   = (params.containsKey('validation')) ? params.validation : ''
351        String assayToken   = (params.containsKey('assayToken')) ? params.assayToken : ''
352
353        // fetch user and study
354        def user    = Token.findByDeviceID(deviceID)?.user
355        def assay   = Assay.findByAssayUUID(assayToken)
356
357        // check
358        if (!apiService.validateRequest(deviceID,validation)) {
359            response.sendError(401, 'Unauthorized')
360        } else if (!assay) {
361            response.sendError(400, 'No such assay')
362        } else if (!assay.parent.canRead(user)) {
363            response.sendError(401, 'Unauthorized')
364        } else {
365            // define result
366            def result = [
367                    'measurements'  : apiService.getMeasurements(assay, user),
368                    'data'          : apiService.getMeasurementData(assay, user),
369                    'metaData'      : apiService.getMeasurementMetaData(assay, user)
370            ]
371
372            // set output headers
373            response.status = 200
374            response.contentType = 'application/json;charset=UTF-8'
375
376            if (params.containsKey('callback')) {
377                render "${params.callback}(${result as JSON})"
378            } else {
379                render result as JSON
380            }
381        }
382    }
383}
Note: See TracBrowser for help on using the browser.