forked from Scifabric/pybossa
-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
RDISCROWD-5637 add project details endpoint (#814)
* add boilerplate endpoint * implement API query * refactor based on team review * cleanup project_details * added tests * clean _create_json_response * CR updates --------- Co-authored-by: nsyed22 <nsyed22@bloomberg.net>
- Loading branch information
Showing
4 changed files
with
301 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
# -*- coding: utf8 -*- | ||
# This file is part of PYBOSSA. | ||
# | ||
# Copyright (C) 2017 Scifabric LTD. | ||
# | ||
# PYBOSSA is free software: you can redistribute it and/or modify | ||
# it under the terms of the GNU Affero General Public License as published by | ||
# the Free Software Foundation, either version 3 of the License, or | ||
# (at your option) any later version. | ||
# | ||
# PYBOSSA is distributed in the hope that it will be useful, | ||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
# GNU Affero General Public License for more details. | ||
# | ||
# You should have received a copy of the GNU Affero General Public License | ||
# along with PYBOSSA. If not, see <http://www.gnu.org/licenses/>. | ||
|
||
import json | ||
from flask import request, abort | ||
from flask_login import current_user | ||
from werkzeug.exceptions import BadRequest, Unauthorized, Forbidden | ||
from pybossa.api.api_base import APIBase | ||
from pybossa.model.project import Project | ||
from pybossa.model import DomainObject | ||
|
||
class ProjectDetailsAPI(APIBase): | ||
""" | ||
Class for retreiving details about projects. | ||
""" | ||
__class__ = Project | ||
|
||
def _filter_query(self, repo_info, limit, offset, orderby): | ||
if (len(request.args.keys()) == 0 or | ||
(len(request.args.keys()) == 1 and "api_key" in request.args.keys())): | ||
return [] | ||
if (not current_user.is_authenticated or | ||
(not current_user.admin and not current_user.subadmin)): | ||
raise Unauthorized("User not authorized for request") | ||
|
||
return APIBase._filter_query(self, repo_info, limit, offset, orderby) | ||
|
||
def _create_json_response(self, query_result, oid): | ||
if len(query_result) == 1 and query_result[0] is None: | ||
raise abort(404) | ||
items = [] | ||
for result in query_result: | ||
try: | ||
item = result | ||
datum = self._create_dict_from_model(item) | ||
items.append(datum) | ||
except Exception: # pragma: no cover | ||
raise | ||
if oid is not None: | ||
self._sign_item(items[0]) | ||
items = items[0] | ||
return json.dumps(items) | ||
|
||
|
||
def _select_attributes(self, data): | ||
tmp = {} | ||
tmp['id'] = data.get('id') | ||
tmp['short_name'] = data.get('short_name') | ||
tmp['product'] = data.get('info', {}).get('product') | ||
tmp['subproduct'] = data.get('info', {}).get('subproduct') | ||
tmp['created'] = data.get('created') | ||
|
||
return tmp |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,226 @@ | ||
# -*- coding: utf8 -*- | ||
# This file is part of PYBOSSA. | ||
# | ||
# Copyright (C) 2015 Scifabric LTD. | ||
# | ||
# PYBOSSA is free software: you can redistribute it and/or modify | ||
# it under the terms of the GNU Affero General Public License as published by | ||
# the Free Software Foundation, either version 3 of the License, or | ||
# (at your option) any later version. | ||
# | ||
# PYBOSSA is distributed in the hope that it will be useful, | ||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
# GNU Affero General Public License for more details. | ||
# | ||
# You should have received a copy of the GNU Affero General Public License | ||
# along with PYBOSSA. If not, see <http://www.gnu.org/licenses/>. | ||
import json | ||
from unittest.mock import patch, call | ||
|
||
from nose.tools import assert_equal | ||
|
||
from pybossa.model.project import Project | ||
from test import db, with_context | ||
from test.factories import (ProjectFactory, UserFactory) | ||
from test.test_api import TestAPI | ||
|
||
|
||
|
||
class TestProjectAPI(TestAPI): | ||
|
||
def setUp(self): | ||
super(TestProjectAPI, self).setUp() | ||
db.session.query(Project).delete() | ||
|
||
def setupProjects(self): | ||
project = ProjectFactory.create( | ||
updated='2015-01-01T14:37:30.642119', | ||
short_name='test-app1', | ||
info={ | ||
'total': 150, | ||
'task_presenter': 'foo', | ||
'data_classification': dict(input_data="L4 - public", output_data="L4 - public"), | ||
'product' : 'test_product', | ||
'subproduct': 'test_subproduct1' | ||
}) | ||
|
||
projects = ProjectFactory.create_batch(5, | ||
info={ | ||
'total': 150, | ||
'task_presenter': 'foo', | ||
'data_classification': dict(input_data="L4 - public", output_data="L4 - public"), | ||
'product' : 'test_product', | ||
'subproduct': 'test_subproduct2' | ||
}) | ||
return project | ||
|
||
@with_context | ||
def test_project_details_user_not_logged_in(self): | ||
""" Test should return 401 if the user is not logged in""" | ||
project = self.setupProjects() | ||
project_id = str(project.id) | ||
|
||
res = self.app.get('/api/projectdetails/' + project_id) | ||
err = json.loads(res.data) | ||
assert res.status_code == 401, err | ||
assert err['status'] == 'failed', err | ||
assert err['target'] == 'project', err | ||
assert err['exception_cls'] == 'Unauthorized', err | ||
assert err['action'] == 'GET', err | ||
|
||
@with_context | ||
def test_project_details_user_worker(self): | ||
""" Test API should return 401 if user is worker""" | ||
admin = UserFactory.create(admin=True) | ||
worker = UserFactory.create(admin=False, subadmin=False) | ||
|
||
project = self.setupProjects() | ||
project_id = str(project.id) | ||
|
||
res = self.app.get('/api/projectdetails?id=' + project_id + '&api_key=' + worker.api_key + '&all=1') | ||
err = json.loads(res.data) | ||
assert res.status_code == 401, err | ||
assert err['status'] == 'failed', err | ||
assert err['target'] == 'project', err | ||
assert err['exception_cls'] == 'Unauthorized', err | ||
assert err['action'] == 'GET', err | ||
|
||
@with_context | ||
def test_project_details_user_subadmin(self): | ||
""" Test API should work if user is subadmin""" | ||
admin = UserFactory.create(admin=True) | ||
subadmin = UserFactory.create(admin=False, subadmin=True) | ||
|
||
project = self.setupProjects() | ||
project_id = str(project.id) | ||
|
||
res = self.app.get('/api/projectdetails?id=' + project_id + '&api_key=' + subadmin.api_key + '&all=1') | ||
data = json.loads(res.data) | ||
assert res.status_code == 200, data | ||
assert data[0]['product'] == 'test_product', data | ||
assert data[0]['short_name'] == 'test-app1', data | ||
|
||
@with_context | ||
def test_project_details_user_admin(self): | ||
""" Test API should work if user is admin""" | ||
admin = UserFactory.create(admin=True) | ||
|
||
project = self.setupProjects() | ||
project_id = str(project.id) | ||
|
||
res = self.app.get('/api/projectdetails?id=' + project_id + '&api_key=' + admin.api_key + '&all=1') | ||
data = json.loads(res.data) | ||
assert res.status_code == 200, data | ||
assert data[0]['product'] == 'test_product', data | ||
assert data[0]['short_name'] == 'test-app1', data | ||
|
||
|
||
@with_context | ||
def test_project_details_get_by_id_1(self): | ||
""" Test get by id when result exists""" | ||
admin = UserFactory.create(admin=True) | ||
project1 = self.setupProjects() | ||
|
||
# Test get by id | ||
res = self.app.get('/api/projectdetails?id=' + str(project1.id) + '&api_key=' + admin.api_key + '&all=1') | ||
data = json.loads(res.data) | ||
assert res.status_code == 200, data | ||
assert len(data) == 1, data | ||
assert data[0]['product'] == 'test_product', data | ||
assert data[0]['short_name'] == 'test-app1', data | ||
|
||
@with_context | ||
def test_project_details_get_by_id_2(self): | ||
""" Test get by id when result exists""" | ||
admin = UserFactory.create(admin=True) | ||
project1 = self.setupProjects() | ||
|
||
# Test get by id | ||
res = self.app.get('/api/projectdetails/' + str(project1.id) + '?api_key=' + admin.api_key) | ||
data = json.loads(res.data) | ||
assert res.status_code == 200, data | ||
assert data['product'] == 'test_product', data | ||
assert data['short_name'] == 'test-app1', data | ||
|
||
@with_context | ||
def test_project_details_get_by_product(self): | ||
""" Test search by product when result exists""" | ||
admin = UserFactory.create(admin=True) | ||
project1 = self.setupProjects() | ||
|
||
# Test get by product | ||
res = self.app.get('/api/projectdetails?info=product::' + project1.info['product'] + '&api_key=' + admin.api_key + '&all=1') | ||
data = json.loads(res.data) | ||
assert res.status_code == 200, data | ||
assert len(data) == 6, data | ||
assert data[0]['product'] == 'test_product', data | ||
assert data[1]['product'] == 'test_product', data | ||
|
||
@with_context | ||
def test_project_details_get_by_subproduct(self): | ||
""" Test search by subproduct when result exists""" | ||
admin = UserFactory.create(admin=True) | ||
project1 = self.setupProjects() | ||
|
||
# Test get by subproduct | ||
res = self.app.get('/api/projectdetails?info=subproduct::' + project1.info['subproduct'] + '&api_key=' + admin.api_key + '&all=1') | ||
data = json.loads(res.data) | ||
assert res.status_code == 200, data | ||
assert len(data) == 1, data | ||
assert data[0]['product'] == 'test_product', data | ||
assert data[0]['short_name'] == 'test-app1', data | ||
|
||
@with_context | ||
def test_project_details_no_params(self): | ||
""" Test API project query when no search params""" | ||
admin = UserFactory.create(admin=True) | ||
project1 = self.setupProjects() | ||
|
||
# Test no params | ||
res = self.app.get('/api/projectdetails?api_key=' + admin.api_key) | ||
data = json.loads(res.data) | ||
assert res.status_code == 200, data | ||
assert len(data) == 0, data | ||
|
||
@with_context | ||
def test_project_details_value_does_not_match(self): | ||
""" Test API project query when search value does not match""" | ||
admin = UserFactory.create(admin=True) | ||
project1 = self.setupProjects() | ||
|
||
# Test value DNE | ||
res = self.app.get('/api/projectdetails?id=' + '9999' + '&api_key=' + admin.api_key + '&all=1') | ||
data = json.loads(res.data) | ||
assert res.status_code == 200, data | ||
assert len(data) == 0, data | ||
|
||
|
||
@with_context | ||
def test_project_details_param_does_not_exist(self): | ||
""" Test API project query when search value does not match""" | ||
admin = UserFactory.create(admin=True) | ||
project1 = self.setupProjects() | ||
|
||
# Test bad param | ||
res = self.app.get('/api/projectdetails?fakeparam=product::' + project1.info['product'] + '&api_key=' + admin.api_key + '&all=1') | ||
err = json.loads(res.data) | ||
assert res.status_code == 415, data | ||
assert err['status'] == 'failed', err | ||
assert err['target'] == 'project', err | ||
assert err['exception_cls'] == 'AttributeError', err | ||
assert err['action'] == 'GET', err | ||
|
||
@with_context | ||
def test_project_details_multiple_params(self): | ||
""" Test API project query when result exists""" | ||
admin = UserFactory.create(admin=True) | ||
project1 = self.setupProjects() | ||
|
||
# Test get by product and subproduct | ||
res = self.app.get('/api/projectdetails?info=product::' + project1.info['product'] + '&info=subproduct::' + project1.info['subproduct'] + '&api_key=' + admin.api_key + '&all=1') | ||
data = json.loads(res.data) | ||
assert res.status_code == 200, data | ||
assert data[0]['product'] == 'test_product', data | ||
assert data[0]['subproduct'] == 'test_subproduct1', data | ||
|