Merge pull request #289 from anujm/jsonreports_search_created_modification
Stacky search API
This commit is contained in:
commit
9ae85db457
71
docs/api.rst
71
docs/api.rst
@ -488,6 +488,77 @@ stacky/report/<report_id>
|
||||
...
|
||||
]
|
||||
|
||||
stacky/reports/search/
|
||||
=========================
|
||||
|
||||
.. http:get:: http://example.com/stacky/reports/search
|
||||
|
||||
Returns reports that match the search criteria in descending order of id.
|
||||
|
||||
The contents of the report varies by the specific report, but
|
||||
all are in row/column format with Row 0 being a special *metadata* row.
|
||||
The actual row/columns of the report start at Row 1 onwards.
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
GET /stacky/reports/search/ HTTP/1.1
|
||||
Host: example.com
|
||||
Accept: application/json
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Vary: Accept
|
||||
Content-Type: text/json
|
||||
|
||||
[
|
||||
[
|
||||
"Id",
|
||||
"Start",
|
||||
"End",
|
||||
"Created",
|
||||
"Name",
|
||||
"Version"
|
||||
],
|
||||
|
||||
[
|
||||
4253,
|
||||
"2013-11-21 00:00:00",
|
||||
"2013-11-22 00:00:00",
|
||||
"2013-11-22 01:44:55",
|
||||
"public outbound bandwidth",
|
||||
1
|
||||
],
|
||||
[
|
||||
4252,
|
||||
"2014-01-18 00:00:00",
|
||||
"2013-11-22 00:00:00",
|
||||
"2013-11-22 01:44:55",
|
||||
"image events audit",
|
||||
1
|
||||
],
|
||||
[
|
||||
4248,
|
||||
"2013-11-21 00:00:00",
|
||||
"2013-11-22 00:00:00",
|
||||
"2013-11-22 01:44:55",
|
||||
"Error detail report",
|
||||
1
|
||||
],
|
||||
|
||||
...
|
||||
]
|
||||
|
||||
:query id: integer report id
|
||||
:query name: string report name(can include spaces)
|
||||
:query period_start: start of period, which the report pertains to, in the following format: YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]
|
||||
:query period_end: end of period, which the report pertains to, in the following format: YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]
|
||||
:query created: the day, when the report was created, in the following format: YYYY-MM-DD
|
||||
|
||||
stacky/show/<event_id>
|
||||
======================
|
||||
|
||||
|
@ -628,53 +628,67 @@ class BadRequestException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def _parse_created(request_filters):
|
||||
def _parse_created(created):
|
||||
try:
|
||||
created_datetime = datetime.datetime.strptime(
|
||||
request_filters['created'], '%Y-%m-%d %H:%M:%S')
|
||||
created_datetime = datetime.datetime.strptime(created, '%Y-%m-%d')
|
||||
return dt.dt_to_decimal(created_datetime)
|
||||
except ValueError:
|
||||
raise BadRequestException(
|
||||
"'%s' value has an invalid format. It must be in "
|
||||
"YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ] format." %
|
||||
request_filters['created'])
|
||||
"'%s' value has an invalid format. It must be in YYYY-MM-DD format."
|
||||
% created)
|
||||
|
||||
|
||||
def _create_query_filters_from_request(request_filters, model):
|
||||
allowed_fields = [field.name for field in models.get_model_fields(model)]
|
||||
invalid_fields = []
|
||||
def _parse_id(id):
|
||||
try:
|
||||
return int(id)
|
||||
except ValueError:
|
||||
raise BadRequestException(
|
||||
"'%s' value has an invalid format. It must be in integer "
|
||||
"format." % id)
|
||||
|
||||
|
||||
def _parse_fields_and_create_query_filters(request_filters):
|
||||
query_filters = {}
|
||||
|
||||
for field, value in request_filters.iteritems():
|
||||
if field in allowed_fields:
|
||||
query_filters[field + '__exact'] = value
|
||||
if field == 'created':
|
||||
decimal_created = _parse_created(value)
|
||||
query_filters['created__gt'] = decimal_created
|
||||
query_filters['created__lt'] = decimal_created + SECS_PER_DAY
|
||||
elif field == 'id':
|
||||
id = _parse_id(value)
|
||||
query_filters['id__exact'] = id
|
||||
else:
|
||||
invalid_fields.append(field)
|
||||
|
||||
if invalid_fields:
|
||||
raise BadRequestException(
|
||||
"The requested fields do not exist for the corresponding "
|
||||
"object: %s. Note: The field names of database "
|
||||
"are case-sensitive." %
|
||||
', '.join(sorted(invalid_fields)))
|
||||
query_filters[field + '__exact'] = value
|
||||
|
||||
return query_filters
|
||||
|
||||
|
||||
def _get_query_filters(request, model):
|
||||
def _check_if_fields_searchable(request_filters):
|
||||
allowed_fields = ['id', 'name', 'created', 'period_start', 'period_end']
|
||||
invalid_fields = [field for field in request_filters.keys()
|
||||
if field not in allowed_fields]
|
||||
if invalid_fields:
|
||||
raise BadRequestException(
|
||||
"The requested fields either do not exist for the corresponding "
|
||||
"object or are not searchable: %s. Note: The field names of "
|
||||
"database are case-sensitive." %
|
||||
', '.join(sorted(invalid_fields)))
|
||||
|
||||
|
||||
def _create_query_filters(request):
|
||||
request_filters = deepcopy(request.GET)
|
||||
if 'created' in request_filters:
|
||||
request_filters['created'] = _parse_created(request_filters)
|
||||
request_filters.pop('limit', None)
|
||||
request_filters.pop('offset', None)
|
||||
|
||||
return _create_query_filters_from_request(request_filters, model)
|
||||
_check_if_fields_searchable(request_filters)
|
||||
return _parse_fields_and_create_query_filters(request_filters)
|
||||
|
||||
|
||||
def do_jsonreports_search(request):
|
||||
try:
|
||||
model = models.JsonReport
|
||||
filters = _get_query_filters(request, model)
|
||||
filters = _create_query_filters(request)
|
||||
reports = model_search(request, model.objects, filters,
|
||||
order_by='-id')
|
||||
results = [['Id', 'Start', 'End', 'Created', 'Name', 'Version']]
|
||||
|
@ -1528,25 +1528,22 @@ class JsonReportsSearchAPI(StacktachBaseTestCase):
|
||||
def tearDown(self):
|
||||
self.mox.UnsetStubs()
|
||||
|
||||
def test_jsonreports_search_order_by_period_start(self):
|
||||
def test_jsonreports_search_order_by_id(self):
|
||||
request = self.mox.CreateMockAnything()
|
||||
request.GET = {
|
||||
'id': 1,
|
||||
'name': 'nova_usage_audit',
|
||||
'period_start': '2014-01-01 00:00:00',
|
||||
'period_end': '2014-01-02 00:00:00',
|
||||
'created': '2014-01-01 09:40:00',
|
||||
'version': 4,
|
||||
'json': 'json'
|
||||
'created': '2014-01-01',
|
||||
}
|
||||
filters = {
|
||||
'id__exact': 1,
|
||||
'period_start__exact': '2014-01-01 00:00:00',
|
||||
'name__exact': 'nova_usage_audit',
|
||||
'period_end__exact': '2014-01-02 00:00:00',
|
||||
'created__exact': decimal.Decimal('1388569200'),
|
||||
'version__exact': 4,
|
||||
'json__exact': 'json'
|
||||
'created__lt': decimal.Decimal('1388620800'),
|
||||
'created__gt': decimal.Decimal('1388534400'),
|
||||
}
|
||||
self.mox.StubOutWithMock(stacky_server, 'model_search')
|
||||
stacky_server.model_search(request, self.model, filters,
|
||||
@ -1555,10 +1552,11 @@ class JsonReportsSearchAPI(StacktachBaseTestCase):
|
||||
self.mox.ReplayAll()
|
||||
|
||||
actual_result = stacky_server.do_jsonreports_search(request).content
|
||||
expected_result = \
|
||||
[['Id', 'Start', 'End', 'Created', 'Name', 'Version'],
|
||||
['5975', '2014-01-18 00:00:00', '2014-01-19 00:00:00',
|
||||
'2014-01-01 09:40:00', 'nova usage audit', 4]]
|
||||
expected_result = [
|
||||
['Id', 'Start', 'End', 'Created', 'Name', 'Version'],
|
||||
['5975', '2014-01-18 00:00:00', '2014-01-19 00:00:00',
|
||||
'2014-01-01 09:40:00', 'nova usage audit', 4]
|
||||
]
|
||||
|
||||
self.assertEquals(ast.literal_eval(actual_result), expected_result)
|
||||
self.mox.VerifyAll()
|
||||
@ -1590,32 +1588,33 @@ class JsonReportsSearchAPI(StacktachBaseTestCase):
|
||||
self.assertEquals(ast.literal_eval(actual_result), expected_result)
|
||||
self.mox.VerifyAll()
|
||||
|
||||
def test_jsonreports_search_with_invalid_field_names_400(self):
|
||||
def test_jsonreports_search_with_invalid_fields(self):
|
||||
request = self.mox.CreateMockAnything()
|
||||
request.GET = {'invalid_column_1': 'value_1',
|
||||
'invalid_column_2': 'value_2',
|
||||
'version': 4,
|
||||
'json': 'json',
|
||||
'period_start': '2014-01-01 00:00:00'}
|
||||
self.mox.ReplayAll()
|
||||
|
||||
actual_result = stacky_server.do_jsonreports_search(request).content
|
||||
expected_result = \
|
||||
[
|
||||
expected_result = [
|
||||
["Error", "Message"],
|
||||
["Bad Request", "The requested fields do not exist for the "
|
||||
"corresponding object: invalid_column_1, invalid_column_2. Note: "
|
||||
"The field names of database are case-sensitive."]
|
||||
["Bad Request", "The requested fields either do not exist for the "
|
||||
"corresponding object or are not searchable: invalid_column_1, "
|
||||
"invalid_column_2, json, version. Note: The field names of "
|
||||
"database are case-sensitive."]
|
||||
]
|
||||
self.assertEqual(ast.literal_eval(actual_result), expected_result)
|
||||
self.mox.VerifyAll()
|
||||
|
||||
def test_jsonreports_search_with_invalid_format_of_field_values_400(self):
|
||||
def test_jsonreports_search_with_invalid_period_start(self):
|
||||
request = self.mox.CreateMockAnything()
|
||||
request.GET = {'period_start': '1234'}
|
||||
self.mox.ReplayAll()
|
||||
|
||||
actual_result = stacky_server.do_jsonreports_search(request).content
|
||||
expected_result = \
|
||||
[
|
||||
expected_result = [
|
||||
["Error", "Message"],
|
||||
["Bad Request", "'1234' value has an invalid format. It must be in "
|
||||
"YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ] format."]
|
||||
@ -1623,25 +1622,49 @@ class JsonReportsSearchAPI(StacktachBaseTestCase):
|
||||
self.assertEqual(ast.literal_eval(actual_result), expected_result)
|
||||
self.mox.VerifyAll()
|
||||
|
||||
def test_jsonreports_search_by_created(self):
|
||||
def test_jsonreports_search_with_invalid_period_end(self):
|
||||
request = self.mox.CreateMockAnything()
|
||||
request.GET = {
|
||||
'created': '2014-01-01 09:40:20'}
|
||||
filters = {
|
||||
'created__exact': 1388569220}
|
||||
self.mox.StubOutWithMock(stacky_server, 'model_search')
|
||||
stacky_server.model_search(request, self.model, filters,
|
||||
order_by='-id').AndReturn(
|
||||
[self.model_search_result])
|
||||
request.GET = {'period_end': '1234'}
|
||||
self.mox.ReplayAll()
|
||||
|
||||
actual_result = stacky_server.do_jsonreports_search(request).content
|
||||
expected_result = \
|
||||
[['Id', 'Start', 'End', 'Created', 'Name', 'Version'],
|
||||
['5975', '2014-01-18 00:00:00', '2014-01-19 00:00:00',
|
||||
'2014-01-01 09:40:00', 'nova usage audit', 4]]
|
||||
expected_result = [
|
||||
["Error", "Message"],
|
||||
["Bad Request", "'1234' value has an invalid format. It must be in "
|
||||
"YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ] format."]
|
||||
]
|
||||
self.assertEqual(ast.literal_eval(actual_result), expected_result)
|
||||
self.mox.VerifyAll()
|
||||
|
||||
self.assertEquals(ast.literal_eval(actual_result), expected_result)
|
||||
def test_jsonreports_search_with_invalid_id(self):
|
||||
request = self.mox.CreateMockAnything()
|
||||
request.GET = {'id': 'abcd'}
|
||||
self.mox.ReplayAll()
|
||||
|
||||
actual_result = stacky_server.do_jsonreports_search(request).content
|
||||
expected_result = [
|
||||
["Error", "Message"],
|
||||
["Bad Request", "'abcd' value has an invalid format. It must be in "
|
||||
"integer format."]
|
||||
]
|
||||
self.assertEqual(ast.literal_eval(actual_result), expected_result)
|
||||
self.mox.VerifyAll()
|
||||
|
||||
def test_jsonreports_search_with_invalid_created_format(self):
|
||||
request = self.mox.CreateMockAnything()
|
||||
request.GET = {
|
||||
'created': '2014-01-01 00:00:00'
|
||||
}
|
||||
self.mox.ReplayAll()
|
||||
|
||||
actual_result = stacky_server.do_jsonreports_search(request).content
|
||||
expected_result = [
|
||||
["Error", "Message"],
|
||||
["Bad Request", "'2014-01-01 00:00:00' value has an invalid format."
|
||||
" It must be in YYYY-MM-DD format."]
|
||||
]
|
||||
|
||||
self.assertEqual(ast.literal_eval(actual_result), expected_result)
|
||||
self.mox.VerifyAll()
|
||||
|
||||
def test_jsonreports_search_by_invalid_created_400(self):
|
||||
@ -1655,7 +1678,7 @@ class JsonReportsSearchAPI(StacktachBaseTestCase):
|
||||
[
|
||||
["Error", "Message"],
|
||||
["Bad Request", "'1234' value has an invalid format. It must be in "
|
||||
"YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ] format."]
|
||||
"YYYY-MM-DD format."]
|
||||
]
|
||||
self.assertEquals(ast.literal_eval(actual_result), expected_result)
|
||||
self.mox.VerifyAll()
|
||||
|
Loading…
x
Reference in New Issue
Block a user