diff --git a/galaxy_ng/app/api/v1/serializers.py b/galaxy_ng/app/api/v1/serializers.py index 0388b97671..893efb3a08 100644 --- a/galaxy_ng/app/api/v1/serializers.py +++ b/galaxy_ng/app/api/v1/serializers.py @@ -313,6 +313,15 @@ def get_summary_fields(self, obj): 'pulp_href': pulp_href } + # FIXME - repository is a bit hacky atm + repository = {} + if obj.full_metadata.get('repository'): + repository = obj.full_metadata.get('repository') + if not repository.get('name'): + repository['name'] = obj.full_metadata.get('github_repo') + if not repository.get('original_name'): + repository['original_name'] = obj.full_metadata.get('github_repo') + return { 'dependencies': dependencies, 'namespace': { @@ -321,10 +330,7 @@ def get_summary_fields(self, obj): 'avatar_url': f'https://github.com/{obj.namespace.name}.png' }, 'provider_namespace': provider_ns, - 'repository': { - 'name': obj.name, - 'original_name': obj.full_metadata.get('github_repo') - }, + 'repository': repository, 'tags': tags, 'versions': versions } @@ -336,6 +342,40 @@ def get_download_count(self, obj): return 0 +class LegacyRoleRepositoryUpdateSerializer(serializers.Serializer): + name = serializers.CharField(required=False, allow_blank=False, max_length=50) + original_name = serializers.CharField(required=False, allow_blank=False, max_length=50) + + def is_valid(self, raise_exception=False): + # Check for any unexpected fields + extra_fields = set(self.initial_data.keys()) - set(self.fields.keys()) + if extra_fields: + self._errors = {field: ["Unexpected field."] for field in extra_fields} + else: + # Continue with the original validation logic + super(serializers.Serializer, self).is_valid(raise_exception=raise_exception) + + return not bool(self._errors) + + +class LegacyRoleUpdateSerializer(serializers.Serializer): + github_user = serializers.CharField(required=False, allow_blank=False, max_length=50) + github_repo = serializers.CharField(required=False, allow_blank=False, max_length=50) + github_branch = serializers.CharField(required=False, allow_blank=False, max_length=50) + repository = LegacyRoleRepositoryUpdateSerializer(required=False) + + def is_valid(self, raise_exception=False): + # Check for any unexpected fields + extra_fields = set(self.initial_data.keys()) - set(self.fields.keys()) + if extra_fields: + self._errors = {field: ["Unexpected field."] for field in extra_fields} + else: + # Continue with the original validation logic + super(serializers.Serializer, self).is_valid(raise_exception=raise_exception) + + return not bool(self._errors) + + class LegacyRoleContentSerializer(serializers.ModelSerializer): readme = serializers.SerializerMethodField() diff --git a/galaxy_ng/app/api/v1/urls.py b/galaxy_ng/app/api/v1/urls.py index 5c010f616f..df11e8b94a 100644 --- a/galaxy_ng/app/api/v1/urls.py +++ b/galaxy_ng/app/api/v1/urls.py @@ -36,7 +36,7 @@ path( 'roles//', - LegacyRolesViewSet.as_view({"get": "retrieve", "delete": "destroy"}), + LegacyRolesViewSet.as_view({"get": "retrieve", "delete": "destroy", "put": "update"}), name='legacy_role-detail' ), diff --git a/galaxy_ng/app/api/v1/viewsets/roles.py b/galaxy_ng/app/api/v1/viewsets/roles.py index 0992f593ee..ebf227a9e1 100644 --- a/galaxy_ng/app/api/v1/viewsets/roles.py +++ b/galaxy_ng/app/api/v1/viewsets/roles.py @@ -8,6 +8,7 @@ from rest_framework import mixins from rest_framework import viewsets +from rest_framework import status from rest_framework.response import Response from rest_framework.settings import perform_import from rest_framework.pagination import PageNumberPagination @@ -24,6 +25,7 @@ from galaxy_ng.app.api.v1.serializers import ( LegacyImportSerializer, LegacyRoleSerializer, + LegacyRoleUpdateSerializer, LegacyRoleContentSerializer, LegacyRoleVersionsSerializer, ) @@ -94,6 +96,55 @@ def list(self, request): return super().list(request) + def update(self, request, pk=None): + role = self.get_object() + serializer = LegacyRoleUpdateSerializer(role, data=request.data, partial=True) + if not serializer.is_valid(): + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + changed = {} + for key, newval in serializer.validated_data.items(): + + # repositories are special ... + if key == 'repository': + if not role.full_metadata.get('repository'): + changed['repository'] = {} + role.full_metadata['repository'] = {} + for subkey, subval in newval.items(): + if role.full_metadata.get(key, {}).get(subkey) != subval: + if key not in changed: + changed[key] = {} + role.full_metadata[key][subkey] = subval + changed[key][subkey] = subval + continue + + # github_repo should set repository.name? + if key == 'github_repo': + if not role.full_metadata.get('repository'): + changed['repository'] = {} + role.full_metadata['repository'] = {} + old_name = role.full_metadata['repository'].get('name') + role.full_metadata['repository']['name'] = newval + role.full_metadata['repository']['original_name'] = old_name + + if role.full_metadata.get(key) != newval: + role.full_metadata[key] = newval + changed[key] = newval + + # TODO - get rid of github_reference? + if key == 'github_branch': + key = 'github_reference' + if role.full_metadata.get(key) != newval: + role.full_metadata[key] = newval + changed[key] = newval + + # only save if changes made + if changed: + role.save() + return Response(changed, status=200) + + return Response(changed, status=204) + def destroy(self, request, pk=None): """Delete a single role.""" role = LegacyRole.objects.filter(id=pk).first() diff --git a/galaxy_ng/tests/integration/community/test_role_edits.py b/galaxy_ng/tests/integration/community/test_role_edits.py new file mode 100644 index 0000000000..cfcfb76245 --- /dev/null +++ b/galaxy_ng/tests/integration/community/test_role_edits.py @@ -0,0 +1,133 @@ +import pytest + +# from ..utils import ansible_galaxy, get_client, SocialGithubClient +from ..utils import ansible_galaxy, get_client +from ..utils.legacy import clean_all_roles, cleanup_social_user + + +@pytest.mark.deployment_community +def test_community_legacy_role_edit(ansible_config): + + # namespace_name = painless + # github_user = painless-software + # github_repository = ansible-role-software + # role_name = software + # install fqn = painless.software + # github_branch = main + + admin_config = ansible_config("admin") + admin_client = get_client( + config=admin_config, + request_token=False, + require_auth=True + ) + + namespace_v3name = 'jctannertest' + namespace_v1name = 'jctannerTEST' + github_user = 'jctannerTEST' + github_repo = 'role1' + role_name = 'role1' + branch = 'master' + + # cleanup + clean_all_roles(ansible_config) + cleanup_social_user(namespace_v3name, ansible_config) + cleanup_social_user(namespace_v1name, ansible_config) + + # creat the v3 namespace + v3_ns = admin_client( + '/api/_ui/v1/namespaces/', method='POST', args={'name': namespace_v3name, 'groups': []} + ) + assert v3_ns['name'] == namespace_v3name, v3_ns + + # make the legacy ns + v1_ns = admin_client('/api/v1/namespaces/', method='POST', args={'name': namespace_v1name}) + assert v1_ns['name'] == namespace_v1name, v1_ns + + # bind the v3 namespace to the v1 namespace + v3_bind = { + 'id': v3_ns['id'] + } + admin_client(f"/api/v1/namespaces/{v1_ns['id']}/providers/", method='POST', args=v3_bind) + + # import jctanerTEST role1 + pid = ansible_galaxy( + f"role import {github_user} {github_repo} --branch={branch}", + ansible_config=admin_config, + force_token=True, + cleanup=False, + check_retcode=False + ) + assert pid.returncode == 0, pid.stdout.decode('utf-8') + + # find the new role ... + resp = admin_client(f'/api/v1/roles/?owner__username={namespace_v1name}&name={role_name}') + assert resp['count'] == 1 + role = resp['results'][0] + assert role['summary_fields']['namespace']['name'] == namespace_v1name + assert role['summary_fields']['provider_namespace']['name'] == namespace_v3name + assert role['name'] == role_name + assert role['github_user'] == github_user + assert role['github_repo'] == github_repo + assert role['github_branch'] == branch + + role_id = role['id'] + + # change the branch ... + admin_client( + f'/api/v1/roles/{role_id}/', + method='PUT', + args={'github_branch': 'fakebranch'} + ) + newds = admin_client(f'/api/v1/roles/{role_id}/') + assert newds['github_branch'] == 'fakebranch' + + # change the github_user ... + admin_client( + f'/api/v1/roles/{role_id}/', + method='PUT', + args={'github_user': 'fakeuser'} + ) + newds = admin_client(f'/api/v1/roles/{role_id}/') + assert newds['github_user'] == 'fakeuser' + + # change the github_repo ... + admin_client( + f'/api/v1/roles/{role_id}/', + method='PUT', + args={'github_repo': 'fakerepo'} + ) + newds = admin_client(f'/api/v1/roles/{role_id}/') + assert newds['github_repo'] == 'fakerepo' + + # change the repository.name ... + admin_client( + f'/api/v1/roles/{role_id}/', + method='PUT', + args={ + 'repository': { + 'name': 'foorepo' + } + } + ) + newds = admin_client(f'/api/v1/roles/{role_id}/') + assert newds['summary_fields']['repository']['name'] == 'foorepo' + + # change the repository.original_name ... + admin_client( + f'/api/v1/roles/{role_id}/', + method='PUT', + args={ + 'repository': { + 'original_name': 'foorepo_old' + } + } + ) + newds = admin_client(f'/api/v1/roles/{role_id}/') + assert newds['summary_fields']['repository']['original_name'] == 'foorepo_old' + + # cleanup the role ... + try: + admin_client(f'/api/v1/roles/{role_id}/', method='DELETE') + except Exception: + pass