Repository: StackStorm/st2

Issue:
St2 trace list gives incorrect result
Reference: https://docs.stackstorm.com/traces.html
Steps with Screen Shots:
-   Executed st2 action with trace-tag ‘tracetest-08Aug-01’ 
  ![image](https://cloud.githubusercontent.com/assets/10110869/17495194/17ca039a-5d86-11e6-8272-a73ed0347fe4.png)
-   Trace list o/p:
  ![image](https://cloud.githubusercontent.com/assets/10110869/17495206/2ec675ec-5d86-11e6-8a50-ac59b31dcd69.png)
-   Executed same command with Uppercase T in trace-tag:
  ![image](https://cloud.githubusercontent.com/assets/10110869/17495216/3998f24c-5d86-11e6-8336-3152e84af7ad.png)
-   Trace list o/p. Now I can see trace-tag but it doesn’t seem to sort with timestamp?
  ![image](https://cloud.githubusercontent.com/assets/10110869/17495226/446beb48-5d86-11e6-8abd-463b81ca9755.png)
-   Trace list with –n 1 gives trace info for command executed on 02Aug instead of today’s date but help says “-n LAST, --last LAST  List N most recent traces”

![image](https://cloud.githubusercontent.com/assets/10110869/17495234/4da88b1c-5d86-11e6-89aa-51e4ece0c6ec.png)



Discussion:

Resolving patch:
diff --git a/st2api/st2api/controllers/v1/traces.py b/st2api/st2api/controllers/v1/traces.py
--- a/st2api/st2api/controllers/v1/traces.py
+++ b/st2api/st2api/controllers/v1/traces.py
@@ -16,6 +16,7 @@
 from st2api.controllers.resource import ResourceController
 from st2common.models.api.trace import TraceAPI
 from st2common.persistence.trace import Trace
+from st2common.models.api.base import jsexpose
 
 __all__ = [
     'TracesController'
@@ -33,5 +34,18 @@ class TracesController(ResourceController):
     }
 
     query_options = {
-        'sort': ['trace_tag']
+        'sort': ['-start_timestamp', 'trace_tag']
     }
+
+    @jsexpose()
+    def get_all(self, **kwargs):
+        # Use a custom sort order when filtering on a timestamp so we return a correct result as
+        # expected by the user
+        if 'sort_desc' in kwargs:
+            query_options = {'sort': ['-start_timestamp', 'action.ref']}
+            kwargs['query_options'] = query_options
+        elif 'sort_asc' in kwargs:
+            query_options = {'sort': ['+start_timestamp', 'action.ref']}
+            kwargs['query_options'] = query_options
+
+        return self._get_all(**kwargs)
diff --git a/st2client/st2client/commands/trace.py b/st2client/st2client/commands/trace.py
--- a/st2client/st2client/commands/trace.py
+++ b/st2client/st2client/commands/trace.py
@@ -123,6 +123,12 @@ def __init__(self, resource, *args, **kwargs):
                                  default=50,
                                  help=('List N most recent %s.' %
                                        resource.get_plural_display_name().lower()))
+        self.parser.add_argument('-s', '--sort', type=str, dest='sort_order',
+                                 default='descending',
+                                 help=('Sort %s by start timestamp, '
+                                       'asc|ascending (earliest first) '
+                                       'or desc|descending (latest first)' %
+                                       resource.get_plural_display_name().lower()))
 
         # Filter options
         self.group.add_argument('-c', '--trace-tag', help='Trace-tag to filter the list.')
@@ -152,6 +158,12 @@ def run(self, args, **kwargs):
         if args.rule:
             kwargs['rule'] = args.rule
 
+        if args.sort_order:
+            if args.sort_order in ['asc', 'ascending']:
+                kwargs['sort_asc'] = True
+            elif args.sort_order in ['desc', 'descending']:
+                kwargs['sort_desc'] = True
+
         return self.manager.query(limit=args.last, **kwargs)
 
     def run_and_print(self, args, **kwargs):
diff --git a/st2common/st2common/models/db/trace.py b/st2common/st2common/models/db/trace.py
--- a/st2common/st2common/models/db/trace.py
+++ b/st2common/st2common/models/db/trace.py
@@ -79,7 +79,8 @@ class TraceDB(stormbase.StormFoundationDB):
             {'fields': ['start_timestamp']},
             {'fields': ['action_executions.object_id']},
             {'fields': ['trigger_instances.object_id']},
-            {'fields': ['rules.object_id']}
+            {'fields': ['rules.object_id']},
+            {'fields': ['-start_timestamp', 'trace_tag']},
         ]
     }
 


Test patch:
diff --git a/st2api/tests/unit/controllers/v1/test_traces.py b/st2api/tests/unit/controllers/v1/test_traces.py
--- a/st2api/tests/unit/controllers/v1/test_traces.py
+++ b/st2api/tests/unit/controllers/v1/test_traces.py
@@ -48,10 +48,11 @@ def test_get_all(self):
         self.assertEqual(resp.status_int, 200)
         self.assertEqual(len(resp.json), 3, '/v1/traces did not return all traces.')
 
+        # Note: traces are returned sorted by start_timestamp in descending order by default
         retrieved_trace_tags = [trace['trace_tag'] for trace in resp.json]
 
         self.assertEqual(retrieved_trace_tags,
-                         [self.trace1.trace_tag, self.trace2.trace_tag, self.trace3.trace_tag],
+                         [self.trace3.trace_tag, self.trace2.trace_tag, self.trace1.trace_tag],
                          'Incorrect traces retrieved.')
 
     def test_get_by_id(self):