source: Dev/trunk/src/server/api/util.js @ 525

Last change on this file since 525 was 525, checked in by hendrikvanantwerpen, 11 years ago
  • Allow empty subcodes.
  • Use HTTPResult exclusively on server (no more q).
  • Set readonly & disabled on ourselves as well in _ComplexValueMixin
  • Split server into several modules.
  • Check codes on the variable level, not question level.
  • We can add modules in design documents now.
File size: 9.2 KB
Line 
1var _ = require('underscore')
2  , HTTPResult = require("../util/http-result")
3  , validator = require("../util/validator")
4  , etags = require("../util/etags")
5  , cryptoken = require('../util/crypto-token')
6  , CSV = require("ya-csv")
7  ;
8
9module.exports = function(couch,schema) {
10    var exports = {};
11
12    var identity = exports.identity = function(obj) { return obj; };
13    var stripAndReturnPrivates = exports.stripAndReturnPrivates = function(obj) {
14        var priv = {};
15        _.each(obj||{},function(val,key){
16            if (key.substring(0,1) === '_') {
17                priv[key] = val;
18                delete obj[key];
19            }
20        });
21        return priv;
22    };
23    var areDocsUnique = exports.areDocsUnique = function(docs) {
24        return _.chain(docs)
25                .map(function(doc){ return doc._id; })
26                .uniq()
27                .value().length === docs.length;
28    };
29    var isDocPublished = exports.isDocPublished = function(doc) {
30        return !!doc.publicationDate;
31    };
32    var areDocsPublished = exports.areDocsPublished = function(docs) {
33        return _.every(docs, isDocPublished);
34    };
35
36    var handleUnknownResponse = exports.handleUnknownResponse = function(status,error) {
37        return new HTTPResult(500,{error: error.reason});
38    };
39    var handleRowKeys = exports.handleRowKeys = function(result) {
40        return _.map(result.rows, function(item) { return item.key; });
41    };
42    var handleRowValues = exports.handleRowValues = function(result) {
43        return _.map(result.rows, function(item) { return item.value; });
44    };
45    var handleRowDocs = exports.handleRowDocs = function(result) {
46        return _.map(result.rows, function(item) { return item.doc; });
47    };
48    var handleRowFirstDoc = exports.handleRowFirstDoc = function(result) {
49        if ( result.rows.length > 0 ) {
50            return result.rows[0].doc;
51        } else {
52            return new HTTPResult(404,{error:"No document found."});
53        }
54    };
55    var handleRowFirstValue = exports.handleRowFirstValue = function(result) {
56        if ( result.rows.length > 0 ) {
57            return result.rows[0].value;
58        } else {
59            return new HTTPResult(404,{error:"No document found."});
60        }
61    };
62    var handleRowDictOfDocs = exports.handleRowDictOfDocs = function(result) {
63        return _.reduce(result.rows, function(dict,item) {
64            dict[item.key] = item.doc;
65            return dict;
66        }, {});
67    };
68    var handleRowDictOfValues = exports.handleRowDictOfValues = function(result) {
69        return _.reduce(result.rows, function(dict,item) {
70            dict[item.key] = item.value;
71            return dict;
72        }, {});
73    };
74
75    var getDocumentsOfType = exports.getDocumentsOfType = function(type) {
76        var url = '_design/default/_view/by_type?key='+JSON.stringify(type);
77        return couch.get(url)
78        .handle({
79            200: handleRowValues,
80            404: function() { return {error: "Cannot find collection of type "+type}; },
81            default: handleUnknownResponse
82        });
83    };
84    var getDocument = exports.getDocument = function(id,rev,type) {
85        var opts = {headers:{}};
86        if (rev) {
87            opts.headers['If-Non-Match'] = '"'+rev+'"';
88        }
89        return couch.get(id,opts)
90        .handle({
91            200: function(doc){
92                if ( doc.type !== type ) {
93                    return new HTTPResult(404,{error:"Document not found."});
94                } else {
95                    var priv = stripAndReturnPrivates(doc);
96                    if ( priv._rev !== rev ) {
97                        doc._id = priv._id;
98                        doc._rev = priv._rev;
99                        return doc;
100                    } else {
101                        return new HTTPResult(304);
102                    }
103                }
104            },
105            304: identity,
106            default: handleUnknownResponse
107        });
108    };
109    var putDocument = exports.putDocument = function(id,rev,type,doc) {
110        var priv = stripAndReturnPrivates(doc);
111        var valid;
112        if ( doc.type === type && (valid = validator(doc, schema)).valid ) {
113            var opts = rev ? {headers:{'If-Match':'"'+rev+'"'}} : {};
114            return couch.put(id,doc,opts)
115            .handle({
116                201: function(res){
117                    doc._id = res.id;
118                    doc._rev = res.rev;
119                    return doc;
120                },
121                409: function(error) {
122                    return {error: error.reason};
123                },
124                default: handleUnknownResponse
125            });
126        } else {
127            return new HTTPResult(400,{error: "Document failed schema verification.", valid: valid});
128        }
129    };
130    var deleteDocument = exports.deleteDocument = function(id,rev) {
131        if ( rev ) {
132            var opts = {headers:{'If-Match':'"'+rev+'"'}};
133            return couch.delete(id,opts)
134            .handle({
135                200: identity,
136                409: function(error) {
137                    return {error: error.reason};
138                },
139                default: handleUnknownResponse
140            });
141        } else {
142            return new HTTPResult(409, {error: "Cannot identify document revision to delete."});
143        }
144    };
145    var postDocument = exports.postDocument = function(type,doc) {
146        var priv = stripAndReturnPrivates(doc);
147        var valid;
148        if ( doc.type === type && (valid = validator(doc, schema)).valid ) {
149            return couch.post(doc)
150            .handle({
151                201: function(response) {
152                    doc._id = response.id;
153                    doc._rev = response.rev;
154                    return doc;
155                },
156                default: handleUnknownResponse
157            });
158        } else {
159            return new HTTPResult(400,{error: "Document failed schema verification.", valid: valid});
160        }
161    };
162
163    var makeDocsGet = exports.makeDocsGet = function(type) {
164        return function(req,res) {
165            getDocumentsOfType(type)
166            .handle(res.send.bind(res));
167        };
168    };
169    var makeDocGet_id = exports.makeDocGet_id = function(type) {
170        return function(req,res) {
171            var id = req.params.id;
172            var rev = etags.parse(req.header('If-Non-Match'))[0];
173            getDocument(id,rev,type)
174            .handle({
175                200: function(doc){
176                    res.set({
177                        'ETag': etags.format([doc._rev])
178                    }).send(200, doc);
179                },
180                default: res.send.bind(res)
181            });
182        };
183    };
184    var makeDocPut_id = exports.makeDocPut_id = function(type) {
185        return function(req,res) {
186            var id = req.params.id;
187            var doc = req.body;
188            var rev = etags.parse(req.header('If-Match'))[0] || (doc && doc._rev);
189            putDocument(id,rev,type,doc)
190            .handle({
191                201: function(doc) {
192                    res.set({
193                        'ETag': etags.format([doc._rev])
194                    }).send(201, doc);
195                },
196                default: res.send.bind(res)
197            });
198        };
199    };
200    var makeDocDel_id = exports.makeDocDel_id = function(type) {
201        return function(req,res) {
202            var id = req.params.id;
203            var doc = req.body;
204            var rev = etags.parse(req.header('If-Match'))[0] || (doc && doc._rev);
205            deleteDocument(id,rev)
206            .handle(res.send.bind(res));
207        };
208    };
209    var makeDocPost = exports.makeDocPost = function(type) {
210        return function(req,res) {
211            var doc = req.body;
212            postDocument(type,doc)
213            .handle({
214                201: function(doc) {
215                    res.set({
216                        'ETag': etags.format([doc._rev])
217                    }).send(201, doc);
218                },
219                default: res.send.bind(res)
220            });
221        };
222    };
223
224    var JSON_MIME = exports.JSON_MIME = 'application/json';
225    var CSV_MIME = exports.CSV_MIME = 'application/json';
226    var ensureMIME = exports.ensureMIME = function(mimeType) {
227        return function(req,res,next) {
228            if (!req.accepts(mimeType)) {
229                res.send(406);
230            } else {
231                res.set({
232                    'Content-Type': mimeType
233                });
234                next();
235            }
236        };
237    };
238
239    var writeObjectsToCSVStream = exports.writeObjectsToCSVStream = function(objects, stream, prelude) {
240        var keys = _.chain(objects)
241                    .map(_.keys)
242                    .flatten()
243                    .uniq()
244                    .value();
245        var idxs = {};
246        _.forEach(keys, function(key,idx){
247            idxs[key] = idx;
248        });
249        var writer = new CSV.CsvWriter(stream);
250        if ( prelude ) {
251            _.forEach(prelude, function(val,key){
252                writer.writeRecord([key,val]);
253            });
254        }
255        writer.writeRecord(keys);
256        _.forEach(objects, function(obj){
257            var row = [];
258            _.forEach(obj, function(val,key){
259                row[idxs[key]] = val;
260            });
261            writer.writeRecord(row);
262        });
263    };
264
265    return exports;
266};
Note: See TracBrowser for help on using the repository browser.