1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 | "use strict";
|
14 |
|
15 | var fluid = fluid || require("infusion"),
|
16 | jsonlint = jsonlint || (require && require("jsonlint")),
|
17 | kettle = fluid.registerNamespace("kettle"),
|
18 | JSON5 = JSON5 || require("json5");
|
19 |
|
20 |
|
21 |
|
22 |
|
23 | fluid.defaults("kettle.dataSource.encoding.JSON", {
|
24 | gradeNames: "fluid.component",
|
25 | invokers: {
|
26 | parse: "kettle.dataSource.parseJSON",
|
27 | render: "kettle.dataSource.stringifyJSON"
|
28 | },
|
29 | contentType: "application/json"
|
30 | });
|
31 |
|
32 | fluid.defaults("kettle.dataSource.encoding.JSON5", {
|
33 | gradeNames: "fluid.component",
|
34 | invokers: {
|
35 | parse: "kettle.dataSource.parseJSON5",
|
36 | render: "kettle.dataSource.stringifyJSON5"
|
37 | },
|
38 | contentType: "application/json5"
|
39 | });
|
40 |
|
41 | fluid.defaults("kettle.dataSource.encoding.formenc", {
|
42 | gradeNames: "fluid.component",
|
43 | invokers: {
|
44 | parse: "node.querystring.parse({arguments}.0)",
|
45 | render: "node.querystring.stringify({arguments}.0)"
|
46 | },
|
47 | contentType: "application/x-www-form-urlencoded"
|
48 | });
|
49 |
|
50 | fluid.defaults("kettle.dataSource.encoding.none", {
|
51 | gradeNames: "fluid.component",
|
52 | invokers: {
|
53 | parse: "fluid.identity",
|
54 | render: "fluid.identity"
|
55 | },
|
56 | contentType: "text/plain"
|
57 | });
|
58 |
|
59 |
|
60 |
|
61 |
|
62 | kettle.dataSource.JSONParseErrors = [];
|
63 |
|
64 | kettle.dataSource.accumulateJSONError = function (str, hash) {
|
65 | var error = "JSON parse error at line " + hash.loc.first_line + ", col " + hash.loc.last_column + ", found: \'" + hash.token + "\' - expected: " + hash.expected.join(", ");
|
66 | kettle.dataSource.JSONParseErrors.push(error);
|
67 | };
|
68 |
|
69 |
|
70 |
|
71 | jsonlint.parser.parseError = jsonlint.parser.lexer.parseError = kettle.dataSource.accumulateJSONError;
|
72 |
|
73 |
|
74 |
|
75 |
|
76 |
|
77 |
|
78 |
|
79 | kettle.dataSource.renderJSONDiagnostic = function (string, err, promise) {
|
80 | if (!jsonlint) {
|
81 | return err.toString();
|
82 | }
|
83 | kettle.dataSource.JSONParseErrors = [];
|
84 | var errors = [];
|
85 | try {
|
86 | jsonlint.parse(string);
|
87 | } catch (e) {
|
88 | errors.push(e);
|
89 | }
|
90 | errors = errors.concat(kettle.dataSource.JSONParseErrors);
|
91 | promise.reject({
|
92 | message: errors.join("\n")
|
93 | });
|
94 | };
|
95 |
|
96 | kettle.dataSource.parseJSON = function (string) {
|
97 | var togo = fluid.promise();
|
98 | if (!string) {
|
99 | togo.resolve(undefined);
|
100 | } else {
|
101 | try {
|
102 | togo.resolve(JSON.parse(string));
|
103 | } catch (err) {
|
104 | kettle.dataSource.renderJSONDiagnostic(string, err, togo);
|
105 | }
|
106 | }
|
107 | return togo;
|
108 | };
|
109 |
|
110 | kettle.dataSource.stringifyJSON = function (obj) {
|
111 | return obj === undefined ? "" : JSON.stringify(obj, null, 4);
|
112 | };
|
113 |
|
114 | kettle.dataSource.parseJSON5 = function (string) {
|
115 | var togo = fluid.promise();
|
116 | if (!string) {
|
117 | togo.resolve(undefined);
|
118 | } else {
|
119 | try {
|
120 | togo.resolve(JSON5.parse(string));
|
121 | } catch (err) {
|
122 | togo.reject({
|
123 | message: err.message || err
|
124 | });
|
125 | }
|
126 | }
|
127 | return togo;
|
128 | };
|
129 |
|
130 | kettle.dataSource.stringifyJSON5 = function (obj) {
|
131 | return obj === undefined ? "" : JSON5.stringify(obj, null, 4);
|
132 | };
|
133 |
|
134 |
|
135 |
|
136 |
|
137 |
|
138 |
|
139 |
|
140 |
|
141 |
|
142 | fluid.defaults("kettle.dataSource", {
|
143 | gradeNames: ["fluid.component", "{that}.getWritableGrade"],
|
144 | mergePolicy: {
|
145 | setResponseTransforms: "replace"
|
146 | },
|
147 | events: {
|
148 |
|
149 |
|
150 |
|
151 | onRead: null,
|
152 | onWrite: null,
|
153 | onError: null
|
154 | },
|
155 | components: {
|
156 | encoding: {
|
157 | type: "kettle.dataSource.encoding.JSON"
|
158 | }
|
159 | },
|
160 | listeners: {
|
161 | onRead: {
|
162 | func: "{encoding}.parse",
|
163 | namespace: "encoding"
|
164 | },
|
165 | onWrite: {
|
166 | func: "{encoding}.render",
|
167 | namespace: "encoding"
|
168 | }
|
169 | },
|
170 | invokers: {
|
171 | get: {
|
172 | funcName: "kettle.dataSource.get",
|
173 | args: ["{that}", "{arguments}.0", "{arguments}.1"]
|
174 | },
|
175 |
|
176 | getWritableGrade: {
|
177 | funcName: "kettle.dataSource.getWritableGrade",
|
178 | args: ["{that}", "{that}.options.writable", "{that}.options.readOnlyGrade"]
|
179 | }
|
180 | },
|
181 |
|
182 | setResponseTransforms: ["encoding"],
|
183 | charEncoding: "utf8",
|
184 | writable: false
|
185 | });
|
186 |
|
187 |
|
188 |
|
189 |
|
190 |
|
191 | kettle.dataSource.getWritableGrade = function (that, writable, readOnlyGrade) {
|
192 | if (!readOnlyGrade) {
|
193 | fluid.fail("Cannot evaluate writable grade without readOnlyGrade option");
|
194 | }
|
195 | if (writable) {
|
196 | return fluid.model.composeSegments(readOnlyGrade, "writable");
|
197 | }
|
198 | };
|
199 |
|
200 | fluid.defaults("kettle.dataSource.writable", {
|
201 | gradeNames: ["fluid.component"],
|
202 | invokers: {
|
203 | set: {
|
204 | funcName: "kettle.dataSource.set",
|
205 | args: ["{that}", "{arguments}.0", "{arguments}.1", "{arguments}.2"]
|
206 | }
|
207 |
|
208 | }
|
209 | });
|
210 |
|
211 |
|
212 |
|
213 |
|
214 |
|
215 |
|
216 | kettle.dataSource.registerStandardPromiseHandlers = function (that, promise, options) {
|
217 | promise.then(typeof(options) === "function" ? options : null,
|
218 | options.onError ? options.onError : that.events.onError.fire);
|
219 | };
|
220 |
|
221 | kettle.dataSource.defaultiseOptions = function (componentOptions, options, directModel, isSet) {
|
222 | options = options || {};
|
223 | options.directModel = directModel;
|
224 | options.operation = isSet ? "set" : "get";
|
225 | options.reverse = isSet ? true : false;
|
226 | options.writeMethod = options.writeMethod || componentOptions.writeMethod || "PUT";
|
227 | options.notFoundIsEmpty = options.notFoundIsEmpty || componentOptions.notFoundIsEmpty;
|
228 | return options;
|
229 | };
|
230 |
|
231 |
|
232 |
|
233 |
|
234 |
|
235 |
|
236 |
|
237 |
|
238 |
|
239 |
|
240 |
|
241 |
|
242 |
|
243 | kettle.dataSource.get = function (that, directModel, options) {
|
244 | options = kettle.dataSource.defaultiseOptions(that.options, options, directModel);
|
245 | var initPayload = that.getImpl(options, directModel);
|
246 | var promise = fluid.promise.fireTransformEvent(that.events.onRead, initPayload, options);
|
247 | kettle.dataSource.registerStandardPromiseHandlers(that, promise, options);
|
248 | return promise;
|
249 | };
|
250 |
|
251 |
|
252 |
|
253 |
|
254 |
|
255 |
|
256 |
|
257 |
|
258 |
|
259 |
|
260 |
|
261 |
|
262 | kettle.dataSource.set = function (that, directModel, model, options) {
|
263 | options = kettle.dataSource.defaultiseOptions(that.options, options, directModel, true);
|
264 | var transformPromise = fluid.promise.fireTransformEvent(that.events.onWrite, model, options);
|
265 | var togo = fluid.promise();
|
266 | transformPromise.then(function (transformed) {
|
267 | var innerPromise = that.setImpl(options, directModel, transformed);
|
268 | innerPromise.then(function (setResponse) {
|
269 | var options2 = kettle.dataSource.defaultiseOptions(that.options, fluid.copy(options), directModel);
|
270 | options2.filterNamespaces = that.options.setResponseTransforms;
|
271 | var retransformed = fluid.promise.fireTransformEvent(that.events.onRead, setResponse, options2);
|
272 | fluid.promise.follow(retransformed, togo);
|
273 | }, function (error) {
|
274 | togo.reject(error);
|
275 | });
|
276 | });
|
277 | kettle.dataSource.registerStandardPromiseHandlers(that, togo, options);
|
278 | return togo;
|
279 | };
|
280 |
|
281 |
|
282 |
|
283 |
|
284 |
|
285 |
|
286 | fluid.defaults("kettle.dataSource.CouchDB", {
|
287 | mergePolicy: {
|
288 | "rules": "nomerge"
|
289 | },
|
290 | rules: {
|
291 | writePayload: {
|
292 | value: ""
|
293 | },
|
294 | readPayload: {
|
295 | "": "value"
|
296 | }
|
297 | },
|
298 | listeners: {
|
299 | onRead: {
|
300 | funcName: "kettle.dataSource.CouchDB.read",
|
301 | args: ["{that}", "{arguments}.0"],
|
302 | namespace: "CouchDB",
|
303 | priority: "after:encoding"
|
304 | }
|
305 | }
|
306 | });
|
307 |
|
308 | fluid.defaults("kettle.dataSource.CouchDB.writable", {
|
309 | listeners: {
|
310 | onWrite: {
|
311 | funcName: "kettle.dataSource.CouchDB.write",
|
312 | args: ["{that}", "{arguments}.0", "{arguments}.1"],
|
313 | namespace: "CouchDB",
|
314 | priority: "after:encoding"
|
315 | }
|
316 | }
|
317 | });
|
318 |
|
319 | fluid.makeGradeLinkage("kettle.dataSource.CouchDB.linkage", ["kettle.dataSource.writable", "kettle.dataSource.CouchDB"], "kettle.dataSource.CouchDB.writable");
|
320 |
|
321 |
|
322 |
|
323 |
|
324 |
|
325 |
|
326 |
|
327 |
|
328 |
|
329 |
|
330 |
|
331 | kettle.dataSource.CouchDB.read = function (that, resp) {
|
332 |
|
333 | var togo;
|
334 | if (resp === undefined) {
|
335 | togo = undefined;
|
336 | } else {
|
337 | if (resp.error) {
|
338 | var error = {
|
339 | isError: true,
|
340 | statusCode: resp.statusCode,
|
341 | message: resp.error + ": " + resp.reason
|
342 | };
|
343 | togo = fluid.promise();
|
344 | togo.reject(error);
|
345 | } else {
|
346 | togo = fluid.model.transformWithRules(resp, that.options.rules.readPayload);
|
347 | }
|
348 | }
|
349 | return togo;
|
350 | };
|
351 |
|
352 |
|
353 |
|
354 |
|
355 |
|
356 |
|
357 |
|
358 |
|
359 |
|
360 | kettle.dataSource.CouchDB.write = function (that, model, options) {
|
361 | var directModel = options.directModel;
|
362 | var doc = fluid.model.transformWithRules(model, that.options.rules.writePayload);
|
363 | var original = that.get(directModel, {filterNamespaces: ["encoding"], notFoundIsEmpty: true});
|
364 | var togo = fluid.promise();
|
365 | original.then(function (originalDoc) {
|
366 | if (originalDoc) {
|
367 | doc._id = originalDoc._id;
|
368 | doc._rev = originalDoc._rev;
|
369 | }
|
370 | togo.resolve(doc);
|
371 | });
|
372 | return togo;
|
373 | };
|
374 |
|