diff --git a/ckanext/drupal_api/assets/webassets.yml b/ckanext/drupal_api/assets/webassets.yml index ee0105e..e69de29 100644 --- a/ckanext/drupal_api/assets/webassets.yml +++ b/ckanext/drupal_api/assets/webassets.yml @@ -1,5 +0,0 @@ -# styles: -# filters: cssrewrite -# output: ckanext-drupal_api/%(version)s-styles.css -# contents: -# - css/styles.css diff --git a/ckanext/drupal_api/config.py b/ckanext/drupal_api/config.py index 7c265df..5fd2857 100644 --- a/ckanext/drupal_api/config.py +++ b/ckanext/drupal_api/config.py @@ -1,7 +1,19 @@ +from __future__ import annotations + +from typing import Any + +import ckan.plugins.toolkit as tk + + CONFIG_DRUPAL_URL = "ckanext.drupal_api.drupal_url" +DEFAULT_DRUPAL_URL = "" CONFIG_CACHE_DURATION = "ckanext.drupal_api.cache.duration" +DEFAULT_CACHE_DURATION = 3600 + CONFIG_REQUEST_TIMEOUT = "ckanext.drupal_api.timeout" +DEFAULT_REQUEST_TIMEOUT = 5 + CONFIG_REQUEST_HTTP_USER = "ckanext.drupal_api.request.user" CONFIG_REQUEST_HTTP_PASS = "ckanext.drupal_api.request.pass" @@ -14,5 +26,105 @@ CONFIG_MENU_EXPORT = "ckanext.drupal_api.core.menu_export_endpoint" DEFAULT_MENU_EXPORT_EP = "/resource/layout/export" -DEFAULT_REQUEST_TIMEOUT = 5 -DEFAULT_CACHE_DURATION = 3600 + +def get_cache_ttl() -> int: + return tk.asint(tk.config[CONFIG_CACHE_DURATION] or DEFAULT_CACHE_DURATION) + + +def get_drupal_url() -> str: + return (tk.config[CONFIG_DRUPAL_URL] or DEFAULT_DRUPAL_URL).strip("/") + + +def get_api_version() -> str: + return tk.config[CONFIG_DRUPAL_API_VERSION] or DEFAULT_API_VERSION + + +def get_menu_export_endpoint() -> str: + if get_api_version() == JSON_API: + return "/jsonapi/menu_items/{menu_id}" + + return tk.config[CONFIG_MENU_EXPORT] or DEFAULT_MENU_EXPORT_EP + + +def get_request_timeout() -> int: + return tk.asint(tk.config[CONFIG_REQUEST_TIMEOUT] or DEFAULT_REQUEST_TIMEOUT) + + +def get_http_user() -> str | None: + return tk.config[CONFIG_REQUEST_HTTP_USER] + + +def get_http_pass() -> str | None: + return tk.config[CONFIG_REQUEST_HTTP_PASS] + + +def get_config_options() -> dict[str, dict[str, Any]]: + """Defines how we are going to render the global configuration + options for an extension.""" + unicode_safe = tk.get_validator("unicode_safe") + one_of = tk.get_validator("one_of") + default = tk.get_validator("default") + int_validator = tk.get_validator("is_positive_integer") + url_validator = tk.get_validator("url_validator") + + return { + "cache_ttl": { + "key": CONFIG_CACHE_DURATION, + "label": tk._("Cache TTL"), + "value": get_cache_ttl(), + "validators": [default(DEFAULT_CACHE_DURATION), int_validator], + "type": "number", + }, + "drupal_url": { + "key": CONFIG_DRUPAL_URL, + "label": tk._("Drupal base URL"), + "value": get_drupal_url(), + "validators": [unicode_safe, url_validator], + "type": "text", + "required": True, + }, + "api_version": { + "key": CONFIG_DRUPAL_API_VERSION, + "label": tk._("API version"), + "value": get_api_version(), + "validators": [default(DEFAULT_API_VERSION), one_of([JSON_API, CORE_API])], + "type": "select", + "options": [ + {"value": JSON_API, "text": "JSON API"}, + {"value": CORE_API, "text": "Core REST API"}, + ], + }, + "menu_export_endpoint": { + "key": CONFIG_MENU_EXPORT, + "label": tk._("Menu export API endpoint"), + "value": get_menu_export_endpoint(), + "validators": [unicode_safe], + "type": "text", + "disabled": get_api_version() == JSON_API, + "required": True, + "help_text": tk._( + "If you are using the core API version, you might face the situation when your endpoint differ from the default one" + ), + }, + "request_timeout": { + "key": CONFIG_REQUEST_TIMEOUT, + "label": tk._("API request timeout"), + "value": get_request_timeout(), + "validators": [default(DEFAULT_REQUEST_TIMEOUT), int_validator], + "type": "number", + }, + "http_user": { + "key": CONFIG_REQUEST_HTTP_USER, + "label": tk._("HTTP auth user"), + "value": get_http_user(), + "validators": [unicode_safe], + "type": "text", + }, + "http_pass": { + "key": CONFIG_REQUEST_HTTP_PASS, + "label": tk._("HTTP auth password"), + "value": get_http_pass(), + "validators": [unicode_safe], + "type": "password", + }, + } diff --git a/ckanext/drupal_api/config_declaration.yaml b/ckanext/drupal_api/config_declaration.yaml index e4d44db..def8436 100644 --- a/ckanext/drupal_api/config_declaration.yaml +++ b/ckanext/drupal_api/config_declaration.yaml @@ -3,6 +3,8 @@ groups: - annotation: ckanext-drupal-api options: - key: ckanext.drupal_api.drupal_url + default: "" + - key: ckanext.drupal_api.cache.duration type: int default: 3600 diff --git a/ckanext/drupal_api/helpers.py b/ckanext/drupal_api/helpers.py index 076d6ce..bfba45c 100644 --- a/ckanext/drupal_api/helpers.py +++ b/ckanext/drupal_api/helpers.py @@ -6,10 +6,8 @@ from requests.exceptions import RequestException -import ckan.plugins.toolkit as tk - -import ckanext.drupal_api.config as c -from ckanext.drupal_api.utils import cached, _get_api_version +import ckanext.drupal_api.config as da_conf +from ckanext.drupal_api.utils import cached, get_api_version from ckanext.drupal_api.logic.api import make_request from ckanext.drupal_api.types import Menu, T, MaybeNotCached, DontCache @@ -30,7 +28,7 @@ def get_helpers(): @helper @cached def menu(name: str, cache_extras: Optional[dict[str, Any]] = None) -> MaybeNotCached[Menu]: - api_connector = _get_api_version() + api_connector = get_api_version() drupal_api = api_connector.get() if not drupal_api: @@ -55,9 +53,10 @@ def custom_endpoint(endpoint: str) -> dict: Returns: dict: response from Drupal """ - base_url = tk.config.get(c.CONFIG_DRUPAL_URL) + base_url = da_conf.get_drupal_url() + if not base_url: - log.error("Drupal URL is missing: %s", c.CONFIG_DRUPAL_URL) + log.error("Drupal URL is missing: %s", da_conf.CONFIG_DRUPAL_URL) return DontCache({}) try: @@ -65,4 +64,5 @@ def custom_endpoint(endpoint: str) -> dict: except RequestException as e: log.error(f"Custom endpoint request error - {endpoint}: {e}") return DontCache({}) + return resp diff --git a/ckanext/drupal_api/logic/api.py b/ckanext/drupal_api/logic/api.py index 540b5d4..7965223 100644 --- a/ckanext/drupal_api/logic/api.py +++ b/ckanext/drupal_api/logic/api.py @@ -1,4 +1,5 @@ from __future__ import annotations + import logging import requests from typing import Optional @@ -6,18 +7,17 @@ import ckan.plugins.toolkit as tk import ckan.plugins as p -import ckanext.drupal_api.config as c + +import ckanext.drupal_api.config as da_conf log = logging.getLogger(__name__) def make_request(url: str) -> dict: - http_user: str = tk.config.get(c.CONFIG_REQUEST_HTTP_USER) - http_pass: str = tk.config.get(c.CONFIG_REQUEST_HTTP_PASS) - timeout = tk.asint( - tk.config.get(c.CONFIG_REQUEST_TIMEOUT, c.DEFAULT_REQUEST_TIMEOUT) - ) + http_user = da_conf.get_http_user() + http_pass = da_conf.get_http_pass() + timeout = da_conf.get_request_timeout() session = requests.Session() @@ -35,10 +35,12 @@ class Drupal: @classmethod def get(cls, instance: str = "default") -> Optional[Drupal]: - url = tk.config.get(c.CONFIG_DRUPAL_URL) + url = da_conf.get_drupal_url() + if not url: - log.error("Drupal URL is missing: %s", c.CONFIG_DRUPAL_URL) + log.error("Drupal URL is missing: %s", da_conf.CONFIG_DRUPAL_URL) return + default_lang = tk.config.get("ckan.locale_default") current_lang = tk.h.lang() localised_url = url.format( @@ -92,12 +94,10 @@ def _request(self, endpoint: str) -> dict: return make_request(url) def get_menu(self, name: str) -> dict: - data: dict = self._request( - endpoint=tk.config.get(c.CONFIG_MENU_EXPORT, c.DEFAULT_MENU_EXPORT_EP) - ) + data: dict = self._request(endpoint=da_conf.get_menu_export_endpoint()) log.info( f"Menu {name} has been fetched successfully. Cached for \ - {tk.config.get(c.CONFIG_CACHE_DURATION, c.DEFAULT_CACHE_DURATION)} seconds" + {da_conf.get_cache_ttl()} seconds" ) return data.get(name, {}) diff --git a/ckanext/drupal_api/plugin.py b/ckanext/drupal_api/plugin.py index af86dea..9460978 100644 --- a/ckanext/drupal_api/plugin.py +++ b/ckanext/drupal_api/plugin.py @@ -1,13 +1,22 @@ +from __future__ import annotations + import ckan.plugins as p import ckan.plugins.toolkit as tk import ckanext.drupal_api.helpers as helpers from ckanext.drupal_api.views import blueprints +import ckanext.ap_main.types as ap_types +from ckanext.ap_main.interfaces import IAdminPanel + +import ckanext.drupal_api.config as da_config + + class DrupalApiPlugin(p.SingletonPlugin): p.implements(p.ITemplateHelpers) p.implements(p.IConfigurer) p.implements(p.IBlueprint) + p.implements(IAdminPanel, inherit=True) # ITemplateHelpers @@ -18,13 +27,37 @@ def get_helpers(self): def update_config(self, config_): tk.add_template_directory(config_, "templates") - tk.add_ckan_admin_tab(config_, "drupal_api.drupal_api_config", "Drupal API") + + def update_config_schema(self, schema): + for _, config in da_config.get_config_options().items(): + schema.update({config["key"]: config["validators"]}) + + return schema # IBlueprint def get_blueprint(self): return blueprints + # IAdminPanel + + def register_config_sections( + self, config_list: list[ap_types.SectionConfig] + ) -> list[ap_types.SectionConfig]: + config_list.append( + ap_types.SectionConfig( + name="Drupal API", + configs=[ + ap_types.ConfigurationItem( + name="Configuration", + blueprint="drupal_api.config", + info="Drupal API settings", + ) + ], + ) + ) + return config_list + if tk.check_ckan_version("2.10"): tk.blanket.config_declarations(DrupalApiPlugin) diff --git a/ckanext/drupal_api/templates/admin/drupal_api_config.html b/ckanext/drupal_api/templates/admin/drupal_api_config.html deleted file mode 100644 index 71166e7..0000000 --- a/ckanext/drupal_api/templates/admin/drupal_api_config.html +++ /dev/null @@ -1,108 +0,0 @@ -{% extends "admin/base.html" %} - -{% block primary_content_inner %} - -{% if not drupal_url %} -
-
{{ _('Cache info') }}
-- {{ _('Default cache TTL is {} sec.').format(cache_ttl_default) }} - {{ _('You can specify the TTL by providing next config option:') }} -
ckanext.drupal_api.cache.duration- - -
{{ _('API versions') }}
-- {{ _("Currently, there are two supported API versions:") }} - JSON API {{ _('and') }} - RESTful Web Services - ({{ _('default') }}) - {{ _("You can specify the API version with next config option:") }} -
ckanext.drupal_api.api_version- {{ _("There are two options: ") }}
json
{{ _('and') }} core
-
-