Browse Source

Added support for Shields.io badges. cc: #304, #305

pull/313/head
Pēteris Caune 5 years ago
parent
commit
22d4d55340
No known key found for this signature in database GPG Key ID: E28D7679E9A9EDE2
9 changed files with 74 additions and 28 deletions
  1. +1
    -0
      CHANGELOG.md
  2. +4
    -0
      hc/api/tests/test_badge.py
  3. +2
    -14
      hc/api/urls.py
  4. +15
    -3
      hc/api/views.py
  5. +2
    -1
      hc/front/views.py
  6. +3
    -5
      hc/lib/badges.py
  7. +5
    -1
      static/css/settings.css
  8. +9
    -2
      static/js/badges.js
  9. +33
    -2
      templates/front/badges.html

+ 1
- 0
CHANGELOG.md View File

@ -6,6 +6,7 @@ All notable changes to this project will be documented in this file.
### Improvements
- "Filtering Rules" dialog, an option to require HTTP POST (#297)
- Show Healthchecks version in Django admin header (#306)
- Added JSON endpoint for Shields.io (#304)
## v1.11.0 - 2019-11-22


+ 4
- 0
hc/api/tests/test_badge.py View File

@ -27,6 +27,10 @@ class BadgeTestCase(BaseTestCase):
self.assertEqual(r["Access-Control-Allow-Origin"], "*")
self.assertContains(r, "#4c1")
def test_it_rejects_bad_format(self):
r = self.client.get(self.json_url + "foo")
self.assertEqual(r.status_code, 404)
def test_it_handles_options(self):
r = self.client.options(self.svg_url)
self.assertEqual(r.status_code, 204)


+ 2
- 14
hc/api/urls.py View File

@ -27,27 +27,15 @@ urlpatterns = [
path("api/v1/notifications/<uuid:code>/bounce", views.bounce, name="hc-api-bounce"),
path("api/v1/channels/", views.channels),
path(
"badge/<slug:badge_key>/<slug:signature>/<quoted:tag>.svg",
"badge/<slug:badge_key>/<slug:signature>/<quoted:tag>.<slug:fmt>",
views.badge,
name="hc-badge",
),
path(
"badge/<slug:badge_key>/<slug:signature>.svg",
"badge/<slug:badge_key>/<slug:signature>.<slug:fmt>",
views.badge,
{"tag": "*"},
name="hc-badge-all",
),
path(
"badge/<slug:badge_key>/<slug:signature>/<quoted:tag>.json",
views.badge,
{"format": "json"},
name="hc-badge-json",
),
path(
"badge/<slug:badge_key>/<slug:signature>.json",
views.badge,
{"format": "json", "tag": "*"},
name="hc-badge-json-all",
),
path("api/v1/status/", views.status),
]

+ 15
- 3
hc/api/views.py View File

@ -203,10 +203,13 @@ def pause(request, code):
@never_cache
@cors("GET")
def badge(request, badge_key, signature, tag, format="svg"):
def badge(request, badge_key, signature, tag, fmt="svg"):
if not check_signature(badge_key, tag, signature):
return HttpResponseNotFound()
if fmt not in ("svg", "json", "shields"):
return HttpResponseNotFound()
q = Check.objects.filter(project__badge_key=badge_key)
if tag != "*":
q = q.filter(tags__contains=tag)
@ -225,7 +228,7 @@ def badge(request, badge_key, signature, tag, format="svg"):
if check_status == "down":
down += 1
status = "down"
if format == "svg":
if fmt == "svg":
# For SVG badges, we can leave the loop as soon as we
# find the first "down"
break
@ -234,7 +237,16 @@ def badge(request, badge_key, signature, tag, format="svg"):
if status == "up":
status = "late"
if format == "json":
if fmt == "shields":
color = "success"
if status == "down":
color = "critical"
elif status == "late":
color = "important"
return JsonResponse({"label": label, "message": status, "color": color})
if fmt == "json":
return JsonResponse(
{"status": status, "total": total, "grace": grace, "down": down}
)


+ 2
- 1
hc/front/views.py View File

@ -590,7 +590,8 @@ def badges(request, code):
{
"tag": tag,
"svg": get_badge_url(project.badge_key, tag),
"json": get_badge_url(project.badge_key, tag, format="json"),
"json": get_badge_url(project.badge_key, tag, fmt="json"),
"shields": get_badge_url(project.badge_key, tag, fmt="shields"),
}
)


+ 3
- 5
hc/lib/badges.py View File

@ -103,14 +103,12 @@ def check_signature(username, tag, sig):
return ours == sig
def get_badge_url(username, tag, format="svg"):
def get_badge_url(username, tag, fmt="svg"):
sig = base64_hmac(str(username), tag, settings.SECRET_KEY)
if tag == "*":
view = "hc-badge-json-all" if format == "json" else "hc-badge-all"
url = reverse(view, args=[username, sig[:8]])
url = reverse("hc-badge-all", args=[username, sig[:8], fmt])
else:
view = "hc-badge-json" if format == "json" else "hc-badge"
url = reverse(view, args=[username, sig[:8], tag])
url = reverse("hc-badge", args=[username, sig[:8], tag, fmt])
return settings.SITE_ROOT + url

+ 5
- 1
static/css/settings.css View File

@ -37,10 +37,14 @@
padding-top: 32px;
}
#badges-json {
#badges-json, #badges-shields {
display: none;
}
#badges-shields label:first-child {
margin: 20px 0 10px 0;
}
.json-response code {
display: inline-block;
background: #eee;


+ 9
- 2
static/js/badges.js View File

@ -7,13 +7,20 @@ $(function() {
});
$("#show-svg").click(function() {
$("#badges-json").hide();
$("#badges-svg").show();
$("#badges-json").hide();
$("#badges-shields").hide();
})
$("#show-json").click(function() {
$("#badges-svg").hide();
$("#badges-json").show();
$("#badges-shields").hide();
})
});
$("#show-shields").click(function() {
$("#badges-svg").hide();
$("#badges-json").hide();
$("#badges-shields").show();
})
});

+ 33
- 2
templates/front/badges.html View File

@ -24,6 +24,9 @@
<label id="show-json" class="btn btn-default">
<input type="radio" autocomplete="off"> JSON
</label>
<label id="show-shields" class="btn btn-default">
<input type="radio" autocomplete="off"> Shields.io
</label>
</div>
<table id="badges-svg" class="badges table">
@ -44,7 +47,7 @@
<td>
<img src="{{ urldict.svg }}" alt="" />
</td>
<td class="svg-url">
<td>
<code>{{ urldict.svg }}</code>
</td>
</tr>
@ -67,12 +70,40 @@
<tr>
<td class="json-response" data-url="{{ urldict.json }}">
</td>
<td class="json-url">
<td>
<code>{{ urldict.json }}</code>
</td>
</tr>
{% endfor %}
</table>
<div id="badges-shields">
<table class="badges table">
{% if have_tags %}
<tr>
<th>Shields.io badge</th>
<th>JSON endpoint for Shields.io <a href="https://shields.io/endpoint">(how to use)</a></th>
</tr>
{% endif %}
{% for urldict in badges %}
{% if urldict.tag == "*" %}
<tr>
<th colspan="2">Overall Status</th>
</tr>
{% endif %}
<tr>
<td>
<img src="https://img.shields.io/endpoint?url={{ urldict.shields|urlencode:"" }}" alt="" />
</td>
<td>
<code>{{ urldict.shields }}</code>
</td>
</tr>
{% endfor %}
</table>
</div>
</div>
</div>
{% endblock %}


Loading…
Cancel
Save