Page MenuHomeSoftware Heritage

D2310.id8399.diff
No OneTemporary

D2310.id8399.diff

diff --git a/mypy.ini b/mypy.ini
--- a/mypy.ini
+++ b/mypy.ini
@@ -12,6 +12,9 @@
[mypy-bs4.*]
ignore_missing_imports = True
+[mypy-corsheaders.*]
+ignore_missing_imports = True
+
[mypy-django_js_reverse.*]
ignore_missing_imports = True
diff --git a/requirements.txt b/requirements.txt
--- a/requirements.txt
+++ b/requirements.txt
@@ -5,6 +5,7 @@
# Runtime dependencies
beautifulsoup4
Django >= 1.11.0, < 2.0
+django-cors-headers
djangorestframework >= 3.4.0
django_webpack_loader
django_js_reverse
@@ -18,6 +19,7 @@
pyyaml
requests
python-memcached
+pybadges
# Doc dependencies
sphinx
diff --git a/swh/web/misc/badges.py b/swh/web/misc/badges.py
new file mode 100644
--- /dev/null
+++ b/swh/web/misc/badges.py
@@ -0,0 +1,173 @@
+# Copyright (C) 2019 The Software Heritage developers
+# See the AUTHORS file at the top-level directory of this distribution
+# License: GNU Affero General Public License version 3, or any later version
+# See top-level LICENSE file for more information
+
+from base64 import b64encode
+from typing import cast, Optional
+
+from django.conf.urls import url
+from django.contrib.staticfiles import finders
+from django.http import HttpResponse, HttpRequest
+
+from pybadges import badge
+
+from swh.model.exceptions import ValidationError
+from swh.model.identifiers import (
+ persistent_identifier, parse_persistent_identifier,
+ CONTENT, DIRECTORY, ORIGIN, RELEASE, REVISION, SNAPSHOT
+)
+from swh.web.common import service
+from swh.web.common.exc import BadInputExc, NotFoundExc
+from swh.web.common.utils import reverse, resolve_swh_persistent_id
+
+
+_orange = '#f36a24'
+_yellow = '#fac11f'
+_red = '#cd5741'
+
+_swh_logo_data = None
+
+_badge_config = {
+ CONTENT: {
+ 'color': _yellow,
+ 'title': 'Archived source file',
+ },
+ DIRECTORY: {
+ 'color': _yellow,
+ 'title': 'Archived source tree',
+ },
+ ORIGIN: {
+ 'color': _orange,
+ 'title': 'Archived software repository',
+ },
+ RELEASE: {
+ 'color': _yellow,
+ 'title': 'Archived software release',
+ },
+ REVISION: {
+ 'color': _yellow,
+ 'title': 'Archived commit',
+ },
+ SNAPSHOT: {
+ 'color': _yellow,
+ 'title': 'Archived software repository snapshot',
+ },
+ 'error': {
+ 'color': _red,
+ 'title': 'An error occurred when generating the badge'
+ }
+}
+
+
+def _get_logo_data() -> str:
+ """
+ Get data-URI for Software Heritage SVG logo to embed it in
+ the generated badges.
+ """
+ global _swh_logo_data
+ if _swh_logo_data is None:
+ swh_logo_path = cast(str, finders.find('img/swh-logo-white.svg'))
+ with open(swh_logo_path, 'rb') as swh_logo_file:
+ _swh_logo_data = ('data:image/svg+xml;base64,%s' %
+ b64encode(swh_logo_file.read()).decode('ascii'))
+ return _swh_logo_data
+
+
+def _swh_badge(request: HttpRequest, object_type: str, object_id: str,
+ object_pid: Optional[str] = '') -> HttpResponse:
+ """
+ Generate a Software Heritage badge for a given object type and id.
+
+ Args:
+ request: input http request
+ object_type: The type of swh object to generate a badge for,
+ either *content*, *directory*, *revision*, *release*, *origin*
+ or *snapshot*
+ object_id: The id of the swh object, either an url for origin
+ type or a *sha1* for other object types
+ object_pid: If provided, the object persistent
+ identifier will not be recomputed
+
+ Returns:
+ HTTP response with content type *image/svg+xml* containing the SVG
+ badge data. If the provided parameters are invalid, HTTP 400 status
+ code will be returned. If the object can not be found in the archive,
+ HTTP 404 status code will be returned.
+
+ """
+ left_text = 'error'
+ whole_link = ''
+ status = 200
+
+ try:
+ if object_type == ORIGIN:
+ service.lookup_origin({'url': object_id})
+ right_text = 'repository'
+ whole_link = reverse('browse-origin',
+ url_args={'origin_url': object_id})
+ else:
+ # when pid is provided, object type and id will be parsed
+ # from it
+ if object_pid:
+ parsed_pid = parse_persistent_identifier(object_pid)
+ object_type = parsed_pid.object_type
+ object_id = parsed_pid.object_id
+ swh_object = service.lookup_object(object_type, object_id)
+ if object_pid:
+ right_text = object_pid
+ else:
+ right_text = persistent_identifier(object_type, object_id)
+
+ whole_link = resolve_swh_persistent_id(right_text)['browse_url']
+ # remove pid metadata if any for badge text
+ if object_pid:
+ right_text = right_text.split(';')[0]
+ # use release name for badge text
+ if object_type == RELEASE:
+ right_text = 'release %s' % swh_object['name']
+ left_text = 'archived'
+ except (BadInputExc, ValidationError):
+ right_text = f'invalid {object_type if object_type else "object"} id'
+ status = 400
+ object_type = 'error'
+ except NotFoundExc:
+ right_text = f'{object_type if object_type else "object"} not found'
+ status = 404
+ object_type = 'error'
+
+ badge_data = badge(left_text=left_text,
+ right_text=right_text,
+ right_color=_badge_config[object_type]['color'],
+ whole_link=request.build_absolute_uri(whole_link),
+ whole_title=_badge_config[object_type]['title'],
+ logo=_get_logo_data(),
+ embed_logo=True)
+
+ return HttpResponse(badge_data, content_type='image/svg+xml',
+ status=status)
+
+
+def _swh_badge_pid(request: HttpRequest, object_pid: str) -> HttpResponse:
+ """
+ Generate a Software Heritage badge for a given object persistent
+ identifier.
+
+ Args:
+ request (django.http.HttpRequest): input http request
+ object_pid (str): A swh object persistent identifier
+
+ Returns:
+ django.http.HttpResponse: An http response with content type
+ *image/svg+xml* containing the SVG badge data. If any error
+ occurs, a status code of 400 will be returned.
+ """
+ return _swh_badge(request, '', '', object_pid)
+
+
+urlpatterns = [
+ url(r'^badge/(?P<object_type>[a-z]+)/(?P<object_id>.+)/$', _swh_badge,
+ name='swh-badge'),
+ url(r'^badge/(?P<object_pid>swh:[0-9]+:[a-z]+:[0-9a-f]+.*)/$',
+ _swh_badge_pid, name='swh-badge-pid'),
+]
diff --git a/swh/web/misc/urls.py b/swh/web/misc/urls.py
--- a/swh/web/misc/urls.py
+++ b/swh/web/misc/urls.py
@@ -45,6 +45,7 @@
url(r'^jslicenses/$', _jslicenses, name='jslicenses'),
url(r'^', include('swh.web.misc.origin_save')),
url(r'^stat_counters', _stat_counters, name='stat-counters'),
+ url(r'^', include('swh.web.misc.badges')),
]
diff --git a/swh/web/settings/common.py b/swh/web/settings/common.py
--- a/swh/web/settings/common.py
+++ b/swh/web/settings/common.py
@@ -43,12 +43,14 @@
'swh.web.api',
'swh.web.browse',
'webpack_loader',
- 'django_js_reverse'
+ 'django_js_reverse',
+ 'corsheaders'
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
+ 'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
@@ -277,3 +279,6 @@
}
JS_REVERSE_JS_MINIFY = False
+
+CORS_ORIGIN_ALLOW_ALL = True
+CORS_URLS_REGEX = r'^/badge/.*$'
diff --git a/swh/web/static/img/swh-logo-white.svg b/swh/web/static/img/swh-logo-white.svg
new file mode 100644
--- /dev/null
+++ b/swh/web/static/img/swh-logo-white.svg
@@ -0,0 +1,237 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
+ xml:space="preserve"
+ width="78.270248"
+ height="77.394997"
+ viewBox="0 0 78.27025 77.394997"
+ sodipodi:docname="swh-logo-white.svg"
+ inkscape:export-filename="swh-logo.png"
+ inkscape:export-xdpi="630.15387"
+ inkscape:export-ydpi="630.15387"><metadata
+ id="metadata8"><rdf:RDF><cc:Work
+ rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
+ id="defs6"><clipPath
+ clipPathUnits="userSpaceOnUse"
+ id="clipPath16"><path
+ d="m 30.027,68.987 1.337,-1.343 4.826,1.607 -1.61,-4.823 1.34,-1.34 2.95,8.844 z m -3.387,-1.073 5.746,-10.675 1.252,1.252 -5.747,10.674 z m -2.552,-10.762 1.608,4.823 -1.34,1.34 -2.949,-8.846 8.842,2.949 -1.338,1.343 z m 19.376,8.722 2.274,-5.144 h 1.894 l -4.167,8.935 -4.17,-8.038 1.895,-0.154 z m -5.797,-7.103 12,-3.485 v 1.77 l -12,3.484 z m 5.797,-9.502 -2.273,4.461 h -1.895 l 4.169,-8.252 4.167,8.379 -1.896,-0.018 z m 13.276,20.108 -8.843,2.95 2.945,-8.844 1.342,1.338 -1.607,4.825 4.823,-1.609 z m 4.597,-2.282 -10.674,-5.748 1.251,-1.25 10.675,5.746 z m -0.269,-4.728 1.609,-4.822 -4.822,1.607 -1.34,-1.34 8.843,-2.948 -2.948,8.842 z m -3.401,18.722 -8.219,-4.168 8.396,-4.17 0.033,1.896 -4.653,2.274 4.443,2.273 z m 3.391,-10.359 3.486,12 h -1.771 l -3.484,-12 z m 5.609,2.022 7.967,4.169 -8.522,4.168 -0.095,-1.896 4.873,-2.272 -4.223,-2.274 z m -10.321,11.547 -1.339,1.342 -4.824,-1.608 1.608,4.823 -1.34,1.341 -2.948,-8.844 z m 3.387,1.072 -5.746,10.675 -1.253,-1.253 5.748,-10.673 z m 2.551,10.763 -1.608,-4.823 1.341,-1.339 2.947,8.843 -8.842,-2.949 1.339,-1.341 z M 45.182,91.364 42.908,86.999 40.634,91.73 H 38.74 l 4.168,-8.521 4.17,8.245 z m 4.485,3.151 -12,3.485 v -1.77 l 12,-3.485 z m -6.759,9.088 2.274,-4.873 h 1.895 l -4.169,8.664 -4.169,-8.175 1.896,-0.083 z m -7.379,-13.8 -1.341,-1.34 1.608,-4.823 -4.823,1.608 -1.34,-1.34 8.844,-2.949 z m -10.494,-3.611 10.674,5.746 -1.251,1.251 -10.675,-5.746 z m 0.268,4.726 -1.608,4.823 4.824,-1.608 1.339,1.341 -8.844,2.948 2.949,-8.844 z m 3.364,-18.722 8.338,4.169 -8.338,4.169 v -1.896 l 4.547,-2.273 -4.547,-2.274 z m -5.069,-1.466 3.485,11 h -1.769 l -3.485,-11 z m -10.269,5.635 8.338,4.169 v -1.896 l -4.547,-2.273 4.547,-2.274 v -1.895 z"
+ id="path18"
+ inkscape:connector-curvature="0" /></clipPath><linearGradient
+ x1="0"
+ y1="0"
+ x2="1"
+ y2="0"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(61.305366,0,0,-61.305366,13.87633,76.436119)"
+ spreadMethod="pad"
+ id="linearGradient24"><stop
+ style="stop-opacity:1;stop-color:#ec1c28"
+ offset="0"
+ id="stop26" /><stop
+ style="stop-opacity:1;stop-color:#ec1c28"
+ offset="0.331971"
+ id="stop28" /><stop
+ style="stop-opacity:1;stop-color:#fbc81f"
+ offset="1"
+ id="stop30" /></linearGradient></defs><sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1918"
+ inkscape:window-height="1016"
+ id="namedview4"
+ showgrid="false"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0"
+ inkscape:zoom="4.8342548"
+ inkscape:cx="26.556774"
+ inkscape:cy="40.636261"
+ inkscape:window-x="0"
+ inkscape:window-y="36"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="g10" /><g
+ id="g10"
+ inkscape:groupmode="layer"
+ inkscape:label="SWH-logo"
+ transform="matrix(1.25,0,0,-1.25,-15.706625,134.2425)"
+ style="display:inline"><path
+ style="fill:#ffffff;stroke:none;fill-opacity:1"
+ d="m 30.5745,68.987 1.337,-1.343 4.826,1.607 -1.61,-4.823 1.34,-1.34 2.95,8.844 z"
+ id="path941"
+ inkscape:connector-curvature="0" /><path
+ style="fill:#ffffff;stroke:none;fill-opacity:1"
+ d="m 27.1875,67.914 5.746,-10.675 1.252,1.252 -5.747,10.674 z"
+ id="path939"
+ inkscape:connector-curvature="0" /><path
+ style="fill:#ffffff;stroke:none;fill-opacity:1"
+ d="m 24.6355,57.152 1.608,4.823 -1.34,1.34 -2.949,-8.846 8.842,2.949 -1.338,1.343 z"
+ id="path937"
+ inkscape:connector-curvature="0" /><path
+ style="fill:#ffffff;stroke:none;fill-opacity:1"
+ d="m 44.0115,65.874 2.274,-5.144 h 1.894 l -4.167,8.935 -4.17,-8.038 1.895,-0.154 z"
+ id="path935"
+ inkscape:connector-curvature="0" /><path
+ style="fill:#ffffff;stroke:none;fill-opacity:1"
+ d="m 38.2145,58.771 12,-3.485 v 1.77 l -12,3.484 z"
+ id="path933"
+ inkscape:connector-curvature="0" /><path
+ style="fill:#ffffff;stroke:none;fill-opacity:1"
+ d="m 44.0115,49.269 -2.273,4.461 h -1.895 l 4.169,-8.252 4.167,8.379 -1.896,-0.018 z"
+ id="path931"
+ inkscape:connector-curvature="0" /><path
+ style="fill:#ffffff;stroke:none;fill-opacity:1"
+ d="m 57.2875,69.377 -8.843,2.95 2.945,-8.844 1.342,1.338 -1.607,4.825 4.823,-1.609 z"
+ id="path929"
+ inkscape:connector-curvature="0" /><path
+ style="fill:#ffffff;stroke:none;fill-opacity:1"
+ d="m 61.8845,67.095 -10.674,-5.748 1.251,-1.25 10.675,5.746 z"
+ id="path927"
+ inkscape:connector-curvature="0" /><path
+ style="fill:#ffffff;stroke:none;fill-opacity:1"
+ d="m 61.6155,62.367 1.609,-4.822 -4.822,1.607 -1.34,-1.34 8.843,-2.948 -2.948,8.842 z"
+ id="path925"
+ inkscape:connector-curvature="0" /><path
+ style="fill:#ffffff;stroke:none;fill-opacity:1"
+ d="m 58.2145,81.089 -8.219,-4.168 8.396,-4.17 0.033,1.896 -4.653,2.274 4.443,2.273 z"
+ id="path923"
+ inkscape:connector-curvature="0" /><path
+ style="fill:#ffffff;stroke:none;fill-opacity:1"
+ d="m 61.6055,70.73 3.486,12 h -1.771 l -3.484,-12 z"
+ id="path921"
+ inkscape:connector-curvature="0" /><path
+ style="fill:#ffffff;stroke:none;fill-opacity:1"
+ d="m 67.2145,72.752 7.967,4.169 -8.522,4.168 -0.095,-1.896 4.873,-2.272 -4.223,-2.274 z"
+ id="path919"
+ inkscape:connector-curvature="0" /><path
+ style="fill:#ffffff;stroke:none;fill-opacity:1"
+ d="m 56.8935,84.299 -1.339,1.342 -4.824,-1.608 1.608,4.823 -1.34,1.341 -2.948,-8.844 z"
+ id="path917"
+ inkscape:connector-curvature="0" /><path
+ style="fill:#ffffff;stroke:none;fill-opacity:1"
+ d="m 60.2805,85.371 -5.746,10.675 -1.253,-1.253 5.748,-10.673 z"
+ id="path915"
+ inkscape:connector-curvature="0" /><path
+ style="fill:#ffffff;stroke:none;fill-opacity:1"
+ d="m 62.8315,96.134 -1.608,-4.823 1.341,-1.339 2.947,8.843 -8.842,-2.949 1.339,-1.341 z"
+ id="path913"
+ inkscape:connector-curvature="0" /><path
+ style="fill:#ffffff;stroke:none;fill-opacity:1"
+ d="m 45.7295,91.364 -2.274,-4.365 -2.274,4.731 h -1.894 l 4.168,-8.521 4.17,8.245 z"
+ id="path911"
+ inkscape:connector-curvature="0" /><path
+ style="fill:#ffffff;stroke:none;fill-opacity:1"
+ d="m 50.2145,94.515 -12,3.485 v -1.77 l 12,-3.485 z"
+ id="path909"
+ inkscape:connector-curvature="0" /><path
+ style="fill:#ffffff;stroke:none;fill-opacity:1"
+ d="m 43.4555,103.603 2.274,-4.873 h 1.895 l -4.169,8.664 -4.169,-8.175 1.896,-0.083 z"
+ id="path907"
+ inkscape:connector-curvature="0" /><path
+ style="fill:#ffffff;stroke:none;fill-opacity:1"
+ d="m 36.0765,89.803 -1.341,-1.34 1.608,-4.823 -4.823,1.608 -1.34,-1.34 8.844,-2.949 z"
+ id="path905"
+ inkscape:connector-curvature="0" /><path
+ style="fill:#ffffff;stroke:none;fill-opacity:1"
+ d="m 25.5825,86.192 10.674,5.746 -1.251,1.251 -10.675,-5.746 z"
+ id="path903"
+ inkscape:connector-curvature="0" /><path
+ style="fill:#ffffff;stroke:none;fill-opacity:1"
+ d="m 25.8505,90.918 -1.608,4.823 4.824,-1.608 1.339,1.341 -8.844,2.948 2.949,-8.844 z"
+ id="path901"
+ inkscape:connector-curvature="0" /><path
+ style="fill:#ffffff;stroke:none;fill-opacity:1"
+ d="m 29.2145,72.196 8.338,4.169 -8.338,4.169 v -1.896 l 4.547,-2.273 -4.547,-2.274 z"
+ id="path899"
+ inkscape:connector-curvature="0" /><path
+ style="fill:#ffffff;stroke:none;fill-opacity:1"
+ d="m 24.1455,70.73 3.485,11 h -1.769 l -3.485,-11 z"
+ id="path897"
+ inkscape:connector-curvature="0" /><path
+ style="fill:#ffffff;stroke:none;stroke-width:1.00002396;fill-opacity:1"
+ d="m 12.5653,76.365 8.3384,4.169 V 78.638 L 16.356482,76.365 20.9037,74.091 v -1.895 z"
+ id="path32"
+ inkscape:connector-curvature="0" /><text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:13.99999905px;line-height:18.75px;font-family:'Alegreya SC';-inkscape-font-specification:'Alegreya SC';letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.75px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;"
+ x="21.706713"
+ y="-49.201427"
+ id="text24"
+ transform="scale(1,-1)"><tspan
+ sodipodi:role="line"
+ id="tspan22"
+ x="21.706713"
+ y="-36.37529"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'Alegreya SC';-inkscape-font-specification:'Alegreya SC';stroke-width:0.75px;fill:#ffffff;fill-opacity:1;" /></text>
+
+<text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:13.99999905px;line-height:18.75px;font-family:'Alegreya SC';-inkscape-font-specification:'Alegreya SC';letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.75px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;"
+ x="17.983284"
+ y="-46.719143"
+ id="text28"
+ transform="scale(1,-1)"><tspan
+ sodipodi:role="line"
+ id="tspan26"
+ x="17.983284"
+ y="-33.893005"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;font-family:'Alegreya SC';-inkscape-font-specification:'Alegreya SC';fill:#ffffff;fill-opacity:1;stroke-width:0.75px;" /></text>
+
+<text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:18.75px;font-family:'Alegreya SC';-inkscape-font-specification:'Alegreya SC';letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.75px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;"
+ x="16.121571"
+ y="-47.960289"
+ id="text4526"
+ transform="scale(1,-1)"><tspan
+ sodipodi:role="line"
+ id="tspan4524"
+ x="16.121571"
+ y="-34.641129"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;font-family:'Alegreya SC';-inkscape-font-specification:'Alegreya SC';fill:#ffffff;fill-opacity:1;stroke-width:0.75px;" /></text>
+
+<text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:18.75px;font-family:'Alegreya SC';-inkscape-font-specification:'Alegreya SC';letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.75px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;"
+ x="16.431856"
+ y="-48.270573"
+ id="text4530"
+ transform="scale(1,-1)"><tspan
+ sodipodi:role="line"
+ id="tspan4528"
+ x="16.431856"
+ y="-34.951412"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;font-family:'Alegreya SC';-inkscape-font-specification:'Alegreya SC';fill:#ffffff;fill-opacity:1;stroke-width:0.75px;" /></text>
+
+<text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:13.99999905px;line-height:18.75px;font-family:'Alegreya SC';-inkscape-font-specification:'Alegreya SC';letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.75px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;"
+ x="14.570143"
+ y="-46.719143"
+ id="text4557"
+ transform="scale(1,-1)"><tspan
+ sodipodi:role="line"
+ id="tspan4555"
+ x="14.570143"
+ y="-33.893005"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:16px;font-family:'Alegreya SC';-inkscape-font-specification:'Alegreya SC Bold';fill:#ffffff;fill-opacity:1;stroke-width:0.75px;" /></text>
+
+</g></svg>
\ No newline at end of file
diff --git a/swh/web/tests/misc/test_badges.py b/swh/web/tests/misc/test_badges.py
new file mode 100644
--- /dev/null
+++ b/swh/web/tests/misc/test_badges.py
@@ -0,0 +1,166 @@
+# Copyright (C) 2019 The Software Heritage developers
+# See the AUTHORS file at the top-level directory of this distribution
+# License: GNU Affero General Public License version 3, or any later version
+# See top-level LICENSE file for more information
+
+from corsheaders.middleware import ACCESS_CONTROL_ALLOW_ORIGIN
+from hypothesis import given
+
+from swh.model.identifiers import (
+ persistent_identifier,
+ CONTENT, DIRECTORY, ORIGIN, RELEASE, REVISION, SNAPSHOT
+)
+from swh.web.common import service
+from swh.web.common.utils import reverse, resolve_swh_persistent_id
+from swh.web.misc.badges import _badge_config, _get_logo_data
+from swh.web.tests.django_asserts import assert_contains
+from swh.web.tests.strategies import (
+ content, directory, origin, release, revision, snapshot,
+ unknown_content, unknown_directory, new_origin, unknown_release,
+ unknown_revision, unknown_snapshot, invalid_sha1
+)
+
+
+@given(content())
+def test_content_badge(client, content):
+ _test_badge_endpoints(client, CONTENT, content['sha1_git'])
+
+
+@given(directory())
+def test_directory_badge(client, directory):
+ _test_badge_endpoints(client, DIRECTORY, directory)
+
+
+@given(origin())
+def test_origin_badge(client, origin):
+ _test_badge_endpoints(client, ORIGIN, origin['url'])
+
+
+@given(release())
+def test_release_badge(client, release):
+ _test_badge_endpoints(client, RELEASE, release)
+
+
+@given(revision())
+def test_revision_badge(client, revision):
+ _test_badge_endpoints(client, REVISION, revision)
+
+
+@given(snapshot())
+def test_snapshot_badge(client, snapshot):
+ _test_badge_endpoints(client, SNAPSHOT, snapshot)
+
+
+@given(unknown_content(), unknown_directory(), new_origin(),
+ unknown_release(), unknown_revision(), unknown_snapshot(),
+ invalid_sha1())
+def test_badge_errors(client, unknown_content, unknown_directory, new_origin,
+ unknown_release, unknown_revision, unknown_snapshot,
+ invalid_sha1):
+ for object_type, object_id in (
+ (CONTENT, unknown_content['sha1_git']),
+ (DIRECTORY, unknown_directory),
+ (ORIGIN, new_origin['url']),
+ (RELEASE, unknown_release),
+ (REVISION, unknown_revision),
+ (SNAPSHOT, unknown_snapshot)
+ ):
+ url_args = {
+ 'object_type': object_type,
+ 'object_id': object_id
+ }
+ url = reverse('swh-badge', url_args=url_args)
+ resp = client.get(url)
+ _check_generated_badge(resp, 404, **url_args)
+
+ if object_type != ORIGIN:
+ object_pid = persistent_identifier(object_type, object_id)
+ url = reverse('swh-badge-pid',
+ url_args={'object_pid': object_pid})
+ resp = client.get(url)
+ _check_generated_badge(resp, 404, **url_args)
+
+ for object_type, object_id in (
+ (CONTENT, invalid_sha1),
+ (DIRECTORY, invalid_sha1),
+ (RELEASE, invalid_sha1),
+ (REVISION, invalid_sha1),
+ (SNAPSHOT, invalid_sha1)
+ ):
+ url_args = {
+ 'object_type': object_type,
+ 'object_id': object_id
+ }
+ url = reverse('swh-badge', url_args=url_args)
+ resp = client.get(url)
+ _check_generated_badge(resp, 400, **url_args)
+
+ object_pid = f'swh:1:{object_type[:3]}:{object_id}'
+ url = reverse('swh-badge-pid',
+ url_args={'object_pid': object_pid})
+ resp = client.get(url)
+ _check_generated_badge(resp, 400, '', '')
+
+
+@given(origin(), release())
+def test_badge_endpoints_have_cors_header(client, origin, release):
+ url = reverse('swh-badge', url_args={'object_type': ORIGIN,
+ 'object_id': origin['url']})
+ resp = client.get(url, HTTP_ORIGIN='https://example.org')
+ assert resp.status_code == 200, resp.content
+ assert ACCESS_CONTROL_ALLOW_ORIGIN in resp
+
+ release_pid = persistent_identifier(RELEASE, release)
+ url = reverse('swh-badge-pid', url_args={'object_pid': release_pid})
+ resp = client.get(url, HTTP_ORIGIN='https://example.org')
+ assert resp.status_code == 200, resp.content
+ assert ACCESS_CONTROL_ALLOW_ORIGIN in resp
+
+
+def _test_badge_endpoints(client, object_type, object_id):
+ url_args = {'object_type': object_type,
+ 'object_id': object_id}
+ url = reverse('swh-badge', url_args=url_args)
+ resp = client.get(url)
+ _check_generated_badge(resp, 200, **url_args)
+ if object_type != ORIGIN:
+ pid = persistent_identifier(object_type, object_id)
+ url = reverse('swh-badge-pid', url_args={'object_pid': pid})
+ resp = client.get(url)
+ _check_generated_badge(resp, 200, **url_args)
+
+
+def _check_generated_badge(response, status_code, object_type, object_id):
+ assert response.status_code == status_code, response.content
+ assert response['Content-Type'] == 'image/svg+xml'
+
+ if not object_type:
+ object_type = 'object'
+
+ if object_type == ORIGIN and status_code == 200:
+ link = reverse('browse-origin', url_args={'origin_url': object_id})
+ text = 'repository'
+ elif status_code == 200:
+ text = persistent_identifier(object_type, object_id)
+ link = resolve_swh_persistent_id(text)['browse_url']
+ if object_type == RELEASE:
+ release = service.lookup_release(object_id)
+ text = release['name']
+ elif status_code == 400:
+ text = 'error'
+ link = f'invalid {object_type} id'
+ object_type = 'error'
+ elif status_code == 404:
+ text = 'error'
+ link = f'{object_type} not found'
+ object_type = 'error'
+
+ assert_contains(response, '<svg ', status_code=status_code)
+ assert_contains(response, '</svg>', status_code=status_code)
+ assert_contains(response, _get_logo_data(), status_code=status_code)
+ assert_contains(response, _badge_config[object_type]['color'],
+ status_code=status_code)
+ assert_contains(response, _badge_config[object_type]['title'],
+ status_code=status_code)
+ assert_contains(response, text, status_code=status_code)
+ assert_contains(response, link, status_code=status_code)

File Metadata

Mime Type
text/plain
Expires
Nov 5 2024, 3:18 PM (12 w, 4 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3217582

Event Timeline