See #192: replaced old stats endpoint with nodeinfo
This commit is contained in:
		
							parent
							
								
									e31bed050e
								
							
						
					
					
						commit
						b4ad7a4a71
					
				|  | @ -68,3 +68,31 @@ class RavenEnabled(types.BooleanPreference): | |||
|         'Wether error reporting to a Sentry instance using raven is enabled' | ||||
|         ' for front-end errors' | ||||
|     ) | ||||
| 
 | ||||
| 
 | ||||
| @global_preferences_registry.register | ||||
| class InstanceNodeinfoEnabled(types.BooleanPreference): | ||||
|     show_in_api = False | ||||
|     section = instance | ||||
|     name = 'nodeinfo_enabled' | ||||
|     default = True | ||||
|     verbose_name = 'Enable nodeinfo endpoint' | ||||
|     help_text = ( | ||||
|         'This endpoint is needed for your about page to work.' | ||||
|         'It\'s also helpful for the various monitoring ' | ||||
|         'tools that map and analyzize the fediverse, ' | ||||
|         'but you can disable it completely if needed.' | ||||
|     ) | ||||
| 
 | ||||
| 
 | ||||
| @global_preferences_registry.register | ||||
| class InstanceNodeinfoStatsEnabled(types.BooleanPreference): | ||||
|     show_in_api = False | ||||
|     section = instance | ||||
|     name = 'nodeinfo_stats_enabled' | ||||
|     default = True | ||||
|     verbose_name = 'Enable usage and library stats in nodeinfo endpoint' | ||||
|     help_text = ( | ||||
|         'Disable this f you don\'t want to share usage and library statistics' | ||||
|         'in the nodeinfo endpoint but don\'t want to disable it completely.' | ||||
|     ) | ||||
|  |  | |||
|  | @ -0,0 +1,74 @@ | |||
| import memoize.djangocache | ||||
| 
 | ||||
| import funkwhale_api | ||||
| from funkwhale_api.common import preferences | ||||
| 
 | ||||
| from . import stats | ||||
| 
 | ||||
| 
 | ||||
| store = memoize.djangocache.Cache('default') | ||||
| memo = memoize.Memoizer(store, namespace='instance:stats') | ||||
| 
 | ||||
| 
 | ||||
| def get(): | ||||
|     share_stats = preferences.get('instance__nodeinfo_stats_enabled') | ||||
|     data = { | ||||
|         'version': '2.0', | ||||
|         'software': { | ||||
|             'name': 'funkwhale', | ||||
|             'version': funkwhale_api.__version__ | ||||
|         }, | ||||
|         'protocols': ['activitypub'], | ||||
|         'services': { | ||||
|             'inbound': [], | ||||
|             'outbound': [] | ||||
|         }, | ||||
|         'openRegistrations': preferences.get('users__registration_enabled'), | ||||
|         'usage': { | ||||
|             'users': { | ||||
|                 'total': 0, | ||||
|             }, | ||||
|             'localPosts': 0, | ||||
|             'localComments': 0, | ||||
|         }, | ||||
|         'metadata': { | ||||
|             'shortDescription': preferences.get('instance__short_description'), | ||||
|             'longDescription': preferences.get('instance__long_description'), | ||||
|             'name': preferences.get('instance__name'), | ||||
|             'library': { | ||||
|                 'federationEnabled': preferences.get('federation__enabled'), | ||||
|                 'federationNeedsApproval': preferences.get('federation__music_needs_approval'), | ||||
|             }, | ||||
|         } | ||||
|     } | ||||
|     if share_stats: | ||||
|         getter = memo( | ||||
|             lambda: stats.get(), | ||||
|             max_age=600 | ||||
|         ) | ||||
|         statistics = getter() | ||||
|         data['usage']['users']['total'] = statistics['users'] | ||||
|         data['metadata']['library']['tracks'] = { | ||||
|             'total': statistics['tracks'], | ||||
|         } | ||||
|         data['metadata']['library']['artists'] = { | ||||
|             'total': statistics['artists'], | ||||
|         } | ||||
|         data['metadata']['library']['albums'] = { | ||||
|             'total': statistics['albums'], | ||||
|         } | ||||
|         data['metadata']['library']['music'] = { | ||||
|             'hours': statistics['music_duration'] | ||||
|         } | ||||
| 
 | ||||
|         data['metadata']['usage'] = { | ||||
|             'favorites': { | ||||
|                 'tracks': { | ||||
|                     'total': statistics['track_favorites'], | ||||
|                 } | ||||
|             }, | ||||
|             'listenings': { | ||||
|                 'total': statistics['listenings'] | ||||
|             } | ||||
|         } | ||||
|     return data | ||||
|  | @ -1,11 +1,9 @@ | |||
| from django.conf.urls import url | ||||
| from django.views.decorators.cache import cache_page | ||||
| 
 | ||||
| from . import views | ||||
| 
 | ||||
| 
 | ||||
| urlpatterns = [ | ||||
|     url(r'^nodeinfo/$', views.NodeInfo.as_view(), name='nodeinfo'), | ||||
|     url(r'^settings/$', views.InstanceSettings.as_view(), name='settings'), | ||||
|     url(r'^stats/$', | ||||
|         cache_page(60 * 5)(views.InstanceStats.as_view()), name='stats'), | ||||
| ] | ||||
|  |  | |||
|  | @ -4,6 +4,9 @@ from rest_framework.response import Response | |||
| from dynamic_preferences.api import serializers | ||||
| from dynamic_preferences.registries import global_preferences_registry | ||||
| 
 | ||||
| from funkwhale_api.common import preferences | ||||
| 
 | ||||
| from . import nodeinfo | ||||
| from . import stats | ||||
| 
 | ||||
| 
 | ||||
|  | @ -27,10 +30,12 @@ class InstanceSettings(views.APIView): | |||
|         return Response(data, status=200) | ||||
| 
 | ||||
| 
 | ||||
| class InstanceStats(views.APIView): | ||||
| class NodeInfo(views.APIView): | ||||
|     permission_classes = [] | ||||
|     authentication_classes = [] | ||||
| 
 | ||||
|     def get(self, request, *args, **kwargs): | ||||
|         data = stats.get() | ||||
|         if not preferences.get('instance__nodeinfo_enabled'): | ||||
|             return Response(status=404) | ||||
|         data = nodeinfo.get() | ||||
|         return Response(data, status=200) | ||||
|  |  | |||
|  | @ -0,0 +1,107 @@ | |||
| from django.urls import reverse | ||||
| 
 | ||||
| import funkwhale_api | ||||
| 
 | ||||
| from funkwhale_api.instance import nodeinfo | ||||
| 
 | ||||
| 
 | ||||
| def test_nodeinfo_dump(preferences, mocker): | ||||
|     preferences['instance__nodeinfo_stats_enabled'] = True | ||||
|     stats = { | ||||
|         'users': 1, | ||||
|         'tracks': 2, | ||||
|         'albums': 3, | ||||
|         'artists': 4, | ||||
|         'track_favorites': 5, | ||||
|         'music_duration': 6, | ||||
|         'listenings': 7, | ||||
|     } | ||||
|     mocker.patch('funkwhale_api.instance.stats.get', return_value=stats) | ||||
| 
 | ||||
|     expected = { | ||||
|         'version': '2.0', | ||||
|         'software': { | ||||
|             'name': 'funkwhale', | ||||
|             'version': funkwhale_api.__version__ | ||||
|         }, | ||||
|         'protocols': ['activitypub'], | ||||
|         'services': { | ||||
|             'inbound': [], | ||||
|             'outbound': [] | ||||
|         }, | ||||
|         'openRegistrations': preferences['users__registration_enabled'], | ||||
|         'usage': { | ||||
|             'users': { | ||||
|                 'total': stats['users'], | ||||
|             }, | ||||
|             'localPosts': 0, | ||||
|             'localComments': 0, | ||||
|         }, | ||||
|         'metadata': { | ||||
|             'shortDescription': preferences['instance__short_description'], | ||||
|             'longDescription': preferences['instance__long_description'], | ||||
|             'name': preferences['instance__name'], | ||||
|             'library': { | ||||
|                 'federationEnabled': preferences['federation__enabled'], | ||||
|                 'federationNeedsApproval': preferences['federation__music_needs_approval'], | ||||
|                 'tracks': { | ||||
|                     'total': stats['tracks'], | ||||
|                 }, | ||||
|                 'artists': { | ||||
|                     'total': stats['artists'], | ||||
|                 }, | ||||
|                 'albums': { | ||||
|                     'total': stats['albums'], | ||||
|                 }, | ||||
|                 'music': { | ||||
|                     'hours': stats['music_duration'] | ||||
|                 }, | ||||
|             }, | ||||
|             'usage': { | ||||
|                 'favorites': { | ||||
|                     'tracks': { | ||||
|                         'total': stats['track_favorites'], | ||||
|                     } | ||||
|                 }, | ||||
|                 'listenings': { | ||||
|                     'total': stats['listenings'] | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     assert nodeinfo.get() == expected | ||||
| 
 | ||||
| 
 | ||||
| def test_nodeinfo_dump_stats_disabled(preferences, mocker): | ||||
|     preferences['instance__nodeinfo_stats_enabled'] = False | ||||
| 
 | ||||
|     expected = { | ||||
|         'version': '2.0', | ||||
|         'software': { | ||||
|             'name': 'funkwhale', | ||||
|             'version': funkwhale_api.__version__ | ||||
|         }, | ||||
|         'protocols': ['activitypub'], | ||||
|         'services': { | ||||
|             'inbound': [], | ||||
|             'outbound': [] | ||||
|         }, | ||||
|         'openRegistrations': preferences['users__registration_enabled'], | ||||
|         'usage': { | ||||
|             'users': { | ||||
|                 'total': 0, | ||||
|             }, | ||||
|             'localPosts': 0, | ||||
|             'localComments': 0, | ||||
|         }, | ||||
|         'metadata': { | ||||
|             'shortDescription': preferences['instance__short_description'], | ||||
|             'longDescription': preferences['instance__long_description'], | ||||
|             'name': preferences['instance__name'], | ||||
|             'library': { | ||||
|                 'federationEnabled': preferences['federation__enabled'], | ||||
|                 'federationNeedsApproval': preferences['federation__music_needs_approval'], | ||||
|             }, | ||||
|         } | ||||
|     } | ||||
|     assert nodeinfo.get() == expected | ||||
|  | @ -3,16 +3,6 @@ from django.urls import reverse | |||
| from funkwhale_api.instance import stats | ||||
| 
 | ||||
| 
 | ||||
| def test_can_get_stats_via_api(db, api_client, mocker): | ||||
|     stats = { | ||||
|         'foo': 'bar' | ||||
|     } | ||||
|     mocker.patch('funkwhale_api.instance.stats.get', return_value=stats) | ||||
|     url = reverse('api:v1:instance:stats') | ||||
|     response = api_client.get(url) | ||||
|     assert response.data == stats | ||||
| 
 | ||||
| 
 | ||||
| def test_get_users(mocker): | ||||
|     mocker.patch( | ||||
|         'funkwhale_api.users.models.User.objects.count', return_value=42) | ||||
|  |  | |||
|  | @ -0,0 +1,22 @@ | |||
| from django.urls import reverse | ||||
| 
 | ||||
| 
 | ||||
| def test_nodeinfo_endpoint(db, api_client, mocker): | ||||
|     payload = { | ||||
|         'test': 'test' | ||||
|     } | ||||
|     mocked_nodeinfo = mocker.patch( | ||||
|         'funkwhale_api.instance.nodeinfo.get', return_value=payload) | ||||
|     url = reverse('api:v1:instance:nodeinfo') | ||||
|     response = api_client.get(url) | ||||
| 
 | ||||
|     assert response.status_code == 200 | ||||
|     assert response.data == payload | ||||
| 
 | ||||
| 
 | ||||
| def test_nodeinfo_endpoint_disabled(db, api_client, preferences): | ||||
|     preferences['instance__nodeinfo_enabled'] = False | ||||
|     url = reverse('api:v1:instance:nodeinfo') | ||||
|     response = api_client.get(url) | ||||
| 
 | ||||
|     assert response.status_code == 404 | ||||
		Loading…
	
		Reference in New Issue
	
	 Eliot Berriot
						Eliot Berriot