diff --git a/swh/web/ui/templates/revision.html b/swh/web/ui/templates/revision.html
index fc224b40..fa974ec3 100644
--- a/swh/web/ui/templates/revision.html
+++ b/swh/web/ui/templates/revision.html
@@ -1,45 +1,45 @@
{% extends "layout.html" %}
{% block title %}Revision{% endblock %}
{% block content %}
{% if message is not none %}
{{ message }}
{% endif %}
{% if revision is not none %}
- {% for key in keys %}
- {% if revision[key] is not none %}
+ {% for key in revision.keys() %}
+ {% if key != 'directory' and key != 'parents' and key != 'children' and revision[key] is not none %}
{{ key }}
{% if key == 'message' %}
{% else %}
{% endif %}
{% endif %}
{% endfor %}
{% if revision['directory'] is not none %}
{% endif %}
{% for key in ['parents', 'children'] %}
{% if revision[key] is not none %}
{{ key }}
{% for link in revision[key] %}
{% endfor %}
{% endif %}
{% endfor %}
{% endif %}
{% endblock %}
diff --git a/swh/web/ui/tests/test_views.py b/swh/web/ui/tests/test_views.py
index 6f791cc3..0e04a988 100644
--- a/swh/web/ui/tests/test_views.py
+++ b/swh/web/ui/tests/test_views.py
@@ -1,1264 +1,1330 @@
# Copyright (C) 2015 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 nose.tools import istest
from swh.web.ui.tests import test_app
from unittest.mock import patch
from swh.web.ui.exc import BadInputExc, NotFoundExc
class FileMock():
def __init__(self, filename):
self.filename = filename
class ViewTestCase(test_app.SWHViewTestCase):
render_template = False
@patch('swh.web.ui.views.flask')
@istest
def homepage(self, mock_flask):
# given
mock_flask.flash.return_value = 'something'
# when
rv = self.client.get('/')
# then
self.assertEquals(rv.status_code, 200)
self.assert_template_used('home.html')
mock_flask.flash.assert_called_once_with(
'This Web app is still work in progress, use at your own risk',
'warning')
@istest
def info(self):
# when
rv = self.client.get('/about/')
self.assertEquals(rv.status_code, 200)
self.assert_template_used('about.html')
self.assertIn(b'About', rv.data)
@istest
def search_default(self):
# when
rv = self.client.get('/search/')
self.assertEquals(rv.status_code, 200)
self.assertEqual(self.get_context_variable('q'), '')
self.assertEqual(self.get_context_variable('messages'), [])
self.assertEqual(self.get_context_variable('filename'), None)
self.assertEqual(self.get_context_variable('file'), None)
self.assert_template_used('upload_and_search.html')
@patch('swh.web.ui.views.service')
@istest
def search_get_query_hash_not_found(self, mock_service):
# given
mock_service.lookup_hash.return_value = {'found': None}
# when
rv = self.client.get('/search/?q=sha1:456')
self.assertEquals(rv.status_code, 200)
self.assertEqual(self.get_context_variable('q'), 'sha1:456')
self.assertEqual(self.get_context_variable('messages'),
['Content with hash sha1:456 not found!'])
self.assertEqual(self.get_context_variable('filename'), None)
self.assertEqual(self.get_context_variable('file'), None)
self.assert_template_used('upload_and_search.html')
mock_service.lookup_hash.assert_called_once_with('sha1:456')
@patch('swh.web.ui.views.service')
@istest
def search_get_query_hash_bad_input(self, mock_service):
# given
mock_service.lookup_hash.side_effect = BadInputExc('error msg')
# when
rv = self.client.get('/search/?q=sha1_git:789')
self.assertEquals(rv.status_code, 200)
self.assertEqual(self.get_context_variable('q'), 'sha1_git:789')
self.assertEqual(self.get_context_variable('messages'),
['error msg'])
self.assertEqual(self.get_context_variable('filename'), None)
self.assertEqual(self.get_context_variable('file'), None)
self.assert_template_used('upload_and_search.html')
mock_service.lookup_hash.assert_called_once_with('sha1_git:789')
@patch('swh.web.ui.views.service')
@istest
def search_get_query_hash_found(self, mock_service):
# given
mock_service.lookup_hash.return_value = {'found': True}
# when
rv = self.client.get('/search/?q=sha1:123')
self.assertEquals(rv.status_code, 200)
self.assertEqual(self.get_context_variable('q'), 'sha1:123')
self.assertEqual(self.get_context_variable('messages'),
['Content with hash sha1:123 found!'])
self.assertEqual(self.get_context_variable('filename'), None)
self.assertEqual(self.get_context_variable('file'), None)
self.assert_template_used('upload_and_search.html')
mock_service.lookup_hash.assert_called_once_with('sha1:123')
@patch('swh.web.ui.views.service')
@istest
def search_post_query_hash_not_found(self, mock_service):
# given
mock_service.lookup_hash.return_value = {'found': None}
# when
rv = self.client.get('/search/?q=sha1:456')
self.assertEquals(rv.status_code, 200)
self.assertEqual(self.get_context_variable('q'), 'sha1:456')
self.assertEqual(self.get_context_variable('messages'),
['Content with hash sha1:456 not found!'])
self.assertEqual(self.get_context_variable('filename'), None)
self.assertEqual(self.get_context_variable('file'), None)
self.assert_template_used('upload_and_search.html')
mock_service.lookup_hash.assert_called_once_with('sha1:456')
@patch('swh.web.ui.views.service')
@istest
def search_post_query_hash_bad_input(self, mock_service):
# given
mock_service.lookup_hash.side_effect = BadInputExc('error msg!')
# when
rv = self.client.post('/search/', data=dict(q='sha1_git:987'))
self.assertEquals(rv.status_code, 200)
self.assertEqual(self.get_context_variable('q'), 'sha1_git:987')
self.assertEqual(self.get_context_variable('messages'),
['error msg!'])
self.assertEqual(self.get_context_variable('filename'), None)
self.assertEqual(self.get_context_variable('file'), None)
self.assert_template_used('upload_and_search.html')
mock_service.lookup_hash.assert_called_once_with('sha1_git:987')
@patch('swh.web.ui.views.service')
@istest
def search_post_query_hash_found(self, mock_service):
# given
mock_service.lookup_hash.return_value = {'found': True}
# when
rv = self.client.post('/search/', data=dict(q='sha1:321'))
self.assertEquals(rv.status_code, 200)
self.assertEqual(self.get_context_variable('q'), 'sha1:321')
self.assertEqual(self.get_context_variable('messages'),
['Content with hash sha1:321 found!'])
self.assertEqual(self.get_context_variable('filename'), None)
self.assertEqual(self.get_context_variable('file'), None)
self.assert_template_used('upload_and_search.html')
mock_service.lookup_hash.assert_called_once_with('sha1:321')
@patch('swh.web.ui.views.service')
@patch('swh.web.ui.views.request')
@istest
def search_post_upload_and_hash_bad_input(self, mock_request,
mock_service):
# given
mock_request.data = {}
mock_request.method = 'POST'
mock_request.files = dict(filename=FileMock('foobar'))
mock_service.upload_and_search.side_effect = BadInputExc(
'error bad input')
# when (mock_request completes the post request)
rv = self.client.post('/search/')
# then
self.assertEquals(rv.status_code, 200)
self.assertEqual(self.get_context_variable('messages'),
['error bad input'])
self.assert_template_used('upload_and_search.html')
mock_service.upload_and_search.called = True
@patch('swh.web.ui.views.service')
@patch('swh.web.ui.views.request')
@istest
def search_post_upload_and_hash_not_found(self, mock_request,
mock_service):
# given
mock_request.data = {}
mock_request.method = 'POST'
mock_request.files = dict(filename=FileMock('foobar'))
mock_service.upload_and_search.return_value = {'filename': 'foobar',
'sha1': 'blahhash',
'found': False}
# when (mock_request completes the post request)
rv = self.client.post('/search/')
# then
self.assertEquals(rv.status_code, 200)
self.assertEqual(self.get_context_variable('messages'),
["File foobar with hash blahhash not found!"])
self.assertEqual(self.get_context_variable('filename'), 'foobar')
self.assertEqual(self.get_context_variable('sha1'), 'blahhash')
self.assert_template_used('upload_and_search.html')
mock_service.upload_and_search.called = True
@patch('swh.web.ui.views.service')
@patch('swh.web.ui.views.request')
@istest
def search_post_upload_and_hash_found(self, mock_request, mock_service):
# given
mock_request.data = {}
mock_request.method = 'POST'
mock_request.files = dict(filename=FileMock('foobar'))
mock_service.upload_and_search.return_value = {'filename': 'foobar',
'sha1': '123456789',
'found': True}
# when (mock_request completes the post request)
rv = self.client.post('/search/')
# then
self.assertEquals(rv.status_code, 200)
self.assertEqual(self.get_context_variable('messages'),
["File foobar with hash 123456789 found!"])
self.assertEqual(self.get_context_variable('filename'), 'foobar')
self.assertEqual(self.get_context_variable('sha1'), '123456789')
self.assert_template_used('upload_and_search.html')
mock_service.upload_and_search.called = True
@patch('swh.web.ui.views.service')
@istest
def browse_content_detail_not_found(self, mock_service):
# given
mock_service.lookup_content.return_value = None
# when
rv = self.client.get('/browse/content/sha1:sha1-hash/')
# then
self.assertEquals(rv.status_code, 200)
self.assert_template_used('content.html')
self.assertEqual(self.get_context_variable('message'),
'Content with sha1:sha1-hash not found.')
self.assertEqual(self.get_context_variable('content'),
None)
mock_service.lookup_content.assert_called_once_with(
'sha1:sha1-hash')
@patch('swh.web.ui.views.service')
@istest
def browse_content_detail_bad_input(self, mock_service):
# given
mock_service.lookup_content.side_effect = BadInputExc('Bad input!')
# when
rv = self.client.get('/browse/content/sha1:sha1-hash/')
# then
self.assertEquals(rv.status_code, 200)
self.assert_template_used('content.html')
self.assertEqual(self.get_context_variable('message'),
'Bad input!')
self.assertIsNone(self.get_context_variable('content'))
mock_service.lookup_content.assert_called_once_with(
'sha1:sha1-hash')
@patch('swh.web.ui.views.service')
@istest
def browse_content_detail(self, mock_service):
# given
stub_content = {'sha1': 'sha1_hash'}
mock_service.lookup_content.return_value = stub_content
# when
rv = self.client.get('/browse/content/sha1:sha1-hash/')
# then
self.assertEquals(rv.status_code, 200)
self.assert_template_used('content.html')
self.assertIsNone(self.get_context_variable('message'))
self.assertEqual(self.get_context_variable('content'),
{'sha1': 'sha1_hash'})
mock_service.lookup_content.assert_called_once_with(
'sha1:sha1-hash')
@patch('swh.web.ui.views.service')
@istest
def browse_content_data(self, mock_service):
# given
stub_content_raw = {
'sha1': 'sha1-hash',
'data': b'some-data'
}
mock_service.lookup_content_raw.return_value = stub_content_raw
# when
rv = self.client.get('/browse/content/sha1:sha1-hash/raw/')
self.assertEquals(rv.status_code, 200)
self.assert_template_used('content-data.html')
self.assertEqual(self.get_context_variable('message'),
'Content sha1-hash')
self.assertEqual(self.get_context_variable('content'),
stub_content_raw)
mock_service.lookup_content_raw.assert_called_once_with(
'sha1:sha1-hash')
@patch('swh.web.ui.views.service')
@istest
def browse_content_data_not_found(self, mock_service):
# given
mock_service.lookup_content_raw.return_value = None
# when
rv = self.client.get('/browse/content/sha1:sha1-unknown/raw/')
self.assertEquals(rv.status_code, 200)
self.assert_template_used('content-data.html')
self.assertEqual(self.get_context_variable('message'),
'Content with sha1:sha1-unknown not found.')
self.assertEqual(self.get_context_variable('content'), None)
mock_service.lookup_content_raw.assert_called_once_with(
'sha1:sha1-unknown')
@patch('swh.web.ui.views.service')
@istest
def browse_content_data_invalid_hash(self, mock_service):
# given
mock_service.lookup_content_raw.side_effect = BadInputExc(
'Invalid hash')
# when
rv = self.client.get('/browse/content/sha2:sha1-invalid/raw/')
self.assertEquals(rv.status_code, 200)
self.assert_template_used('content-data.html')
self.assertEqual(self.get_context_variable('message'),
'Invalid hash')
self.assertEqual(self.get_context_variable('content'), None)
mock_service.lookup_content_raw.assert_called_once_with(
'sha2:sha1-invalid')
@patch('swh.web.ui.views.service')
@patch('swh.web.ui.utils')
@istest
def browse_directory_bad_input(self, mock_utils, mock_service):
# given
mock_service.lookup_directory.side_effect = BadInputExc('Invalid hash')
# when
rv = self.client.get('/browse/directory/sha2-invalid/')
# then
self.assertEquals(rv.status_code, 200)
self.assert_template_used('directory.html')
self.assertEqual(self.get_context_variable('message'),
'Invalid hash')
self.assertEqual(self.get_context_variable('files'), [])
mock_service.lookup_directory.assert_called_once_with(
'sha2-invalid')
@patch('swh.web.ui.views.service')
@patch('swh.web.ui.utils')
@istest
def browse_directory_empty_result(self, mock_utils, mock_service):
# given
mock_service.lookup_directory.return_value = None
# when
rv = self.client.get('/browse/directory/some-sha1/')
# then
self.assertEquals(rv.status_code, 200)
self.assert_template_used('directory.html')
self.assertEqual(self.get_context_variable('message'),
'Directory some-sha1 not found.')
self.assertEqual(self.get_context_variable('files'), [])
mock_service.lookup_directory.assert_called_once_with(
'some-sha1')
@patch('swh.web.ui.views.service')
@patch('swh.web.ui.views.utils')
@istest
def browse_directory(self, mock_utils, mock_service):
# given
stub_directory_ls = [
{'type': 'dir',
'target': '123',
'name': 'some-dir-name'},
{'type': 'file',
'sha1': '654',
'name': 'some-filename'},
{'type': 'dir',
'target': '987',
'name': 'some-other-dirname'}
]
mock_service.lookup_directory.return_value = stub_directory_ls
stub_directory_map = [
{'link': '/path/to/url/dir/123',
'name': 'some-dir-name'},
{'link': '/path/to/url/file/654',
'name': 'some-filename'},
{'link': '/path/to/url/dir/987',
'name': 'some-other-dirname'}
]
mock_utils.prepare_directory_listing.return_value = stub_directory_map
# when
rv = self.client.get('/browse/directory/some-sha1/')
# then
self.assertEquals(rv.status_code, 200)
self.assert_template_used('directory.html')
self.assertEqual(self.get_context_variable('message'),
'Listing for directory some-sha1:')
self.assertEqual(self.get_context_variable('files'),
stub_directory_map)
mock_service.lookup_directory.assert_called_once_with(
'some-sha1')
mock_utils.prepare_directory_listing.assert_called_once_with(
stub_directory_ls)
@patch('swh.web.ui.views.service')
# @istest
def browse_content_with_origin_content_not_found(self, mock_service):
# given
mock_service.lookup_hash.return_value = {'found': False}
# when
rv = self.client.get('/browse/content/sha256:some-sha256/origin/')
# then
self.assertEquals(rv.status_code, 200)
self.assert_template_used('content-with-origin.html')
self.assertEqual(self.get_context_variable('message'),
'Hash sha256:some-sha256 was not found.')
mock_service.lookup_hash.assert_called_once_with(
'sha256:some-sha256')
mock_service.lookup_hash_origin.called = False
@patch('swh.web.ui.views.service')
# @istest
def browse_content_with_origin_bad_input(self, mock_service):
# given
mock_service.lookup_hash.side_effect = BadInputExc('Invalid hash')
# when
rv = self.client.get('/browse/content/sha256:some-sha256/origin/')
# then
self.assertEquals(rv.status_code, 200)
self.assert_template_used('content-with-origin.html')
self.assertEqual(
self.get_context_variable('message'), 'Invalid hash')
mock_service.lookup_hash.assert_called_once_with(
'sha256:some-sha256')
mock_service.lookup_hash_origin.called = False
@patch('swh.web.ui.views.service')
# @istest
def browse_content_with_origin(self, mock_service):
# given
mock_service.lookup_hash.return_value = {'found': True}
mock_service.lookup_hash_origin.return_value = {
'origin_type': 'ftp',
'origin_url': '/some/url',
'revision': 'revision-hash',
'branch': 'master',
'path': '/path/to',
}
# when
rv = self.client.get('/browse/content/sha256:some-sha256/origin/')
# then
self.assertEquals(rv.status_code, 200)
self.assert_template_used('content-with-origin.html')
self.assertEqual(
self.get_context_variable('message'),
"The content with hash sha256:some-sha256 has been seen on " +
"origin with type 'ftp'\n" +
"at url '/some/url'. The revision was identified at " +
"'revision-hash' on branch 'master'.\n" +
"The file's path referenced was '/path/to'.")
mock_service.lookup_hash.assert_called_once_with(
'sha256:some-sha256')
mock_service.lookup_hash_origin.assert_called_once_with(
'sha256:some-sha256')
@patch('swh.web.ui.views.service')
@istest
def browse_origin_not_found(self, mock_service):
# given
mock_service.lookup_origin.return_value = None
# when
rv = self.client.get('/browse/origin/1/')
# then
self.assertEquals(rv.status_code, 200)
self.assert_template_used('origin.html')
self.assertEqual(self.get_context_variable('origin_id'), 1)
self.assertEqual(
self.get_context_variable('message'),
'Origin 1 not found!')
mock_service.lookup_origin.assert_called_once_with(1)
@patch('swh.web.ui.views.service')
@istest
def browse_origin_found(self, mock_service):
# given
mock_origin = {'type': 'git',
'lister': None,
'project': None,
'url': 'rsync://some/url',
'id': 426}
mock_service.lookup_origin.return_value = mock_origin
# when
rv = self.client.get('/browse/origin/426/')
# then
self.assertEquals(rv.status_code, 200)
self.assert_template_used('origin.html')
self.assertEqual(self.get_context_variable('origin_id'), 426)
self.assertEqual(self.get_context_variable('origin'), mock_origin)
mock_service.lookup_origin.assert_called_once_with(426)
@patch('swh.web.ui.views.service')
@istest
def browse_origin_bad_input(self, mock_service):
# given
mock_service.lookup_origin.side_effect = BadInputExc('wrong input')
# when
rv = self.client.get('/browse/origin/426/')
# then
self.assertEquals(rv.status_code, 200)
self.assert_template_used('origin.html')
self.assertEqual(self.get_context_variable('origin_id'), 426)
mock_service.lookup_origin.assert_called_once_with(426)
@patch('swh.web.ui.views.service')
@istest
def browse_person_not_found(self, mock_service):
# given
mock_service.lookup_person.return_value = None
# when
rv = self.client.get('/browse/person/1/')
# then
self.assertEquals(rv.status_code, 200)
self.assert_template_used('person.html')
self.assertEqual(self.get_context_variable('person_id'), 1)
self.assertEqual(
self.get_context_variable('message'),
'Person 1 not found!')
mock_service.lookup_person.assert_called_once_with(1)
@patch('swh.web.ui.views.service')
@istest
def browse_person_found(self, mock_service):
# given
mock_person = {'type': 'git',
'lister': None,
'project': None,
'url': 'rsync://some/url',
'id': 426}
mock_service.lookup_person.return_value = mock_person
# when
rv = self.client.get('/browse/person/426/')
# then
self.assertEquals(rv.status_code, 200)
self.assert_template_used('person.html')
self.assertEqual(self.get_context_variable('person_id'), 426)
self.assertEqual(self.get_context_variable('person'), mock_person)
mock_service.lookup_person.assert_called_once_with(426)
@patch('swh.web.ui.views.service')
@istest
def browse_person_bad_input(self, mock_service):
# given
mock_service.lookup_person.side_effect = BadInputExc('wrong input')
# when
rv = self.client.get('/browse/person/426/')
# then
self.assertEquals(rv.status_code, 200)
self.assert_template_used('person.html')
self.assertEqual(self.get_context_variable('person_id'), 426)
mock_service.lookup_person.assert_called_once_with(426)
@patch('swh.web.ui.views.service')
@istest
def browse_release_not_found(self, mock_service):
# given
mock_service.lookup_release.return_value = None
# when
rv = self.client.get('/browse/release/1/')
# then
self.assertEquals(rv.status_code, 200)
self.assert_template_used('release.html')
self.assertEqual(self.get_context_variable('sha1_git'), '1')
self.assertEqual(
self.get_context_variable('message'),
'Release 1 not found!')
mock_service.lookup_release.assert_called_once_with('1')
@patch('swh.web.ui.views.service')
@istest
def browse_release_bad_input(self, mock_service):
# given
mock_service.lookup_release.side_effect = BadInputExc('wrong input')
# when
rv = self.client.get('/browse/release/426/')
# then
self.assertEquals(rv.status_code, 200)
self.assert_template_used('release.html')
self.assertEqual(self.get_context_variable('sha1_git'), '426')
mock_service.lookup_release.assert_called_once_with('426')
@patch('swh.web.ui.views.service')
@istest
def browse_release(self, mock_service):
# given
mock_release = {
"date": "Sun, 05 Jul 2015 18:02:06 GMT",
"id": "1e951912027ea6873da6985b91e50c47f645ae1a",
"target": "d770e558e21961ad6cfdf0ff7df0eb5d7d4f0754",
"synthetic": False,
"target_type": "revision",
"author": {
"email": "torvalds@linux-foundation.org",
"name": "Linus Torvalds"
},
"message": "Linux 4.2-rc1\n",
"name": "v4.2-rc1"
}
mock_service.lookup_release.return_value = mock_release
expected_release = {
"date": "Sun, 05 Jul 2015 18:02:06 GMT",
"id": "1e951912027ea6873da6985b91e50c47f645ae1a",
"target": '/browse/revision/d770e558e21961ad6cfdf0ff7df0'
'eb5d7d4f0754/',
"synthetic": False,
"target_type": "revision",
"author": "Linus Torvalds ",
"message": "Linux 4.2-rc1\n",
"name": "v4.2-rc1"
}
# when
rv = self.client.get('/browse/release/426/')
# then
self.assertEquals(rv.status_code, 200)
self.assert_template_used('release.html')
self.assertEqual(self.get_context_variable('sha1_git'), '426')
self.assertEqual(self.get_context_variable('release'),
expected_release)
self.assertEqual(self.get_context_variable('keys'), [
'id', 'name', 'date', 'message', 'author', 'target',
'target_type'])
mock_service.lookup_release.assert_called_once_with('426')
@patch('swh.web.ui.views.service')
@istest
def browse_revision_not_found(self, mock_service):
# given
mock_service.lookup_revision.return_value = None
# when
rv = self.client.get('/browse/revision/1/')
# then
self.assertEquals(rv.status_code, 200)
self.assert_template_used('revision.html')
self.assertEqual(self.get_context_variable('sha1_git'), '1')
self.assertEqual(
self.get_context_variable('message'),
'Revision 1 not found!')
mock_service.lookup_revision.assert_called_once_with('1')
@patch('swh.web.ui.views.service')
@istest
def browse_revision_bad_input(self, mock_service):
# given
mock_service.lookup_revision.side_effect = BadInputExc('wrong input')
# when
rv = self.client.get('/browse/revision/426/')
# then
self.assertEquals(rv.status_code, 200)
self.assert_template_used('revision.html')
self.assertEqual(self.get_context_variable('sha1_git'), '426')
mock_service.lookup_revision.assert_called_once_with('426')
@patch('swh.web.ui.views.service')
@istest
def browse_revision_found(self, mock_service):
# given
mock_revision = {
'id': 'd770e558e21961ad6cfdf0ff7df0eb5d7d4f0754',
'date': 'Sun, 05 Jul 2015 18:01:52 GMT',
'committer': {
'email': 'torvalds@linux-foundation.org',
'name': 'Linus Torvalds'
},
'committer_date': 'Sun, 05 Jul 2015 18:01:52 GMT',
'metadata': None,
'type': 'git',
'author': {
'email': 'torvalds@linux-foundation.org',
'name': 'Linus Torvalds'
},
'message': 'Linux 4.2-rc1\n',
'synthetic': False,
'directory': '2a1dbabeed4dcf1f4a4c441993b2ffc9d972780b',
'parents': [
'a585d2b738bfa26326b3f1f40f0f1eda0c067ccf'
],
}
mock_service.lookup_revision.return_value = mock_revision
expected_revision = {
'id': 'd770e558e21961ad6cfdf0ff7df0eb5d7d4f0754',
'date': 'Sun, 05 Jul 2015 18:01:52 GMT',
'committer': 'Linus Torvalds ',
'committer_date': 'Sun, 05 Jul 2015 18:01:52 GMT',
'type': 'git',
'author': 'Linus Torvalds ',
'message': 'Linux 4.2-rc1\n',
'synthetic': False,
'metadata': None,
'parents': [
'/browse/revision/a585d2b738bfa26326b3f1f40f0f1eda0c067ccf/'
],
'directory': '/browse/directory/2a1dbabeed4dcf1f4a4c441993b2f'
'fc9d972780b/',
}
# when
rv = self.client.get('/browse/revision/426/')
# then
self.assertEquals(rv.status_code, 200)
self.assert_template_used('revision.html')
self.assertEqual(self.get_context_variable('sha1_git'), '426')
self.assertEqual(self.get_context_variable('revision'),
expected_revision)
- self.assertEqual(self.get_context_variable('keys'),
- set(expected_revision.keys()) - set(['directory',
- 'parents',
- 'children']))
mock_service.lookup_revision.assert_called_once_with('426')
@istest
def browse_revision_history_same_sha1(self):
# when
rv = self.client.get('/browse/revision/10/history/10/')
# then
self.assertEquals(rv.status_code, 302)
@patch('swh.web.ui.views.service')
@istest
def browse_revision_history_not_found(self, mock_service):
# given
mock_service.lookup_revision_with_context.return_value = None
# when
rv = self.client.get('/browse/revision/1/history/2/')
# then
self.assertEquals(rv.status_code, 200)
self.assert_template_used('revision.html')
self.assertEqual(self.get_context_variable('sha1_git_root'), '1')
self.assertEqual(self.get_context_variable('sha1_git'), '2')
self.assertEqual(
self.get_context_variable('message'),
"Possibly sha1_git '2' is not an ancestor of sha1_git_root '1'")
mock_service.lookup_revision_with_context.assert_called_once_with(
'1', '2', 100)
@patch('swh.web.ui.views.service')
@istest
def browse_revision_history_root_not_found(self, mock_service):
# given
mock_service.lookup_revision_with_context.side_effect = NotFoundExc(
'Revision root 123 not found')
# when
rv = self.client.get('/browse/revision/123/history/456/')
# then
self.assertEquals(rv.status_code, 200)
self.assert_template_used('revision.html')
self.assertEqual(self.get_context_variable('sha1_git_root'), '123')
self.assertEqual(self.get_context_variable('sha1_git'), '456')
self.assertEqual(
self.get_context_variable('message'),
"Revision root 123 not found")
mock_service.lookup_revision_with_context.assert_called_once_with(
'123', '456', 100)
@patch('swh.web.ui.views.service')
@istest
def browse_revision_history_root_bad_input(self, mock_service):
# given
mock_service.lookup_revision_with_context.side_effect = NotFoundExc(
'Input incorrect')
# when
rv = self.client.get('/browse/revision/321/history/654/')
# then
self.assertEquals(rv.status_code, 200)
self.assert_template_used('revision.html')
self.assertEqual(self.get_context_variable('sha1_git_root'), '321')
self.assertEqual(self.get_context_variable('sha1_git'), '654')
self.assertEqual(
self.get_context_variable('message'),
"Input incorrect")
mock_service.lookup_revision_with_context.assert_called_once_with(
'321', '654', 100)
@patch('swh.web.ui.views.utils')
@patch('swh.web.ui.views.service')
@istest
def browse_revision_history(self, mock_service, mock_utils):
# given
stub_revision = {'id': 'some-rev'}
mock_service.lookup_revision_with_context.return_value = stub_revision
expected_revision = {'id': 'some-rev-id'}
mock_utils.prepare_revision_view.return_value = expected_revision
# when
rv = self.client.get('/browse/revision/426/history/789/')
# then
self.assertEquals(rv.status_code, 200)
self.assert_template_used('revision.html')
self.assertEqual(self.get_context_variable('sha1_git_root'), '426')
self.assertEqual(self.get_context_variable('sha1_git'), 'some-rev-id')
self.assertEqual(self.get_context_variable('revision'),
expected_revision)
- self.assertEqual(self.get_context_variable('keys'),
- set(expected_revision.keys()) - set(['directory',
- 'parents',
- 'children']))
mock_service.lookup_revision_with_context.assert_called_once_with(
'426', '789', 100)
mock_utils.prepare_revision_view.assert_called_once_with(stub_revision)
@patch('swh.web.ui.views.service')
@istest
def browse_revision_directory_not_found(self, mock_service):
# given
mock_service.lookup_directory_through_revision.side_effect = NotFoundExc( # noqa
'Not found!')
# when
rv = self.client.get('/browse/revision/1/directory/')
# then
self.assertEquals(rv.status_code, 200)
self.assert_template_used('revision-directory.html')
self.assertEqual(self.get_context_variable('sha1_git'), '1')
self.assertEqual(self.get_context_variable('path'), '.')
self.assertIsNone(self.get_context_variable('result'))
self.assertEqual(
self.get_context_variable('message'),
"Not found!")
mock_service.lookup_directory_through_revision.assert_called_once_with(
{'sha1_git': '1'}, None)
@patch('swh.web.ui.views.service')
@istest
def browse_revision_directory_bad_input(self, mock_service):
# given
mock_service.lookup_directory_through_revision.side_effect = BadInputExc( # noqa
'Bad input!')
# when
rv = self.client.get('/browse/revision/10/directory/')
# then
self.assertEquals(rv.status_code, 200)
self.assert_template_used('revision-directory.html')
self.assertEqual(self.get_context_variable('sha1_git'), '10')
self.assertEqual(self.get_context_variable('path'), '.')
self.assertIsNone(self.get_context_variable('result'))
self.assertEqual(
self.get_context_variable('message'),
"Bad input!")
mock_service.lookup_directory_through_revision.assert_called_once_with(
{'sha1_git': '10'}, None)
@patch('swh.web.ui.views.service')
@istest
def browse_revision_directory_not_implemented(self, mock_service):
# given
mock_service.lookup_directory_through_revision.side_effect = NotImplementedError( # noqa
'Oops! Not implemented!')
# when
rv = self.client.get('/browse/revision/10/directory/path/')
# then
self.assertEquals(rv.status_code, 200)
self.assert_template_used('revision-directory.html')
self.assertEqual(self.get_context_variable('sha1_git'), '10')
self.assertEqual(self.get_context_variable('path'), 'path')
self.assertIsNone(self.get_context_variable('result'))
self.assertEqual(
self.get_context_variable('message'),
'Oops! Not implemented!')
mock_service.lookup_directory_through_revision.assert_called_once_with(
{'sha1_git': '10'}, 'path')
@patch('swh.web.ui.views.service')
@istest
def browse_revision_directory(self, mock_service):
# given
stub_result0 = {'type': 'dir',
'content': [{'id': 'some-result',
'type': 'file',
'name': 'blah'}]}
mock_service.lookup_directory_through_revision.return_value = (
'100', stub_result0)
stub_result1 = {
'type': 'dir',
'content': [
{'type': 'file',
'name': 'blah',
'link': '/browse/revision/100/directory/some/path/blah/'}
]
}
# when
rv = self.client.get('/browse/revision/100/directory/some/path/')
# then
self.assertEquals(rv.status_code, 200)
self.assert_template_used('revision-directory.html')
self.assertEqual(self.get_context_variable('sha1_git'), '100')
self.assertEqual(self.get_context_variable('path'), 'some/path')
self.assertIsNone(self.get_context_variable('message'))
self.assertEqual(self.get_context_variable('result'), stub_result1)
mock_service.lookup_directory_through_revision.assert_called_once_with(
{'sha1_git': '100'}, 'some/path')
@patch('swh.web.ui.views.service')
@istest
def browse_revision_history_directory_redirect(self, mock_service):
# when
rv = self.client.get('/browse/revision/1/history/1/directory/path/to/')
# then
self.assertEquals(rv.status_code, 301)
@patch('swh.web.ui.views.service')
@istest
def browse_revision_history_directory_not_found(self, mock_service):
# given
mock_service.lookup_directory_through_revision.side_effect = NotFoundExc('not found') # noqa
# when
rv = self.client.get('/browse/revision/123/history/456/directory/'
'?limit=666')
# then
self.assertEquals(rv.status_code, 200)
self.assert_template_used('revision-directory.html')
self.assertEqual(self.get_context_variable('sha1_git_root'), '123')
self.assertEqual(self.get_context_variable('sha1_git'), '456')
self.assertEqual(self.get_context_variable('path'), '.')
self.assertEqual(self.get_context_variable('message'), 'not found')
self.assertIsNone(self.get_context_variable('result'))
mock_service.lookup_directory_through_revision.assert_called_once_with(
{'sha1_git': '456', 'sha1_git_root': '123'}, None, 666)
@patch('swh.web.ui.views.service')
@istest
def browse_revision_history_directory_bad_input(self, mock_service):
# given
mock_service.lookup_directory_through_revision.side_effect = BadInputExc('bad input') # noqa
# when
rv = self.client.get('/browse/revision/123/history/456/directory/')
# then
self.assertEquals(rv.status_code, 200)
self.assert_template_used('revision-directory.html')
self.assertEqual(self.get_context_variable('sha1_git_root'), '123')
self.assertEqual(self.get_context_variable('sha1_git'), '456')
self.assertEqual(self.get_context_variable('path'), '.')
self.assertEqual(self.get_context_variable('message'), 'bad input')
self.assertIsNone(self.get_context_variable('result'))
mock_service.lookup_directory_through_revision.assert_called_once_with(
{'sha1_git': '456', 'sha1_git_root': '123'}, None, 100)
@patch('swh.web.ui.views.service')
@istest
def browse_revision_history_directory_not_implemented(self, mock_service):
# given
mock_service.lookup_directory_through_revision.side_effect = NotImplementedError('not yet') # noqa
# when
rv = self.client.get('/browse/revision/123/history/456/directory/'
'path/to/?limit=9000')
# then
self.assertEquals(rv.status_code, 200)
self.assert_template_used('revision-directory.html')
self.assertEqual(self.get_context_variable('sha1_git_root'), '123')
self.assertEqual(self.get_context_variable('sha1_git'), '456')
self.assertEqual(self.get_context_variable('path'), 'path/to')
self.assertEqual(self.get_context_variable('message'), 'not yet')
self.assertIsNone(self.get_context_variable('result'))
mock_service.lookup_directory_through_revision.assert_called_once_with(
{'sha1_git': '456', 'sha1_git_root': '123'}, 'path/to', 9000)
@patch('swh.web.ui.views.service')
@istest
def browse_revision_history_directory(self, mock_service):
# given
stub_result0 = {'type': 'dir',
'content': [{'id': 'some-result',
'type': 'file',
'name': 'blah'}]}
mock_service.lookup_directory_through_revision.return_value = (
'1000',
stub_result0)
stub_result1 = {
'type': 'dir',
'content': [
{'type': 'file',
'name': 'blah',
'link': '/browse/revision/100/history/999/directory/'
'path/to/blah/'}
]
}
# when
rv = self.client.get('/browse/revision/100/history/999/directory/'
'path/to/')
# then
self.assertEquals(rv.status_code, 200)
self.assert_template_used('revision-directory.html')
self.assertEqual(self.get_context_variable('sha1_git_root'), '100')
self.assertEqual(self.get_context_variable('sha1_git'), '1000')
self.assertEqual(self.get_context_variable('path'), 'path/to')
self.assertIsNone(self.get_context_variable('message'))
self.assertEqual(self.get_context_variable('result'), stub_result1)
mock_service.lookup_directory_through_revision.assert_called_once_with(
{'sha1_git': '999', 'sha1_git_root': '100'}, 'path/to', 100)
@patch('swh.web.ui.views.service')
@istest
def browse_entity_not_found(self, mock_service):
# given
mock_service.lookup_entity_by_uuid.return_value = []
# when
rv = self.client.get('/browse/entity/')
# then
self.assertEquals(rv.status_code, 200)
self.assert_template_used('entity.html')
self.assertEqual(self.get_context_variable('entities'), [])
self.assertEqual(
self.get_context_variable('message'),
"Entity '5f4d4c51-498a-4e28-88b3-b3e4e8396cba' not found!")
mock_service.lookup_entity_by_uuid.assert_called_once_with(
'5f4d4c51-498a-4e28-88b3-b3e4e8396cba')
@patch('swh.web.ui.views.service')
@istest
def browse_entity_bad_input(self, mock_service):
# given
mock_service.lookup_entity_by_uuid.side_effect = BadInputExc(
'wrong input')
# when
rv = self.client.get('/browse/entity/blah-blah-uuid/')
# then
self.assertEquals(rv.status_code, 200)
self.assert_template_used('entity.html')
self.assertEqual(self.get_context_variable('entities'), [])
mock_service.lookup_entity_by_uuid.assert_called_once_with(
'blah-blah-uuid')
@patch('swh.web.ui.views.service')
@istest
def browse_entity(self, mock_service):
# given
stub_entities = [
{'id': '5f4d4c51-5a9b-4e28-88b3-b3e4e8396cba'}]
mock_service.lookup_entity_by_uuid.return_value = stub_entities
# when
rv = self.client.get('/browse/entity/'
'5f4d4c51-5a9b-4e28-88b3-b3e4e8396cba/')
# then
self.assertEquals(rv.status_code, 200)
self.assert_template_used('entity.html')
self.assertEqual(self.get_context_variable('entities'), stub_entities)
self.assertIsNone(self.get_context_variable('message'))
mock_service.lookup_entity_by_uuid.assert_called_once_with(
'5f4d4c51-5a9b-4e28-88b3-b3e4e8396cba')
@patch('swh.web.ui.views.api')
@istest
def browse_revision_history_through_origin_KO_bad_input(self, mock_api):
# given
mock_api.api_history_through_revision_with_origin.side_effect = BadInputExc('Problem input.') # noqa
# when
rv = self.client.get('/browse/revision/origin/99'
'/history/123/')
# then
self.assertEquals(rv.status_code, 200)
self.assert_template_used('revision.html')
self.assertIsNone(self.get_context_variable('revision'))
self.assertEqual(self.get_context_variable('message'),
'Problem input.')
mock_api.api_history_through_revision_with_origin.assert_called_once_with( # noqa
99, 'refs/heads/master', None, '123')
@patch('swh.web.ui.views.api')
@istest
def browse_revision_history_through_origin_KO_not_found(self, mock_api):
# given
mock_api.api_history_through_revision_with_origin.side_effect = NotFoundExc('Not found.') # noqa
# when
rv = self.client.get('/browse/revision/origin/999/'
'branch/dev/history/123/')
# then
self.assertEquals(rv.status_code, 200)
self.assert_template_used('revision.html')
self.assertIsNone(self.get_context_variable('revision'))
self.assertEqual(self.get_context_variable('message'),
'Not found.')
mock_api.api_history_through_revision_with_origin.assert_called_once_with( # noqa
999, 'dev', None, '123')
@patch('swh.web.ui.views.api')
@istest
def browse_revision_history_through_origin_KO_other_error(self, mock_api):
# given
mock_api.api_history_through_revision_with_origin.side_effect = ValueError('Other Error.') # noqa
# when
rv = self.client.get('/browse/revision/origin/438'
'/branch/scratch'
'/ts/2016'
'/history/789/')
# then
self.assertEquals(rv.status_code, 200)
self.assert_template_used('revision.html')
self.assertIsNone(self.get_context_variable('revision'))
self.assertEqual(self.get_context_variable('message'),
'Other Error.')
mock_api.api_history_through_revision_with_origin.assert_called_once_with( # noqa
438, 'scratch', '2016', '789')
@patch('swh.web.ui.views.api')
@istest
def browse_revision_history_through_origin(self, mock_api):
# given
stub_rev = {'id': 'some-id'}
mock_api.api_history_through_revision_with_origin.return_value = stub_rev # noqa
# when
rv = self.client.get('/browse/revision/origin/99/history/123/')
# then
self.assertEquals(rv.status_code, 200)
self.assert_template_used('revision.html')
self.assertEqual(self.get_context_variable('revision'), stub_rev)
self.assertIsNone(self.get_context_variable('message'))
mock_api.api_history_through_revision_with_origin.assert_called_once_with( # noqa
99, 'refs/heads/master', None, '123')
+
+ @patch('swh.web.ui.views.api')
+ @istest
+ def browse_revision_with_origin_KO_not_found(self, mock_api):
+ # given
+ mock_api.api_revision_with_origin.side_effect = NotFoundExc(
+ 'Not found')
+
+ # when
+ rv = self.client.get('/browse/revision/origin/1/')
+
+ self.assertEquals(rv.status_code, 200)
+ self.assert_template_used('revision.html')
+ self.assertIsNone(self.get_context_variable('revision'))
+ self.assertEqual(self.get_context_variable('message'), 'Not found')
+
+ mock_api.api_revision_with_origin.assert_called_once_with(
+ 1, 'refs/heads/master', None)
+
+ @patch('swh.web.ui.views.api')
+ @istest
+ def browse_revision_with_origin_KO_bad_input(self, mock_api):
+ # given
+ mock_api.api_revision_with_origin.side_effect = BadInputExc(
+ 'Bad Input')
+
+ # when
+ rv = self.client.get('/browse/revision/origin/1000/branch/dev/')
+
+ self.assertEquals(rv.status_code, 200)
+ self.assert_template_used('revision.html')
+ self.assertIsNone(self.get_context_variable('revision'))
+ self.assertEqual(self.get_context_variable('message'), 'Bad Input')
+
+ mock_api.api_revision_with_origin.assert_called_once_with(
+ 1000, 'dev', None)
+
+ @patch('swh.web.ui.views.api')
+ @istest
+ def browse_revision_with_origin_KO_other(self, mock_api):
+ # given
+ mock_api.api_revision_with_origin.side_effect = ValueError(
+ 'Other')
+
+ # when
+ rv = self.client.get('/browse/revision/origin/1999'
+ '/branch/scratch/master'
+ '/ts/1990-01-10/')
+
+ self.assertEquals(rv.status_code, 200)
+ self.assert_template_used('revision.html')
+ self.assertIsNone(self.get_context_variable('revision'))
+ self.assertEqual(self.get_context_variable('message'), 'Other')
+
+ mock_api.api_revision_with_origin.assert_called_once_with(
+ 1999, 'scratch/master', '1990-01-10')
+
+ @patch('swh.web.ui.views.api')
+ @istest
+ def browse_revision_with_origin(self, mock_api):
+ # given
+ stub_rev = {'id': 'some-id'}
+ mock_api.api_revision_with_origin.return_value = stub_rev
+
+ # when
+ rv = self.client.get('/browse/revision/origin/1/')
+
+ self.assertEquals(rv.status_code, 200)
+ self.assert_template_used('revision.html')
+ self.assertEqual(self.get_context_variable('revision'), stub_rev)
+ self.assertIsNone(self.get_context_variable('message'))
+
+ mock_api.api_revision_with_origin.assert_called_once_with(
+ 1, 'refs/heads/master', None)
diff --git a/swh/web/ui/views.py b/swh/web/ui/views.py
index c0794a0e..0277f83e 100644
--- a/swh/web/ui/views.py
+++ b/swh/web/ui/views.py
@@ -1,671 +1,647 @@
# Copyright (C) 2015 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
import flask
from flask import render_template, request, url_for, redirect
from flask.ext.api.decorators import set_renderers
from flask.ext.api.renderers import HTMLRenderer
from swh.core.hashutil import ALGORITHMS
from swh.web.ui import service, utils, api
from swh.web.ui.exc import BadInputExc, NotFoundExc
from swh.web.ui.main import app
hash_filter_keys = ALGORITHMS
@app.route('/')
@set_renderers(HTMLRenderer)
def homepage():
"""Home page
"""
flask.flash('This Web app is still work in progress, use at your own risk',
'warning')
return render_template('home.html')
@app.route('/about/')
@set_renderers(HTMLRenderer)
def about():
return render_template('about.html')
@app.route('/search/', methods=['GET', 'POST'])
@set_renderers(HTMLRenderer)
def search():
"""Search for hashes in swh-storage.
One form to submit either:
- hash query to look up in swh storage
- some file content to upload, compute its hash and look it up in swh
storage
- both
Returns:
dict representing data to look for in swh storage.
The following keys are returned:
- file: File submitted for upload
- filename: Filename submitted for upload
- q: Query on hash to look for
- message: Message detailing if data has been found or not.
"""
env = {'filename': None,
'q': None,
'file': None}
data = None
q = env['q']
file = env['file']
if request.method == 'GET':
data = request.args
elif request.method == 'POST':
data = request.data
# or hash and search a file
file = request.files.get('filename')
# could either be a query for sha1 hash
q = data.get('q')
messages = []
if q:
env['q'] = q
try:
r = service.lookup_hash(q)
messages.append('Content with hash %s%sfound!' % (
q, ' ' if r.get('found') else ' not '))
except BadInputExc as e:
messages.append(str(e))
if file and file.filename:
env['file'] = file
try:
uploaded_content = service.upload_and_search(file)
filename = uploaded_content['filename']
sha1 = uploaded_content['sha1']
found = uploaded_content['found']
messages.append('File %s with hash %s%sfound!' % (
filename, sha1, ' ' if found else ' not '))
env.update({
'filename': filename,
'sha1': sha1,
})
except BadInputExc as e:
messages.append(str(e))
env['q'] = q if q else ''
env['messages'] = messages
return render_template('upload_and_search.html', **env)
def _origin_seen(q, data):
"""Given an origin, compute a message string with the right information.
Args:
origin: a dictionary with keys:
- origin: a dictionary with type and url keys
- occurrence: a dictionary with a validity range
Returns:
Message as a string
"""
origin_type = data['origin_type']
origin_url = data['origin_url']
revision = data['revision']
branch = data['branch']
path = data['path']
return """The content with hash %s has been seen on origin with type '%s'
at url '%s'. The revision was identified at '%s' on branch '%s'.
The file's path referenced was '%s'.""" % (q,
origin_type,
origin_url,
revision,
branch,
path)
@app.route('/browse/content/')
@app.route('/browse/content//')
@set_renderers(HTMLRenderer)
def browse_content_detail(q='5d448a06f02d9de748b6b0b9620cba1bed8480da'):
"""Given a hash and a checksum, display the content's meta-data.
Args:
q is of the form algo_hash:hash with algo_hash in
(sha1, sha1_git, sha256)
Returns:
Information on one possible origin for such content.
Raises:
BadInputExc in case of unknown algo_hash or bad hash
NotFoundExc if the content is not found.
"""
env = {}
message = None
content = None
try:
content = service.lookup_content(q)
if not content:
message = 'Content with %s not found.' % q
except BadInputExc as e:
message = str(e)
env['message'] = message
env['content'] = content
return render_template('content.html', **env)
@app.route('/browse/content//raw/')
@set_renderers(HTMLRenderer)
def browse_content_data(q):
"""Given a hash and a checksum, display the content's raw data.
Args:
q is of the form algo_hash:hash with algo_hash in
(sha1, sha1_git, sha256)
Returns:
Information on one possible origin for such content.
Raises:
BadInputExc in case of unknown algo_hash or bad hash
NotFoundExc if the content is not found.
"""
env = {}
content = None
try:
content = service.lookup_content_raw(q)
if content:
# FIXME: will break if not utf-8
content['data'] = content['data'].decode('utf-8')
message = 'Content %s' % content['sha1']
else:
message = 'Content with %s not found.' % q
except BadInputExc as e:
message = str(e)
env['message'] = message
env['content'] = content
return render_template('content-data.html', **env)
# @app.route('/browse/content//origin/')
@set_renderers(HTMLRenderer)
def browse_content_with_origin(
q='sha1:4320781056e5a735a39de0b8c229aea224590052'):
"""Show content information.
Args:
- q: query string of the form with
`algo_hash` in sha1, sha1_git, sha256.
This means that several different URLs (at least one per
HASH_ALGO) will point to the same content sha: the sha with
'hash' format
Returns:
The content's information at for a given checksum.
"""
env = {'q': q}
try:
content = service.lookup_hash(q)
if not content.get('found'):
message = "Hash %s was not found." % q
else:
origin = service.lookup_hash_origin(q)
message = _origin_seen(q, origin)
except BadInputExc as e: # do not like it but do not duplicate code
message = str(e)
env['message'] = message
return render_template('content-with-origin.html', **env)
@app.route('/browse/directory/')
@app.route('/browse/directory//')
@set_renderers(HTMLRenderer)
def browse_directory(sha1_git='dcf3289b576b1c8697f2a2d46909d36104208ba3'):
"""Show directory information.
Args:
- sha1_git: the directory's sha1 git identifier.
Returns:
The content's information at sha1_git
"""
env = {'sha1_git': sha1_git}
files = []
try:
directory_files = service.lookup_directory(sha1_git)
if directory_files:
message = "Listing for directory %s:" % sha1_git
files = utils.prepare_directory_listing(directory_files)
else:
message = "Directory %s not found." % sha1_git
except BadInputExc as e: # do not like it but do not duplicate code
message = str(e)
env['message'] = message
env['files'] = files
return render_template('directory.html', **env)
@app.route('/browse/origin/')
@app.route('/browse/origin//')
@set_renderers(HTMLRenderer)
def browse_origin(origin_id=1):
"""Browse origin with id id.
"""
env = {'origin_id': origin_id,
'origin': None}
try:
ori = service.lookup_origin(origin_id)
if ori:
env.update({'origin': ori})
else:
env.update({'message': 'Origin %s not found!' % origin_id})
except BadInputExc as e:
env.update({'message': str(e)})
return render_template('origin.html', **env)
@app.route('/browse/person/')
@app.route('/browse/person//')
@set_renderers(HTMLRenderer)
def browse_person(person_id=1):
"""Browse person with id id.
"""
env = {'person_id': person_id,
'person': None}
try:
ori = service.lookup_person(person_id)
if ori:
env.update({'person': ori})
else:
env.update({'message': 'Person %s not found!' % person_id})
except BadInputExc as e:
env.update({'message': str(e)})
return render_template('person.html', **env)
@app.route('/browse/release/')
@app.route('/browse/release//')
@set_renderers(HTMLRenderer)
def browse_release(sha1_git='1e951912027ea6873da6985b91e50c47f645ae1a'):
"""Browse release with sha1_git.
"""
env = {'sha1_git': sha1_git,
'release': None}
try:
rel = service.lookup_release(sha1_git)
if rel:
author = rel.get('author')
if author:
rel['author'] = utils.person_to_string(author)
target_type = rel.get('target_type')
if target_type == 'revision':
rel['target'] = url_for('browse_revision',
sha1_git=rel['target'])
env.update({'release': rel,
'keys': ['id', 'name', 'date', 'message', 'author',
'target', 'target_type']})
else:
env.update({'message': 'Release %s not found!' % sha1_git})
except BadInputExc as e:
env.update({'message': str(e)})
return render_template('release.html', **env)
@app.route('/browse/revision/')
@app.route('/browse/revision//')
@set_renderers(HTMLRenderer)
def browse_revision(sha1_git='d770e558e21961ad6cfdf0ff7df0eb5d7d4f0754'):
"""Browse revision with sha1_git.
"""
env = {'sha1_git': sha1_git,
'keys': [],
'revision': None}
try:
rev = service.lookup_revision(sha1_git)
if rev:
- rev = utils.prepare_revision_view(rev)
- env.update({
- 'revision': rev,
- 'keys': set(rev.keys()) - set(['directory', 'parents',
- 'children'])
- })
+ env['revision'] = utils.prepare_revision_view(rev)
else:
- env.update({'message': 'Revision %s not found!' % sha1_git})
+ env['message'] = 'Revision %s not found!' % sha1_git
except BadInputExc as e:
env.update({'message': str(e)})
return render_template('revision.html', **env)
@app.route('/browse/revision//history//')
@set_renderers(HTMLRenderer)
def browse_revision_history(sha1_git_root, sha1_git):
"""Display information about revision sha1_git, limited to the
sub-graph of all transitive parents of sha1_git_root.
In other words, sha1_git is an ancestor of sha1_git_root.
Args:
sha1_git_root: latest revision of the browsed history.
sha1_git: one of sha1_git_root's ancestors.
limit: optional query parameter to limit the revisions log
(default to 100). For now, note that this limit could impede the
transitivity conclusion about sha1_git not being an ancestor of
sha1_git_root (even if it is).
Returns:
Information on sha1_git if it is an ancestor of sha1_git_root
including children leading to sha1_git_root.
"""
limit = int(request.args.get('limit', '100'))
env = {'sha1_git_root': sha1_git_root,
'sha1_git': sha1_git,
'message': None,
'keys': [],
'revision': None}
if sha1_git == sha1_git_root:
return redirect(url_for('browse_revision',
sha1_git=sha1_git,
limit=limit))
try:
revision = service.lookup_revision_with_context(sha1_git_root,
sha1_git,
limit)
if revision:
revision = utils.prepare_revision_view(revision)
env.update({
'sha1_git': revision['id'],
'revision': revision,
- 'keys': set(revision.keys()) - set(['directory', 'parents',
- 'children'])
})
else:
env['message'] = "Possibly sha1_git '%s' is not an ancestor " \
"of sha1_git_root '%s'" % (sha1_git,
sha1_git_root)
except (BadInputExc, NotFoundExc) as e:
env['message'] = str(e)
return render_template('revision.html', **env)
@app.route('/browse/revision//directory/')
@app.route('/browse/revision//directory//')
@set_renderers(HTMLRenderer)
def browse_revision_directory(sha1_git, path=None):
"""Browse directory from revision with sha1_git.
"""
env = {
'sha1_git': sha1_git,
'path': '.' if not path else path,
'message': None,
'result': None
}
try:
rev_id, result = service.lookup_directory_through_revision({
'sha1_git': sha1_git
}, path)
if result['type'] == 'dir': # dir_entries
result['content'] = utils.prepare_directory_listing_with_revision(
sha1_git,
path,
result['content'])
env['sha1_git'] = rev_id
env['result'] = result
except (BadInputExc, NotFoundExc, NotImplementedError) as e:
env['message'] = str(e)
return render_template('revision-directory.html', **env)
@app.route('/browse/revision/'
'/history/'
'/directory/')
@app.route('/browse/revision/'
'/history/'
'/directory//')
@set_renderers(HTMLRenderer)
def browse_revision_history_directory(sha1_git_root, sha1_git, path=None):
"""Return information about directory pointed to by the revision
defined as: revision sha1_git, limited to the sub-graph of all
transitive parents of sha1_git_root.
Args:
sha1_git_root: latest revision of the browsed history.
sha1_git: one of sha1_git_root's ancestors.
path: optional directory pointed to by that revision.
limit: optional query parameter to limit the revisions log
(default to 100). For now, note that this limit could impede the
transitivity conclusion about sha1_git not being an ancestor of
sha1_git_root (even if it is).
Returns:
Information on the directory pointed to by that revision.
Raises:
BadInputExc in case of unknown algo_hash or bad hash.
NotFoundExc if either revision is not found or if sha1_git is not an
ancestor of sha1_git_root or the path referenced does not exist
"""
limit = int(request.args.get('limit', '100'))
env = {
'sha1_git_root': sha1_git_root,
'sha1_git': sha1_git,
'path': '.' if not path else path,
'message': None,
'result': None
}
if sha1_git == sha1_git_root:
return redirect(url_for('browse_revision_directory',
sha1_git=sha1_git,
path=path),
code=301)
try:
rev_id, result = service.lookup_directory_through_revision({
'sha1_git_root': sha1_git_root,
'sha1_git': sha1_git
}, path, limit)
if result['type'] == 'dir': # dir_entries
result['content'] = utils.prepare_directory_listing_with_revision_history( # noqa
sha1_git_root,
sha1_git,
path,
result['content'])
env['sha1_git'] = rev_id
env['result'] = result
except (BadInputExc, NotFoundExc, NotImplementedError) as e:
env['message'] = str(e)
return render_template('revision-directory.html', **env)
@app.route('/browse/revision'
'/origin/')
@app.route('/browse/revision'
'/origin//')
@app.route('/browse/revision'
'/origin/'
'/branch//')
@app.route('/browse/revision'
'/origin/'
'/branch/'
'/ts//')
@app.route('/browse/revision'
'/origin/'
'/ts//')
@set_renderers(HTMLRenderer)
def browse_revision_with_origin(origin_id=1,
branch_name="refs/heads/master",
ts=None):
"""Instead of having to specify a (root) revision by SHA1_GIT, users
might want to specify a place and a time. In SWH a "place" is an
origin; a "time" is a timestamp at which some place has been
observed by SWH crawlers.
Args:
origin_id: origin's identifier (default to 1).
branch_name: the optional branch for the given origin (default
to master).
timestamp: optional timestamp (default to the nearest time
crawl of timestamp).
Returns:
Information on the revision if found.
Raises:
BadInputExc in case of unknown algo_hash or bad hash.
NotFoundExc if the revision is not found.
"""
env = {'message': None,
- 'origin_id': origin_id,
- 'branch_name': branch_name,
- 'ts': ts,
'revision': None}
-
try:
- if ts:
- ts = utils.parse_timestamp(ts)
-
- revision = service.lookup_revision_by(origin_id, branch_name, ts)
- if not revision:
- env['message'] = 'No revision at (origin_id: %s, ' \
- 'branch_name: %s, ts: %s).' % (
- origin_id, branch_name, ts)
- else:
- revision = utils.prepare_revision_view(revision)
- env.update({
- 'sha1_git': revision['id'],
- 'revision': revision,
- 'keys': set(revision.keys()) - set(['directory', 'parents',
- 'children'])
- })
+ env['revision'] = api.api_revision_with_origin(origin_id,
+ branch_name,
+ ts)
except (ValueError, NotFoundExc, BadInputExc) as e:
env['message'] = str(e)
return render_template('revision.html', **env)
@app.route('/browse/revision'
'/origin/'
'/history//')
@app.route('/browse/revision'
'/origin/'
'/branch/'
'/history//')
@app.route('/browse/revision'
'/origin/'
'/branch/'
'/ts/'
'/history//')
@set_renderers(HTMLRenderer)
def browse_revision_history_through_origin(origin_id,
branch_name='refs/heads/master',
ts=None,
sha1_git=None):
"""Return information about revision sha1_git, limited to the
sub-graph of all transitive parents of the revision root identified
by (origin_id, branch_name, ts).
Given sha1_git_root such root revision's identifier, in other words,
sha1_git is an ancestor of sha1_git_root.
Args:
origin_id: origin's identifier (default to 1).
branch_name: the optional branch for the given origin (default
to master).
timestamp: optional timestamp (default to the nearest time
crawl of timestamp).
sha1_git: one of sha1_git_root's ancestors.
limit: optional query parameter to limit the revisions log
(default to 100). For now, note that this limit could impede the
transitivity conclusion about sha1_git not being an ancestor of
sha1_git_root (even if it is).
Returns:
Information on sha1_git if it is an ancestor of sha1_git_root
including children leading to sha1_git_root.
"""
env = {'message': None,
'revision': None}
try:
env['revision'] = api.api_history_through_revision_with_origin(
origin_id,
branch_name,
ts,
sha1_git)
except (ValueError, BadInputExc, NotFoundExc) as e:
env['message'] = str(e)
return render_template('revision.html', **env)
@app.route('/browse/entity/')
@app.route('/browse/entity//')
@set_renderers(HTMLRenderer)
def browse_entity(uuid='5f4d4c51-498a-4e28-88b3-b3e4e8396cba'):
env = {'entities': [],
'message': None}
entities = env['entities']
try:
entities = service.lookup_entity_by_uuid(uuid)
if not entities:
env['message'] = "Entity '%s' not found!" % uuid
except BadInputExc as e:
env.update({'message': str(e)})
env['entities'] = entities
return render_template('entity.html', **env)