Skip to content

Commit

Permalink
Add basic datastore extension skeleton with create logic/auth functio…
Browse files Browse the repository at this point in the history
…ns and tests.
  • Loading branch information
johnglover committed Jul 24, 2012
1 parent 1a5712d commit a62293a
Show file tree
Hide file tree
Showing 12 changed files with 179 additions and 0 deletions.
Empty file added ckanext/datastore/__init__.py
Empty file.
25 changes: 25 additions & 0 deletions ckanext/datastore/db.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@


def create(resource_id, fields, rows):
'''
The first row will be used to guess types not in the fields and the
guessed types will be added to the headers permanently.
Consecutive rows have to conform to the field definitions.
rows can be empty so that you can just set the fields.
fields are optional but needed if you want to do type hinting or
add extra information for certain columns or to explicitly
define ordering.
eg [{"id": "dob", "label": ""Date of Birth",
"type": "timestamp" ,"concept": "day"},
{"name": "some_stuff": ..].
A header items values can not be changed after it has been defined
nor can the ordering of them be changed. They can be extended though.
Any error results in total failure! For now pass back the actual error.
Should be transactional.
'''
pass
Empty file.
Empty file.
43 changes: 43 additions & 0 deletions ckanext/datastore/logic/action/create.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import logging
import ckan.logic as logic
import ckan.logic.action
import ckan.lib.dictization
import ckan.plugins as p
import ckanext.datastore.logic.schema
import ckanext.datastore.db as db

log = logging.getLogger(__name__)

_validate = ckan.lib.navl.dictization_functions.validate
_check_access = logic.check_access
_get_or_bust = logic.get_or_bust


def datastore_create(context, data_dict):
'''Adds a new table to the datastore.
:param resource_id: resource id that the data is going to be stored under.
:type resource_id: string
:param fields: fields/columns and their extra metadata.
:type fields: list of dictionaries
:param records: the data, eg: [{"dob": "2005", "some_stuff": ['a', b']}]
:type records: list of dictionaries
:returns: the newly created data object.
:rtype: dictionary
'''
model = _get_or_bust(context, 'model')
resource_id = _get_or_bust(data_dict, 'resource_id')
fields = data_dict.get('fields')
records = _get_or_bust(data_dict, 'records')

_check_access('datastore_create', context, data_dict)

schema = ckanext.datastore.logic.schema.default_datastore_create_schema()
data, errors = _validate(data_dict, schema, context)
if errors:
model.Session.rollback()
raise p.toolkit.ValidationError(errors)

return db.create(resource_id, fields, records)
Empty file.
12 changes: 12 additions & 0 deletions ckanext/datastore/logic/auth/create.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import ckan.plugins as p


def datastore_create(context, data_dict):
model = context['model']
user = context['user']
userobj = model.User.get(user)

if userobj:
return {'success': True}
return {'success': False,
'msg': p.toolkit._('You must be logged in to use the datastore.')}
22 changes: 22 additions & 0 deletions ckanext/datastore/logic/schema.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from ckan.lib.navl.validators import (not_empty,
not_missing,
empty,
ignore_missing,
ignore)


def default_fields_schema():
return {
'id': [not_missing, not_empty, unicode],
'type': [not_missing, not_empty, unicode],
'label': [ignore_missing, unicode],
}


def default_datastore_create_schema():
return {
'resource_id': [not_missing, not_empty, unicode],
'fields': default_fields_schema(),
'records': [ignore],
'__extras': [empty],
}
17 changes: 17 additions & 0 deletions ckanext/datastore/plugin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import ckan.plugins as p
import ckanext.datastore.logic.action.create as action_create
import ckanext.datastore.logic.auth.create as auth_create


class DatastorePlugin(p.SingletonPlugin):
'''
Datastore plugin.
'''
p.implements(p.IActions)
p.implements(p.IAuthFunctions)

def get_actions(self):
return {'datastore_create': action_create.datastore_create}

def get_auth_functions(self):
return {'datastore_create': auth_create.datastore_create}
Empty file.
59 changes: 59 additions & 0 deletions ckanext/datastore/tests/test_datastore.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import json
import ckan.plugins as p
import ckan.lib.create_test_data as ctd
import ckan.model as model
import ckan.tests as tests


class TestDatastore(tests.WsgiAppCase):
sysadmin_user = None
normal_user = None

@classmethod
def setup_class(cls):
p.load('datastore')
ctd.CreateTestData.create()
cls.sysadmin_user = model.User.get('testsysadmin')
cls.normal_user = model.User.get('annafan')

@classmethod
def teardown_class(cls):
model.repo.rebuild_db()

def test_create_empty_fails(self):
postparams = '%s=1' % json.dumps({})
res = self.app.post('/api/action/datastore_create', params=postparams,
status=409)
res_dict = json.loads(res.body)
assert res_dict['success'] is False

def test_create_requires_auth(self):
resource = model.Package.get('annakarenina').resources[0]
data = {
'resource_id': resource.id
}
postparams = '%s=1' % json.dumps(data)
res = self.app.post('/api/action/datastore_create', params=postparams,
status=403)
res_dict = json.loads(res.body)
assert res_dict['success'] is False

def test_create_basic(self):
resource = model.Package.get('annakarenina').resources[0]
data = {
'resource_id': resource.id,
'fields': [{'id': 'book', 'label': 'Name', 'type': 'text'},
{'id': 'author', 'label': 'Author ', 'type': 'text'}],
'records': [{'book': 'annakarenina', 'author': 'tolstoy'},
{'book': 'warandpeace', 'author': 'tolstoy'}]
}
postparams = '%s=1' % json.dumps(data)
auth = {'Authorization': str(self.sysadmin_user.apikey)}
res = self.app.post('/api/action/datastore_create', params=postparams,
extra_environ=auth)
res_dict = json.loads(res.body)

assert res_dict['success'] is True
assert res_dict['result']['resource_id'] == data['resource_id']
assert res_dict['result']['fields'] == data['fields']
assert res_dict['result']['records'] == data['records']
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@
multilingual_tag=ckanext.multilingual.plugin:MultilingualTag
organizations=ckanext.organizations.forms:OrganizationForm
organizations_dataset=ckanext.organizations.forms:OrganizationDatasetForm
datastore=ckanext.datastore.plugin:DatastorePlugin
test_tag_vocab_plugin=ckanext.test_tag_vocab_plugin:MockVocabTagsPlugin
[ckan.system_plugins]
Expand Down

0 comments on commit a62293a

Please sign in to comment.