Add deploy results 99/51299/10
authorthuva4 <tharma.thuva@gmail.com>
Mon, 29 Jan 2018 11:38:17 +0000 (17:08 +0530)
committerthuva4 <tharma.thuva@gmail.com>
Tue, 6 Feb 2018 15:43:52 +0000 (21:13 +0530)
Create view and controller for deploy results
Add tests for deploy results

Change-Id: Iba29e7a867d45aacd18a26dc4e2d9363cdf8928d
Signed-off-by: thuva4 <tharma.thuva@gmail.com>
testapi/opnfv_testapi/handlers/deploy_result_handlers.py
testapi/opnfv_testapi/router/url_mappings.py
testapi/opnfv_testapi/tests/UI/e2e/deployResultsControllerSpec.js [new file with mode: 0644]
testapi/opnfv_testapi/ui/Gruntfile.js
testapi/opnfv_testapi/ui/app.js
testapi/opnfv_testapi/ui/components/deploy-results/deploy-result/deployResult.html [new file with mode: 0644]
testapi/opnfv_testapi/ui/components/deploy-results/deploy-result/deployResultController.js [new file with mode: 0644]
testapi/opnfv_testapi/ui/components/deploy-results/deployResults.html [new file with mode: 0644]
testapi/opnfv_testapi/ui/components/deploy-results/deployResultsController.js [new file with mode: 0644]
testapi/opnfv_testapi/ui/index.html
testapi/opnfv_testapi/ui/shared/header/header.html

index 973bfef..a8fcd88 100644 (file)
@@ -1,6 +1,7 @@
 from opnfv_testapi.handlers import result_handlers
 from opnfv_testapi.models import deploy_result_models
 from opnfv_testapi.tornado_swagger import swagger
+from bson import objectid
 
 
 class GenericDeployResultHandler(result_handlers.GenericResultHandler):
@@ -113,3 +114,17 @@ class DeployResultsHandler(GenericDeployResultHandler):
         self._create(miss_fields=miss_fields,
                      carriers=carriers,
                      values_check=values_check)
+
+
+class DeployResultHandler(GenericDeployResultHandler):
+    @swagger.operation(nickname='getTestDeployResultById')
+    def get(self, result_id):
+        """
+            @description: get a single deploy result by result_id
+            @rtype: L{DeployResult}
+            @return 200: Deploy result exist
+            @raise 404: Deploy result not exist
+        """
+        query = dict()
+        query["_id"] = objectid.ObjectId(result_id)
+        self._get_one(query=query)
index a857725..b9dd231 100644 (file)
@@ -52,6 +52,7 @@ mappings = [
     (r'/api/v1/results/upload', result_handlers.ResultsUploadHandler),
     (r"/api/v1/results/([^/]+)", result_handlers.ResultsGURHandler),
     (r"/api/v1/deployresults", deploy_handlers.DeployResultsHandler),
+    (r"/api/v1/deployresults/([^/]+)", deploy_handlers.DeployResultHandler),
 
     # scenarios
     (r"/api/v1/scenarios", scenario_handlers.ScenariosCLHandler),
diff --git a/testapi/opnfv_testapi/tests/UI/e2e/deployResultsControllerSpec.js b/testapi/opnfv_testapi/tests/UI/e2e/deployResultsControllerSpec.js
new file mode 100644 (file)
index 0000000..e00243b
--- /dev/null
@@ -0,0 +1,397 @@
+'use strict';
+
+var mock = require('protractor-http-mock');
+var baseURL = "http://localhost:8000/"
+
+describe('testing the result page for anonymous user', function () {
+    beforeEach(function(){
+        mock([
+            {
+                request: {
+                    path: '/api/v1/pods',
+                    method: 'GET'
+                },
+                response: {
+                    data: {
+                        pods: [{role: "community-ci", name: "test", owner: "testUser",
+                        details: "DemoDetails", mode: "metal", _id: "59f02f099a07c84bfc5c7aed",
+                        creation_date: "2017-10-25 11:58:25.926168"}]
+                    }
+                }
+            },
+            {
+                request: {
+                    path: '/api/v1/deployresults',
+                    method: 'GET',
+                    queryString: {
+                        page: '1'
+                    }
+                },
+                response: {
+                    data: {
+                        "pagination": {
+                          "current_page": 1,
+                          "total_pages": 1
+                        },
+                        "deployresults": [
+                          {
+                            "build_id": 411,
+                            "upstream_build_id": 184,
+                            "scenario": "os-nosdn-nofeature-ha",
+                            "stop_date": "2018-01-2723:21:31.3N",
+                            "start_date": "2018-01-2723:21:28.3N",
+                            "upstream_job_name": "daisy-os-nosdn-nofeature-ha-baremetal-daily-master",
+                            "version": "master",
+                            "pod_name": "zte-pod",
+                            "criteria": "PASS",
+                            "installer": "daisy",
+                            "_id": "5a6dc1089a07c80f3c9f8d62",
+                            "job_name": "daisy-deploy-baremetal-daily-master",
+                            "details": null
+                          }
+                        ]
+                    }
+                }
+            },
+            {
+                request: {
+                    path: '/api/v1/deployresults',
+                    method: 'GET',
+                    queryString: {
+                        page: '1',
+                        installer: 'daisy'
+                      }
+                },
+                response: {
+                    data: {
+                        "pagination": {
+                          "current_page": 1,
+                          "total_pages": 1
+                        },
+                        "deployresults": [
+                          {
+                            "build_id": 411,
+                            "upstream_build_id": 184,
+                            "scenario": "os-nosdn-nofeature-ha",
+                            "stop_date": "2018-01-2723:21:31.3N",
+                            "start_date": "2018-01-2723:21:28.3N",
+                            "upstream_job_name": "daisy-os-nosdn-nofeature-ha-baremetal-daily-master",
+                            "version": "master",
+                            "pod_name": "zte-pod",
+                            "criteria": "PASS",
+                            "installer": "daisy",
+                            "_id": "5a6dc1089a07c80f3c9f8d63",
+                            "job_name": "daisy-deploy-baremetal-daily-master",
+                            "details": null
+                          }
+                        ]
+                    }
+                }
+            },
+            {
+                request: {
+                    path: '/api/v1/deployresults',
+                    method: 'GET',
+                    queryString: {
+                        page: '1',
+                        installer: 'daisy',
+                        job_name: 'daisy-deploy-baremetal-daily-master'
+                      }
+                },
+                response: {
+                    data: {
+                        "pagination": {
+                          "current_page": 1,
+                          "total_pages": 1
+                        },
+                        "deployresults": [
+                          {
+                            "build_id": 411,
+                            "upstream_build_id": 184,
+                            "scenario": "os-nosdn-nofeature-ha",
+                            "stop_date": "2018-01-2723:21:31.3N",
+                            "start_date": "2018-01-2723:21:28.3N",
+                            "upstream_job_name": "daisy-os-nosdn-nofeature-ha-baremetal-daily-master",
+                            "version": "master",
+                            "pod_name": "zte-pod",
+                            "criteria": "PASS",
+                            "installer": "daisy",
+                            "_id": "5a6dc1089a07c80f3c9f8d64",
+                            "job_name": "daisy-deploy-baremetal-daily-master",
+                            "details": null
+                          }
+                        ]
+                    }
+                }
+            }
+        ]);
+    });
+
+    afterEach(function(){
+        mock.teardown();
+    });
+
+    it( 'should show the deploy results page for anonymous user', function() {
+        browser.get(baseURL+"#/deployresults");
+        expect(element(by.cssContainingText(".ng-binding.ng-scope","Deploy Results")).isDisplayed()).toBe(true);
+    });
+
+    it( 'navigate anonymous user to results page', function() {
+        browser.get(baseURL);
+        var resultLink = element(by.linkText('Deploy Results')).click();
+        var EC = browser.ExpectedConditions;
+        browser.wait(EC.urlContains(baseURL+ '#/deployresults'), 10000);
+    });
+
+    it('Should show the results in results page for anonymous user ', function () {
+        browser.get(baseURL+"#/deployresults");
+        var row = element.all(by.repeater('(index, result) in ctrl.data.deployresults')).first();
+        var cells = row.all(by.tagName('td'));
+        expect(cells.get(0).getText()).toContain("3c9f8d62");
+    });
+
+    it('Should show the results in results page related to the filters for anonymous user ', function () {
+        browser.get(baseURL+"#/deployresults");
+        var filter = element(by.model('ctrl.filter'));
+        var filterText = element(by.model('ctrl.filterText'));
+        filter.sendKeys('installer');
+        filterText.sendKeys('daisy');
+        var buttonFilter = element(by.buttonText('Filter'));
+        buttonFilter.click();
+        var row = element.all(by.repeater('(index, result) in ctrl.data.deployresults')).first();
+        var cells = row.all(by.tagName('td'));
+        expect(cells.get(0).getText()).toContain("3c9f8d63");
+        filter.sendKeys('job_name');
+        filterText.sendKeys('daisy-deploy-baremetal-daily-master')
+        buttonFilter.click();
+        expect(cells.get(0).getText()).toContain("3c9f8d64");
+    });
+    it('Should not show the results in results page related to the filters for anonymous user ', function () {
+        browser.get(baseURL+"#/deployresults");
+        var filter = element(by.model('ctrl.filter'));
+        var filterText = element(by.model('ctrl.filterText'));
+        filter.sendKeys('installer');
+        filterText.sendKeys('daisyl');
+        var buttonFilter = element(by.buttonText('Filter'));
+        buttonFilter.click();
+        expect(element(by.css('.alert.alert-danger.ng-binding.ng-scope'))
+            .isDisplayed()).toBe(true);
+    });
+
+});
+
+describe('testing the result page for user', function () {
+    beforeEach(function(){
+        mock([
+            {
+                request: {
+                    path: '/api/v1/pods',
+                    method: 'GET'
+                },
+                response: {
+                    data: {
+                        pods: [{role: "community-ci", name: "test", owner: "testUser",
+                        details: "DemoDetails", mode: "metal", _id: "59f02f099a07c84bfc5c7aed",
+                        creation_date: "2017-10-25 11:58:25.926168"}]
+                    }
+                }
+            },
+            {
+                request: {
+                path: '/api/v1/profile',
+                method: 'GET'
+                },
+                response: {
+                    data: {
+                        "fullname": "Test User", "_id": "79f82eey9a00c84bfhc7aed",
+                        "user": "testUser", "groups": ["opnfv-testapi-users"],
+                        "email": "testuser@test.com"
+                    }
+                }
+            },
+            {
+                request: {
+                    path: '/api/v1/deployresults',
+                    method: 'GET',
+                    queryString: {
+                        page: '1'
+                    }
+                },
+                response: {
+                    data: {
+                        "pagination": {
+                          "current_page": 1,
+                          "total_pages": 1
+                        },
+                        "deployresults": [
+                          {
+                            "build_id": 411,
+                            "upstream_build_id": 184,
+                            "scenario": "os-nosdn-nofeature-ha",
+                            "stop_date": "2018-01-2723:21:31.3N",
+                            "start_date": "2018-01-2723:21:28.3N",
+                            "upstream_job_name": "daisy-os-nosdn-nofeature-ha-baremetal-daily-master",
+                            "version": "master",
+                            "pod_name": "zte-pod",
+                            "criteria": "PASS",
+                            "installer": "daisy",
+                            "_id": "5a6dc1089a07c80f3c9f8d62",
+                            "job_name": "daisy-deploy-baremetal-daily-master",
+                            "details": null
+                          }
+                        ]
+                    }
+                }
+            },
+            {
+                request: {
+                    path: '/api/v1/deployresults',
+                    method: 'GET',
+                    queryString: {
+                        page: '1',
+                        installer: 'daisy'
+                      }
+                },
+                response: {
+                    data: {
+                        "pagination": {
+                          "current_page": 1,
+                          "total_pages": 1
+                        },
+                        "deployresults": [
+                          {
+                            "build_id": 411,
+                            "upstream_build_id": 184,
+                            "scenario": "os-nosdn-nofeature-ha",
+                            "stop_date": "2018-01-2723:21:31.3N",
+                            "start_date": "2018-01-2723:21:28.3N",
+                            "upstream_job_name": "daisy-os-nosdn-nofeature-ha-baremetal-daily-master",
+                            "version": "master",
+                            "pod_name": "zte-pod",
+                            "criteria": "PASS",
+                            "installer": "daisy",
+                            "_id": "5a6dc1089a07c80f3c9f8d63",
+                            "job_name": "daisy-deploy-baremetal-daily-master",
+                            "details": null
+                          }
+                        ]
+                    }
+                }
+            },
+            {
+                request: {
+                    path: '/api/v1/deployresults',
+                    method: 'GET',
+                    queryString: {
+                        page: '1',
+                        installer: 'daisy',
+                        job_name : 'daisy-deploy-baremetal-daily-master'
+                      }
+                },
+                response: {
+                    data: {
+                        "pagination": {
+                          "current_page": 1,
+                          "total_pages": 1
+                        },
+                        "deployresults": [
+                          {
+                            "build_id": 411,
+                            "upstream_build_id": 184,
+                            "scenario": "os-nosdn-nofeature-ha",
+                            "stop_date": "2018-01-2723:21:31.3N",
+                            "start_date": "2018-01-2723:21:28.3N",
+                            "upstream_job_name": "daisy-os-nosdn-nofeature-ha-baremetal-daily-master",
+                            "version": "master",
+                            "pod_name": "zte-pod",
+                            "criteria": "PASS",
+                            "installer": "daisy",
+                            "_id": "5a6dc1089a07c80f3c9f8d64",
+                            "job_name": "daisy-deploy-baremetal-daily-master",
+                            "details": null
+                          }
+                        ]
+                    }
+                }
+            }
+        ]);
+    });
+
+    afterEach(function(){
+        mock.teardown();
+    });
+
+    it( 'should show the deploy results page for user', function() {
+        browser.get(baseURL+"#/deployresults");
+        expect(element(by.cssContainingText(".ng-binding.ng-scope","Deploy Results")).isDisplayed()).toBe(true);
+    });
+
+    it( 'navigate user to results page', function() {
+        browser.get(baseURL);
+        var resultLink = element(by.linkText('Deploy Results')).click();
+        var EC = browser.ExpectedConditions;
+        browser.wait(EC.urlContains(baseURL+ '#/deployresults'), 10000);
+    });
+
+    it('Should show the results in results page for user ', function () {
+        browser.get(baseURL+"#/deployresults");
+        var row = element.all(by.repeater('(index, result) in ctrl.data.deployresults')).first();
+        var cells = row.all(by.tagName('td'));
+        expect(cells.get(0).getText()).toContain("3c9f8d62");
+    });
+
+    it('Should show the results in results page related to the filters for user ', function () {
+        browser.get(baseURL+"#/deployresults");
+        var filter = element(by.model('ctrl.filter'));
+        var filterText = element(by.model('ctrl.filterText'));
+        filter.sendKeys('installer');
+        filterText.sendKeys('daisy');
+        var buttonFilter = element(by.buttonText('Filter'));
+        buttonFilter.click();
+        var row = element.all(by.repeater('(index, result) in ctrl.data.deployresults')).first();
+        var cells = row.all(by.tagName('td'));
+        expect(cells.get(0).getText()).toContain("3c9f8d63");
+        filter.sendKeys('job_name');
+        filterText.sendKeys('daisy-deploy-baremetal-daily-master')
+        buttonFilter.click();
+        expect(cells.get(0).getText()).toContain("3c9f8d64");
+    });
+    it('Should not show the results in results page related to the filters for user ', function () {
+        browser.get(baseURL+"#/deployresults");
+        var filter = element(by.model('ctrl.filter'));
+        var filterText = element(by.model('ctrl.filterText'));
+        filter.sendKeys('installer');
+        filterText.sendKeys('daisy1');
+        var buttonFilter = element(by.buttonText('Filter'));
+        buttonFilter.click();
+        expect(element(by.css('.alert.alert-danger.ng-binding.ng-scope'))
+            .isDisplayed()).toBe(true);
+    });
+
+    it('Clear the filter', function () {
+        browser.get(baseURL+"#/deployresults");
+        var filter = element(by.model('ctrl.filter'));
+        var filterText = element(by.model('ctrl.filterText'));
+        filter.sendKeys('installer');
+        filterText.sendKeys('daisy');
+        var buttonFilter = element(by.buttonText('Filter'));
+        buttonFilter.click();
+        var row = element.all(by.repeater('(index, result) in ctrl.data.deployresults')).first();
+        var cells = row.all(by.tagName('td'));
+        expect(cells.get(0).getText()).toContain("3c9f8d63");
+        var buttonClear = element(by.buttonText('Clear'));
+        buttonClear.click();
+        var row = element.all(by.repeater('(index, result) in ctrl.data.deployresults')).first();
+        var cells = row.all(by.tagName('td'));
+        expect(cells.get(0).getText()).toContain("3c9f8d62");
+    });
+
+    it('view the deploy results ', function () {
+               browser.get(baseURL+"#/deployresults");
+               var viewOperation = element(by.linkText('3c9f8d62'))
+        viewOperation.click();
+        var EC = browser.ExpectedConditions;
+        browser.wait(EC.urlContains('#/deployresults/5a6dc1089a07c80f3c9f8d62'), 10000);
+       });
+
+});
\ No newline at end of file
index 1be08b5..cfa8095 100644 (file)
@@ -122,7 +122,8 @@ module.exports = function (grunt) {
                             '../tests/UI/e2e/testCasesControllerSpec.js',
                             '../tests/UI/e2e/resultsControllerSpec.js',
                             '../tests/UI/e2e/scenariosControllerSpec.js',
-                            '../tests/UI/e2e/scenarioControllerSpec.js'
+                            '../tests/UI/e2e/scenarioControllerSpec.js',
+                            '../tests/UI/e2e/deployResultsControllerSpec.js'
                         ]
                 }
             },
index c2fa774..ae50166 100644 (file)
                 templateUrl: 'testapi-ui/components/results/result/result.html',
                 controller: 'ResultController as ctrl'
             }).
+            state('deployresults', {
+                url: '/deployresults',
+                templateUrl: 'testapi-ui/components/deploy-results/deployResults.html',
+                controller: 'DeployResultsController as ctrl'
+            }).
+            state('deployresult', {
+                url: '/deployresults/:_id',
+                templateUrl: 'testapi-ui/components/deploy-results/deploy-result/deployResult.html',
+                controller: 'DeployResultController as ctrl'
+            }).
             state('profile', {
                 url: '/profile',
                 templateUrl: 'testapi-ui/components/profile/profile.html',
diff --git a/testapi/opnfv_testapi/ui/components/deploy-results/deploy-result/deployResult.html b/testapi/opnfv_testapi/ui/components/deploy-results/deploy-result/deployResult.html
new file mode 100644 (file)
index 0000000..ba9bee7
--- /dev/null
@@ -0,0 +1,93 @@
+<legend>Result</legend>
+<div style="padding-right:0px">
+    <div class="table-responsive">
+        <table class="table"  ng-data="ctrl.data.pods">
+            <tbody>
+                <tr style="padding:9px">
+                    <td class="podsTableTd">Id&nbsp;:</td>
+                    <td class="podsTableLeftTd">{{ctrl.data._id}}</td>
+                </tr>
+                <tr style="padding:9px">
+                    <td class="podsTableTd">Installer&nbsp;:</td>
+                    <td  width="90%" class="podsTableLeftTd">{{ctrl.data.installer}}</td>
+                </tr>
+                <tr style="padding:9px">
+                    <td class="podsTableTd">Scenario&nbsp;:</td>
+                    <td width="90%" class="podsTableLeftTd">{{ctrl.data.scenario}}</td>
+                </tr>
+                <tr style="padding:9px">
+                    <td class="podsTableTd">Pod&nbsp;Name&nbsp;:</td>
+                    <td  width="90%" class="podsTableLeftTd">{{ctrl.data.pod_name}}</td>
+                </tr>
+                <tr style="padding:9px">
+                    <td class="podsTableTd">Criteria&nbsp;:</td>
+                    <td  width="90%" class="podsTableLeftTd">{{ctrl.data.criteria}}</td>
+                </tr>
+                <tr style="padding:9px">
+                    <td class="podsTableTd">Start&nbsp;Date&nbsp;:</td>
+                    <td  width="90%" class="podsTableLeftTd">{{ctrl.data.start_date}}</td>
+                </tr>
+                <tr style="padding:9px">
+                    <td class="podsTableTd">Stop&nbsp;Date&nbsp;:</td>
+                    <td width="90%"  class="podsTableLeftTd">{{ctrl.data.stop_date}}</td>
+                </tr>
+                <tr style="padding:9px">
+                    <td class="podsTableTd">Job&nbsp;Name&nbsp;:</td>
+                    <td width="90%" class="podsTableLeftTd">{{ctrl.data.job_name}}</td>
+                </tr>
+                <tr style="padding:9px">
+                    <td class="podsTableTd">Build&nbsp;ID&nbsp;:</td>
+                    <td width="90%" class="podsTableLeftTd">{{ctrl.data['build_id']}}</td>
+                </tr>
+                <tr style="padding:9px">
+                    <td class="podsTableTd">Upstream&nbsp;Job&nbsp;Name&nbsp;:</td>
+                    <td width="90%" class="podsTableLeftTd">{{ctrl.data.upstream_job_name}}</td>
+                </tr>
+                <tr style="padding:9px">
+                    <td class="podsTableTd">Upstream&nbspBuild&nbsp;ID&nbsp;:</td>
+                    <td width="90%" class="podsTableLeftTd">{{ctrl.data.upstream_build_id}}</td>
+                </tr>
+                <tr style="padding:9px">
+                    <td class="podsTableTd">Details&nbsp;:</td>
+                    <td width="90%" class="podsTableLeftTd">
+                            <a ng-click="ctrl.showDetails()">
+                                <p ng-if="ctrl.details">Hide</p>
+                                <p ng-if="!ctrl.details">Show</p>
+                            </a>
+                            <table class="table" ng-class="{'hidden' : !ctrl.details}" style="margin:10px">
+                            <tbody>
+                                <tr style="padding:9px"></tr>
+                                <tr style="padding:9px">
+                                    <td class="podsTableTd">Failures&nbsp;:</td>
+                                    <td width="90%" class="podsTableLeftTd">{{ctrl.data.details.failures}}</td>
+                                </tr>
+                                <tr style="padding:9px">
+                                    <td class="podsTableTd">Details&nbsp;:</td>
+                                    <td width="90%" class="podsTableLeftTd">{{ctrl.data.details.errors}}</td>
+                                </tr>
+                                <tr style="padding:9px">
+                                    <td class="podsTableTd">Stream&nbsp;:</td>
+                                    <td width="90%" class="podsTableLeftTd"><p>{{ctrl.data.details.stream}}</p></td>
+                                </tr>
+                                <tr style="padding:9px">
+                                    <td class="podsTableTd">TestsRun&nbsp;:</td>
+                                    <td width="90%" class="podsTableLeftTd"><p>{{ctrl.data.details.testsRun}}</p></td>
+                                </tr>
+                            </tbody>
+                        </table>
+                    </td>
+                </tr>
+            </tbody>
+        </table>
+    </div>
+</div>
+<div class="col-md-12">
+        <div ng-show="ctrl.showError" class="col-md-12 alert alert-danger" role="alert">
+            <span class="pull-right">&nbsp;{{ctrl.error}}</span>
+            <span class="glyphicon glyphicon-exclamation-sign pull-right" aria-hidden="true" >Error:</span>
+        </div>
+        <div ng-show="ctrl.showSuccess" class="col-md-12 alert alert-success" role="alert">
+            <span class="pull-right">&nbsp;{{ctrl.success}}</span>
+            <span class="glyphicon glyphicon-ok pull-right" aria-hidden="true"></span>
+        </div>
+    </div>
\ No newline at end of file
diff --git a/testapi/opnfv_testapi/ui/components/deploy-results/deploy-result/deployResultController.js b/testapi/opnfv_testapi/ui/components/deploy-results/deploy-result/deployResultController.js
new file mode 100644 (file)
index 0000000..40cf1cb
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+(function () {
+    'use strict';
+
+    angular
+        .module('testapiApp')
+        .controller('DeployResultController', DeployResultController);
+
+    DeployResultController.$inject = [
+        '$scope', '$http', '$filter', '$state', '$window', '$uibModal', 'testapiApiUrl','raiseAlert',
+        'confirmModal'
+    ];
+
+    /**
+     * TestAPI DeployResultController
+     * This controller is for the '/result/:_id' page where a user can browse
+     * through result declared in TestAPI.
+     */
+    function DeployResultController($scope, $http, $filter, $state, $window, $uibModal, testapiApiUrl,
+        raiseAlert, confirmModal) {
+        var ctrl = this;
+        ctrl.url = testapiApiUrl + '/deployresults';
+        ctrl._id = $state.params['_id'];
+        ctrl.loadDetails = loadDetails
+        ctrl.showDetails = showDetails
+
+        /**
+         *Contact the testapi and retrevie the result details
+         */
+        function loadDetails(){
+            var resultUrl = ctrl.url + '/' + ctrl._id;
+            ctrl.showError = false;
+            ctrl.podsRequest =
+                $http.get(resultUrl).success(function (data) {
+                    ctrl.data = data;
+                }).catch(function (error) {
+                    ctrl.data = null;
+                    ctrl.showError = true;
+                    ctrl.error = error.statusText;
+                });
+        }
+
+        function showDetails(){
+            if(ctrl.details){
+                ctrl.details = false
+            }else{
+                ctrl.details = true
+            }
+        }
+        ctrl.loadDetails();
+    }
+})();
\ No newline at end of file
diff --git a/testapi/opnfv_testapi/ui/components/deploy-results/deployResults.html b/testapi/opnfv_testapi/ui/components/deploy-results/deployResults.html
new file mode 100644 (file)
index 0000000..6fbaaea
--- /dev/null
@@ -0,0 +1,126 @@
+<h3>{{ctrl.pageHeader}}</h3>
+<p>{{ctrl.pageParagraph}}</p>
+<div class="row" style="margin-bottom:24px;"></div>
+<div class="result-filters" style="border-top: none;">
+    <div class="row podTable" style="vertical-align:middle">
+            <div class="col-sm-1 pull-right">
+                    <button type="button" class="btn btn-danger" ng-click="ctrl.clearFilters()">
+                      <i class="fa fa-search"></i> Clear
+                    </button>
+            </div>
+        <div class="col-sm-1 pull-right">
+            <button type="button" class="btn btn-success" ng-click="ctrl.filterList()">
+              <i class="fa fa-search"></i> Filter</button>
+        </div>
+        <div class="col-sm-2 pull-right" ng-class="{'hidden': ctrl.filter=='start_date' || ctrl.filter=='end_date'}">
+            <span style="margin-top:6px">Search:&nbsp;&nbsp;</span>
+            <input list="filter" name="filter" class="form-control search" style="display:inline;width:105px;padding-left:6px;"
+                ng-Model="ctrl.filterText" placeholder="Search String">
+                <datalist id="filter" ng-class="{ 'hidden' : ctrl.filterOption.length<0}">
+                    <option ng-repeat="(index, filterValue) in ctrl.filterOption " value="{{filterValue}}">{{filterValue}}</option>
+                </datalist>
+        </div>
+        <div class="col-sm-3 pull-right" style="width:20%" ng-class="{'hidden': ctrl.filter!='start_date'}">
+            <span style="margin-top:6px">Start&nbsp;Date:&nbsp;&nbsp;</span>
+            <p class="input-group" style="width:48%;display:inline-flex;">
+                <input type="text" class="form-control"
+                    uib-datepicker-popup="{{ctrl.format}}"
+                    ng-model="ctrl.filterText" is-open="ctrl.startOpen"
+                    close-text="Close" />
+                <span class="input-group-btn">
+                    <button type="button" class="btn btn-default" ng-click="ctrl.open($event, 'startOpen')">
+                        <i class="glyphicon glyphicon-calendar"></i>
+                    </button>
+                </span>
+            </p>
+        </div>
+        <div class="col-sm-3 pull-right" style="width:20%" ng-class="{'hidden': ctrl.filter!='end_date'}">
+            <span style="margin-top:6px">End&nbsp;Date:&nbsp;&nbsp;</span>
+            <p class="input-group" style="width:48%;display:inline-flex;">
+                <input type="text" class="form-control"
+                    uib-datepicker-popup="{{ctrl.format}}"
+                    ng-model="ctrl.filterText" is-open="ctrl.endOpen"
+                    close-text="Close" />
+                <span class="input-group-btn">
+                    <button type="button" class="btn btn-default" ng-click="ctrl.open($event, 'endOpen')">
+                        <i class="glyphicon glyphicon-calendar"></i>
+                    </button>
+                </span>
+            </p>
+        </div>
+        <div class="col-md-2 row pull-right" style="width: 20%;">
+            <span style="margin-top:6px">Filter:&nbsp;&nbsp;</span>
+            <select ng-model="ctrl.filter" ng-change="ctrl.encodeFilter()" class="form-control" style="display:inline; width:150px;">
+                    <option value="pod" ng-disabled="ctrl.testFilter('pod')" >Pod Name</option>
+                    <option value="job_name" ng-disabled="ctrl.testFilter('job_name')" >Job Name</option>
+                    <option value="installer" ng-disabled="ctrl.testFilter('installer')">Installer</option>
+                    <option value="version" ng-disabled="ctrl.testFilter('version')">Version</option>
+                    <option value="scenario" ng-disabled="ctrl.testFilter('scenario')">Scenario</option>
+                    <option value="build_id" ng-disabled="ctrl.testFilter('build_id')">Build ID</option>
+                    <option value="criteria" ng-disabled="ctrl.testFilter('criteria')">Criteria</option>
+                    <option value="start_date" ng-disabled="ctrl.testFilter('start_date')">Start Date</option>
+                    <option value="end_date" ng-disabled="ctrl.testFilter('end_date')">End Date</option>
+            </select>
+        </div>
+        <div class='filter-box'>
+            <div class='filter-tag col-md-1' ng-repeat="(key, tag) in ctrl.tagArray" style="background-color: #f5f5f5;border: 1px solid #e3e3e3;/* border:  1px; */margin-top: 3px;padding: 4px;margin-left: 15px;width: 13%;">
+                {{key}}&nbsp:&nbsp{{tag}}
+                <div class='delete-tag btn btn-danger btn-xs' ng-click='ctrl.deleteTag(key)'>
+                    &times;
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+
+<div cg-busy="{promise:ctrl.authRequest,message:'Loading'}"></div>
+<div cg-busy="{promise:ctrl.resultsRequest,message:'Loading'}"></div>
+<div ng-show="ctrl.data" class="results-table">
+    <table ng-data="ctrl.data.deployresults" ng-show="ctrl.data" class="table table-striped table-hover">
+        <thead>
+            <tr>
+                <th>ID</th>
+                <th>Installer</th>
+                <th>Scenario</th>
+                <th>Pod</th>
+                <th>Criteria</th>
+                <th>Start&nbsp;Date</th>
+                <th>Stop&nbsp;Date</th>
+            </tr>
+        </thead>
+
+        <tbody>
+            <tr ng-repeat-start="(index, result) in ctrl.data.deployresults">
+                <td><a ng-click="ctrl.viewDeployResult(result._id)">{{ result._id.substr(-8) }}</a></td>
+                <td>{{ result.installer }}</td>
+                <td>{{ result.scenario }}</td>
+                <td>{{ result.pod_name }}</td>
+                <td>{{ result.criteria }}</td>
+                <td>{{ result.start_date }}</td>
+                <td>{{ result.stop_date }}</td>
+            </tr>
+            <tr ng-repeat-end=>
+            </tr>
+        </tbody>
+    </table>
+
+    <div class="pages">
+        <uib-pagination
+            total-items="ctrl.totalItems"
+            ng-model="ctrl.currentPage"
+            items-per-page="ctrl.itemsPerPage"
+            max-size="ctrl.maxSize"
+            class="pagination-sm"
+            boundary-links="true"
+            rotate="false"
+            num-pages="ctrl.numPages"
+            ng-change="ctrl.filterList()">
+        </uib-pagination>
+    </div>
+</div>
+
+<div ng-show="ctrl.showError" class="alert alert-danger" role="alert">
+    <span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>
+    <span class="sr-only">Error:</span>
+    {{ctrl.error}}
+</div>
diff --git a/testapi/opnfv_testapi/ui/components/deploy-results/deployResultsController.js b/testapi/opnfv_testapi/ui/components/deploy-results/deployResultsController.js
new file mode 100644 (file)
index 0000000..5230a75
--- /dev/null
@@ -0,0 +1,190 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+(function () {
+    'use strict';
+
+    angular
+        .module('testapiApp')
+        .controller('DeployResultsController', DeployResultsController);
+
+    DeployResultsController.$inject = [
+        '$scope', '$http', '$filter', '$state', 'testapiApiUrl','raiseAlert'
+    ];
+
+    /**
+     * TestAPI Deploy Results Controller
+     * This controller is for the '/deployresults' page where a user can browse
+     * a listing of community uploaded results.
+     */
+    function DeployResultsController($scope, $http, $filter, $state, testapiApiUrl,
+        raiseAlert) {
+        var ctrl = this;
+
+        ctrl.open = open;
+        ctrl.clearFilters = clearFilters;
+        ctrl.deleteTag = deleteTag;
+        ctrl.filterList= filterList;
+        ctrl.testFilter = testFilter
+        ctrl.filter = "pod"
+        ctrl.filterValue = "pod_name"
+        ctrl.encodeFilter = encodeFilter
+        ctrl.viewDeployResult = viewDeployResult
+        ctrl.tagArray = {}
+        ctrl.filterOption=[]
+
+        /** Initial page to be on. */
+        ctrl.currentPage = 1;
+
+        /**
+         * How many results should display on each page. Since pagination
+         * is server-side implemented, this value should match the
+         * 'results_per_page' configuration of the TestAPI server which
+         * defaults to 20.
+         */
+        ctrl.itemsPerPage = 20;
+
+        /**
+         * How many page buttons should be displayed at max before adding
+         * the '...' button.
+         */
+        ctrl.maxSize = 5;
+
+        /** The upload date lower limit to be used in filtering results. */
+        ctrl.startDate = '';
+
+        /** The upload date upper limit to be used in filtering results. */
+        ctrl.endDate = '';
+
+        /** The date format for the date picker. */
+        ctrl.format = 'yyyy-MM-dd';
+
+        ctrl.pageHeader = "Deploy Results"
+
+        ctrl.pageParagraph = 'Your most recently uploaded deploy results are listed here.'
+        ctrl.isPublic = false;
+
+        function encodeFilter(){
+            ctrl.filterText = ''
+            ctrl.filterOption=[]
+            if(ctrl.filter=="pod" || ctrl.filter=="scenario"){
+                var reqURL = testapiApiUrl +"/" + ctrl.filter + "s"
+                ctrl.datasRequest =
+                    $http.get(reqURL).success(function (data) {
+                        ctrl.filterData = data;
+                        for(var index in ctrl.filterData[ctrl.filter + "s"]){
+                            if( ctrl.filterOption.indexOf(ctrl.filterData[ctrl.filter + "s"][index]["name"]) < 0){
+                                ctrl.filterOption.push(ctrl.filterData[ctrl.filter + "s"][index]["name"])
+                            }
+                        }
+                    }).catch(function (data) {
+                        ctrl.data = null;
+                        ctrl.showError = true;
+                        ctrl.error = data.statusText;
+                    });
+
+            }
+        }
+
+        function deleteTag(index){
+            delete  ctrl.tagArray[index];
+            ctrl.filterList();
+        }
+
+        function testFilter(text){
+            for (var filter in ctrl.tagArray){
+                if(text==filter){
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        function viewDeployResult(_id){
+            $state.go('deployresult', {'_id':_id}, {reload: true});
+        }
+
+        /**
+         * This will contact the TestAPI API to get a listing of deploy
+         * results.
+         */
+        function filterList(){
+            if(ctrl.filter && ctrl.filterText!="" && ctrl.filterText!=undefined){
+                ctrl.tagArray[ctrl.filter] =  ctrl.filterText;
+            }
+            ctrl.showError = false;
+            var content_url = testapiApiUrl + '/deployresults' +
+                '?page=' + ctrl.currentPage;
+            for(var key in ctrl.tagArray){
+                if(key=="start_date"){
+                    var start = $filter('date')(ctrl.tagArray[key], 'yyyy-MM-dd');
+                    if (start) {
+                        content_url =
+                            content_url + '&from=' + start + ' 00:00:00';
+                    }
+                }
+                else if(key=="end_date"){
+                    var end = $filter('date')(ctrl.tagArray[key], 'yyyy-MM-dd');
+                    if (end) {
+                        content_url = content_url + '&to=' + end + ' 23:59:59';
+                    }
+                }
+                else{
+                    content_url = content_url + "&" + key + "=" + ctrl.tagArray[key]
+                }
+                if (ctrl.isUserResults) {
+                    content_url = content_url + '&signed';
+                }
+            }
+            ctrl.resultsRequest =
+                    $http.get(content_url).success(function (data) {
+                        ctrl.data = data;
+                        ctrl.totalItems = ctrl.data.pagination.total_pages * ctrl.itemsPerPage;
+                        ctrl.currentPage = ctrl.data.pagination.current_page;
+                        ctrl.encodeFilter();
+                    }).catch(function (error) {
+                        ctrl.data = null;
+                        ctrl.totalItems = 0;
+                        ctrl.showError = true;
+                        ctrl.error = error.statusText
+                    });
+            ctrl.filterText = ''
+        }
+        ctrl.filterList();
+
+
+        /**
+         * This is called when the date filter calendar is opened. It
+         * does some event handling, and sets a scope variable so the UI
+         * knows which calendar was opened.
+         * @param {Object} $event - The Event object
+         * @param {String} openVar - Tells which calendar was opened
+         */
+        function open($event, openVar) {
+            $event.preventDefault();
+            $event.stopPropagation();
+            ctrl[openVar] = true;
+        }
+
+        /**
+         * This function will clear all filters and update the results
+         * listing.
+         */
+        function clearFilters() {
+            ctrl.tagArray = {}
+            ctrl.filter = undefined
+            ctrl.filterList();
+        }
+    }
+})();
index beec61d..68c6cc5 100644 (file)
@@ -51,6 +51,8 @@
         <script src="testapi-ui/components/projects/project/projectController.js"></script>
         <script src="testapi-ui/components/results/resultsController.js"></script>
         <script src="testapi-ui/components/results/result/resultController.js"></script>
+        <script src="testapi-ui/components/deploy-results/deployResultsController.js"></script>
+        <script src="testapi-ui/components/deploy-results/deploy-result/deployResultController.js"></script>
         <script src="testapi-ui/components/profile/profileController.js"></script>
         <script src="testapi-ui/components/auth-failure/authFailureController.js"></script>
         <script src="testapi-ui/components/logout/logoutController.js"></script>
index 3b2aba1..2b7be2f 100644 (file)
@@ -20,6 +20,7 @@ TestAPI
             <li ng-class="{ active: header.isActive('/pods')}"><a ui-sref="pods">Pods</a></li>
             <li ng-class="{ active: header.isActive('/projects')}"><a ui-sref="projects">Projects</a></li>
             <li ng-class="{ active: header.isActive('/results')}"><a ui-sref="results">Results</a></li>
+            <li ng-class="{ active: header.isActive('/deployresults')}"><a ui-sref="deployresults">Deploy Results</a></li>
             <li ng-class="{ active: header.isActive('/scenarios')}"><a ui-sref="scenarios">Scenarios</a></li>
           </ul>
           <ul class="nav navbar-nav navbar-right" ng-class="{'hidden' : !authenticate}">