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

Last change on this file since 531 was 531, checked in by hendrikvanantwerpen, 11 years ago
  • Return to using truly ISO formatted dates, including milliseconds.
  • Also set constraint on surveyrun dates when value is initially set.
  • Separate runs & results from surveys and questions.
  • Moved date & email format to schema itself.
File size: 10.1 KB
RevLine 
[525]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
[527]36    var handleException = exports.handleException = function(error) {
37        return new HTTPResult(500,{
38            reason: error.message,
39            filename: error.filename,
40            lineNumber: error.lineNumber
41        });
42    };
[525]43    var handleUnknownResponse = exports.handleUnknownResponse = function(status,error) {
44        return new HTTPResult(500,{error: error.reason});
45    };
46    var handleRowKeys = exports.handleRowKeys = function(result) {
47        return _.map(result.rows, function(item) { return item.key; });
48    };
49    var handleRowValues = exports.handleRowValues = function(result) {
50        return _.map(result.rows, function(item) { return item.value; });
51    };
52    var handleRowDocs = exports.handleRowDocs = function(result) {
53        return _.map(result.rows, function(item) { return item.doc; });
54    };
55    var handleRowFirstDoc = exports.handleRowFirstDoc = function(result) {
56        if ( result.rows.length > 0 ) {
57            return result.rows[0].doc;
58        } else {
59            return new HTTPResult(404,{error:"No document found."});
60        }
61    };
62    var handleRowFirstValue = exports.handleRowFirstValue = function(result) {
63        if ( result.rows.length > 0 ) {
64            return result.rows[0].value;
65        } else {
66            return new HTTPResult(404,{error:"No document found."});
67        }
68    };
69    var handleRowDictOfDocs = exports.handleRowDictOfDocs = function(result) {
70        return _.reduce(result.rows, function(dict,item) {
71            dict[item.key] = item.doc;
72            return dict;
73        }, {});
74    };
75    var handleRowDictOfValues = exports.handleRowDictOfValues = function(result) {
76        return _.reduce(result.rows, function(dict,item) {
77            dict[item.key] = item.value;
78            return dict;
79        }, {});
80    };
81
82    var getDocumentsOfType = exports.getDocumentsOfType = function(type) {
[531]83        return couch.get('_design/default/_view/by_type',
84                         {query:{reduce:false,include_docs:true,key:type}})
[525]85        .handle({
[527]86            '-1': _.identity,
[531]87            200: handleRowDocs,
[525]88            404: function() { return {error: "Cannot find collection of type "+type}; },
89            default: handleUnknownResponse
90        });
91    };
92    var getDocument = exports.getDocument = function(id,rev,type) {
93        var opts = {headers:{}};
94        if (rev) {
95            opts.headers['If-Non-Match'] = '"'+rev+'"';
96        }
97        return couch.get(id,opts)
98        .handle({
[527]99            '-1': _.identity,
[525]100            200: function(doc){
101                if ( doc.type !== type ) {
102                    return new HTTPResult(404,{error:"Document not found."});
103                } else {
104                    var priv = stripAndReturnPrivates(doc);
105                    if ( priv._rev !== rev ) {
106                        doc._id = priv._id;
107                        doc._rev = priv._rev;
108                        return doc;
109                    } else {
110                        return new HTTPResult(304);
111                    }
112                }
113            },
114            304: identity,
115            default: handleUnknownResponse
116        });
117    };
118    var putDocument = exports.putDocument = function(id,rev,type,doc) {
119        var priv = stripAndReturnPrivates(doc);
120        var valid;
121        if ( doc.type === type && (valid = validator(doc, schema)).valid ) {
122            var opts = rev ? {headers:{'If-Match':'"'+rev+'"'}} : {};
123            return couch.put(id,doc,opts)
124            .handle({
[527]125                '-1': _.identity,
[525]126                201: function(res){
127                    doc._id = res.id;
128                    doc._rev = res.rev;
129                    return doc;
130                },
131                409: function(error) {
132                    return {error: error.reason};
133                },
134                default: handleUnknownResponse
135            });
136        } else {
137            return new HTTPResult(400,{error: "Document failed schema verification.", valid: valid});
138        }
139    };
140    var deleteDocument = exports.deleteDocument = function(id,rev) {
141        if ( rev ) {
142            var opts = {headers:{'If-Match':'"'+rev+'"'}};
143            return couch.delete(id,opts)
144            .handle({
[527]145                '-1': _.identity,
[525]146                200: identity,
147                409: function(error) {
148                    return {error: error.reason};
149                },
150                default: handleUnknownResponse
151            });
152        } else {
153            return new HTTPResult(409, {error: "Cannot identify document revision to delete."});
154        }
155    };
156    var postDocument = exports.postDocument = function(type,doc) {
157        var priv = stripAndReturnPrivates(doc);
158        var valid;
159        if ( doc.type === type && (valid = validator(doc, schema)).valid ) {
160            return couch.post(doc)
161            .handle({
[527]162                '-1': _.identity,
[525]163                201: function(response) {
164                    doc._id = response.id;
165                    doc._rev = response.rev;
166                    return doc;
167                },
168                default: handleUnknownResponse
169            });
170        } else {
171            return new HTTPResult(400,{error: "Document failed schema verification.", valid: valid});
172        }
173    };
174
175    var makeDocsGet = exports.makeDocsGet = function(type) {
176        return function(req,res) {
177            getDocumentsOfType(type)
[527]178            .handle({'-1': handleException})
[525]179            .handle(res.send.bind(res));
180        };
181    };
182    var makeDocGet_id = exports.makeDocGet_id = function(type) {
183        return function(req,res) {
184            var id = req.params.id;
185            var rev = etags.parse(req.header('If-Non-Match'))[0];
186            getDocument(id,rev,type)
187            .handle({
[527]188                '-1': function(result) {
189                    handleException(result)
190                    .handle(res.send.bind(res));
191                },
[525]192                200: function(doc){
193                    res.set({
194                        'ETag': etags.format([doc._rev])
195                    }).send(200, doc);
196                },
197                default: res.send.bind(res)
198            });
199        };
200    };
201    var makeDocPut_id = exports.makeDocPut_id = function(type) {
202        return function(req,res) {
203            var id = req.params.id;
204            var doc = req.body;
205            var rev = etags.parse(req.header('If-Match'))[0] || (doc && doc._rev);
206            putDocument(id,rev,type,doc)
207            .handle({
[527]208                '-1': function(result) {
209                    handleException(result)
210                    .handle(res.send.bind(res));
211                },
[525]212                201: function(doc) {
213                    res.set({
214                        'ETag': etags.format([doc._rev])
215                    }).send(201, doc);
216                },
217                default: res.send.bind(res)
218            });
219        };
220    };
221    var makeDocDel_id = exports.makeDocDel_id = function(type) {
222        return function(req,res) {
223            var id = req.params.id;
224            var doc = req.body;
225            var rev = etags.parse(req.header('If-Match'))[0] || (doc && doc._rev);
226            deleteDocument(id,rev)
[527]227            .handle({'-1': handleException})
[525]228            .handle(res.send.bind(res));
229        };
230    };
231    var makeDocPost = exports.makeDocPost = function(type) {
232        return function(req,res) {
233            var doc = req.body;
234            postDocument(type,doc)
235            .handle({
[527]236                '-1': function(result) {
237                    handleException(result)
238                    .handle(res.send.bind(res));
239                },
[525]240                201: function(doc) {
241                    res.set({
242                        'ETag': etags.format([doc._rev])
243                    }).send(201, doc);
244                },
245                default: res.send.bind(res)
246            });
247        };
248    };
249
250    var JSON_MIME = exports.JSON_MIME = 'application/json';
251    var CSV_MIME = exports.CSV_MIME = 'application/json';
252    var ensureMIME = exports.ensureMIME = function(mimeType) {
253        return function(req,res,next) {
254            if (!req.accepts(mimeType)) {
255                res.send(406);
256            } else {
257                res.set({
258                    'Content-Type': mimeType
259                });
260                next();
261            }
262        };
263    };
264
265    var writeObjectsToCSVStream = exports.writeObjectsToCSVStream = function(objects, stream, prelude) {
266        var keys = _.chain(objects)
267                    .map(_.keys)
268                    .flatten()
269                    .uniq()
270                    .value();
271        var idxs = {};
272        _.forEach(keys, function(key,idx){
273            idxs[key] = idx;
274        });
275        var writer = new CSV.CsvWriter(stream);
276        if ( prelude ) {
277            _.forEach(prelude, function(val,key){
278                writer.writeRecord([key,val]);
279            });
280        }
281        writer.writeRecord(keys);
282        _.forEach(objects, function(obj){
283            var row = [];
284            _.forEach(obj, function(val,key){
285                row[idxs[key]] = val;
286            });
287            writer.writeRecord(row);
288        });
289    };
290
291    return exports;
292};
Note: See TracBrowser for help on using the repository browser.