blob: 69a94b8299bcbfc8956fd8c832798e7a23b5b132 [file] [log] [blame]
Timo Tijhof51516cd2013-04-09 01:32:29 +02001// Client script for Zuul status page
2//
3// Copyright 2012 OpenStack Foundation
4// Copyright 2013 Timo Tijhof
5// Copyright 2013 Wikimedia Foundation
6//
7// Licensed under the Apache License, Version 2.0 (the "License"); you may
8// not use this file except in compliance with the License. You may obtain
9// a copy of the License at
10//
11// http://www.apache.org/licenses/LICENSE-2.0
12//
13// Unless required by applicable law or agreed to in writing, software
14// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
16// License for the specific language governing permissions and limitations
17// under the License.
18
19(function ($) {
20 var $container, $msg, $msgWrap, $indicator, $queueInfo, $queueEventsNum, $queueResultsNum, $pipelines,
21 prevHtml, xhr, zuul, $jq,
22 demo = location.search.match(/[?&]demo=([^?&]*)/),
23 source = demo ?
24 './status-' + (demo[1] || 'basic') + '.json-sample' :
James E. Blair7c7ed7a2013-05-15 13:13:26 -070025 'status.json';
Timo Tijhof51516cd2013-04-09 01:32:29 +020026
27 zuul = {
28 enabled: true,
29
30 schedule: function () {
31 if (!zuul.enabled) {
32 setTimeout(zuul.schedule, 5000);
33 return;
34 }
35 zuul.update().complete(function () {
36 setTimeout(zuul.schedule, 5000);
37 });
38 },
39
40 /** @return {jQuery.Promise} */
41 update: function () {
42 // Cancel the previous update if it hasn't completed yet.
43 if (xhr) {
44 xhr.abort();
45 }
46
47 zuul.emit('update-start');
48
49 xhr = $.ajax({
50 url: source,
51 dataType: 'json',
52 cache: false
53 })
54 .done(function (data) {
55 var html = '';
56 data = data || {};
57
58 if ('message' in data) {
59 $msg.html(data.message);
60 $msgWrap.removeClass('zuul-msg-wrap-off');
61 } else {
62 $msg.empty();
63 $msgWrap.addClass('zuul-msg-wrap-off');
64 }
65
Sergey Lukjanovdf550c52014-01-11 00:05:16 +040066 if ('zuul_version' in data) {
67 $('#zuul-version-span').text(data['zuul_version']);
68 }
69
Timo Tijhof51516cd2013-04-09 01:32:29 +020070 $.each(data.pipelines, function (i, pipeline) {
71 html += zuul.format.pipeline(pipeline);
72 });
73
74 // Only re-parse the DOM if we have to
75 if (html !== prevHtml) {
76 prevHtml = html;
77 $pipelines.html(html);
78 }
79
80 $queueEventsNum.text(
81 data.trigger_event_queue ? data.trigger_event_queue.length : '0'
82 );
83 $queueResultsNum.text(
84 data.result_event_queue ? data.result_event_queue.length : '0'
85 );
86 })
87 .fail(function (err, jqXHR, errMsg) {
88 $msg.text(source + ': ' + errMsg).show();
89 $msgWrap.removeClass('zuul-msg-wrap-off');
90 })
91 .complete(function () {
92 xhr = undefined;
93 zuul.emit('update-end');
94 });
95
96 return xhr;
97 },
98
99 format: {
100 change: function (change) {
101 var html = '<div class="well well-small zuul-change"><ul class="nav nav-list">',
102 id = change.id,
103 url = change.url;
104
105 html += '<li class="nav-header">' + change.project;
106 if (id.length === 40) {
107 id = id.substr(0, 7);
108 }
109 html += ' <span class="zuul-change-id">';
110 if (url !== null) {
111 html += '<a href="' + url + '">';
112 }
113 html += id;
114 if (url !== null) {
115 html += '</a>';
116 }
117 html += '</span></li>';
118
119 $.each(change.jobs, function (i, job) {
120 var result = job.result ? job.result.toLowerCase() : null,
121 resultClass = 'zuul-result label';
122 if (result === null) {
123 result = job.url ? 'in progress' : 'queued';
124 }
125 switch (result) {
126 case 'success':
127 resultClass += ' label-success';
128 break;
129 case 'failure':
130 resultClass += ' label-important';
131 break;
132 case 'lost':
133 case 'unstable':
134 resultClass += ' label-warning';
135 break;
136 }
137 html += '<li class="zuul-change-job">';
138 html += job.url !== null ?
139 '<a href="' + job.url + '" class="zuul-change-job-link">' :
140 '<span class="zuul-change-job-link">';
141 html += job.name;
142 html += ' <span class="' + resultClass + '">' + result + '</span>';
143 if (job.voting === false) {
144 html += ' <span class="muted">(non-voting)</span>';
145 }
146 html += job.url !== null ? '</a>' : '</span>';
147 html += '</li>';
148 });
149
150 html += '</ul></div>';
151 return html;
152 },
153
154 pipeline: function (pipeline) {
155 var html = '<div class="zuul-pipeline span4"><h3>' +
156 pipeline.name + '</h3>';
157 if (typeof pipeline.description === 'string') {
158 html += '<p><small>' + pipeline.description + '</small></p>';
159 }
160
161 $.each(pipeline.change_queues, function (queueNum, changeQueue) {
162 $.each(changeQueue.heads, function (headNum, changes) {
163 if (pipeline.change_queues.length > 1 && headNum === 0) {
164 var name = changeQueue.name;
165 html += '<p>Queue: <abbr title="' + name + '">';
166 if (name.length > 32) {
167 name = name.substr(0, 32) + '...';
168 }
169 html += name + '</abbr></p>';
170 }
171 $.each(changes, function (changeNum, change) {
172 // If there are multiple changes in the same head it means they're connected
173 if (changeNum > 0) {
174 html += '<div class="zuul-change-arrow">&uarr;</div>';
175 }
176 html += zuul.format.change(change);
177 });
178 });
179 });
180
181 html += '</div>';
182 return html;
183 }
184 },
185
186 emit: function () {
187 $jq.trigger.apply($jq, arguments);
188 return this;
189 },
190 on: function () {
191 $jq.on.apply($jq, arguments);
192 return this;
193 },
194 one: function () {
195 $jq.one.apply($jq, arguments);
196 return this;
197 }
198 };
199
200 $jq = $(zuul);
201
202 $jq.on('update-start', function () {
203 $container.addClass('zuul-container-loading');
204 $indicator.addClass('zuul-spinner-on');
205 });
206
207 $jq.on('update-end', function () {
208 $container.removeClass('zuul-container-loading');
209 setTimeout(function () {
210 $indicator.removeClass('zuul-spinner-on');
211 }, 550);
212 });
213
214 $jq.one('update-end', function () {
215 // Do this asynchronous so that if the first update adds a message, it will not animate
216 // while we fade in the content. Instead it simply appears with the rest of the content.
217 setTimeout(function () {
218 $container.addClass('zuul-container-ready'); // Fades in the content
219 });
220 });
221
222 $(function ($) {
223 $msg = $('<div class="zuul-msg alert alert-error"></div>');
224 $msgWrap = $msg.wrap('<div class="zuul-msg-wrap zuul-msg-wrap-off"></div>').parent();
225 $indicator = $('<span class="btn pull-right zuul-spinner">updating <i class="icon-refresh"></i></span>');
226 $queueInfo = $('<p>Queue lengths: <span>0</span> events, <span>0</span> results.</p>');
227 $queueEventsNum = $queueInfo.find('span').eq(0);
228 $queueResultsNum = $queueEventsNum.next();
229 $pipelines = $('<div class="row"></div>');
Sergey Lukjanovdf550c52014-01-11 00:05:16 +0400230 $zuulVersion = $('<p>Zuul version: <span id="zuul-version-span"></span></p>');
Timo Tijhof51516cd2013-04-09 01:32:29 +0200231
Sergey Lukjanovdf550c52014-01-11 00:05:16 +0400232 $container = $('#zuul-container').append($msgWrap, $indicator, $queueInfo, $pipelines, $zuulVersion);
Timo Tijhof51516cd2013-04-09 01:32:29 +0200233
234 zuul.schedule();
235
236 $(document).on({
237 'show.visibility': function () {
238 zuul.enabled = true;
239 zuul.update();
240 },
241 'hide.visibility': function () {
242 zuul.enabled = false;
243 }
244 });
245 });
246}(jQuery));