Skip to content

Add Smart Endpoints framework for ND API v1#186

Open
allenrobel wants to merge 21 commits intond42_integrationfrom
nd42_smart_endpoints
Open

Add Smart Endpoints framework for ND API v1#186
allenrobel wants to merge 21 commits intond42_integrationfrom
nd42_smart_endpoints

Conversation

@allenrobel
Copy link
Collaborator

@allenrobel allenrobel commented Mar 3, 2026

Summary

Adds the Smart Endpoints framework — a type-safe, Pydantic-validated approach to constructing ND REST API endpoint paths and query parameters. This PR is one of three companion PRs targeting nd42_integration:

PR Branch Contents Merge order
#185 nd42_rest_send RestSend framework, enums.py, shared test infrastructure 1st
#184 nd42_logging Log class and logging config 2nd
This PR nd42_smart_endpoints Smart Endpoints framework 3rd

Note: Tests depend on enums.py, pydantic_compat.py, and shared test infrastructure from nd42_rest_send (#185), and on log.py from nd42_logging (#184). Tests will not run until all three branches are merged into nd42_integration.


Plugin files added

Base classes (plugins/module_utils/endpoints/)

File Description
base_path.py ApiPath — string enum providing type-safe base API path constants shared across all endpoint versions
endpoint_mixins.py Reusable endpoint mixins (e.g. LoginIdMixin for endpoints requiring a login ID)
query_params.py EndpointQueryParams, LuceneQueryParams, CompositeQueryParams — Pydantic models for structured, validated query string construction

ND API v1 endpoints (plugins/module_utils/endpoints/v1/)

File Description
base_paths_infra.py Base path constants for the ND /api/v1/infra namespace
base_paths_manage.py Base path constants for the ND /api/v1/manage namespace
infra_aaa.py AAA (login/logout) endpoints
infra_clusterhealth.py Cluster health endpoints
infra_login.py Login endpoint (EpInfraLoginPost) for /api/v1/infra/login
manage_switches.py Switch management endpoint with Lucene query support

Unit tests added

File Covers
test_base_path.py ApiPath base class
test_base_paths_infra.py Infra base paths
test_base_paths_manage.py Manage base paths
test_endpoint_mixins.py LoginIdMixin and other mixins
test_endpoints_api_v1_infra_aaa.py AAA endpoints
test_endpoints_api_v1_infra_clusterhealth.py Cluster health endpoints
test_endpoints_api_v1_infra_login.py Login endpoint
test_endpoints_api_v1_manage_switches.py Switch management endpoint
test_query_params.py All query parameter models

Dependencies

Merge order

  1. Merge nd42_rest_send (Add RestSend framework, enums, and shared unit test infrastructure #185) → nd42_integration
  2. Merge nd42_logging (Add Log class and logging config #184) → nd42_integration
  3. Merge this PR → nd42_integration

Test plan

  • After merging all three PRs into nd42_integration: python -m pytest tests/unit/module_utils/endpoints/ passes cleanly
  • tox -e linters passes (black, isort, pylint, mypy)
  • ansible-test sanity --docker passes

🤖 Generated with Claude Code

Base classes:
- plugins/module_utils/ep/base_path.py: ApiPath base class for type-safe
  endpoint path construction
- plugins/module_utils/ep/base_paths_ndfc.py: NDFC base path constants
- plugins/module_utils/ep/endpoint_mixins.py: Reusable endpoint mixins
  (e.g. LoginIdMixin)
- plugins/module_utils/ep/query_params.py: EndpointQueryParams,
  LuceneQueryParams, CompositeQueryParams for structured query string building

ND API v1 endpoints:
- plugins/module_utils/ep/v1/base_paths_infra.py: Infra API base paths
- plugins/module_utils/ep/v1/base_paths_manage.py: Manage API base paths
- plugins/module_utils/ep/v1/ep_infra_aaa.py: AAA (login) endpoint
- plugins/module_utils/ep/v1/ep_infra_clusterhealth.py: Cluster health endpoint
- plugins/module_utils/ep/v1/ep_manage_switches.py: Switch management endpoint

Design documentation:
- plugins/module_utils/ep/ANALYSIS_COMPLEXITY.md
- plugins/module_utils/ep/ANALYSIS_ENDPOINT_DESIGN.md
- plugins/module_utils/ep/ANALYSIS_TYPE_SAFETY.md

Unit tests:
- tests/unit/module_utils/ep/test_base_path.py
- tests/unit/module_utils/ep/test_base_paths_infra.py
- tests/unit/module_utils/ep/test_base_paths_manage.py
- tests/unit/module_utils/ep/test_endpoint_mixins.py
- tests/unit/module_utils/ep/test_ep_api_v1_infra_aaa.py
- tests/unit/module_utils/ep/test_ep_api_v1_infra_clusterhealth.py
- tests/unit/module_utils/ep/test_ep_api_v1_manage_switches.py
- tests/unit/module_utils/ep/test_query_params.py

Note: tests depend on enums.py, pydantic_compat.py, and shared test
infrastructure (common_utils, etc.) from nd42_rest_send, and on log.py
from nd42_logging. Tests will not run until all branches are merged
into nd42_integration.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@@ -0,0 +1,88 @@
# -*- coding: utf-8 -*-

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would like to discuss this in the standup today

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this is a holdover from Python v2 and is no longer needed in v3. If non-ASCII character accidentally made its way into a Python v2 file, you'd get a SyntaxError exception without it (though, I'd argue that would be a good thing).

Anyway, I'll remove these from all the files in my PRs.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed from all files in this PR. Need to do the same for the other PRs.

allenrobel and others added 10 commits March 3, 2026 07:30
1. Remove /mso, /appcenter/*, /login paths

TODO: Need to add /api/v1/infra/login under infra endpoints.
Removed the following from all files:

# -*- coding: utf-8 -*-
- plugins/module_utils/ep/ → plugins/module_utils/endpoints/
- v1/ep_infra_aaa.py → v1/infra_aaa.py
- v1/ep_infra_clusterhealth.py → v1/infra_clusterhealth.py
- v1/ep_manage_switches.py → v1/manage_switches.py
- tests/unit/module_utils/ep/ → tests/unit/module_utils/endpoints/
- test_ep_api_v1_*.py → test_endpoints_api_v1_*.py
- Update all internal imports to reflect new paths

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces the test_ep_ prefix in all test function names within the
three ep-specific test files to match the renamed file and directory.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Ansible sanity tests use a regex to verify the __metaclass__ = type
declaration and do not expect an inline comment. Move the pylint
disable/enable directives to separate lines.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@@ -0,0 +1,89 @@
# Copyright: (c) 2026, Allen Robel (@arobel) <arobel@cisco.com>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need endpoint_ in this name can be mixins.py

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Akshay has mixins and others will have mixins so should we put them all in onen file called mixins.py or only put "common" things in mixins.py and use a module_name directory or some other way to separate mixin files?

Copy link
Collaborator Author

@allenrobel allenrobel Mar 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed to mixins.py with commit: 26cbd52

Endpoint mixins are associated with endpoints rather than than modules. So, I'd lean toward keeping them in one file (or, if we use multiple files, name them e.g. infra_mixins.py, manage_mixins.py, etc). I had Claude do an analysis of the query parameter usage in infra and manage where he read the schema and reported on the percentage sharing of query params across all endpoints, max query params used by endpoints, etc.

Claude found zero overlap between infra and manage, so these should probably be separate files:

https://github.com/CiscoDevNet/ansible-nd/blob/rest_send_integration/ANALYSIS_ENDPOINT_PARAMETERS.md

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another conclusion Claude made is that there are several mixins defined in mixins.py that are not used in either infra or manage schemas. These are probably legacy mixins from NDFC, so I'll open an issue to remove them. I'll double check these though...

"Key Finding: Only 3 of 11 defined mixins are actually used (LoginIdMixin, FabricNameMixin, ForceShowRunMixin). The 8 unused mixins (ClusterNameMixin, HealthCategoryMixin, InclAllMsdSwitchesMixin, LinkUuidMixin, NetworkNameMixin, NodeNameMixin, SwitchSerialNumberMixin, VrfNameMixin) have no corresponding parameters in either schema."

@@ -0,0 +1,178 @@
# Copyright: (c) 2026, Allen Robel (@arobel) <arobel@cisco.com>

# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this file


## Current Stable API Version

v1 (ND 3.0+, NDFC 12+)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update to be ND 4.2+, Remove NDFC reference

allenrobel and others added 5 commits March 4, 2026 08:50
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
pydantic_compat was moved into module_utils/commmon in nd42_rest_send branch.  nd42_smart_endpoints and nd42_rest_send will be merged into a common integration branch shortly, so am updating at least some of the imports that are known to have changed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Forgot to add this in the earlier commit.
allenrobel and others added 5 commits March 5, 2026 09:55
typing.Final was added in Python 3.8. Since all affected files already
use from __future__ import annotations (PEP 563), Final is only needed
during static type checking and never evaluated at runtime. Guarding the
import with TYPE_CHECKING avoids the ImportError on Python 3.7 with no
dependency on typing_extensions.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adding for Ansible sanity tests.
Literal was added in Python 3.8. ansible-test sanity runs against
Python 3.7, so importing it directly causes ImportError. Since all
three files already have `from __future__ import annotations`, all
annotations are lazy strings at runtime and the TYPE_CHECKING guard
is safe.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Ansible-test sanity requires all __init__.py files under plugins/ to be
completely empty. No imports are broken since all consumers already
import directly from submodules.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants