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

[WIP] Add initial cross-repo search endpoint. #1305

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions functest_requirements.txt
Expand Up @@ -2,3 +2,4 @@ pulp-smash @ git+https://github.com/pulp/pulp-smash.git
orionutils
pytest
dynaconf
epdb
50 changes: 50 additions & 0 deletions pulp_ansible/app/galaxy/v3/serializers.py
Expand Up @@ -129,6 +129,51 @@ def get_href(self, obj) -> str:
)


class CollectionVersionSearchListSerializer(CollectionVersionListSerializer):

href = serializers.SerializerMethodField()
distributions = serializers.SerializerMethodField()
is_deprecated = serializers.SerializerMethodField()
fqn = serializers.SerializerMethodField()
tags = serializers.SerializerMethodField()

class Meta:
fields = (
"fqn",
"namespace",
"name",
"version",
"is_highest",
"is_deprecated",
"href",
"created_at",
"updated_at",
"requires_ansible",
"dependencies",
"distributions",
"tags",
)
model = models.CollectionVersion

def get_fqn(self, obj):
return obj.fqn

def get_href(self, obj) -> str:
return ""

def get_distributions(self, obj):
dnames = [x for x in dir(obj) if x.startswith("in_distro_")]
dnames = [x for x in dnames if getattr(obj, x) is True]
dnames = [x.replace("in_distro_", "") for x in dnames]
return dnames

def get_is_deprecated(self, obj):
return obj.is_deprecated

def get_tags(self, obj):
return [x.name for x in obj.tags.all()]


class ArtifactRefSerializer(serializers.Serializer):
"""A serializer for an Artifact reference."""

Expand Down Expand Up @@ -333,3 +378,8 @@ class ClientConfigurationSerializer(serializers.Serializer):
"""Configuration settings for the ansible-galaxy client."""

default_distribution_path = serializers.CharField(allow_null=True)


class CollectionVersionSearchResultsSerializer(serializers.Serializer):

results = serializers.JSONField()
2 changes: 1 addition & 1 deletion pulp_ansible/app/galaxy/v3/views.py
@@ -1,10 +1,10 @@
from django.db.models import Q, F
from datetime import datetime
from gettext import gettext as _
import semantic_version

from django.contrib.postgres.aggregates import ArrayAgg
from django.db import DatabaseError
from django.db.models import F, Q
from django.db.models.expressions import Window
from django.db.models.functions.window import FirstValue
from django.http import StreamingHttpResponse, HttpResponseNotFound
Expand Down
118 changes: 118 additions & 0 deletions pulp_ansible/app/galaxy/v3/viewsets.py
@@ -0,0 +1,118 @@
from django.db.models import Value, F, CharField
from django.db.models import When, Case
from rest_framework.pagination import PageNumberPagination
from django_filters.rest_framework import DjangoFilterBackend
from django.db.models.functions import Concat
from django_filters import filters
from rest_framework import viewsets

from pulp_ansible.app.galaxy.v3.serializers import (
# CollectionSerializer,
# CollectionVersionSerializer,
# CollectionVersionDocsSerializer,
CollectionVersionSearchListSerializer,
# RepoMetadataSerializer,
# CollectionVersionSearchResultsSerializer,
)
from pulp_ansible.app.models import (
AnsibleCollectionDeprecated,
AnsibleDistribution,
CollectionVersion,
)

from pulp_ansible.app.viewsets import (
CollectionVersionFilter,
)


class CollectionVersionSearchViewSetPagination(PageNumberPagination):
page_size = 100
page_size_query_param = "page_size"
max_page_size = 1000


class CollectionVersionSearchFilter(CollectionVersionFilter):

distributions = filters.CharFilter(
field_name="distributions",
method="filter_by_distribution",
)

repository = filters.CharFilter(
field_name="distributions",
method="filter_by_distribution",
)

repository_name = filters.CharFilter(
field_name="distributions",
method="filter_by_distribution",
)

dependency = filters.CharFilter(field_name="dependencies", method="filter_by_dependency")

deprecated = filters.CharFilter(field_name="deprecated", method="filter_by_deprecated")

def filter_by_distribution(self, qs, name, value):
for distro_name in value.split(","):
kwargs = {f"in_distro_{distro_name}": True}
qs = qs.filter(**kwargs)
return qs

def filter_by_dependency(self, qs, name, value):
"""Return a list of collections that depend on a given collection name."""
kwargs = {f"dependencies__{value}__isnull": False}
qs = qs.filter(**kwargs)
return qs

def filter_by_deprecated(self, qs, name, value):
bool_value = False
if value in [True, "True", "true", "t", 1, "1"]:
bool_value = True
qs = qs.filter(is_deprecated=bool_value)
return qs


class CollectionVersionSearchViewSet(viewsets.ModelViewSet):

queryset = CollectionVersion.objects.all()
serializer_class = CollectionVersionSearchListSerializer
pagination_class = CollectionVersionSearchViewSetPagination
filter_backends = (DjangoFilterBackend,)
filterset_class = CollectionVersionSearchFilter

def get_queryset(self):
qs = CollectionVersion.objects.all()

# add a field for the namespace.name ...
qs = qs.annotate(
fqn=Concat(F("namespace"), Value("."), F("name"), output_field=CharField())
)

# make a queryable list of deprecated collections
deprecation_qs = AnsibleCollectionDeprecated.objects.all()
deprecation_qs = deprecation_qs.annotate(
fqn=Concat(F("namespace"), Value("."), F("name"), output_field=CharField())
)
deprecations = [x.fqn for x in deprecation_qs]

# add a field to indicate deprecation ...
kwargs = {
"is_deprecated": Case(
When(fqn__in=deprecations, then=Value(True)), default=Value(False)
)
}
qs = qs.annotate(**kwargs)

# add a field to indicate if CV in each distro ...
for distro in AnsibleDistribution.objects.all():
kwargs = {
f"in_distro_{distro.name}": Case(
When(pulp_id__in=distro.repository.latest_version().content, then=Value(True)),
default=Value(False),
)
}
qs = qs.annotate(**kwargs)

# add a field for signing state ...

return qs
8 changes: 8 additions & 0 deletions pulp_ansible/app/urls.py
Expand Up @@ -12,6 +12,7 @@
RoleVersionList,
)
from pulp_ansible.app.galaxy.v3 import views as views_v3
from pulp_ansible.app.galaxy.v3 import viewsets as viewsets_v3

from pulp_ansible.app.viewsets import CopyViewSet, CollectionUploadViewSet

Expand Down Expand Up @@ -203,6 +204,13 @@
views_v3.ClientConfigurationView.as_view(),
name="client-configuration-viewset",
),
path(
"search/collection-versions/",
# views_v3.CollectionVersionSearchViewSet.as_view({"get": "list"}),
# views_v3.CollectionVersionSearchViewSet.as_view(),
viewsets_v3.CollectionVersionSearchViewSet.as_view({"get": "list"}),
name="collection-versions-search",
),
]

v3_urls = [
Expand Down