-
Notifications
You must be signed in to change notification settings - Fork 79
Implement global package search for build chroot results #4154
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1750,6 +1750,59 @@ class AdminPlaygroundSearchForm(BaseForm): | |
| project = wtforms.StringField("Project") | ||
|
|
||
|
|
||
| class BuildChrootResultSearchForm(BaseForm): | ||
| """ Search built packages across all build chroot results. """ | ||
| name = wtforms.StringField( | ||
| "Name", | ||
| validators=[wtforms.validators.Optional()], | ||
| filters=[EmptyStringToNone()]) | ||
| epoch = wtforms.IntegerField( | ||
| "Epoch", | ||
| validators=[wtforms.validators.Optional()]) | ||
| version = wtforms.StringField( | ||
| "Version", | ||
| validators=[wtforms.validators.Optional()], | ||
| filters=[EmptyStringToNone()]) | ||
| release = wtforms.StringField( | ||
| "Release", | ||
| validators=[wtforms.validators.Optional()], | ||
| filters=[EmptyStringToNone()]) | ||
| arch = wtforms.StringField( | ||
| "Arch", | ||
| validators=[wtforms.validators.Optional()], | ||
| filters=[EmptyStringToNone()]) | ||
|
|
||
| def validate(self, extra_validators=None): | ||
|
||
| """ Validate that at least one search field is provided. """ | ||
| # pylint: disable=unused-argument | ||
| result = super().validate() | ||
| if not result: | ||
| return False | ||
|
|
||
| has_value = any([ | ||
| self.name.data, | ||
| self.version.data, | ||
| self.release.data, | ||
| self.arch.data, | ||
| self.epoch.data is not None, | ||
| ]) | ||
| if not has_value: | ||
| self.name.errors.append("At least one search field must be provided") | ||
| return False | ||
|
Comment on lines
+1782
to
+1791
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The logic to check if at least one field has a value is duplicated here and in the if not self.search_params():
self.name.errors.append("At least one search field must be provided")
return False |
||
| return True | ||
|
|
||
| def search_params(self): | ||
|
||
| """ Return validated query params to preserve in pagination links. """ | ||
| params = { | ||
| "name": self.name.data, | ||
| "epoch": self.epoch.data, | ||
| "version": self.version.data, | ||
| "release": self.release.data, | ||
| "arch": self.arch.data, | ||
| } | ||
| return {key: value for key, value in params.items() if value is not None} | ||
|
|
||
|
|
||
| class GroupUniqueNameValidator(object): | ||
|
|
||
| def __init__(self, message=None): | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,85 @@ | ||
| {% extends "layout.html" %} | ||
| {% from "_helpers.html" import render_field, render_pagination, build_href %} | ||
|
|
||
| {% block title %}Package search{% endblock %} | ||
| {% block header %}Package search{% endblock %} | ||
|
|
||
| {% block breadcrumbs %} | ||
| <ol class="breadcrumb"> | ||
| <li> | ||
| <a href="{{ url_for('coprs_ns.coprs_show') }}">Home</a> | ||
| </li> | ||
| <li class="active"> | ||
| Package search | ||
| </li> | ||
| </ol> | ||
| {% endblock %} | ||
|
|
||
| {% block body %} | ||
| <h1>Search built packages</h1> | ||
| <p>Search built packages by NEVRA fields across all build chroot results.</p> | ||
|
|
||
| <form class="form-horizontal" method="get" action="{{ url_for('coprs_ns.build_chroot_results_search') }}"> | ||
| {{ form.csrf_token }} | ||
| {{ render_field(form.name, placeholder="git") }} | ||
| {{ render_field(form.epoch, placeholder="0") }} | ||
| {{ render_field(form.version, placeholder="2.32.0") }} | ||
| {{ render_field(form.release, placeholder="1.fc36") }} | ||
| {{ render_field(form.arch, placeholder="x86_64") }} | ||
|
|
||
| <div class="form-group"> | ||
| <div class="col-sm-10 col-sm-offset-2"> | ||
| <button class="btn btn-primary" type="submit"> | ||
| <span class="glyphicon glyphicon-search"></span> Search | ||
| </button> | ||
| </div> | ||
| </div> | ||
| </form> | ||
|
|
||
| {% if search_performed %} | ||
| {% if results %} | ||
| <table class="table table-striped table-bordered"> | ||
| <thead> | ||
| <tr> | ||
| <th>Project</th> | ||
| <th>Build</th> | ||
| <th>Chroot</th> | ||
| <th>Name</th> | ||
| <th>Epoch</th> | ||
| <th>Version</th> | ||
| <th>Release</th> | ||
| <th>Arch</th> | ||
| </tr> | ||
| </thead> | ||
| <tbody> | ||
| {% for result in results %} | ||
| {% set build = result.build_chroot.build %} | ||
| {% set copr = build.copr %} | ||
| <tr> | ||
| <td> | ||
| <a href="{{ copr_url('coprs_ns.copr_detail', copr) }}"> | ||
| {{ copr.full_name }} | ||
| </a> | ||
| </td> | ||
| <td> | ||
| <a href="{{ build_href(build) }}">{{ build.id }}</a> | ||
| </td> | ||
| <td>{{ result.build_chroot.name }}</td> | ||
| <td>{{ result.name }}</td> | ||
| <td>{{ result.epoch if result.epoch is not none else '-' }}</td> | ||
| <td>{{ result.version }}</td> | ||
| <td>{{ result.release }}</td> | ||
| <td>{{ result.arch }}</td> | ||
| </tr> | ||
| {% endfor %} | ||
| </tbody> | ||
| </table> | ||
|
|
||
| {% if paginator %} | ||
| {{ render_pagination(request, paginator) }} | ||
| {% endif %} | ||
| {% else %} | ||
| <p>No matching packages found.</p> | ||
| {% endif %} | ||
| {% endif %} | ||
| {% endblock %} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| """Tests for built package search.""" | ||
|
|
||
| import pytest | ||
|
|
||
| from coprs.logic.builds_logic import BuildChrootResultsLogic | ||
| from tests.coprs_test_case import CoprsTestCase, TransactionDecorator | ||
|
|
||
|
|
||
| class TestPackageSearch(CoprsTestCase): | ||
|
|
||
| @TransactionDecorator("u1") | ||
| @pytest.mark.usefixtures("f_users", "f_coprs", "f_mock_chroots", "f_builds", "f_db") | ||
| def test_search_by_name_and_arch(self): | ||
| self.db.session.add(self.b1) | ||
|
|
||
| built_packages = { | ||
| "packages": [ | ||
| { | ||
| "name": "git", | ||
| "epoch": 0, | ||
| "version": "2.32.0", | ||
| "release": "1.fc36", | ||
| "arch": "x86_64", | ||
| }, | ||
| ] | ||
| } | ||
| BuildChrootResultsLogic.create_from_dict( | ||
| self.b1.build_chroots[0], | ||
| built_packages, | ||
| ) | ||
| self.db.session.commit() | ||
|
|
||
| response = self.test_client.get( | ||
| "/coprs/packages/search/?name=git&arch=x86_64" | ||
| ) | ||
| assert response.status_code == 200 | ||
| assert b"git" in response.data | ||
| assert str(self.b1.id).encode("utf-8") in response.data |
Uh oh!
There was an error while loading. Please reload this page.