UNPKG

foam-framework

Version:
230 lines (200 loc) 6.35 kB
/** * @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); }); } } });