Repository :
http://git.fedorahosted.org/cgit/copr.git
On branch : master
---------------------------------------------------------------
commit b6106eb5501486defdb68e80c63c3903979e7c30
Author: Valentin Gologuzov <vgologuz(a)redhat.com>
Date: Wed Sep 2 14:28:02 2015 +0200
[frontend][api] Updated and test projects collection (GET on /projects). Added an
option to use fulltext search.
---------------------------------------------------------------
frontend/coprs_frontend/coprs/logic/coprs_logic.py | 15 +++-
frontend/coprs_frontend/coprs/rest_api/__init__.py | 4 +-
.../coprs/rest_api/resources/project.py | 19 +++--
.../coprs/views/api_ns/api_general.py | 1 +
.../coprs/views/coprs_ns/coprs_general.py | 9 ++-
.../tests/test_api/test_project_r.py | 85 +++++++++++++++++++-
6 files changed, 118 insertions(+), 15 deletions(-)
diff --git a/frontend/coprs_frontend/coprs/logic/coprs_logic.py
b/frontend/coprs_frontend/coprs/logic/coprs_logic.py
index d5c8725..ec56f0a 100644
--- a/frontend/coprs_frontend/coprs/logic/coprs_logic.py
+++ b/frontend/coprs_frontend/coprs/logic/coprs_logic.py
@@ -69,13 +69,21 @@ class CoprsLogic(object):
db.session.query(models.Copr)
.join(models.Copr.owner)
.options(db.contains_eager(models.Copr.owner))
- .order_by(models.Copr.id.desc()))
+ )
if not include_deleted:
query = query.filter(models.Copr.deleted.is_(False))
return query
+ @classmethod
+ def set_query_order(cls, query, desc=False):
+ if desc:
+ query = query.order_by(models.Copr.id.desc())
+ else:
+ query = query.order_by(models.Copr.id.asc())
+ return query
+
# user_relation="owned", username=username, with_mock_chroots=False
@classmethod
def get_multiple_owned_by_username(cls, username):
@@ -99,6 +107,11 @@ class CoprsLogic(object):
return query.filter(models.Copr.name == name)
@classmethod
+ def filter_by_owner_name(cls, query, username):
+ # should be already joined with the User table
+ return query.filter(models.User.username == username)
+
+ @classmethod
def join_builds(cls, query):
return (query.outerjoin(models.Copr.builds)
.options(db.contains_eager(models.Copr.builds))
diff --git a/frontend/coprs_frontend/coprs/rest_api/__init__.py
b/frontend/coprs_frontend/coprs/rest_api/__init__.py
index 2a4c2f6..2debb8f 100644
--- a/frontend/coprs_frontend/coprs/rest_api/__init__.py
+++ b/frontend/coprs_frontend/coprs/rest_api/__init__.py
@@ -19,8 +19,8 @@ class RootR(Resource):
return {
"_links": {
"self": {"href": url_for(".rootr")},
- "coprs": {"href":
url_for(".projectlistr")},
- "copr_chroots": {"href":
url_for(".mockchrootlistr")},
+ "projects": {"href":
url_for(".projectlistr")},
+ "mock_chroots": {"href":
url_for(".mockchrootlistr")},
"builds": {"href":
url_for(".buildlistr")},
}
}
diff --git a/frontend/coprs_frontend/coprs/rest_api/resources/project.py
b/frontend/coprs_frontend/coprs/rest_api/resources/project.py
index d0b989f..cee7bf7 100644
--- a/frontend/coprs_frontend/coprs/rest_api/resources/project.py
+++ b/frontend/coprs_frontend/coprs/rest_api/resources/project.py
@@ -1,4 +1,5 @@
from logging import getLogger
+from coprs.rest_api.exceptions import MalformedRequest
log = getLogger(__name__)
@@ -52,23 +53,27 @@ class ProjectListR(Resource):
def get(self):
parser = reqparse.RequestParser()
- parser.add_argument('owner', dest='username', type=str)
- parser.add_argument('name', dest='name', type=str)
+ parser.add_argument('owner', type=str)
+ parser.add_argument('name', type=str)
parser.add_argument('limit', type=int)
parser.add_argument('offset', type=int)
+ parser.add_argument('search_query', type=str)
- # parser.add_argument('help', type=bool)
req_args = parser.parse_args()
- if req_args["username"]:
- query =
CoprsLogic.get_multiple_owned_by_username(req_args["username"])
+ if req_args["search_query"]:
+ query = CoprsLogic.get_multiple_fulltext(req_args["search_query"])
else:
query = CoprsLogic.get_multiple(flask.g.user)
+ query = CoprsLogic.set_query_order(query)
+
+ if req_args["owner"]:
+ query = CoprsLogic.filter_by_owner_name(query, req_args["owner"])
+
if req_args["name"]:
query = CoprsLogic.filter_by_name(query, req_args["name"])
- # todo: add maximum allowed limit and also use as a default limit
limit = 100
offset = 0
if req_args["limit"]:
@@ -83,7 +88,7 @@ class ProjectListR(Resource):
"_links": {
"self": {"href": url_for(".projectlistr",
**req_args)}
},
- "coprs": [render_project(copr) for copr in coprs_list],
+ "projects": [render_project(copr) for copr in coprs_list],
}
return result_dict
diff --git a/frontend/coprs_frontend/coprs/views/api_ns/api_general.py
b/frontend/coprs_frontend/coprs/views/api_ns/api_general.py
index 3ded705..eecb919 100644
--- a/frontend/coprs_frontend/coprs/views/api_ns/api_general.py
+++ b/frontend/coprs_frontend/coprs/views/api_ns/api_general.py
@@ -186,6 +186,7 @@ def api_coprs_by_owner(username=None):
if username:
query = CoprsLogic.join_builds(
CoprsLogic.get_multiple_owned_by_username(username))
+ query = CoprsLogic.set_query_order(query)
repos = query.all()
output = {"output": "ok", "repos": []}
diff --git a/frontend/coprs_frontend/coprs/views/coprs_ns/coprs_general.py
b/frontend/coprs_frontend/coprs/views/coprs_ns/coprs_general.py
index 6ff6221..a665099 100644
--- a/frontend/coprs_frontend/coprs/views/coprs_ns/coprs_general.py
+++ b/frontend/coprs_frontend/coprs/views/coprs_ns/coprs_general.py
@@ -19,6 +19,7 @@ from coprs import exceptions
from coprs import forms
from coprs import helpers
from coprs import models
+from coprs.logic.coprs_logic import CoprsLogic
from coprs.logic.stat_logic import CounterStatLogic
from coprs.rmodels import TimedStatEvents
@@ -35,7 +36,9 @@ from coprs.helpers import parse_package_name, generate_repo_url,
CHROOT_RPMS_DL_
@coprs_ns.route("/", defaults={"page": 1})
@coprs_ns.route("/<int:page>/")
def coprs_show(page=1):
- query = coprs_logic.CoprsLogic.get_multiple()
+ query = CoprsLogic.get_multiple()
+ query = CoprsLogic.set_query_order(query, desc=True)
+
paginator = helpers.Paginator(query, query.count(), page)
coprs = paginator.sliced_query
@@ -68,7 +71,8 @@ def coprs_by_owner(username=None, page=1):
return page_not_found(
"User {0} does not exist.".format(username))
- query = coprs_logic.CoprsLogic.get_multiple_owned_by_username(username)
+ query = CoprsLogic.get_multiple_owned_by_username(username)
+ query = CoprsLogic.set_query_order(query, desc=True)
paginator = helpers.Paginator(query, query.count(), page)
@@ -97,6 +101,7 @@ def coprs_by_owner(username=None, page=1):
@coprs_ns.route("/<username>/allowed/<int:page>/")
def coprs_by_allowed(username=None, page=1):
query = coprs_logic.CoprsLogic.get_multiple_allowed_to_username(username)
+ query = CoprsLogic.set_query_order(query, desc=True)
paginator = helpers.Paginator(query, query.count(), page)
coprs = paginator.sliced_query
diff --git a/frontend/coprs_frontend/tests/test_api/test_project_r.py
b/frontend/coprs_frontend/tests/test_api/test_project_r.py
index 316a6b7..9a3fa0b 100644
--- a/frontend/coprs_frontend/tests/test_api/test_project_r.py
+++ b/frontend/coprs_frontend/tests/test_api/test_project_r.py
@@ -11,6 +11,7 @@ from coprs.logic.builds_logic import BuildsLogic
from coprs.logic.users_logic import UsersLogic
from coprs.logic.coprs_logic import CoprsLogic
+from coprs.models import Copr
from tests.coprs_test_case import CoprsTestCase, TransactionDecorator
@@ -37,9 +38,87 @@ class TestProjectResource(CoprsTestCase):
obj = json.loads(r.data)
assert obj["_links"]["self"]["href"] == href
- # test_project_list_all
- # test_project_list_by_name
- # test_project_list_by_user
+ def test_project_list_all(self, f_users, f_mock_chroots, f_coprs, f_db):
+ expected_id_set = set(c.id for c in self.basic_coprs_list)
+ href = "/api_2/projects"
+ r = self.tc.get(href)
+ assert r.status_code == 200
+ obj = json.loads(r.data)
+ assert set(p["project"]["id"] for p in
obj["projects"]) == \
+ expected_id_set
+
+ def test_project_list_by_user(self, f_users, f_mock_chroots, f_coprs, f_db):
+ expected_id_set = set(
+ c.id for c in self.basic_coprs_list
+ if c.owner == self.u1
+ )
+ href = "/api_2/projects?owner={}".format(self.u1.username)
+ r = self.tc.get(href)
+ assert r.status_code == 200
+ obj = json.loads(r.data)
+ assert set(p["project"]["id"] for p in
obj["projects"]) == \
+ expected_id_set
+
+ def test_project_list_by_name(self, f_users, f_mock_chroots, f_coprs, f_db):
+ expected_id_set = set(
+ c.id for c in self.basic_coprs_list
+ if c.name == self.c1.name
+ )
+ href = "/api_2/projects?name={}".format(self.c1.name)
+ r = self.tc.get(href)
+ assert r.status_code == 200
+ obj = json.loads(r.data)
+ assert set(p["project"]["id"] for p in
obj["projects"]) == \
+ expected_id_set
+
+ def test_project_list_limit_offset(self, f_users, f_mock_chroots, f_coprs, f_db):
+ # quite hardcoded test
+ s_1 = set(p.id for p in [self.c1, self.c2])
+ s_2 = set(p.id for p in [self.c2, self.c3])
+ s_3 = set(p.id for p in [self.c3])
+
+ href_list = [
+ "/api_2/projects?limit=2",
+ "/api_2/projects?limit=2&offset=1",
+ "/api_2/projects?limit=2&offset=2"
+ ]
+ for href, expected in zip(href_list, [s_1, s_2, s_3]):
+ r = self.tc.get(href)
+ assert r.status_code == 200
+ obj = json.loads(r.data)
+ assert set(p["project"]["id"] for p in
obj["projects"]) == \
+ expected
+
+ def test_project_list_search(self, f_users, f_mock_chroots, f_coprs, f_db):
+ self.prefix = u"prefix"
+ self.s_coprs = []
+ c1_username = self.c1.owner.username
+
+ k1 = 3
+ k2 = 5
+ for x in range(k1):
+ self.s_coprs.append(Copr(name=self.prefix + str(x), owner=self.u1))
+
+ for x in range(k2):
+ self.s_coprs.append(Copr(name=self.prefix + str(x), owner=self.u2))
+
+ self.db.session.add_all(self.s_coprs)
+ self.db.session.commit()
+
+ r0 =
self.tc.get(u"/api_2/projects?search_query={}".format(self.prefix))
+ assert r0.status_code == 200
+ obj = json.loads(r0.data)
+ assert len(obj["projects"]) == k1 + k2
+ for p in obj["projects"]:
+ assert self.prefix in p["project"]["name"]
+
+ r1 = self.tc.get(u"/api_2/projects?search_query={}&owner={}"
+ .format(self.prefix, c1_username))
+ assert r1.status_code == 200
+ obj = json.loads(r1.data)
+ assert len(obj["projects"]) == k1
+ for p in obj["projects"]:
+ assert self.prefix in p["project"]["name"]
def test_project_create_new(self, f_users, f_mock_chroots, f_users_api):
self.db.session.add_all([self.u1, self.mc1])