Skip to content
This repository has been archived by the owner on Mar 12, 2020. It is now read-only.

Commit

Permalink
Add access level to item
Browse files Browse the repository at this point in the history
Access needs to be known early on in the processing in order to know
where to send the data files. Specifically, this determines which
GeoServer gets the data. This change will require the client to
specify access on submission, defaulting to public.
  • Loading branch information
Mike Graves committed Jun 18, 2015
1 parent 90a3190 commit a7de368
Show file tree
Hide file tree
Showing 9 changed files with 57 additions and 29 deletions.
18 changes: 8 additions & 10 deletions kepler/geoserver.py
Expand Up @@ -11,19 +11,17 @@ def service_url(root_url, workspace):
return '%srest/workspaces/%s/' % (base, workspace)


def put_url(id, mimetype):
svc_url = service_url(current_app.config['GEOSERVER_URL'],
current_app.config['GEOSERVER_WORKSPACE'])
def put_url(root, id, mimetype):
svc_url = service_url(root, current_app.config['GEOSERVER_WORKSPACE'])
if mimetype == 'application/zip':
datastore = current_app.config['GEOSERVER_DATASTORE']
return '%sdatastores/%s/file.shp' % (svc_url, datastore)
elif mimetype == 'image/tiff':
return '%scoveragestores/%s/file.geotiff' % (svc_url, id)


def delete_url(id, mimetype):
svc_url = service_url(current_app.config['GEOSERVER_URL'],
current_app.config['GEOSERVER_WORKSPACE'])
def delete_url(root, id, mimetype):
svc_url = service_url(root, current_app.config['GEOSERVER_WORKSPACE'])
if mimetype == 'application/zip':
datastore = current_app.config['GEOSERVER_DATASTORE']
return '%sdatastores/%s/featuretypes/%s?recurse=true' % (svc_url,
Expand All @@ -32,15 +30,15 @@ def delete_url(id, mimetype):
return '%scoveragestores/%s?recurse=true' % (svc_url, id)


def put(id, data, mimetype):
url = put_url(id, mimetype)
def put(root, id, data, mimetype):
url = put_url(root, id, mimetype)
headers = {'Content-type': mimetype}
with io.open(data, 'rb') as fp:
r = requests.put(url, data=fp, headers=headers)
r.raise_for_status()


def delete(id, mimetype):
url = delete_url(id, mimetype)
def delete(root, id, mimetype):
url = delete_url(root, id, mimetype)
r = requests.delete(url)
r.raise_for_status()
3 changes: 2 additions & 1 deletion kepler/jobs.py
Expand Up @@ -33,7 +33,8 @@ def completed(sender, **kwargs):
def create_job(form, data=None):
uri = form['uri']
job_type = form['type']
item = get_or_create(Item, uri=uri)
access = form.get('access', u'Public')
item = get_or_create(Item, uri=uri, access=access)
job = Job(item=item, status=u'PENDING')
db.session.add(job)
db.session.commit()
Expand Down
1 change: 1 addition & 0 deletions kepler/models.py
Expand Up @@ -36,6 +36,7 @@ def as_dict(self):
class Item(db.Model):
id = db.Column(db.Integer, primary_key=True)
uri = db.Column(db.Unicode(255), unique=True)
access = db.Column(db.Enum(u'Public', u'Restricted'), default=u'Public')
jobs = db.relationship('Job', backref='item', lazy='dynamic')

def __repr__(self):
Expand Down
3 changes: 2 additions & 1 deletion kepler/settings.py
Expand Up @@ -16,7 +16,8 @@ class TestConfig(DefaultConfig):
TESTING = True
DEBUG = True
SQLALCHEMY_DATABASE_URI = 'sqlite://'
GEOSERVER_URL = 'http://example.com/geoserver'
GEOSERVER_PUBLIC_URL = 'http://example.com/geoserver/'
GEOSERVER_RESTRICTED_URL = 'http://example.com/secure-geoserver/'
GEOSERVER_WORKSPACE = 'mit'
GEOSERVER_DATASTORE = 'data'
SOLR_URL = 'http://localhost:8983/solr/geoblacklight/'
Expand Down
6 changes: 5 additions & 1 deletion kepler/tasks.py
Expand Up @@ -129,7 +129,11 @@ def _upload_to_geoserver(job, bag, mimetype):
data = get_shapefile(bag)
elif mimetype == 'image/tiff':
data = get_geotiff(bag)
put(job.item.uri, data, mimetype)
if job.access == 'Restricted':
url = current_app.config['GEOSERVER_RESTRICTED_URL']
else:
url = current_app.config['GEOSERVER_PUBLIC_URL']
put(url, job.item.uri, data, mimetype)


def _index_records(records):
Expand Down
30 changes: 15 additions & 15 deletions tests/test_geoserver.py
Expand Up @@ -9,6 +9,7 @@


pytestmark = pytest.mark.usefixtures('app')
root = 'http://example.com/geoserver/'


@pytest.yield_fixture
Expand All @@ -20,41 +21,40 @@ def requests():

class TestGeoServer(object):
def testServiceUrlGeneratesUrl(self):
assert service_url('http://example.com/geoserver/', 'foo') == \
'http://example.com/geoserver/rest/workspaces/foo/'
assert service_url(root, 'foo') == '%srest/workspaces/foo/' % root

def testPutUrlReturnsShapefileUrl(self):
assert put_url('foo', 'application/zip') == \
'http://example.com/geoserver/rest/workspaces/mit/datastores/data/file.shp'
assert put_url(root, 'foo', 'application/zip') == \
'%srest/workspaces/mit/datastores/data/file.shp' % root

def testPutUrlReturnsCoverageUrl(self):
assert put_url('foo', 'image/tiff') == \
'http://example.com/geoserver/rest/workspaces/mit/coveragestores/foo/file.geotiff'
assert put_url(root, 'foo', 'image/tiff') == \
'%srest/workspaces/mit/coveragestores/foo/file.geotiff' % root

def testDeleteUrlReturnsShapefileUrl(self):
assert delete_url('foo', 'application/zip') == \
'http://example.com/geoserver/rest/workspaces/mit/datastores/data/featuretypes/foo?recurse=true'
assert delete_url(root, 'foo', 'application/zip') == \
'%srest/workspaces/mit/datastores/data/featuretypes/foo?recurse=true' % root

def testDeleteUrlReturnsCoverageUrl(self):
assert delete_url('foo', 'image/tiff') == \
'http://example.com/geoserver/rest/workspaces/mit/coveragestores/foo?recurse=true'
assert delete_url(root, 'foo', 'image/tiff') == \
'%srest/workspaces/mit/coveragestores/foo?recurse=true' % root

def testPutUploadsData(self, requests):
put('foo', 'tests/data/bermuda.zip', 'application/zip')
put(root, 'foo', 'tests/data/bermuda.zip', 'application/zip')
call = requests.put.call_args
assert call[1].get('data').name == 'tests/data/bermuda.zip'

def testPutRaisesHttpErrorWhenUnsuccessful(self, requests):
requests.put.return_value = Mock(**{'raise_for_status.side_effect': HTTPError})
with pytest.raises(HTTPError):
put('foo', 'tests/data/grayscale.tif', 'image/tiff')
put(root, 'foo', 'tests/data/grayscale.tif', 'image/tiff')

def testDeleteDeletesResource(self, requests):
delete('foo', 'image/tiff')
url = delete_url('foo', 'image/tiff')
delete(root, 'foo', 'image/tiff')
url = delete_url(root, 'foo', 'image/tiff')
requests.delete.assert_called_once_with(url)

def testDeleteRaisesHttpErrorWhenUnsuccessful(self, requests):
requests.delete.return_value = Mock(**{'raise_for_status.side_effect': HTTPError})
with pytest.raises(HTTPError):
delete('foo', 'application/zip')
delete(root, 'foo', 'application/zip')
5 changes: 5 additions & 0 deletions tests/test_jobs.py
Expand Up @@ -22,6 +22,11 @@ def testCreatesItem(self, bag):
create_job({'type': u'shapefile', 'uri': u'SEAMUS'}, bag)
assert Item.query.count() == 1

def testSetsAccessLevel(self, bag):
form = {'type': u'shapefile', 'uri': u'FOO', 'access': u'Restricted'}
create_job(form, bag)
assert Item.query.first().access == u'Restricted'

def testCreatesJob(self, bag):
create_job({'type': u'shapefile', 'uri': u'LEURENT'}, bag)
assert Job.query.count() == 1
Expand Down
10 changes: 10 additions & 0 deletions tests/test_models.py
Expand Up @@ -66,3 +66,13 @@ def testUriIsUnique(self, db):
db.session.add_all([item_one, item_two])
with pytest.raises(SQLAlchemyError):
db.session.commit()

def testItemHasAccessLevel(self, db):
item = Item(access=u'Public')
assert item.access == u'Public'

def testItemAccessDefaultsToPublic(self, db):
item = Item()
db.session.add(item)
db.session.commit()
assert item.access == u'Public'
10 changes: 9 additions & 1 deletion tests/test_tasks.py
Expand Up @@ -108,7 +108,15 @@ def testIndexFromFgdcAddsUuid(job, bag):
def testUploadToGeoserverUploadsData(job, bag, shapefile):
with patch('kepler.tasks.put') as mock:
_upload_to_geoserver(job, bag, 'application/zip')
mock.assert_called_once_with(job.item.uri, shapefile, 'application/zip')
mock.assert_called_once_with('http://example.com/geoserver/', job.item.uri,
shapefile, 'application/zip')


def testUploadToGeoServerUsesCorrectUrl(job, bag, shapefile):
with patch('kepler.tasks.put') as mock:
job.access = 'Restricted'
_upload_to_geoserver(job, bag, 'application/zip')
assert mock.call_args[0][0] == 'http://example.com/secure-geoserver/'


def testIndexRecordsAddsRecordsToSolr(pysolr_add):
Expand Down

0 comments on commit a7de368

Please sign in to comment.