瀏覽代碼

[api] Add endpoint to fetch available apps for example installation (#4093)

Harsh Gupta 7 月之前
父節點
當前提交
6255dc0508

+ 15 - 0
desktop/core/src/desktop/api2.py

@@ -1418,6 +1418,21 @@ def _paginate(request, queryset):
   return __paginate(page, limit, queryset)
 
 
+@api_error_handler
+def available_app_examples(request):
+  """Returns a dictionary of available apps whose examples can be installed by the admin user."""
+
+  if not is_admin(request.user):
+    return JsonResponse({'message': "You must be a Hue admin to access this endpoint."}, status=403)
+
+  supported_app_examples = {'hive', 'impala', 'hbase', 'pig', 'oozie', 'notebook', 'search', 'spark', 'jobsub'}
+
+  # Filter apps based on supported list
+  available_examples = {app.name: app.nice_name for app in appmanager.get_apps(request.user) if app.name in supported_app_examples}
+
+  return JsonResponse({'apps': available_examples})
+
+
 @api_error_handler
 def install_app_examples(request):
   app_name = request.POST.get('app_name')

+ 75 - 1
desktop/core/src/desktop/api2_tests.py

@@ -19,12 +19,20 @@
 import re
 import json
 from builtins import object
+from dataclasses import dataclass
 from unittest.mock import Mock, patch
 
 import pytest
 
 from beeswax.conf import HIVE_SERVER_HOST
-from desktop.api2 import _setup_hive_impala_examples, _setup_notebook_examples, _setup_search_examples, check_config, install_app_examples
+from desktop.api2 import (
+  _setup_hive_impala_examples,
+  _setup_notebook_examples,
+  _setup_search_examples,
+  available_app_examples,
+  check_config,
+  install_app_examples,
+)
 from desktop.conf import ENABLE_GIST_PREVIEW
 from desktop.lib.django_test_util import make_logged_in_client
 from desktop.models import Directory, Document2
@@ -728,6 +736,72 @@ class TestDocumentGist(object):
       f()
 
 
+class TestAvailableAppExamplesAPI:
+
+  # Using custom MockApp instead of Mock to avoid conflicts with Mock's built in 'name' attribute.
+  @dataclass
+  class MockApp:
+    name: str
+    nice_name: str
+
+  def test_available_app_examples_success(self):
+    with patch("desktop.api2.is_admin") as mock_is_admin:
+      with patch("desktop.api2.appmanager.get_apps") as mock_get_apps:
+        request = Mock(method='GET', user=Mock())
+
+        mock_is_admin.return_value = True
+        mock_get_apps.return_value = [
+          self.MockApp(name='hive', nice_name='Hive'),
+          self.MockApp(name='impala', nice_name='Impala'),
+          self.MockApp(name='unsupported_app', nice_name='Unsupported App'),
+        ]
+
+        response = available_app_examples(request)
+
+        assert response.status_code == 200
+        assert json.loads(response.content) == {'apps': {'hive': 'Hive', 'impala': 'Impala'}}
+
+  def test_available_app_examples_non_admin(self):
+    with patch("desktop.api2.is_admin") as mock_is_admin:
+      request = Mock(method='GET', user=Mock())
+      mock_is_admin.return_value = False
+
+      response = available_app_examples(request)
+
+      assert response.status_code == 403
+      assert json.loads(response.content) == {"message": "You must be a Hue admin to access this endpoint."}
+
+  def test_available_app_examples_no_apps(self):
+    with patch("desktop.api2.is_admin") as mock_is_admin:
+      with patch("desktop.api2.appmanager.get_apps") as mock_get_apps:
+        mock_is_admin.return_value = True
+        mock_get_apps.return_value = []
+
+        request = Mock(method='GET', user=Mock())
+
+        response = available_app_examples(request)
+
+        assert response.status_code == 200
+        assert json.loads(response.content) == {"apps": {}}
+
+  def test_available_app_examples_only_unsupported_apps(self):
+    with patch("desktop.api2.is_admin") as mock_is_admin:
+      with patch("desktop.api2.appmanager.get_apps") as mock_get_apps:
+        mock_is_admin.return_value = True
+
+        mock_get_apps.return_value = [
+          self.MockApp("unsupported_app_1", "Unsupported App 1"),
+          self.MockApp("unsupported_app_2", "Unsupported App 2"),
+        ]
+
+        request = Mock(method='GET', user=Mock())
+
+        response = available_app_examples(request)
+
+        assert response.status_code == 200
+        assert json.loads(response.content) == {"apps": {}}
+
+
 class TestInstallAppExampleAPI:
   def test_install_app_examples_missing_app_name(self):
     request = Mock(method='POST', POST={}, user=Mock())

+ 7 - 0
desktop/core/src/desktop/api_public.py

@@ -82,6 +82,13 @@ def install_app_examples(request):
   django_request = get_django_request(request)
   return desktop_api.install_app_examples(django_request)
 
+
+@api_view(["GET"])
+def available_app_examples(request):
+  django_request = get_django_request(request)
+  return desktop_api.available_app_examples(django_request)
+
+
 # Editor
 
 

+ 1 - 0
desktop/core/src/desktop/api_public_urls_v1.py

@@ -34,6 +34,7 @@ urlpatterns += [
   re_path(r'^logs/?$', api_public.get_hue_logs, name='core_get_hue_logs'),
   re_path(r'^logs/download/?$', api_public.download_hue_logs, name='core_download_hue_logs'),
   re_path(r'^install_app_examples/?$', api_public.install_app_examples, name='core_install_app_examples'),
+  re_path(r'^available_app_examples/?$', api_public.available_app_examples, name='core_available_app_examples'),
   re_path(r'^get_config/?$', api_public.get_config),
   re_path(r'^check_config/?$', api_public.check_config, name='core_check_config'),
   re_path(r'^get_namespaces/(?P<interface>[\w\-]+)/?$', api_public.get_context_namespaces),  # To remove