Skip to content

Harmonize REST API URL Naming and Availability #730

Description

@nemesifier

⚠️ not suited for first time contributors/beginners.

Is this change related to a problem? Please describe.

Goal

Public REST URL naming and availability are inconsistent across OpenWISP Django modules. This change establishes one convention for public REST URL patterns and removes unsupported settings that conditionally register REST APIs.

This applies to Django URL pattern names used by reverse() and {% url %}. It does not rename view classes unless a separate reason requires that.

Scope

Audit and harmonize public URLConfs in:

  • openwisp-users
  • openwisp-controller
  • openwisp-radius
  • openwisp-firmware-upgrader
  • openwisp-monitoring
  • openwisp-notifications
  • openwisp-ipam
  • openwisp-subscriptions
  • openwisp-network-topology
  • openwisp-utils
  • django-x509 where applicable

Do not include non-Django projects such as openwisp-config.

Current State

URL naming is inconsistent:

  • Firmware Upgrader and Monitoring use api_ prefixes.
  • Controller, IPAM, Notifications, Network Topology, and Utils use generic names inside API URLConfs.
  • RADIUS mixes rest_ and generic names.
  • Subscriptions mixes api_ and generic names.

The audit must identify every public route name, namespace, URL path, caller, test, documentation reference, and REST API route-gate setting before changing a module.

Describe the change

Decisions

Use the following conventions for implementation.

URL Naming

  • Use one namespace per module for all public URLs: users, controller, radius, upgrader, monitoring, notifications, ipam, subscriptions, topology, utils, and x509.
  • Prefix every REST URL pattern name with api_ to distinguish it from HTML and other non-REST views in the same namespace.
  • Name a REST route api_<resource>_<action>, using singular, snake-case resource names. Use list and detail for collection and object routes.
  • For a nested resource, include every semantic parent that scopes it: api_<parent>_<resource>_<action>.
  • For an operation on a resource itself, use api_<resource>_<action> without adding a child resource.
  • Do not include transport-only path segments such as api or v1 in the name.

Examples: users:api_user_list, users:api_user_email_detail, controller:api_device_connection_list, monitoring:api_location_indoor_coordinates_list, and upgrader:api_build_firmware_download.

This keeps one predictable namespace per module while preventing collisions between REST and non-REST views and between similarly named nested resources.

REST API Availability

OpenWISP relies on REST APIs for internal and cross-module behavior. Public REST API URLs are mandatory and cannot be disabled.

  • Do not introduce module-level settings that conditionally register public REST URLConfs.
  • Retire existing public REST API route gates: OPENWISP_USERS_AUTH_API, OPENWISP_CONTROLLER_API, OPENWISP_CONTROLLER_PKI_API, OPENWISP_RADIUS_API, and OPENWISP_FIRMWARE_UPGRADER_API.
  • Retain settings that configure a distinct concern, including API documentation, remote API URL configuration, API hosts, authentication requirements, and feature-specific registration behavior.
  • Authentication, authorization, network controls, and rate limits remain the mechanisms for controlling API access.

Migration Policy

Treat renamed URL pattern names as a documented backward-incompatible change.

  • Keep HTTP paths unchanged unless a separate API-versioning decision changes them.
  • Publish a complete old-to-new reverse() mapping in release notes and changelogs.
  • Update all maintained OpenWISP modules and sample projects in the same release cycle.
  • Update tests, templates, Python callers, JavaScript configuration, and documentation references.
  • Do not introduce aliases unless the maintainers decide that the affected release requires a compatibility window.
  • If aliases are used, test both names and state their removal version.

Treat removal of a REST API route gate as a documented configuration migration.

  • Do not silently register routes for deployments that explicitly set a retiring setting to False.
  • Deprecate each setting before removal and provide a clear warning or configuration error that identifies the obsolete setting and its removal release.
  • Update deployment templates and downstream settings that set a retiring setting.

Example mapping:

users:change_password -> users:api_user_password_change
users:user_list -> users:api_user_list
users:email_update -> users:api_user_email_detail
controller:deviceconnection_list -> controller:api_device_connection_list
upgrader:api_firmware_detail -> upgrader:api_build_firmware_detail

Implementation Plan

  1. Inventory every public production URLConf in scope.
  2. Record each route's path, current namespace, current name, view, callers, and documentation references.
  3. Confirm the canonical module namespace and naming convention, including semantic parents for nested routes.
  4. Inventory all settings, deployment templates, and downstream configuration that conditionally register public REST URLs.
  5. Define the old-to-new mapping for every affected module and the migration path for each retiring API route gate.
  6. Update each module's root URLConf and API URLConf consistently, removing public REST API route gates.
  7. Update all internal reverse(), reverse_lazy(), templates, tests, and downstream OpenWISP callers.
  8. Add regression tests that assert each canonical name, including nested resource names, resolves to the expected path and callback.
  9. Add migration tests for retiring API route gates, including configured False values and deployment-template coverage where applicable.
  10. If aliases are chosen, add equivalent regression tests for legacy names.
  11. Update module documentation and release notes with the complete migration mapping and configuration migration guidance.
  12. Run each affected module's focused URL tests, QA checks, and full suite.

Describe alternatives considered

A dedicated <module>_api namespace with unprefixed URL names would avoid collisions with non-REST views. One module namespace with api_-prefixed names provides the same protection with fewer namespaces and matches the existing module-level URL structure.

Making REST URLConfs independently optional was also considered. OpenWISP relies on REST APIs for internal and cross-module behavior, so omitting an API URLConf creates unsupported configurations that can fail when code reverses a required route. Access must instead be controlled through authentication, authorization, network controls, and rate limits.

Additional context

AGENTS.md Rule

Add this rule to every in-scope Django module's AGENTS.md:

Public REST URLConfs must use the module's project-wide namespace and snake-case api_<resource>_<action> names. Nested resources must include every semantic parent that scopes them: api_<parent>_<resource>_<action>. Do not use rest_ prefixes or include transport-only path segments such as api or v1 in names. Changes to public URL paths or URL pattern names require a documented migration map, updated downstream callers, and compatibility tests or an explicitly announced backward-incompatible release.

Public REST API URLs are mandatory. Do not add settings that conditionally register them. Retiring a public REST API route gate requires a documented configuration migration that does not silently expose routes for deployments that set it to False.

Completion Criteria

  • Every in-scope Django module follows the selected convention.
  • No maintained OpenWISP repository uses an obsolete URL name.
  • No in-scope module conditionally registers public REST URLs through an API enablement setting.
  • Deployment templates and downstream settings no longer use retired REST API route gates.
  • Release notes contain the full migration mapping.
  • Each changed module passes URL regression tests, QA checks, and its full suite.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions