From 74132d36d761271fd880f3874cc657cca8b8b83c Mon Sep 17 00:00:00 2001 From: Rufus Pollock Date: Sat, 18 Aug 2012 02:01:13 +0100 Subject: [PATCH] [#217,backend/ckan][m]: functional CKAN backend (read-only). * limited tests so far (only use mocks so have not tested querying properly) --- src/backend.ckan.js | 88 ++++++++++++++++++++ test/backend.ckan.test.js | 164 ++++++++++++++++++++++++++++++++++++++ test/index.html | 2 + 3 files changed, 254 insertions(+) create mode 100644 src/backend.ckan.js create mode 100644 test/backend.ckan.test.js diff --git a/src/backend.ckan.js b/src/backend.ckan.js new file mode 100644 index 00000000..f700af75 --- /dev/null +++ b/src/backend.ckan.js @@ -0,0 +1,88 @@ +this.recline = this.recline || {}; +this.recline.Backend = this.recline.Backend || {}; +this.recline.Backend.Ckan = this.recline.Backend.Ckan || {}; + +(function($, my) { + // ## CKAN Backend + // + // This provides connection to the CKAN DataStore (v2) + // + // General notes + // + // * Every dataset must have an id equal to its resource id on the CKAN instance + // * You should set the CKAN API endpoint for requests by setting API_ENDPOINT value on this module (recline.Backend.Ckan.API_ENDPOINT) + + my.__type__ = 'ckan'; + + // Default CKAN API endpoint used for requests (you can change this but it will affect every request!) + my.API_ENDPOINT = 'http://datahub.io/api'; + + // ### fetch + my.fetch = function(dataset) { + var wrapper = my.DataStore(); + var dfd = $.Deferred(); + var jqxhr = wrapper.search({resource_id: dataset.id, limit: 0}); + jqxhr.done(function(results) { + // map ckan types to our usual types ... + var fields = _.map(results.result.fields, function(field) { + field.type = field.type in CKAN_TYPES_MAP ? CKAN_TYPES_MAP[field.type] : field.type; + return field; + }); + var out = { + fields: fields, + useMemoryStore: false + }; + dfd.resolve(out); + }); + return dfd.promise(); + }; + + my.query = function(dataset, queryObj) { + var wrapper = my.DataStore(dataset.url); + var actualQuery = { + resource_id: dataset.id, + q: queryObj.q, + limit: queryObj.size, + offset: queryObj.from + }; + var dfd = $.Deferred(); + var jqxhr = wrapper.search(actualQuery); + jqxhr.done(function(results) { + var out = { + total: results.result.total, + hits: results.result.records, + }; + dfd.resolve(out); + }); + return dfd.promise(); + }; + + // ### DataStore + // + // Simple wrapper around the CKAN DataStore API + // + // @param endpoint: CKAN api endpoint (e.g. http://datahub.io/api) + my.DataStore = function(endpoint) { + var that = { + endpoint: endpoint || my.API_ENDPOINT + }; + that.search = function(data) { + var searchUrl = my.endpoint + '/3/action/datastore_search'; + var jqxhr = $.ajax({ + url: searchUrl, + data: data, + dataType: 'json' + }); + return jqxhr; + } + + return that; + } + + var CKAN_TYPES_MAP = { + 'int4': 'integer', + 'float8': 'float', + 'text': 'string' + }; + +}(jQuery, this.recline.Backend.Ckan)); diff --git a/test/backend.ckan.test.js b/test/backend.ckan.test.js new file mode 100644 index 00000000..ccdfd668 --- /dev/null +++ b/test/backend.ckan.test.js @@ -0,0 +1,164 @@ +module("Backend CKAN"); + +test("fetch", function() { + var dataset = new recline.Model.Dataset({ + url: 'http://localhost:5000/dataset/test-data-viewer/resource/4f1299ab-a100-4e5f-ba81-e6d234a2f3bd', + backend: 'ckan' + }); + + var stub = sinon.stub($, 'ajax', function(options) { + if (options.url.indexOf('datastore_search') != -1) { + return { + done: function(callback) { + callback(sample_data); + return this; + }, + fail: function() { + } + }; + } + }); + + dataset.fetch().done(function(dataset) { + deepEqual( + _.pluck(dataset.fields.toJSON(), 'id'), + _.pluck(sample_data.result.fields, 'id') + ); + // check we've mapped types correctly + equal(dataset.fields.get('x').get('type'), 'integer'); + equal(dataset.fields.get('country').get('type'), 'string'); + + // fetch does a query so we can check for records + equal(dataset.recordCount, 6); + equal(dataset.records.length, 6); + equal(dataset.records.at(0).get('id'), 0); + equal(dataset.records.at(0).get('country'), 'DE'); + }); + $.ajax.restore(); +}); + +var sample_data = { + "help": "", + "result": { + "fields": [ + { + "id": "_id", + "type": "int4" + }, + { + "id": "id", + "type": "int4" + }, + { + "id": "date", + "type": "date" + }, + { + "id": "x", + "type": "int4" + }, + { + "id": "y", + "type": "int4" + }, + { + "id": "z", + "type": "int4" + }, + { + "id": "country", + "type": "text" + }, + { + "id": "title", + "type": "text" + }, + { + "id": "lat", + "type": "float8" + }, + { + "id": "lon", + "type": "float8" + } + ], + "records": [ + { + "_id": 1, + "country": "DE", + "date": "2011-01-01", + "id": 0, + "lat": 52.56, + "lon": 13.4, + "title": "first", + "x": 1, + "y": 2, + "z": 3 + }, + { + "_id": 2, + "country": "UK", + "date": "2011-02-02", + "id": 1, + "lat": 54.97, + "lon": -1.6, + "title": "second", + "x": 2, + "y": 4, + "z": 24 + }, + { + "_id": 3, + "country": "US", + "date": "2011-03-03", + "id": 2, + "lat": 40.0, + "lon": -75.5, + "title": "third", + "x": 3, + "y": 6, + "z": 9 + }, + { + "_id": 4, + "country": "UK", + "date": "2011-04-04", + "id": 3, + "lat": 57.27, + "lon": -6.2, + "title": "fourth", + "x": 4, + "y": 8, + "z": 6 + }, + { + "_id": 5, + "country": "UK", + "date": "2011-05-04", + "id": 4, + "lat": 51.58, + "lon": 0.0, + "title": "fifth", + "x": 5, + "y": 10, + "z": 15 + }, + { + "_id": 6, + "country": "DE", + "date": "2011-06-02", + "id": 5, + "lat": 51.04, + "lon": 7.9, + "title": "sixth", + "x": 6, + "y": 12, + "z": 18 + } + ], + "resource_id": "4f1299ab-a100-4e5f-ba81-e6d234a2f3bd", + "total": 6 + }, + "success": true +}; + diff --git a/test/index.html b/test/index.html index 83eeacb9..8830b603 100644 --- a/test/index.html +++ b/test/index.html @@ -36,6 +36,7 @@ + @@ -43,6 +44,7 @@ +