root/trunk/grails-app/services/api/ApiService.groovy @ 2222

Revision 2222, 9.0 KB (checked in by work@…, 2 years ago)

one return statement instead of two

Line 
1/**
2 * ApiService Service
3 *
4 * Description of my service
5 *
6 * @author  Jeroen Wesbeek <work@osx.eu>
7 * @since       20120328
8 * @package     api
9 *
10 * Revision information:
11 * $Rev: 1430 $
12 * $Author: work@osx.eu $
13 * $Date: 2011-01-21 21:05:36 +0100 (Fri, 21 Jan 2011) $
14 */
15package api
16
17import java.security.MessageDigest
18import dbnp.studycapturing.Assay
19import dbnp.authentication.SecUser
20import grails.converters.JSON
21import org.dbnp.gdt.TemplateEntity
22import org.springframework.context.ApplicationContextAware
23import org.codehaus.groovy.grails.plugins.web.taglib.ApplicationTagLib
24import org.springframework.context.ApplicationContext
25
26class ApiService implements Serializable, ApplicationContextAware {
27    // inject the module communication service
28    def moduleCommunicationService
29
30    // the shared secret used to validate api calls
31    static final String API_SECRET = "th!s_sH0uld^Pr0bab7y_m0v3_t%_th3_uSeR_d0Ma!n_ins7ead!"
32
33    // transactional
34    static transactional = false
35
36    // characters to split on when converting a string to camelCased format
37    static camelCaseSeperators = " |-|_"
38
39    // hasMany keys to ignore when flattening domain data
40    static ignoreHasManyKeys = [
41            "systemFields",
42            "templateBooleanFields",
43            "templateDateFields",
44            "templateDoubleFields",
45            "templateExtendableStringListFields",
46            "templateFileFields",
47            "templateLongFields",
48            "templateModuleFields",
49            "templateTermFields",
50            "templateRelTimeFields",
51            "templateStringFields",
52            "templateStringListFields",
53            "templateTemplateFields",
54            "templateTextFields"
55    ]
56
57    private ApplicationTagLib g
58
59    void setApplicationContext(ApplicationContext applicationContext) {
60        g = applicationContext.getBean(ApplicationTagLib)
61
62        // now you have a reference to g that you can call render() on
63    }
64
65    /**
66     * validate a client request by checking the validation checksum
67     * @param deviceID
68     * @param validation
69     * @return
70     */
71    def validateRequest(String deviceID, String validation) {
72        def validated = false
73
74        // disable validation check on development and ci
75        if (['development', 'ci'].contains(grails.util.GrailsUtil.environment)) {
76            return true
77        }
78
79        // get token for this device ID
80        Token token = Token.findByDeviceID(deviceID)
81
82        // increase sequence
83        if (token) {
84            token.sequence = token.sequence+1
85            token.merge(flush: true)
86
87            // generate the validation checksum
88            MessageDigest digest = MessageDigest.getInstance("MD5")
89            String validationSum = new BigInteger(1,digest.digest("${token.deviceToken}${token.sequence}${API_SECRET}".getBytes())).toString(16).padLeft(32,"0")
90
91            // check if the validation confirms
92            validated = (validation == validationSum)
93        }
94
95        return validated
96    }
97
98    /**
99     * flatten domain data to relevant data to return in an api
100     * call and not to expose domain internals
101     *
102     * @param elements (List or Set)
103     * @return
104     */
105    def flattenDomainData(elements) {
106        def items = []
107
108        // iterate through elements
109        elements.each {
110            def fields  = (it.respondsTo('giveFields')) ? it.giveFields(): []
111            def item    = [:]
112
113            // check if element has a name
114            ['name','description'].each { checkName ->
115                if (it.hasProperty(checkName)) item[checkName] = it[checkName]
116            }
117
118            // add token
119            if (it.respondsTo('getToken')) {
120                // some domain methods implement getToken...
121                item['token'] = it.getToken()
122            } else if (it.respondsTo('giveUUID')) {
123                // ...while others implement giveUUID
124                item['token'] = it.giveUUID()
125            } else {
126                // and others don't at all, so far
127                // the consistency...
128                item['id'] = it.id
129            }
130
131            // add subject field values
132            fields.each { field ->
133                // get a camelCased version of the field name
134                def name = field.name.split(camelCaseSeperators).collect {it[0].toUpperCase() + it.substring(1)}.join('')
135                    name = name[0].toLowerCase() + name.substring(1)
136
137                // get the value for this field
138                def value = it.getFieldValue( field.name )
139
140                // add value
141                if (value.hasProperty('name')) {
142                    item[ name ] = value.name
143                } else {
144                    item[ name ] = value
145                }
146            }
147
148            // list hasMany sizes
149            it.properties.hasMany.each { hasManyItem ->
150                if (!ignoreHasManyKeys.contains(hasManyItem.key)) {
151                    // add count for this hasMany item
152                    item[ hasManyItem.key ] = it[ hasManyItem.key ].size()
153                }
154            }
155
156            // add item to resultset
157            items[ items.size() ] = item
158        }
159
160        return items
161    }
162
163    /**
164     * wrapper for performing api calls
165     *
166     * validates if the user may call this api
167     *
168     * @param params
169     * @param response
170     * @param itemName
171     * @param item
172     * @param block
173     */
174    def executeApiCall(params,response,itemName,item,block) {
175        // get variables from parameters
176        String deviceID     = (params.containsKey('deviceID')) ? params.deviceID : ''
177        String validation   = (params.containsKey('validation')) ? params.validation : ''
178
179        // fetch user based on deviceID
180        def user = Token.findByDeviceID(deviceID)?.user
181
182        // check if api call may be performed
183        if (!validateRequest(deviceID,validation)) {
184            // validation md5sum does not match predicted hash
185            response.sendError(401, "Unauthorized")
186        } else if (!item) {
187            // no results, invalid 'item'
188            response.sendError(400, "No such ${itemName}")
189        } else if (item.respondsTo('canRead') && !item.canRead(user)) {
190            // the user cannot read this data
191            response.sendError(401, "Unauthorized")
192        } else if (item.hasProperty('parent') && item.parent.respondsTo('canRead') && !item.parent.canRead(user)) {
193            // the user cannot read this data
194            response.sendError(401, "Unauthorized")
195        } else {
196            // allowed api call, execute block / closure
197            block()
198        }
199    }
200
201    /**
202     * get the measurement tokens from the remote module
203     *
204     * @param assay
205     * @param user
206     * @return
207     */
208    def getMeasurements(Assay assay, SecUser user) {
209        def serviceURL = "${assay.module.url}/rest/getMeasurements"
210        def serviceArguments = "assayToken=${assay.assayUUID}"
211        def json
212
213        // call module method
214        try {
215            json = moduleCommunicationService.callModuleMethod(
216                    assay.module.url,
217                    serviceURL,
218                    serviceArguments,
219                    "POST",
220                    user
221            );
222        } catch (Exception e) {
223            log.error "api.getMeasurements failed :: ${e.getMessage()}"
224            json = new org.codehaus.groovy.grails.web.json.JSONArray()
225        }
226
227        return json
228    }
229
230    /**
231     * get measurement data from the remote module in verbose format
232     *
233     * @param assay
234     * @param user
235     * @return
236     */
237    def getMeasurementData(Assay assay, SecUser user) {
238        def serviceURL = "${assay.module.url}/rest/getMeasurementData"
239        def serviceArguments = "assayToken=${assay.assayUUID}&verbose=true"
240        def json
241
242        // call module method
243        try {
244            json = moduleCommunicationService.callModuleMethod(
245                    assay.module.url,
246                    serviceURL,
247                    serviceArguments,
248                    "POST",
249                    user
250            );
251        } catch (Exception e) {
252            log.error "api.getMeasurementData failed :: ${e.getMessage()}"
253            json = new org.codehaus.groovy.grails.web.json.JSONArray()
254        }
255
256        return json
257    }
258
259    /**
260     * get the measurement meta data from the remote module
261     *
262     * @param assay
263     * @param user
264     * @return
265     */
266    def getMeasurementMetaData(Assay assay, SecUser user) {
267        def serviceURL = "${assay.module.url}/rest/getMeasurementMetaData"
268        def serviceArguments = "assayToken=${assay.assayUUID}"
269        def json
270
271        // call module method
272        try {
273            json = moduleCommunicationService.callModuleMethod(
274                    assay.module.url,
275                    serviceURL,
276                    serviceArguments,
277                    "POST",
278                    user
279            );
280        } catch (Exception e) {
281            log.error "api.getMeasurementMetaData failed :: ${e.getMessage()}"
282            json = new org.codehaus.groovy.grails.web.json.JSONArray()
283        }
284
285        return json
286    }
287}
Note: See TracBrowser for help on using the browser.