Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
smotornyuk committed Feb 2, 2024
2 parents d6950f0 + 6029ce2 commit a7a3944
Show file tree
Hide file tree
Showing 15 changed files with 293 additions and 255 deletions.
5 changes: 0 additions & 5 deletions ckanext/drupal_api/assets/webassets.yml
Original file line number Diff line number Diff line change
@@ -1,5 +0,0 @@
# styles:
# filters: cssrewrite
# output: ckanext-drupal_api/%(version)s-styles.css
# contents:
# - css/styles.css
116 changes: 114 additions & 2 deletions ckanext/drupal_api/config.py
Original file line number Diff line number Diff line change
@@ -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"

Expand All @@ -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",
},
}
14 changes: 14 additions & 0 deletions ckanext/drupal_api/config_declaration.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,32 @@ groups:
- annotation: ckanext-drupal-api
options:
- key: ckanext.drupal_api.drupal_url
editable: true
validators: ignore_empty unicode_safe url_validator

- key: ckanext.drupal_api.cache.duration
type: int
default: 3600
editable: true

- key: ckanext.drupal_api.timeout
type: int
default: 5
editable: true

- key: ckanext.drupal_api.request.user
editable: true

- key: ckanext.drupal_api.request.pass
editable: true

- key: ckanext.drupal_api.api_version
default: core
editable: true

- key: ckanext.drupal_api.core.menu_export_endpoint
editable: true
default: "/resource/layout/export"

- key: ckanext.drupal_api.footer_html
editable: true
49 changes: 49 additions & 0 deletions ckanext/drupal_api/config_schema.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
scheming_version: 2
schema_id: drupal_api_config
about: An extension config form schema

fields:
- field_name: ckanext.drupal_api.cache.duration
label: Cache TTL
form_placeholder: 600
validators: default(3600) int_validator
input_type: number

- field_name: ckanext.drupal_api.drupal_url
label: Drupal base URL
required: true

- field_name: ckanext.drupal_api.api_version
label: API version
validators: default(core), one_of([json, core])
preset: select
required: true
choices:
- value: json
label: JSON API
- value: core
label: Core REST API

- field_name: ckanext.drupal_api.core.menu_export_endpoint
label: Menu export API endpoint
validators: unicode_safe
required: true
help_text: If you are using the core API version, you might face the situation when your endpoint differ from the default one
form_attrs:
data-module: ap-disable-field
data-module-field-id: ckanext.drupal_api.api_version
data-module-field-value: json

- field_name: ckanext.drupal_api.timeout
label: API request timeout
validators: default(5) int_validator
input_type: number

- field_name: ckanext.drupal_api.request.user
label: HTTP auth user
validators: unicode_safe

- field_name: ckanext.drupal_api.request.pass
label: HTTP auth password
validators: unicode_safe
input_type: password
24 changes: 16 additions & 8 deletions ckanext/drupal_api/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -29,8 +27,10 @@ def get_helpers():

@helper
@cached
def menu(name: str, cache_extras: Optional[dict[str, Any]] = None) -> MaybeNotCached[Menu]:
api_connector = _get_api_version()
def menu(
name: str, cache_extras: Optional[dict[str, Any]] = None
) -> MaybeNotCached[Menu]:
api_connector = get_api_version()
drupal_api = api_connector.get()

if not drupal_api:
Expand All @@ -44,6 +44,7 @@ def menu(name: str, cache_extras: Optional[dict[str, Any]] = None) -> MaybeNotCa

return menu


@helper
@cached
def custom_endpoint(endpoint: str) -> dict:
Expand All @@ -55,14 +56,21 @@ 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:
resp = make_request(urljoin(base_url, endpoint))
except RequestException as e:
log.error(f"Custom endpoint request error - {endpoint}: {e}")
return DontCache({})

return resp


@helper
def get_drupal_url() -> str:
return da_conf.get_drupal_url()
24 changes: 12 additions & 12 deletions ckanext/drupal_api/logic/api.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
from __future__ import annotations

import logging
import requests
from typing import Optional
from urllib.parse import urljoin

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()

Expand All @@ -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(
Expand Down Expand Up @@ -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, {})

Expand Down
27 changes: 26 additions & 1 deletion ckanext/drupal_api/plugin.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
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


class DrupalApiPlugin(p.SingletonPlugin):
p.implements(p.ITemplateHelpers)
p.implements(p.IConfigurer)
p.implements(p.IBlueprint)
p.implements(IAdminPanel, inherit=True)

# ITemplateHelpers

Expand All @@ -18,13 +25,31 @@ 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")

# 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)
Loading

0 comments on commit a7a3944

Please sign in to comment.