Merge "divide resources into handlers and models"
[releng.git] / utils / test / testapi / 3rd_party / static / testapi-ui / components / results / resultsController.js
1 /*
2  * Licensed under the Apache License, Version 2.0 (the "License");
3  * you may not use this file except in compliance with the License.
4  * You may obtain a copy of the License at
5  *
6  * http://www.apache.org/licenses/LICENSE-2.0
7  *
8  * Unless required by applicable law or agreed to in writing, software
9  * distributed under the License is distributed on an "AS IS" BASIS,
10  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11  * See the License for the specific language governing permissions and
12  * limitations under the License.
13  */
14
15 (function () {
16     'use strict';
17
18     angular
19         .module('testapiApp')
20         .controller('ResultsController', ResultsController);
21
22     angular
23         .module('testapiApp')
24         .directive('fileModel', ['$parse', function ($parse) {
25             return {
26                restrict: 'A',
27                link: function(scope, element, attrs) {
28                   var model = $parse(attrs.fileModel);
29                   var modelSetter = model.assign;
30
31                   element.bind('change', function(){
32                      scope.$apply(function(){
33                         modelSetter(scope, element[0].files[0]);
34                      });
35                   });
36                }
37             };
38          }]);
39
40     ResultsController.$inject = [
41         '$scope', '$http', '$filter', '$state', 'testapiApiUrl','raiseAlert'
42     ];
43
44     /**
45      * TestAPI Results Controller
46      * This controller is for the '/results' page where a user can browse
47      * a listing of community uploaded results.
48      */
49     function ResultsController($scope, $http, $filter, $state, testapiApiUrl,
50         raiseAlert) {
51         var ctrl = this;
52
53         ctrl.uploadFile=uploadFile;
54         ctrl.update = update;
55         ctrl.open = open;
56         ctrl.clearFilters = clearFilters;
57         ctrl.associateMeta = associateMeta;
58         ctrl.getVersionList = getVersionList;
59         ctrl.getUserProducts = getUserProducts;
60         ctrl.associateProductVersion = associateProductVersion;
61         ctrl.getProductVersions = getProductVersions;
62         ctrl.prepVersionEdit = prepVersionEdit;
63
64         /** Mappings of Interop WG components to marketing program names. */
65         ctrl.targetMappings = {
66             'platform': 'Openstack Powered Platform',
67             'compute': 'OpenStack Powered Compute',
68             'object': 'OpenStack Powered Object Storage'
69         };
70
71         /** Initial page to be on. */
72         ctrl.currentPage = 1;
73
74         /**
75          * How many results should display on each page. Since pagination
76          * is server-side implemented, this value should match the
77          * 'results_per_page' configuration of the TestAPI server which
78          * defaults to 20.
79          */
80         ctrl.itemsPerPage = 20;
81
82         /**
83          * How many page buttons should be displayed at max before adding
84          * the '...' button.
85          */
86         ctrl.maxSize = 5;
87
88         /** The upload date lower limit to be used in filtering results. */
89         ctrl.startDate = '';
90
91         /** The upload date upper limit to be used in filtering results. */
92         ctrl.endDate = '';
93
94         /** The date format for the date picker. */
95         ctrl.format = 'yyyy-MM-dd';
96
97         /** Check to see if this page should display user-specific results. */
98         // ctrl.isUserResults = $state.current.name === 'userResults';
99         // need auth to browse
100         ctrl.isUserResults = $state.current.name === 'userResults';
101
102         // Should only be on user-results-page if authenticated.
103         if (ctrl.isUserResults && !$scope.auth.isAuthenticated) {
104             $state.go('home');
105         }
106
107         ctrl.pageHeader = ctrl.isUserResults ?
108             'Private test results' : 'Community test results';
109
110         ctrl.pageParagraph = ctrl.isUserResults ?
111             'Your most recently uploaded test results are listed here.' :
112             'The most recently uploaded community test results are listed ' +
113             'here.';
114
115         ctrl.uploadState = '';
116
117         ctrl.isPublic = false;
118
119         if (ctrl.isUserResults) {
120             ctrl.authRequest = $scope.auth.doSignCheck()
121                 .then(ctrl.update);
122             // ctrl.getUserProducts();
123         } else {
124             ctrl.update();
125         }
126
127
128         function uploadFileToUrl(file, uploadUrl){
129            var fd = new FormData();
130            fd.append('file', file);
131            fd.append('public', ctrl.isPublic)
132
133            $http.post(uploadUrl, fd, {
134               transformRequest: angular.identity,
135               headers: {'Content-Type': undefined}
136            })
137
138            .success(function(data){
139               var id = data.href.substr(data.href.lastIndexOf('/')+1);
140               ctrl.uploadState = "Upload succeed. Result id is " + id;
141               ctrl.update();
142            })
143
144            .error(function(data, status){
145               ctrl.uploadState = "Upload failed. Error code is " + status;
146            });
147         }
148
149         function uploadFile(){
150            var file = $scope.resultFile;
151            console.log('file is ' );
152            console.dir(file);
153
154            var uploadUrl = testapiApiUrl + "/results/upload";
155            uploadFileToUrl(file, uploadUrl);
156         };
157
158         /**
159          * This will contact the TestAPI API to get a listing of test run
160          * results.
161          */
162         function update() {
163             ctrl.showError = false;
164             // Construct the API URL based on user-specified filters.
165             var content_url = testapiApiUrl + '/results' +
166                 '?page=' + ctrl.currentPage;
167             var start = $filter('date')(ctrl.startDate, 'yyyy-MM-dd');
168             if (start) {
169                 content_url =
170                     content_url + '&from=' + start + ' 00:00:00';
171             }
172             var end = $filter('date')(ctrl.endDate, 'yyyy-MM-dd');
173             if (end) {
174                 content_url = content_url + '&to=' + end + ' 23:59:59';
175             }
176             if (ctrl.isUserResults) {
177                 content_url = content_url + '&signed';
178             }
179             ctrl.resultsRequest =
180                 $http.get(content_url).success(function (data) {
181                     ctrl.data = data;
182                     ctrl.totalItems = ctrl.data.pagination.total_pages * ctrl.itemsPerPage;
183                     ctrl.currentPage = ctrl.data.pagination.current_page;
184                 }).error(function (error) {
185                     ctrl.data = null;
186                     ctrl.totalItems = 0;
187                     ctrl.showError = true;
188                     ctrl.error =
189                         'Error retrieving results listing from server: ' +
190                         angular.toJson(error);
191                 });
192         }
193
194         /**
195          * This is called when the date filter calendar is opened. It
196          * does some event handling, and sets a scope variable so the UI
197          * knows which calendar was opened.
198          * @param {Object} $event - The Event object
199          * @param {String} openVar - Tells which calendar was opened
200          */
201         function open($event, openVar) {
202             $event.preventDefault();
203             $event.stopPropagation();
204             ctrl[openVar] = true;
205         }
206
207         /**
208          * This function will clear all filters and update the results
209          * listing.
210          */
211         function clearFilters() {
212             ctrl.startDate = null;
213             ctrl.endDate = null;
214             ctrl.update();
215         }
216
217         /**
218          * This will send an API request in order to associate a metadata
219          * key-value pair with the given testId
220          * @param {Number} index - index of the test object in the results list
221          * @param {String} key - metadata key
222          * @param {String} value - metadata value
223          */
224         function associateMeta(index, key, value) {
225             var testId = ctrl.data.results[index].id;
226             var metaUrl = [
227                 testapiApiUrl, '/results/', testId, '/meta/', key
228             ].join('');
229
230             var editFlag = key + 'Edit';
231             if (value) {
232                 ctrl.associateRequest = $http.post(metaUrl, value)
233                     .success(function () {
234                         ctrl.data.results[index][editFlag] = false;
235                     }).error(function (error) {
236                         raiseAlert('danger', error.title, error.detail);
237                     });
238             }
239             else {
240                 ctrl.unassociateRequest = $http.delete(metaUrl)
241                     .success(function () {
242                         ctrl.data.results[index][editFlag] = false;
243                     }).error(function (error) {
244                         if (error.code == 404) {
245                             // Key doesn't exist, so count it as a success,
246                             // and don't raise an alert.
247                             ctrl.data.results[index][editFlag] = false;
248                         }
249                         else {
250                             raiseAlert('danger', error.title, error.detail);
251                         }
252                     });
253             }
254         }
255
256         /**
257          * Retrieve an array of available capability files from the TestAPI
258          * API server, sort this array reverse-alphabetically, and store it in
259          * a scoped variable.
260          * Sample API return array: ["2015.03.json", "2015.04.json"]
261          */
262         function getVersionList() {
263             if (ctrl.versionList) {
264                 return;
265             }
266             var content_url = testapiApiUrl + '/guidelines';
267             ctrl.versionsRequest =
268                 $http.get(content_url).success(function (data) {
269                     ctrl.versionList = data.sort().reverse();
270                 }).error(function (error) {
271                     raiseAlert('danger', error.title,
272                                'Unable to retrieve version list');
273                 });
274         }
275
276         /**
277          * Get products user has management rights to or all products depending
278          * on the passed in parameter value.
279          */
280         function getUserProducts() {
281             if (ctrl.products) {
282                 return;
283             }
284             var contentUrl = testapiApiUrl + '/products';
285             ctrl.productsRequest =
286                 $http.get(contentUrl).success(function (data) {
287                     ctrl.products = {};
288                     angular.forEach(data.products, function(prod) {
289                         if (prod.can_manage) {
290                             ctrl.products[prod.id] = prod;
291                         }
292                     });
293                 }).error(function (error) {
294                     ctrl.products = null;
295                     ctrl.showError = true;
296                     ctrl.error =
297                         'Error retrieving Products listing from server: ' +
298                         angular.toJson(error);
299                 });
300         }
301
302         /**
303          * Send a PUT request to the API server to associate a product with
304          * a test result.
305          */
306         function associateProductVersion(result) {
307             var verId = (result.selectedVersion ?
308                          result.selectedVersion.id : null);
309             var testId = result.id;
310             var url = testapiApiUrl + '/results/' + testId;
311             ctrl.associateRequest = $http.put(url, {'product_version_id':
312                                                     verId})
313                 .success(function (data) {
314                     result.product_version = result.selectedVersion;
315                     if (result.selectedVersion) {
316                         result.product_version.product_info =
317                             result.selectedProduct;
318                     }
319                     result.productEdit = false;
320                 }).error(function (error) {
321                     raiseAlert('danger', error.title, error.detail);
322                 });
323         }
324
325         /**
326          * Get all versions for a product.
327          */
328         function getProductVersions(result) {
329             if (!result.selectedProduct) {
330                 result.productVersions = [];
331                 result.selectedVersion = null;
332                 return;
333             }
334
335             var url = testapiApiUrl + '/products/' +
336                 result.selectedProduct.id + '/versions';
337             ctrl.getVersionsRequest = $http.get(url)
338                 .success(function (data) {
339                     result.productVersions = data;
340
341                     // If the test result isn't already associated to a
342                     // version, default it to the null version.
343                     if (!result.product_version) {
344                         angular.forEach(data, function(ver) {
345                             if (!ver.version) {
346                                 result.selectedVersion = ver;
347                             }
348                         });
349                     }
350                 }).error(function (error) {
351                     raiseAlert('danger', error.title, error.detail);
352                 });
353         }
354
355         /**
356          * Instantiate variables needed for editing product/version
357          * associations.
358          */
359         function prepVersionEdit(result) {
360             result.productEdit = true;
361             if (result.product_version) {
362                 result.selectedProduct =
363                     ctrl.products[result.product_version.product_info.id];
364             }
365             result.selectedVersion = result.product_version;
366             ctrl.getProductVersions(result);
367         }
368
369     }
370 })();