Skip to content

API v3: added support for tags in API #9513

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

Merged
merged 13 commits into from
Nov 1, 2022
15 changes: 14 additions & 1 deletion docs/user/api/v3.rst
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,11 @@ Project create
},
"homepage": "http://template.readthedocs.io/",
"programming_language": "py",
"language": "es"
"language": "es",
"tags": [
"automation",
"sphinx"
]
}

**Example response**:
Expand Down Expand Up @@ -413,6 +417,10 @@ Project update
"language": "ja",
"programming_language": "py",
"homepage": "https://readthedocs.org/",
"tags" : [
"extension",
"mkdocs"
]
"default_version": "v0.27.0",
"default_branch": "develop",
"analytics_code": "UA000000",
Expand All @@ -422,6 +430,11 @@ Project update

}

.. note::

Adding ``tags`` will replace existing tags with the new list,
and if omitted won't change the tags.

:statuscode 204: Updated successfully


Expand Down
30 changes: 17 additions & 13 deletions readthedocs/api/v3/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from rest_flex_fields import FlexFieldsModelSerializer
from rest_flex_fields.serializers import FlexFieldsSerializerMixin
from rest_framework import serializers
from taggit.serializers import TaggitSerializer, TagListSerializerField

from readthedocs.builds.models import Build, Version
from readthedocs.core.utils import slugify
Expand Down Expand Up @@ -457,21 +458,23 @@ def get_translations(self, obj):
return self._absolute_url(path)


class ProjectCreateSerializerBase(FlexFieldsModelSerializer):
class ProjectCreateSerializerBase(TaggitSerializer, FlexFieldsModelSerializer):

"""Serializer used to Import a Project."""

repository = RepositorySerializer(source='*')
homepage = serializers.URLField(source='project_url', required=False)
tags = TagListSerializerField(required=False)

class Meta:
model = Project
fields = (
'name',
'language',
'programming_language',
'repository',
'homepage',
"name",
"language",
"programming_language",
"repository",
"homepage",
"tags",
)

def _validate_remote_repository(self, data):
Expand Down Expand Up @@ -533,7 +536,7 @@ class ProjectCreateSerializer(SettingsOverrideObject):
_default_class = ProjectCreateSerializerBase


class ProjectUpdateSerializerBase(FlexFieldsModelSerializer):
class ProjectUpdateSerializerBase(TaggitSerializer, FlexFieldsModelSerializer):

"""Serializer used to modify a Project once imported."""

Expand All @@ -542,17 +545,18 @@ class ProjectUpdateSerializerBase(FlexFieldsModelSerializer):
source='project_url',
required=False,
)
tags = TagListSerializerField(required=False)

class Meta:
model = Project
fields = (
# Settings
'name',
'repository',
'language',
'programming_language',
'homepage',

"name",
"repository",
"language",
"programming_language",
"homepage",
"tags",
# Advanced Settings -> General Settings
'default_version',
'default_branch',
Expand Down
2 changes: 1 addition & 1 deletion readthedocs/api/v3/tests/responses/projects-detail.json
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,8 @@
"slug": "project",
"subproject_of": null,
"tags": [
"tag",
"project",
"tag",
"test"
],
"translation_of": null,
Expand Down
2 changes: 1 addition & 1 deletion readthedocs/api/v3/tests/responses/projects-list.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@
"versions": "https://readthedocs.org/projects/project/versions/"
},
"tags": [
"tag",
"project",
"tag",
"test"
],
"users": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
},
"slug": "test-project",
"subproject_of": null,
"tags": [],
"tags": ["template tag", "test tag"],
"translation_of": null,
"urls": {
"builds": "https://readthedocs.org/projects/test-project/builds/",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@
"slug": "project",
"subproject_of": null,
"tags": [
"tag",
"project",
"tag",
"test"
],
"translation_of": null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@
"slug": "project",
"subproject_of": null,
"tags": [
"tag",
"project",
"tag",
"test"
],
"translation_of": null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
"repository": { "type": "git", "url": "https://github.com/rtfd/project" },
"slug": "project",
"subproject_of": null,
"tags": ["tag", "project", "test"],
"tags": ["project", "tag", "test"],
"translation_of": null,
"urls": {
"builds": "https://readthedocs.org/projects/project/builds/",
Expand Down
76 changes: 43 additions & 33 deletions readthedocs/api/v3/tests/test_projects.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
import django_dynamic_fixture as fixture
from django.urls import reverse

from readthedocs.projects.models import Project
from readthedocs.oauth.models import RemoteRepository
from readthedocs.projects.models import Project

from .mixins import APIEndpointMixin

Expand Down Expand Up @@ -118,8 +118,9 @@ def test_import_project(self):
'url': 'https://github.com/rtfd/template',
'type': 'git',
},
'homepage': 'http://template.readthedocs.io/',
'programming_language': 'py',
"homepage": "http://template.readthedocs.io/",
"programming_language": "py",
"tags": ["test tag", "template tag"],
}

self.client.credentials(HTTP_AUTHORIZATION=f'Token {self.token.key}')
Expand All @@ -131,12 +132,13 @@ def test_import_project(self):

project = query.first()
self.assertIsNone(project.remote_repository)
self.assertEqual(project.name, 'Test Project')
self.assertEqual(project.slug, 'test-project')
self.assertEqual(project.repo, 'https://github.com/rtfd/template')
self.assertEqual(project.language, 'en')
self.assertEqual(project.programming_language, 'py')
self.assertEqual(project.project_url, 'http://template.readthedocs.io/')
self.assertEqual(project.name, "Test Project")
self.assertEqual(project.slug, "test-project")
self.assertEqual(project.repo, "https://github.com/rtfd/template")
self.assertEqual(project.language, "en")
self.assertEqual(project.programming_language, "py")
self.assertEqual(project.project_url, "http://template.readthedocs.io/")
self.assertEqual(list(project.tags.names()), ["template tag", "test tag"])
self.assertIn(self.me, project.users.all())
self.assertEqual(project.builds.count(), 1)

Expand Down Expand Up @@ -243,15 +245,16 @@ def test_update_project(self):
'url': 'https://bitbucket.com/rtfd/updated-repository',
'type': 'hg',
},
'language': 'es',
'programming_language': 'js',
'homepage': 'https://updated-homepage.org',
'default_version': 'stable',
'default_branch': 'updated-default-branch',
'analytics_code': 'UA-XXXXXX',
'show_version_warning': False,
'single_version': True,
"external_builds_enabled": True
"language": "es",
"programming_language": "js",
"homepage": "https://updated-homepage.org",
"tags": ["updated tag", "test tag"],
"default_version": "stable",
"default_branch": "updated-default-branch",
"analytics_code": "UA-XXXXXX",
"show_version_warning": False,
"single_version": True,
"external_builds_enabled": True,
}

self.client.credentials(HTTP_AUTHORIZATION=f'Token {self.token.key}')
Expand All @@ -267,16 +270,19 @@ def test_update_project(self):
self.assertEqual(response.status_code, 204)

self.project.refresh_from_db()
self.assertEqual(self.project.name, 'Updated name')
self.assertEqual(self.project.slug, 'project')
self.assertEqual(self.project.repo, 'https://bitbucket.com/rtfd/updated-repository')
self.assertEqual(self.project.repo_type, 'hg')
self.assertEqual(self.project.language, 'es')
self.assertEqual(self.project.programming_language, 'js')
self.assertEqual(self.project.project_url, 'https://updated-homepage.org')
self.assertEqual(self.project.default_version, 'stable')
self.assertEqual(self.project.default_branch, 'updated-default-branch')
self.assertEqual(self.project.analytics_code, 'UA-XXXXXX')
self.assertEqual(self.project.name, "Updated name")
self.assertEqual(self.project.slug, "project")
self.assertEqual(
self.project.repo, "https://bitbucket.com/rtfd/updated-repository"
)
self.assertEqual(self.project.repo_type, "hg")
self.assertEqual(self.project.language, "es")
self.assertEqual(self.project.programming_language, "js")
self.assertEqual(self.project.project_url, "https://updated-homepage.org")
self.assertEqual(list(self.project.tags.names()), ["test tag", "updated tag"])
self.assertEqual(self.project.default_version, "stable")
self.assertEqual(self.project.default_branch, "updated-default-branch")
self.assertEqual(self.project.analytics_code, "UA-XXXXXX")
self.assertEqual(self.project.show_version_warning, False)
self.assertEqual(self.project.single_version, True)
self.assertEqual(self.project.external_builds_enabled, True)
Expand All @@ -287,7 +293,8 @@ def test_partial_update_project(self):
'repository': {
'url': 'https://github.com/rtfd/updated-repository',
},
'default_branch': 'updated-default-branch',
"default_branch": "updated-default-branch",
"tags": ["partial tags", "updated"],
}

self.client.credentials(HTTP_AUTHORIZATION=f'Token {self.token.key}')
Expand All @@ -303,10 +310,13 @@ def test_partial_update_project(self):
self.assertEqual(response.status_code, 204)

self.project.refresh_from_db()
self.assertEqual(self.project.name, 'Updated name')
self.assertEqual(self.project.slug, 'project')
self.assertEqual(self.project.repo, 'https://github.com/rtfd/updated-repository')
self.assertNotEqual(self.project.default_version, 'updated-default-branch')
self.assertEqual(self.project.name, "Updated name")
self.assertEqual(self.project.slug, "project")
self.assertEqual(
self.project.repo, "https://github.com/rtfd/updated-repository"
)
self.assertEqual(list(self.project.tags.names()), ["partial tags", "updated"])
self.assertNotEqual(self.project.default_version, "updated-default-branch")

def test_partial_update_others_project(self):
data = {
Expand Down
2 changes: 1 addition & 1 deletion readthedocs/projects/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -442,7 +442,7 @@ class Project(models.Model):
help_text=_('This project has been successfully cloned'),
)

tags = TaggableManager(blank=True)
tags = TaggableManager(blank=True, ordering=["name"])
history = ExtraHistoricalRecords()
objects = ProjectQuerySet.as_manager()

Expand Down