Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added support of Flask and python3 to integrate in CKAN v2.10.1 #19

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
176 changes: 5 additions & 171 deletions ckanext/right_time_context/controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
# along with Orion Context Broker. If not, see http://www.gnu.org/licenses/.

import json
from logging import getLogger
import logging

import os
import urlparse

Expand All @@ -31,180 +32,13 @@
import requests
import six

from .plugin import NGSI_REG_FORMAT

log = getLogger(__name__)
import ckanext.right_time_context.utils as utils

CHUNK_SIZE = 512
log = logging.getLogger(__name__)


class ProxyNGSIController(base.BaseController):

def _proxy_query_resource(self, resource, parsed_url, headers, verify=True):

if parsed_url.path.lower().find('/v1/querycontext') != -1:
if resource.get("payload", "").strip() == "":
details = 'Please add a payload to complete the query.'
base.abort(409, detail=details)

try:
json.loads(resource['payload'])
except json.JSONDecodeError:
details = "Payload field doesn't contain valid JSON data."
base.abort(409, detail=details)

headers['Content-Type'] = "application/json"
r = requests.post(resource['url'], headers=headers, data=resource["payload"], stream=True, verify=verify)

else:
r = requests.get(resource['url'], headers=headers, stream=True, verify=verify)

return r

def _proxy_registration_resource(self, resource, parsed_url, headers, verify=True):
path = parsed_url.path

if path.endswith('/'):
path = path[:-1]

path = path + '/v2/op/query'
attrs = []

if 'attrs_str' in resource and len(resource['attrs_str']):
attrs = resource['attrs_str'].split(',')
body = {
'entities': [],
'attrs': attrs
}

# Include entity information
for entity in resource['entity']:
query_entity = {
'type': entity['value']
}
if 'isPattern' in entity and entity['isPattern'] == 'on':
query_entity['idPattern'] = entity['id']
else:
query_entity['id'] = entity['id']

body['entities'].append(query_entity)

# Parse expression to include georel information
if 'expression' in resource and len(resource['expression']):
# Separate expresion query strings
supported_expressions = ['georel', 'geometry', 'coords']
parsed_expression = resource['expression'].split('&')

expression = {}
for exp in parsed_expression:
parsed_exp = exp.split('=')

if len(parsed_exp) != 2 or not parsed_exp[0] in supported_expressions:
base.abort(422, detail='The expression is not a valid one for NGSI Registration, only georel, geometry, and coords is supported')
else:
expression[parsed_exp[0]] = parsed_exp[1]

body['expression'] = expression

headers['Content-Type'] = 'application/json'
url = urlparse.urljoin(parsed_url.scheme + '://' + parsed_url.netloc, path)
response = requests.post(url, headers=headers, json=body, stream=True, verify=verify)

return response

def process_auth_credentials(self, resource, headers):
auth_method = resource.get('auth_type', 'none')

if auth_method == "oauth2":
token = toolkit.c.usertoken['access_token']
headers['Authorization'] = "Bearer %s" % token
elif auth_method == "x-auth-token-fiware":
# Deprecated method, Including OAuth2 token retrieved from the IdM
# on the Open Stack X-Auth-Token header
token = toolkit.c.usertoken['access_token']
headers['X-Auth-Token'] = token

def proxy_ngsi_resource(self, resource_id):
# Chunked proxy for ngsi resources.
context = {'model': base.model, 'session': base.model.Session, 'user': base.c.user or base.c.author}

log.info('Proxify resource {id}'.format(id=resource_id))
resource = logic.get_action('resource_show')(context, {'id': resource_id})

headers = {
'Accept': 'application/json'
}

resource.setdefault('auth_type', 'none')
self.process_auth_credentials(resource, headers)

if resource.get('tenant', '') != '':
headers['FIWARE-Service'] = resource['tenant']
if resource.get('service_path', '') != '':
headers['FIWARE-ServicePath'] = resource['service_path']

url = resource['url']
parsed_url = urlparse.urlsplit(url)

if parsed_url.scheme not in ("http", "https") or not parsed_url.netloc:
base.abort(409, detail='Invalid URL.')

# Process verify configuration
verify_conf = os.environ.get('CKAN_RIGHT_TIME_CONTEXT_VERIFY_REQUESTS', toolkit.config.get('ckan.right_time_context.verify_requests'))
if verify_conf is None or (isinstance(verify_conf, six.string_types) and verify_conf.strip() == ""):
verify_conf = os.environ.get('CKAN_VERIFY_REQUESTS', toolkit.config.get('ckan.verify_requests'))

if isinstance(verify_conf, six.string_types) and verify_conf.strip() != "":
compare_env = verify_conf.lower().strip()
if compare_env in ("true", "1", "on"):
verify = True
elif compare_env in ("false", "0", "off"):
verify = False
else:
verify = verify_conf
elif isinstance(verify_conf, bool):
verify = verify_conf
else:
verify = True

# Make the request to the server
try:
if resource['format'].lower() == NGSI_REG_FORMAT:
r = self._proxy_registration_resource(resource, parsed_url, headers, verify=verify)
else:
r = self._proxy_query_resource(resource, parsed_url, headers, verify=verify)

except requests.HTTPError:
details = 'Could not proxy ngsi_resource. We are working to resolve this issue as quickly as possible'
base.abort(409, detail=details)
except requests.ConnectionError:
details = 'Could not proxy ngsi_resource because a connection error occurred.'
base.abort(502, detail=details)
except requests.Timeout:
details = 'Could not proxy ngsi_resource because the connection timed out.'
base.abort(504, detail=details)

if r.status_code == 401:
if resource.get('auth_type', 'none') != 'none':
details = 'ERROR 401 token expired. Retrieving new token, reload please.'
log.info(details)
toolkit.c.usertoken_refresh()
base.abort(409, detail=details)
elif resource.get('auth_type', 'none') == 'none':
details = 'Authentication requested by server, please check resource configuration.'
log.info(details)
base.abort(409, detail=details)

elif r.status_code == 400:
response = r.json()
details = response['description']
log.info(details)
base.abort(422, detail=details)

else:
r.raise_for_status()
base.response.content_type = r.headers['content-type']
base.response.charset = r.encoding
utils.proxy_ngsi_resource(resource_id)

for chunk in r.iter_content(chunk_size=CHUNK_SIZE):
base.response.body_file.write(chunk)
35 changes: 0 additions & 35 deletions ckanext/right_time_context/fanstatic/view_ngsi.min.js

This file was deleted.

19 changes: 19 additions & 0 deletions ckanext/right_time_context/fanstatic/webassets.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
main-css:
output: ckanext-right_time_context/%(version)s_ngsi_icons.css
contents:
- vendor/bootstrap/css/bootstrap-responsive.css
- styles/github.css
- css/ngsi.css
- css/ngsi_icons.css

main:
output: ckanext-right_time_context/%(version)s_ngsi_icons.js
extra:
preload:
- base/main
- ckanext-right_time_context/main-css
contents:
- vendor/highlight/highlight.pack.js
- vendor/dygraph/dygraph-combined.js
- view_ngsi.js

Original file line number Diff line number Diff line change
Expand Up @@ -23,38 +23,35 @@
import logging

from ckan.common import _, json
import ckan.plugins as p
from ckan import plugins as p
import ckan.lib.helpers as h
from ckan.plugins import toolkit

log = logging.getLogger(__name__)

import ckanext.right_time_context.utils as utils

NGSI_FORMAT = 'fiware-ngsi'
NGSI_REG_FORMAT = 'fiware-ngsi-registry'

if toolkit.check_ckan_version("2.10.1"):
from ckanext.right_time_context.plugin.flask_plugin import MixinPlugin
else:
from ckanext.right_time_context.plugin.pylons_plugin import MixinPlugin


def check_query(resource):
parsedurl = resource['url'].lower()
return parsedurl.find('/v2/entities') != -1 or parsedurl.find('/v1/querycontext') != -1 or parsedurl.find('/v1/contextentities/') != -1
return parsedurl.find('/entities') != -1 or parsedurl.find('/querycontext') != -1 or parsedurl.find('/contextentities/') != -1


class NgsiView(p.SingletonPlugin):
class NgsiView(MixinPlugin, p.SingletonPlugin):

p.implements(p.IRoutes, inherit=True)
p.implements(p.IConfigurer, inherit=True)
p.implements(p.IConfigurable, inherit=True)
p.implements(p.IResourceView, inherit=True)
p.implements(p.IResourceController, inherit=True)
p.implements(p.ITemplateHelpers)

def before_map(self, m):
m.connect(
'/dataset/{id}/resource/{resource_id}/ngsiproxy',
controller='ckanext.right_time_context.controller:ProxyNGSIController',
action='proxy_ngsi_resource'
)
return m

def get_helpers(self):

def get_available_auth_methods():
Expand All @@ -74,24 +71,17 @@ def get_available_auth_methods():
"right_time_context_get_available_auth_methods": get_available_auth_methods,
}

def get_proxified_ngsi_url(self, data_dict):
url = h.url_for(
action='proxy_ngsi_resource',
controller='ckanext.right_time_context.controller:ProxyNGSIController',
id=data_dict['package']['name'],
resource_id=data_dict['resource']['id']
)
log.info('Proxified url is {0}'.format(url))
return url

def configure(self, config):
self.proxy_is_enabled = p.plugin_loaded('resource_proxy')
self.oauth2_is_enabled = p.plugin_loaded('oauth2')

def update_config(self, config):
p.toolkit.add_template_directory(config, 'templates')
p.toolkit.add_resource('fanstatic', 'right_time_context')
p.toolkit.add_public_directory(config, 'public')
p.toolkit.add_template_directory(config, '../templates')
p.toolkit.add_resource('../fanstatic', 'ckanext-right_time_context')
p.toolkit.add_public_directory(config, '../public')



def info(self):
return {
Expand Down Expand Up @@ -132,7 +122,7 @@ def setup_template_variables(self, context, data_dict):
h.flash_error(f_details, allow_html=False)
view_enable = [False, details]
elif format_lower == NGSI_FORMAT and not check_query(resource):
details = "</br></br>This is not a ContextBroker query, please check <a href='https://forge.fiware.org/plugins/mediawiki/wiki/fiware/index.php/Publish/Subscribe_Broker_-_Orion_Context_Broker_-_User_and_Programmers_Guide'>Orion Context Broker documentation</a></br></br></br>"
#details = "</br></br>This is not a ContextBroker query, please check <a href='https://forge.fiware.org/plugins/mediawiki/wiki/fiware/index.php/Publish/Subscribe_Broker_-_Orion_Context_Broker_-_User_and_Programmers_Guide'>Orion Context Broker documentation</a></br></br></br>"
f_details = "This is not a ContextBroker query, please check Orion Context Broker documentation."
h.flash_error(f_details, allow_html=False)
view_enable = [False, details]
Expand All @@ -143,7 +133,7 @@ def setup_template_variables(self, context, data_dict):
view_enable = [False, details]
else:
# All checks passed
url = self.get_proxified_ngsi_url(data_dict)
url = utils.get_proxified_ngsi_url(data_dict)

data_dict['resource']['url'] = url
view_enable = [True, 'OK']
Expand Down Expand Up @@ -209,20 +199,37 @@ def remove_serialized(prefix):

return serialized_resource

# CKAN < 2.10 hooks
def before_create(self, context, resource):
return self._serialize_resource(resource)
return self.before_resource_create(context, resource)

def after_create(self, context, resource):
return self.after_resource_create(context, resource)

def before_update(self, context, current, resource):
return self.before_resource_update(context, current, resource)

def after_update(self, context, resource):
return self.after_resource_update(context, resource)

def before_show(self, resource):
return self.before_resource_show(resource)

# CKAN >= 2.10 hooks
def before_resource_create(self, context, resource):
return self._serialize_resource(resource)

def after_resource_create(self, context, resource):
# Create entry in the NGSI registry
pass

def before_update(self, context, current, resource):
def before_resource_update(self, context, current, resource):
return self._serialize_resource(resource)

def after_update(self, context, resource):
def after_resource_update(self, context, resource):
pass

def before_show(self, resource):
def before_resource_show(self, resource):
# Deserialize resource information
entities = []

Expand All @@ -242,3 +249,4 @@ def deserilize_handler(prefix):
resource['entity'] = entities

return resource

Loading