foam-framework
Version:
MVC metaprogramming framework
230 lines (200 loc) • 6.35 kB
JavaScript
/**
* @license
* Copyright 2015 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
CLASS({
name: 'RestDAO',
package: 'foam.core.dao',
extends: 'AbstractDAO',
imports: [
'ajsonp'
],
properties: [
{
name: 'model',
label: 'Type of data stored in this DAO.'
},
{
name: 'url',
label: 'REST API URL.'
},
{
model_: 'ArrayProperty',
subType: 'Property',
name: 'paramProperties',
help: 'Properties that are handled as separate parameters rather than in the query.'
},
{
model_: 'IntProperty',
name: 'batchSize',
defaultValue: 200
},
{
model_: 'IntProperty',
name: 'skipThreshold',
defaultValue: 1000
}
],
methods: {
jsonToObj: function(json) {
return this.model.create(json);
},
objToJson: function(obj) {
return JSONUtil.compact.stringify(obj);
},
buildURL: function(query) {
return this.url;
},
buildFindURL: function(key) {
return this.url + '/' + key;
},
buildPutURL: function(obj) {
return this.url;
},
buildPutParams: function(obj) {
return [];
},
buildSelectParams: function(sink, query) {
return [];
},
put: function(value, sink) {
var self = this;
var extra = {};
this.ajsonp(this.buildPutURL(value),
this.buildPutParams(value),
"POST",
this.objToJson(value, extra)
)(
function(resp, status) {
var obj;
if ( ( status !== undefined && status !== 200 ) ||
( ! ( obj = self.jsonToObj(resp, extra) ) ) ) {
sink && sink.error && sink.error([resp, status]);
return;
}
sink && sink.put && sink.put(obj);
self.notify_('put', [obj]);
});
},
remove: function(query, sink) {
},
select: function(sink, options) {
sink = sink || [].sink;
var fut = afuture();
var self = this;
var limit;
var skipped = 0;
var index = 0;
var fc = this.createFlowControl_();
// TODO: This is a very ugly way of passing additional data
// from buildURL to jsonToObj, used by the IssueCommentNetworkDAO
// Clean this up.
var extra = {};
var params = [];
if ( options ) {
index += options.skip || 0;
var query = options.query.partialEval();
var url;
if ( query ) {
var origQuery = query;
query = query.normalize();
var outquery = [query, origQuery.deepClone()];
params = this.buildSelectParams(sink, outquery);
url = this.buildURL(outquery, extra);
query = outquery[0];
origQuery = outquery[1];
var mql = query.toMQL();
if ( mql ) params.push('q=' + encodeURIComponent(query.toMQL()));
} else {
url = this.buildURL();
}
if ( options.order ) {
var sort = options.order.toMQL();
params.push("sort=" + sort);
}
if ( options.limit ) {
limit = options.limit;
}
}
var finished = false;
awhile(
function() { return !finished; },
function(ret) {
var batch = self.batchSize;
if ( Number.isFinite(limit) )
var batch = Math.min(batch, limit);
// No need to fetch items for count.
if ( CountExpr.isInstance(sink) ) {
batch = 0;
}
var myparams = params.slice();
myparams.push('maxResults=' + batch);
myparams.push('startIndex=' + index);
self.ajsonp(url, myparams)(Movement.whenIdle(function(data) {
// Short-circuit count.
// TODO: This count is wrong for queries that use
if ( CountExpr.isInstance(sink) ) {
sink.count = data.totalResults;
finished = true;
ret(); return;
}
var items = data && data.items ? data.items : [];
// Fetching no items indicates EOF.
if ( items.length == 0 ) finished = true;
index += items.length;
for ( var i = 0 ; i < items.length; i++ ) {
var item = self.jsonToObj(items[i], extra)
// Filter items that don't match due to
// low resolution of Date parameters in MQL
if ( origQuery && !origQuery.f(item) ) {
skipped++;
continue;
}
if ( Number.isFinite(limit) ) {
if ( limit <= 0 ) { finished = true; break; }
limit--;
}
if ( fc.stopped ) { finished = true; break; }
if ( fc.errorEvt ) {
sink.error && sink.error(fc.errorEvt);
finished = true;
break;
}
sink && sink.put && sink.put(item, null, fc);
}
if ( limit <= 0 ) finished = true;
if ( ! data || index >= data.totalResults ) finished = true;
if ( skipped >= self.skipThreshold ) finished = true;
ret();
}));
})(function() { sink && sink.eof && sink.eof(); fut.set(sink); });
return fut.get;
},
buildFindParams: function(key) {
return [];
},
find: function(key, sink) {
var self = this;
this.ajsonp(this.buildFindURL(key), this.buildFindParams())(function(data, status) {
var deserialized;
if ( status !== 200 || ! (deserialized = self.jsonToObj(data)) ) {
sink && sink.error && sink.error('Network error');
return;
}
sink && sink.put && sink.put(deserialized);
});
}
}
});