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

Add setting for configuring optional URL prefix for /api #14939

Merged
merged 2 commits into from
Mar 19, 2024

Conversation

TheRealHaoLiu
Copy link
Member

SUMMARY

Add OPTIONAL_API_URLPATTERN_PREFIX setting

examples:

  • if set to '' (empty string) API pattern will be /api
  • if set to 'controller' API pattern will be /api AND /api/controller
ISSUE TYPE
  • New or Enhanced Feature
COMPONENT NAME
  • API
AWX VERSION
awx: 23.9.1.dev4+gec1a556ba0.d20240228
ADDITIONAL INFORMATION

@TheRealHaoLiu TheRealHaoLiu changed the title Add setting for url prefix Add setting for configuring optional URL prefix for /api Feb 28, 2024
@dmzoneill dmzoneill marked this pull request as draft February 28, 2024 22:04
@TheRealHaoLiu TheRealHaoLiu marked this pull request as ready for review February 28, 2024 22:20
@dmzoneill
Copy link
Member

dmzoneill commented Feb 28, 2024

@TheRealHaoLiu what is the deal with the swagger tests all failing? Looking at your changes and the errors in swagger, it looks to me like something is off with the URL patterns you have changed and the associated tests?
Could you provide clarification?

@dmzoneill dmzoneill marked this pull request as draft February 28, 2024 22:59
@dmzoneill dmzoneill marked this pull request as ready for review February 28, 2024 23:00
@dmzoneill dmzoneill marked this pull request as draft February 29, 2024 00:19
@dmzoneill dmzoneill marked this pull request as ready for review February 29, 2024 12:43
@dmzoneill
Copy link
Member

mmm, it seems some new issues have been uncovered

@TheRealHaoLiu
Copy link
Member Author

fixed the swagger sanity test failure

@TheRealHaoLiu TheRealHaoLiu force-pushed the optional_api_url_prefix branch 2 times, most recently from 90f2bbe to 8c7c0d3 Compare February 29, 2024 20:24
@dmzoneill dmzoneill marked this pull request as draft March 6, 2024 00:22
@TheRealHaoLiu TheRealHaoLiu marked this pull request as ready for review March 7, 2024 16:57
re_path(r'^login/$', LoggedLoginView.as_view(template_name='rest_framework/login.html', extra_context={'inside_login_context': True}), name='login'),
re_path(r'^logout/$', LoggedLogoutView.as_view(next_page='/api/', redirect_field_name='next'), name='logout'),
re_path(r'^o/', include(oauth2_root_urls)),
re_path(r'^(' + settings.OPTIONAL_API_URLPATTERN_PREFIX + '/)?$', ApiRootView.as_view(), name='api_root_view'),
Copy link
Contributor

Choose a reason for hiding this comment

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

There is a better way.

@chrismeyersfsu
Copy link
Member

The Djangoy way to solve this would be via ROOT_URLCONF + middleware that would dynamically set request.urlconf based on the incoming url path.

But there are complications. Our url building and registration happens on import. So what would we dynamically set urlconf to? Let's say you somehow had a urlconf_base and urlconf_with_extra_path where urlconf_base is what ROOT_URLCONF is and urlconf_with_extra_path is all of the urls in ROOT_URLCONF BUT with the api/<extra_path_here>/... modified. But where and how? We could hard-code it in the middelware and hard-code the fact that we want to only operate on api/<extra_path_here> urls but that isn't any better than this hack. So let's say we sprinkle in something to the url that we can inspect in the middleware.

Let's say this below.

 re_path(
        r'^(' + settings.OPTIONAL_API_URLPATTERN_PREFIX + '/)?logout/$', LoggedLogoutView.as_view(next_page='/api/', redirect_field_name='next'), name='logout'
    )

Becomes this:

 re_path(
        r'^logout/$', LoggedLogoutView.as_view(next_page='/api/', redirect_field_name='next'), name='logout'
    options_prepend_path='awx/')

Now the middleware can find options_prepend_path and use that version.

But is this any better than this hack? It does become more testable. I tried testing this hack version and it sucks to test because the urls are "built" and registered on import and mocking/changing things on import sucks.

I'd say this version is more discoverable. I'd say a middleware version is more hidden BUT I think other components could re-use this code if it were middleware.

Ultimately, it's a judgement call as to if it's better to do this the Django way or via this hack. I'm of the opinion that this hack is OK. I'd love someone to do it "proper" so that it is more testable, but I'm ok with this version.

@jbradberry
Copy link
Contributor

My preferred idiomatic Django way would be for an upstream urls.py file to use this setting as its prefix for importing the awx/main urls.

@@ -24,6 +24,10 @@ def drf_reverse(viewname, args=None, kwargs=None, request=None, format=None, **e
else:
url = _reverse(viewname, args, kwargs, request, format, **extra)

if settings.OPTIONAL_API_URLPATTERN_PREFIX and request:
if request._request.path.startswith(f"/api/{settings.OPTIONAL_API_URLPATTERN_PREFIX}/v2/"):
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm kind of not sure what is intended here, but this almost certainly does not do whatever that is. The condition virtually guarantees that the replace will be a no-op.

assert set(list(sorted(paths['/api/v2/credentials/{id}/'].keys()))) == set(['delete', 'get', 'patch', 'put', 'parameters'])
assert set(list(paths['/api/v2/settings/'].keys())) == set(['get', 'parameters'])
assert set(list(paths['/api/v2/settings/{category_slug}/'].keys())) == set(['get', 'put', 'patch', 'delete', 'parameters'])
assert set(list(paths['/api/{var}'].keys())) == set(['get', 'parameters'])
Copy link
Contributor

Choose a reason for hiding this comment

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

What is this doing? {var} looks like string templating, but I see no f-string or .format() in the changeset here.

Add OPTIONAL_API_URLPATTERN_PREFIX setting

examples:
- if set to `''` (empty string) API pattern will be `/api`
- if set to 'controller' API pattern will be `/api` AND `/api/controller`
@TheRealHaoLiu
Copy link
Member Author

Addressed @jbradberry's review in a collab session

@@ -24,6 +24,10 @@ def drf_reverse(viewname, args=None, kwargs=None, request=None, format=None, **e
else:
url = _reverse(viewname, args, kwargs, request, format, **extra)

if settings.OPTIONAL_API_URLPATTERN_PREFIX and request:
if request._request.path.startswith(f"/api/{settings.OPTIONAL_API_URLPATTERN_PREFIX}"):
Copy link
Contributor

Choose a reason for hiding this comment

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

Is request._request.path really necessary, or does request.path work?

Copy link
Member Author

Choose a reason for hiding this comment

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

yah ur right request.path works

Copy link
Member Author

Choose a reason for hiding this comment

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

updated

@TheRealHaoLiu TheRealHaoLiu enabled auto-merge (squash) March 19, 2024 15:43
@TheRealHaoLiu TheRealHaoLiu merged commit ab593bd into ansible:devel Mar 19, 2024
19 of 20 checks passed
@TheRealHaoLiu TheRealHaoLiu deleted the optional_api_url_prefix branch March 19, 2024 19:08
djyasin pushed a commit to djyasin/awx that referenced this pull request Sep 16, 2024
* Add setting for configuring optional URL prefix for /api

Add OPTIONAL_API_URLPATTERN_PREFIX setting

examples:
- if set to `''` (empty string) API pattern will be `/api`
- if set to 'controller' API pattern will be `/api` AND `/api/controller`
djyasin pushed a commit to djyasin/awx that referenced this pull request Nov 11, 2024
* Add setting for configuring optional URL prefix for /api

Add OPTIONAL_API_URLPATTERN_PREFIX setting

examples:
- if set to `''` (empty string) API pattern will be `/api`
- if set to 'controller' API pattern will be `/api` AND `/api/controller`
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants