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

Allow custom routes to be picked up by community links template #1258

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
22 changes: 2 additions & 20 deletions invenio_communities/communities/services/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
from ...permissions import CommunityPermissionPolicy, can_perform_action
from ..schema import CommunityFeaturedSchema, CommunitySchema, TombstoneSchema
from .components import DefaultCommunityComponents
from .links import CommunityLink
from .links import CommunityLink, CommunityLinks
from .search_params import IncludeDeletedCommunitiesParam, StatusParam
from .sort import CommunitiesSortParam

Expand Down Expand Up @@ -114,25 +114,7 @@ class CommunityServiceConfig(RecordServiceConfig, ConfiguratorMixin):
result_list_cls_featured = CommunityFeaturedList
result_item_cls_featured = FeaturedCommunityItem

links_item = {
"self": CommunityLink("{+api}/communities/{id}"),
"self_html": CommunityLink("{+ui}/communities/{slug}"),
"settings_html": CommunityLink("{+ui}/communities/{slug}/settings"),
"logo": CommunityLink("{+api}/communities/{id}/logo"),
"rename": CommunityLink("{+api}/communities/{id}/rename"),
"members": CommunityLink("{+api}/communities/{id}/members"),
"public_members": CommunityLink("{+api}/communities/{id}/members/public"),
"invitations": CommunityLink("{+api}/communities/{id}/invitations"),
"requests": CommunityLink("{+api}/communities/{id}/requests"),
"records": CommunityLink("{+api}/communities/{id}/records"),
"subcommunities": CommunityLink(
"{+api}/communities/{id}/subcommunities",
when=children_allowed,
),
"membership_requests": CommunityLink(
"{+api}/communities/{id}/membership-requests"
),
}
links_item = CommunityLinks.get_item_links

action_link = CommunityLink(
"{+api}/communities/{id}/{action_name}", when=can_perform_action
Expand Down
86 changes: 72 additions & 14 deletions invenio_communities/communities/services/links.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,81 @@

"""Utility for rendering URI template links."""

from flask import current_app
from invenio_records_resources.services.base.links import Link, LinksTemplate


def children_allowed(record, _):
"""Determine if children are allowed."""
try:
return getattr(record.children, "allow", False)
except AttributeError:
# This is needed because a types.SimpleNamespace object can be passed by
# the entity_resolver when generating the logo which does not have
# `children` and fails
return False


class CommunityLink(Link):
"""Link variables setter for Community Members links."""

@staticmethod
def vars(record, vars):
"""Variables for the URI template."""
vars.update(
{
"id": record.id,
"slug": record.slug,
}
)


class CommunityLinks(object):
"""Factory class for Community routes."""

communities_default_routes = {
"self": CommunityLink("{+api}/communities/{id}"),
"self_html": CommunityLink("{+ui}/communities/{slug}"),
"settings_html": CommunityLink("{+ui}/communities/{slug}/settings"),
"logo": CommunityLink("{+api}/communities/{id}/logo"),
"rename": CommunityLink("{+api}/communities/{id}/rename"),
"members": CommunityLink("{+api}/communities/{id}/members"),
"public_members": CommunityLink("{+api}/communities/{id}/members/public"),
"invitations": CommunityLink("{+api}/communities/{id}/invitations"),
"requests": CommunityLink("{+api}/communities/{id}/requests"),
"records": CommunityLink("{+api}/communities/{id}/records"),
"subcommunities": CommunityLink(
"{+api}/communities/{id}/subcommunities",
when=children_allowed,
),
"membership_requests": CommunityLink(
"{+api}/communities/{id}/membership-requests"
),
}

@classmethod
def convert_config_routes(cls, custom_routes: dict) -> dict:
"""Convert config routes to the links string template format.

Only convert routes that have a corresponding default link
ending in "_html".
"""
return {
f"{k}_html": CommunityLink("{+ui}" + v.replace("<pid_value>", "{slug}"))
for k, v in custom_routes.items()
if f"{k}_html" in cls.communities_default_routes.keys()
}

@classmethod
def get_item_links(cls, routes: dict = None) -> dict:
"""Get the item links customized from the config routes."""
routes = cls.communities_default_routes.copy() if not routes else routes
routes_from_config = current_app.config.get("COMMUNITIES_ROUTES", {})
routes.update(cls.convert_config_routes(routes_from_config))

return routes


class CommunityLinksTemplate(LinksTemplate):
"""Templates for generating links for a community object."""

Expand Down Expand Up @@ -42,17 +114,3 @@ def expand(self, identity, community):
links[key] = link.expand(community, ctx)

return links


class CommunityLink(Link):
"""Link variables setter for Community Members links."""

@staticmethod
def vars(record, vars):
"""Variables for the URI template."""
vars.update(
{
"id": record.id,
"slug": record.slug,
}
)
2 changes: 1 addition & 1 deletion invenio_communities/communities/services/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ def __init__(
def links_item_tpl(self):
"""Item links template."""
return CommunityLinksTemplate(
self.config.links_item,
self.config.links_item(),
self.config.action_link,
self.config.available_actions,
context={
Expand Down
1 change: 1 addition & 0 deletions invenio_communities/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"invitations": "/communities/<pid_value>/invitations",
"about": "/communities/<pid_value>/about",
"curation_policy": "/communities/<pid_value>/curation-policy",
"self": "/communities/<pid_value>",
}

"""Communities ui endpoints."""
Expand Down
36 changes: 36 additions & 0 deletions tests/communities/test_services.py
Original file line number Diff line number Diff line change
Expand Up @@ -762,3 +762,39 @@ def test_bulk_update_parent_overwrite(
for c_id in children:
c_comm = community_service.record_cls.pid.resolve(c_id)
assert str(c_comm.parent.id) == str(parent_community.id)


def test_links_with_custom_routes(community_service, db, comm, app, search_clear):
"""Test that links pick up custom routes from config."""

# test without custom routes configured
get_result = community_service.read(system_identity, comm.id)
assert (
get_result.data["links"]["self_html"]
== f"https://127.0.0.1:5000/communities/{comm['slug']}"
)
assert (
get_result.data["links"]["settings_html"]
== f"https://127.0.0.1:5000/communities/{comm['slug']}/settings"
)

# test with custom routes configured
default_routes = {**app.config["COMMUNITIES_ROUTES"]}
app.config["COMMUNITIES_ROUTES"].update(
{
"self": "/collections/<pid_value>",
"settings": "/collections/<pid_value>/settings",
}
)
get_result = community_service.read(system_identity, comm.id)
assert (
get_result.data["links"]["self_html"]
== f"https://127.0.0.1:5000/collections/{comm['slug']}"
)
assert (
get_result.data["links"]["settings_html"]
== f"https://127.0.0.1:5000/collections/{comm['slug']}/settings"
)

# reset routes to default
app.config["COMMUNITIES_ROUTES"] = default_routes