+
+ {% for route, doc in doc_routes %}
+ {% if 'tags' in doc and doc['tags'] is not none %}
+
+{% endblock %}
diff --git a/swh/web/ui/templates/api.html b/swh/web/ui/templates/api.html
index c650e47ef..12236a5ca 100644
--- a/swh/web/ui/templates/api.html
+++ b/swh/web/ui/templates/api.html
@@ -1,21 +1,8 @@
{% extends "layout.html" %}
-{% block title %}API Overview{% endblock %}
+{% block title %}API Documentation{% endblock %}
{% block content %}
- {% include 'includes/api-header.html' %}
- {% for route, doc in doc_routes %}
- {% if 'tags' in doc and doc['tags'] is not none %}
-
{% endblock %}
diff --git a/swh/web/ui/templates/includes/api-header.html b/swh/web/ui/templates/includes/apidoc-header.html
similarity index 93%
rename from swh/web/ui/templates/includes/api-header.html
rename to swh/web/ui/templates/includes/apidoc-header.html
index 2ca3de372..ae60032bd 100644
--- a/swh/web/ui/templates/includes/api-header.html
+++ b/swh/web/ui/templates/includes/apidoc-header.html
@@ -1,178 +1,176 @@
-
Welcome to Software Heritage project's API.
+
Welcome to Software Heritage project's API documentation.
Table of Contents
Version
-
Current version is 1.
+
Current version is 1.
Schema
-
Api access is over https and accessed through https://archive.softwareheritage.org/api/1/.
-
Data is sent and received as json.
+
Api access is over https and accessed through https://archive.softwareheritage.org/api/1/.
+
Data is sent and received as json by default.
Example:
$ curl -i https://archive.softwareheritage.org/api/1/stat/counters/
HTTP/1.1 200 OK
Date: Mon, 16 Jan 2017 10:57:56 GMT
Server: Apache
Content-Type: application/json
Content-Length: 395
Vary: Accept-Encoding
Access-Control-Allow-Origin: *
Connection: close
{
"directory_entry_rev": 3039473,
"person": 13903080,
"entity": 7103795,
"skipped_content": 17864,
"entity_history": 7147753,
"revision_history": 720840448,
"revision": 703277184,
"directory": 2616883200,
"release": 5692900,
"origin": 49938216,
"directory_entry_dir": 2140887552,
"occurrence_history": 254274832,
"occurrence": 241899344,
"content": 3155739136,
"directory_entry_file": 3173807104
}
Mimetype override
The response output can be sent as yaml provided the client specifies it using the header field.
For example:
curl -i -H 'Accept: application/yaml' https://archive.softwareheritage.org/api/1/stat/counters/
HTTP/1.1 200 OK
Date: Mon, 16 Jan 2017 12:31:50 GMT
Server: Apache
Content-Type: application/yaml
Content-Length: 372
Access-Control-Allow-Origin: *
Connection: close
{content: 3155758336,
directory: 2616955136,
directory_entry_dir: 2140925824,
directory_entry_file: 3173833984,
directory_entry_rev: 3039473,
entity: 7103741,
entity_history: 7148121,
occurrence: 241887488,
occurrence_history: 254277584,
origin: 49939848,
person: 13898394,
release: 5693922,
revision: 703275840,
revision_history: 720842176,
skipped_content: 17864}
Parameters
Some API endpoints can be used with with local parameters. The url then needs to be adapted accordingly.
For example:
https://archive.softwareheritage.org/api/1/<endpoint-name>?<field0>=<value0>&<field1>=<value1>
where:
- field0 is an appropriate field for the and value0
- field1 is an appropriate field for the and value1
Global parameter
One parameter is defined for all api endpoints fields
. It permits to filter the output fields per key.
For example, to only list the number of contents, revisions, directories on the statistical endpoints, one uses:
$ curl https://archive.softwareheritage.org/api/1/stat/counters/\?fields\=content,directory,revision | jq
{
"content": 3155739136,
"revision": 703277184,
"directory": 2616883200
}
Note: If the keys provided to filter on do not exist, they are ignored.
Client errors
There are 2 kinds of error.
In that case, the http error code will reflect that error and a json response is sent with the detailed error.
Bad request
This means that the input is incorrect.
Example:
curl -i https://archive.softwareheritage.org/api/1/content/1/
HTTP/1.1 400 BAD REQUEST
Date: Mon, 16 Jan 2017 11:28:08 GMT
Server: Apache
Content-Type: application/json
Content-Length: 44
Connection: close
{"error": "Invalid checksum query string 1"}
Here, the api content expects an hash identifier.
Not found
This means that the request is ok but we do not found the information the user requests.
Example:
curl -i https://archive.softwareheritage.org/api/1/content/04740277a81c5be6c16f6c9da488ca073b770d7f/
HTTP/1.1 404 NOT FOUND
Date: Mon, 16 Jan 2017 11:31:46 GMT
Server: Apache
Content-Type: application/json
Content-Length: 77
Connection: close
{"error": "Content with 04740277a81c5be6c16f6c9da488ca073b770d7f not found."}
Terminology
You will find below the terminology the project swh uses. More details can be found on swh's wiki glossary page.
Content
A (specific version of a) file stored in the archive, identified by its cryptographic hashes (SHA1, "git-like" SHA1, SHA256) and its size.
Also known as: Blob Note.
(Cryptographic) hash
A fixed-size "summary" of a stream of bytes that is easy to compute, and hard to reverse.
Also known as: Checksum, Digest.
Directory
A set of named pointers to contents (file entries), directories (directory entries) and revisions (revision entries).
Origin
A location from which a coherent set of sources has been obtained.
Also known as: Data source.
Examples:
- a Git repository
- a directory containing tarballs
- the history of a Debian package on snapshot.debian.org.
Project
An organized effort to develop a software product.
Projects might be nested following organizational structures (sub-project, sub-sub-project), are associated to a number of human-meaningful metadata, and release software products via Origins.
Release
A revision that has been marked by a project as noteworthy with a specific, usually mnemonic, name (for instance, a version number).
Also known as: Tag (Git-specific terminology).
Examples:
- a Git tag with its name
- a tarball with its name
- a Debian source package with its version number.
Revision
A "point in time" snapshot in the development history of a project.
Also known as: Commit
Examples:
Opened endpoints
-
(may have to move to /api/1/)
-
Below, you will find the opened endpoints. This will permit you to read information on the origin we load up until now.
+
Open api endpoints is accessed at https://archive.softwareheritage.org/api/1/.
diff --git a/swh/web/ui/templates/includes/api-header.md b/swh/web/ui/templates/includes/apidoc-header.md
similarity index 85%
rename from swh/web/ui/templates/includes/api-header.md
rename to swh/web/ui/templates/includes/apidoc-header.md
index 7f89a6622..5b7e62a55 100644
--- a/swh/web/ui/templates/includes/api-header.md
+++ b/swh/web/ui/templates/includes/apidoc-header.md
@@ -1,255 +1,253 @@
-Welcome to Software Heritage project's API.
+Welcome to Software Heritage project's API documentation.
**Table of Contents**
+- [Version](#version)
- [Schema](#schema)
- - [Mimetype override](#mimetype-override)
- - [Parameters](#parameters)
- - [Global parameter](#global-parameter)
- - [Client errors](#client-errors)
- - [Bad request](#bad-request)
- - [Not found](#not-found)
- - [Terminology](#terminology)
- - [Content](#content)
- - [(Cryptographic) hash](#cryptographic-hash)
- - [Directory](#directory)
- - [Origin](#origin)
- - [Project](#project)
- - [Release](#release)
- - [Revision](#revision)
- - [Opened endpoints](#opened-endpoints)
+- [Mimetype override](#mimetype-override)
+- [Parameters](#parameters)
+ - [Global parameter](#global-parameter)
+- [Client errors](#client-errors)
+ - [Bad request](#bad-request)
+ - [Not found](#not-found)
+- [Terminology](#terminology)
+ - [Content](#content)
+ - [(Cryptographic) hash](#cryptographic-hash)
+ - [Directory](#directory)
+ - [Origin](#origin)
+ - [Project](#project)
+ - [Release](#release)
+ - [Revision](#revision)
+- [Opened endpoints](#opened-endpoints)
### Version
-Current version is 1.
+Current version is [1](/api/1/).
### Schema
-Api access is over https and accessed through [https://archive.softwareheritage.org/api/1/](https://archive.softwareheritage.org/api/).
+Api access is over https and accessed through [https://archive.softwareheritage.org/api/1/](/api/1/).
-Data is sent and received as json.
+Data is sent and received as json by default.
Example:
``` shell
$ curl -i https://archive.softwareheritage.org/api/1/stat/counters/
HTTP/1.1 200 OK
Date: Mon, 16 Jan 2017 10:57:56 GMT
Server: Apache
Content-Type: application/json
Content-Length: 395
Vary: Accept-Encoding
Access-Control-Allow-Origin: *
Connection: close
{
"directory_entry_rev": 3039473,
"person": 13903080,
"entity": 7103795,
"skipped_content": 17864,
"entity_history": 7147753,
"revision_history": 720840448,
"revision": 703277184,
"directory": 2616883200,
"release": 5692900,
"origin": 49938216,
"directory_entry_dir": 2140887552,
"occurrence_history": 254274832,
"occurrence": 241899344,
"content": 3155739136,
"directory_entry_file": 3173807104
}
```
#### Mimetype override
The response output can be sent as yaml provided the client specifies
it using the header field.
For example:
``` shell
curl -i -H 'Accept: application/yaml' https://archive.softwareheritage.org/api/1/stat/counters/
HTTP/1.1 200 OK
Date: Mon, 16 Jan 2017 12:31:50 GMT
Server: Apache
Content-Type: application/yaml
Content-Length: 372
Access-Control-Allow-Origin: *
Connection: close
{content: 3155758336,
directory: 2616955136,
directory_entry_dir: 2140925824,
directory_entry_file: 3173833984,
directory_entry_rev: 3039473,
entity: 7103741,
entity_history: 7148121,
occurrence: 241887488,
occurrence_history: 254277584,
origin: 49939848,
person: 13898394,
release: 5693922,
revision: 703275840,
revision_history: 720842176,
skipped_content: 17864}
```
### Parameters
Some API endpoints can be used with with local parameters. The url
then needs to be adapted accordingly.
For example:
``` text
https://archive.softwareheritage.org/api/1/
?=&=
```
where:
- field0 is an appropriate field for the and value0
- field1 is an appropriate field for the and value1
#### Global parameter
One parameter is defined for all api endpoints `fields`. It permits
to filter the output fields per key.
For example, to only list the number of contents, revisions,
directories on the statistical endpoints, one uses:
``` shell
$ curl https://archive.softwareheritage.org/api/1/stat/counters/\?fields\=content,directory,revision | jq
{
"content": 3155739136,
"revision": 703277184,
"directory": 2616883200
}
```
Note: If the keys provided to filter on do not exist, they are
ignored.
### Client errors
There are 2 kinds of error.
In that case, the http error code will reflect that error and a json
response is sent with the detailed error.
#### Bad request
This means that the input is incorrect.
Example:
``` shell
curl -i https://archive.softwareheritage.org/api/1/content/1/
HTTP/1.1 400 BAD REQUEST
Date: Mon, 16 Jan 2017 11:28:08 GMT
Server: Apache
Content-Type: application/json
Content-Length: 44
Connection: close
{"error": "Invalid checksum query string 1"}
```
Here, the api content expects an hash identifier.
#### Not found
This means that the request is ok but we do not found the information
the user requests.
Example:
``` shell
curl -i https://archive.softwareheritage.org/api/1/content/04740277a81c5be6c16f6c9da488ca073b770d7f/
HTTP/1.1 404 NOT FOUND
Date: Mon, 16 Jan 2017 11:31:46 GMT
Server: Apache
Content-Type: application/json
Content-Length: 77
Connection: close
{"error": "Content with 04740277a81c5be6c16f6c9da488ca073b770d7f not found."}
```
### Terminology
You will find below the terminology the project swh uses.
More details can be found
on
[swh's wiki glossary page](https://wiki.softwareheritage.org/index.php?title=Glossary).
#### Content
A (specific version of a) file stored in the archive, identified by
its cryptographic hashes (SHA1, "git-like" SHA1, SHA256) and its size.
Also known as: Blob Note.
#### (Cryptographic) hash
A fixed-size "summary" of a stream of bytes that is easy to compute,
and hard to reverse.
Also known as: Checksum, Digest.
#### Directory
A set of named pointers to contents (file entries), directories
(directory entries) and revisions (revision entries).
#### Origin
A location from which a coherent set of sources has been obtained.
Also known as: Data source.
Examples:
- a Git repository
- a directory containing tarballs
- the history of a Debian package on snapshot.debian.org.
#### Project
An organized effort to develop a software product.
Projects might be nested following organizational structures
(sub-project, sub-sub-project), are associated to a number of
human-meaningful metadata, and release software products via Origins.
#### Release
A revision that has been marked by a project as noteworthy with a
specific, usually mnemonic, name (for instance, a version number).
Also known as: Tag (Git-specific terminology).
Examples:
- a Git tag with its name
- a tarball with its name
- a Debian source package with its version number.
#### Revision
A "point in time" snapshot in the development history of a project.
Also known as: Commit
Examples:
- a Git commit
### Opened endpoints
-(may have to move to /api/1/)
-
-Below, you will find the opened endpoints. This will permit you to
-read information on the origin we load up until now.
+Open api endpoints is accessed at [https://archive.softwareheritage.org/api/1/](/api/1/).
diff --git a/swh/web/ui/tests/views/test_browse.py b/swh/web/ui/tests/views/test_browse.py
index 843f5e8fe..d1ced636b 100644
--- a/swh/web/ui/tests/views/test_browse.py
+++ b/swh/web/ui/tests/views/test_browse.py
@@ -1,2092 +1,2103 @@
# 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 unittest import TestCase
from unittest.mock import patch
from flask import url_for
from swh.web.ui.views import browse
from swh.web.ui.exc import BadInputExc, NotFoundExc
from .. import test_app
class FileMock():
def __init__(self, filename):
self.filename = filename
class StaticViews(test_app.SWHViewTestCase):
render_template = False
@patch('swh.web.ui.apidoc.APIUrls')
@istest
- def browse_api_doc(self, mock_api_urls):
+ def browse_api_endpoints(self, mock_api_urls):
# given
endpoints = {
'/a/doc/endpoint/': 'relevant documentation',
'/some/other/endpoint/': 'more docstrings'}
mock_api_urls.apidoc_routes = endpoints
# when
- rv = self.client.get('/api/')
+ rv = self.client.get('/api/1/')
# then
self.assertEquals(rv.status_code, 200)
self.assertIsNotNone(
self.get_context_variable('doc_routes'),
sorted(endpoints.items())
)
+ self.assert_template_used('api-endpoints.html')
+
+ @istest
+ def browse_api_doc(self):
+ # given
+
+ # when
+ rv = self.client.get('/api/')
+
+ # then
+ self.assertEquals(rv.status_code, 200)
self.assert_template_used('api.html')
@istest
def browse_archive(self):
# when
rv = self.client.get('/browse/')
# then
self.assertEquals(rv.status_code, 200)
self.assert_template_used('browse.html')
class SearchRedirectsView(test_app.SWHViewTestCase):
render_template = False
@istest
def search_origin_simple(self):
# when
rv = self.client.get('/origin/search/?origin_id=1&meaningless_arg=42')
# then
self.assertRedirects(rv, url_for('browse_origin', origin_id=1))
@istest
def search_origin_type_url(self):
# when
rv = self.client.get('/origin/search/?origin_type=git'
'&origin_url=http://cool/project/url'
'&meaningless_arg=42')
# then
self.assertRedirects(rv, url_for('browse_origin',
origin_type='git',
origin_url='http://cool/project/url'))
@istest
def search_directory_dir_sha1(self):
# when
rv = self.client.get('/directory/search/?sha1_git=some_sha1'
'&path=some/path/in/folder'
'&meaningless_arg=gandalf')
# then
self.assertRedirects(rv, url_for('browse_directory',
sha1_git='some_sha1',
path='some/path/in/folder'))
@istest
def search_directory_dir_sha1_nopath(self):
# when
rv = self.client.get('/directory/search/?sha1_git=some_sha1'
'&meaningless_arg=gandalf')
# then
self.assertRedirects(rv, url_for('browse_directory',
sha1_git='some_sha1'))
@istest
def search_directory_rev_sha1(self):
# when
rv = self.client.get('/directory/search/?sha1_git=some_sha1'
'&dir_path=some/path/in/folder'
'&meaningless_arg=gandalf')
# then
self.assertRedirects(rv, url_for('browse_revision_directory',
sha1_git='some_sha1',
dir_path='some/path/in/folder'))
@istest
def search_directory_rev_sha1_nopath(self):
# when
rv = self.client.get('/directory/search/?sha1_git=some_sha1'
'&dir_path='
'&meaningless_arg=gandalf')
# then
self.assertRedirects(rv, url_for('browse_revision_directory',
sha1_git='some_sha1'))
@istest
def search_directory_dir_time_place(self):
# when
rv = self.client.get('/directory/search/?origin_id=42'
'&branch_name=refs/heads/tail'
'&meaningless_arg=gandalf'
'&path=some/path')
# then
self.assertRedirects(rv, url_for(
'browse_revision_directory_through_origin',
origin_id=42, branch_name='refs/heads/tail',
path='some/path', ts=None))
@istest
def search_revision_sha1(self):
# when
rv = self.client.get('/revision/search/?sha1_git=some_sha1')
# then
self.assertRedirects(rv, url_for('browse_revision',
sha1_git='some_sha1'))
@istest
def search_revision_time_place(self):
# when
rv = self.client.get('/revision/search/?origin_id=42'
'&branch_name=big/branch/on/tree'
'&ts=meaningful_ts')
# then
self.assertRedirects(rv, url_for('browse_revision_with_origin',
origin_id=42,
branch_name='big/branch/on/tree',
ts='meaningful_ts'))
class SearchSymbolView(test_app.SWHViewTestCase):
render_template = False
@istest
def search_symbol(self):
# when
rv = self.client.get('/content/symbol/')
self.assertEqual(rv.status_code, 200)
self.assertEqual(self.get_context_variable('result'), None)
self.assertEqual(self.get_context_variable('message'), '')
self.assertEqual(self.get_context_variable('linknext'), None)
self.assertEqual(self.get_context_variable('linkprev'), None)
self.assert_template_used('symbols.html')
@patch('swh.web.ui.views.browse.api')
@istest
def search_symbol_with_result(self, mock_api):
# given
stub_results = [
{
'kind': 'function',
'name': 'hy',
'sha1': 'some-hash',
},
]
mock_api.api_content_symbol.return_value = {
'results': stub_results,
}
# when
rv = self.client.get('/content/symbol/?q=hy')
self.assertEqual(rv.status_code, 200)
self.assertEqual(self.get_context_variable('result'), stub_results)
self.assertEqual(self.get_context_variable('message'), '')
self.assertEqual(self.get_context_variable('linknext'), None)
self.assertEqual(self.get_context_variable('linkprev'), None)
self.assert_template_used('symbols.html')
mock_api.api_content_symbol.assert_called_once_with('hy')
@patch('swh.web.ui.views.browse.api')
@istest
def search_symbol_with_result_and_pages(self, mock_api):
# given
stub_results = [
{
'kind': 'function',
'name': 'hy',
'sha1': 'some-hash',
}
]
mock_api.api_content_symbol.return_value = {
'results': stub_results,
'headers': {
'link-next': 'some-link',
}
}
# when
rv = self.client.get('/content/symbol/?q=hy&per_page=1')
self.assertEqual(rv.status_code, 200)
self.assertEqual(self.get_context_variable('result'), stub_results)
self.assertEqual(self.get_context_variable('message'), '')
self.assertEqual(
self.get_context_variable('linknext'),
'/content/symbol/?q=hy&last_sha1=some-hash&per_page=1')
self.assertEqual(self.get_context_variable('linkprev'), None)
self.assert_template_used('symbols.html')
mock_api.api_content_symbol.assert_called_once_with('hy')
@patch('swh.web.ui.views.browse.api')
@istest
def search_symbol_bad_input(self, mock_api):
# given
mock_api.api_content_symbol.side_effect = BadInputExc('error msg')
# when
rv = self.client.get('/content/symbol/?q=hello|hy')
self.assertEqual(rv.status_code, 200)
self.assertEqual(self.get_context_variable('message'), 'error msg')
self.assertEqual(self.get_context_variable('result'), None)
self.assertEqual(self.get_context_variable('linknext'), None)
self.assertEqual(self.get_context_variable('linkprev'), None)
self.assert_template_used('symbols.html')
mock_api.api_content_symbol.assert_called_once_with('hello|hy')
class SearchView(test_app.SWHViewTestCase):
render_template = False
@istest
def search_default(self):
# when
rv = self.client.get('/content/search/')
self.assertEqual(rv.status_code, 200)
self.assertEqual(self.get_context_variable('message'), '')
self.assertEqual(self.get_context_variable('search_res'), None)
self.assert_template_used('search.html')
@patch('swh.web.ui.views.browse.api')
@istest
def search_get_query_hash_not_found(self, mock_api):
# given
mock_api.api_search.return_value = {
'search_res': [{
'filename': None,
'sha1': 'sha1:456',
'found': False}],
'search_stats': {'nbfiles': 1, 'pct': 100}}
# when
rv = self.client.get('/content/search/?q=sha1:456')
self.assertEqual(rv.status_code, 200)
self.assertEqual(self.get_context_variable('message'), '')
self.assertEqual(self.get_context_variable('search_res'), [
{'filename': None,
'sha1': 'sha1:456',
'found': False}])
self.assert_template_used('search.html')
mock_api.api_search.assert_called_once_with('sha1:456')
@patch('swh.web.ui.views.browse.api')
@istest
def search_get_query_hash_bad_input(self, mock_api):
# given
mock_api.api_search.side_effect = BadInputExc('error msg')
# when
rv = self.client.get('/content/search/?q=sha1_git:789')
self.assertEqual(rv.status_code, 200)
self.assertEqual(self.get_context_variable('message'), 'error msg')
self.assertEqual(self.get_context_variable('search_res'), None)
self.assert_template_used('search.html')
mock_api.api_search.assert_called_once_with('sha1_git:789')
@patch('swh.web.ui.views.browse.api')
@istest
def search_get_query_hash_found(self, mock_api):
# given
mock_api.api_search.return_value = {
'search_res': [{
'filename': None,
'sha1': 'sha1:123',
'found': True}],
'search_stats': {'nbfiles': 1, 'pct': 100}}
# when
rv = self.client.get('/content/search/?q=sha1:123')
self.assertEqual(rv.status_code, 200)
self.assertEqual(self.get_context_variable('message'), '')
self.assertEqual(len(self.get_context_variable('search_res')), 1)
resp = self.get_context_variable('search_res')[0]
self.assertTrue(resp is not None)
self.assertEqual(resp['sha1'], 'sha1:123')
self.assertEqual(resp['found'], True)
self.assert_template_used('search.html')
mock_api.api_search.assert_called_once_with('sha1:123')
@patch('swh.web.ui.views.browse.request')
@patch('swh.web.ui.views.browse.api')
@istest
def search_post_hashes_bad_input(self, mock_api, mock_request):
# given
mock_request.form = {'a': ['456caf10e9535160d90e874b45aa426de762f19f'],
'b': ['745bab676c8f3cec8016e0c39ea61cf57e518865']}
mock_request.method = 'POST'
mock_api.api_search.side_effect = BadInputExc(
'error bad input')
# when (mock_request completes the post request)
rv = self.client.post('/content/search/')
# then
self.assertEqual(rv.status_code, 200)
self.assertEqual(self.get_context_variable('search_stats'),
{'nbfiles': 0, 'pct': 0})
self.assertEqual(self.get_context_variable('search_res'), None)
self.assertEqual(self.get_context_variable('message'),
'error bad input')
self.assert_template_used('search.html')
@patch('swh.web.ui.views.browse.request')
@patch('swh.web.ui.views.browse.api')
@istest
def search_post_hashes_none(self, mock_api, mock_request):
# given
mock_request.form = {'a': ['456caf10e9535160d90e874b45aa426de762f19f'],
'b': ['745bab676c8f3cec8016e0c39ea61cf57e518865']}
mock_request.method = 'POST'
mock_api.api_search.return_value = {
'search_stats': {'nbfiles': 2, 'pct': 0},
'search_res': [{'filename': 'a',
'sha1': '456caf10e9535160d90e874b45aa426de762f19f',
'found': False},
{'filename': 'b',
'sha1': '745bab676c8f3cec8016e0c39ea61cf57e518865',
'found': False}]}
# when (mock_request completes the post request)
rv = self.client.post('/content/search/')
# then
self.assertEqual(rv.status_code, 200)
self.assertIsNotNone(self.get_context_variable('search_res'))
self.assertTrue(self.get_context_variable('search_stats') is not None)
self.assertEqual(len(self.get_context_variable('search_res')), 2)
stats = self.get_context_variable('search_stats')
self.assertEqual(stats['nbfiles'], 2)
self.assertEqual(stats['pct'], 0)
a, b = self.get_context_variable('search_res')
self.assertEqual(a['found'], False)
self.assertEqual(b['found'], False)
self.assertEqual(self.get_context_variable('message'), '')
self.assert_template_used('search.html')
@patch('swh.web.ui.views.browse.request')
@patch('swh.web.ui.views.browse.api')
@istest
def search_post_hashes_some(self, mock_api, mock_request):
# given
mock_request.form = {'a': '456caf10e9535160d90e874b45aa426de762f19f',
'b': '745bab676c8f3cec8016e0c39ea61cf57e518865'}
mock_request.method = 'POST'
mock_api.api_search.return_value = {
'search_stats': {'nbfiles': 2, 'pct': 50},
'search_res': [{'filename': 'a',
'sha1': '456caf10e9535160d90e874b45aa426de762f19f',
'found': False},
{'filename': 'b',
'sha1': '745bab676c8f3cec8016e0c39ea61cf57e518865',
'found': True}]}
# when (mock_request completes the post request)
rv = self.client.post('/content/search/')
# then
self.assertEqual(rv.status_code, 200)
self.assertIsNotNone(self.get_context_variable('search_res'))
self.assertEqual(len(self.get_context_variable('search_res')), 2)
self.assertTrue(self.get_context_variable('search_stats') is not None)
stats = self.get_context_variable('search_stats')
self.assertEqual(stats['nbfiles'], 2)
self.assertEqual(stats['pct'], 50)
self.assertEqual(self.get_context_variable('message'), '')
a, b = self.get_context_variable('search_res')
self.assertEqual(a['found'], False)
self.assertEqual(b['found'], True)
self.assert_template_used('search.html')
class ContentView(test_app.SWHViewTestCase):
render_template = False
@patch('swh.web.ui.views.browse.api')
@istest
def browse_content_ko_not_found(self, mock_api):
# given
mock_api.api_content_metadata.side_effect = NotFoundExc(
'Not found!')
# when
rv = self.client.get('/browse/content/sha1:sha1-hash/')
# then
self.assertEqual(rv.status_code, 200)
self.assert_template_used('content.html')
self.assertEqual(self.get_context_variable('message'),
'Not found!')
self.assertIsNone(self.get_context_variable('content'))
mock_api.api_content_metadata.assert_called_once_with(
'sha1:sha1-hash')
@patch('swh.web.ui.views.browse.api')
@istest
def browse_content_ko_bad_input(self, mock_api):
# given
mock_api.api_content_metadata.side_effect = BadInputExc(
'Bad input!')
# when
rv = self.client.get('/browse/content/sha1:sha1-hash/')
# then
self.assertEqual(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_api.api_content_metadata.assert_called_once_with(
'sha1:sha1-hash')
@patch('swh.web.ui.views.browse.service')
@patch('swh.web.ui.views.browse.api')
@istest
def browse_content(self, mock_api, mock_service):
# given
stub_content = {
'sha1': 'sha1-hash'
}
mock_api.api_content_metadata.return_value = stub_content
mock_api.api_content_filetype.return_value = {
'mimetype': 'text/plain',
}
mock_api.api_content_language.return_value = {
'lang': 'Hy',
}
mock_api.api_content_license.return_value = {
'licenses': ['MIT', 'BSD'],
}
mock_service.lookup_content_raw.return_value = {
'data': b'blah'
}
mock_api.api_content_ctags.return_value = [
{
'line': 12,
},
{
'line': 14,
}
]
expected_content = {
'sha1': 'sha1-hash',
'data': 'blah',
'encoding': None,
'mimetype': 'text/plain',
'language': 'Hy',
'licenses': "MIT, BSD",
}
# when
rv = self.client.get('/browse/content/sha1:sha1-hash/')
# then
self.assertEqual(rv.status_code, 200)
self.assert_template_used('content.html')
self.assertIsNone(self.get_context_variable('message'))
actual_content = self.get_context_variable('content')
actual_content.pop('ctags')
self.assertEqual(actual_content, expected_content)
mock_service.lookup_content_raw.assert_called_once_with(
'sha1:sha1-hash')
mock_api.api_content_language.assert_called_once_with('sha1:sha1-hash')
mock_api.api_content_filetype.assert_called_once_with('sha1:sha1-hash')
mock_api.api_content_license.assert_called_once_with('sha1:sha1-hash')
mock_api.api_content_metadata.assert_called_once_with('sha1:sha1-hash')
mock_api.api_content_ctags.assert_called_once_with('sha1:sha1-hash')
@patch('swh.web.ui.views.browse.service')
@patch('swh.web.ui.views.browse.api')
@istest
def browse_content_less_data(self, mock_api, mock_service):
# given
stub_content = {
'sha1': 'ha1',
}
mock_api.api_content_metadata.return_value = stub_content
mock_api.api_content_filetype.return_value = None
mock_api.api_content_language.return_value = None
mock_api.api_content_license.return_value = None
mock_service.lookup_content_raw.return_value = None
mock_api.api_content_ctags.return_value = []
expected_content = {
'sha1': 'ha1',
'data': None,
'encoding': None,
'mimetype': None,
'language': None,
'licenses': None,
'ctags': None,
}
# when
rv = self.client.get('/browse/content/sha1:ha1/')
# then
self.assertEqual(rv.status_code, 200)
self.assert_template_used('content.html')
self.assertIsNone(self.get_context_variable('message'))
actual_content = self.get_context_variable('content')
self.assertEqual(actual_content, expected_content)
mock_service.lookup_content_raw.assert_called_once_with('sha1:ha1')
mock_api.api_content_language.assert_called_once_with('sha1:ha1')
mock_api.api_content_filetype.assert_called_once_with('sha1:ha1')
mock_api.api_content_license.assert_called_once_with('sha1:ha1')
mock_api.api_content_metadata.assert_called_once_with('sha1:ha1')
mock_api.api_content_ctags.assert_called_once_with('sha1:ha1')
@patch('swh.web.ui.views.browse.redirect')
@patch('swh.web.ui.views.browse.url_for')
@istest
def browse_content_raw(self, mock_urlfor, mock_redirect):
# given
stub_content_raw = b'some-data'
mock_urlfor.return_value = '/api/content/sha1:sha1-hash/raw/'
mock_redirect.return_value = stub_content_raw
# when
rv = self.client.get('/browse/content/sha1:sha1-hash/raw/')
self.assertEqual(rv.status_code, 200)
self.assertEqual(rv.data, stub_content_raw)
mock_urlfor.assert_called_once_with('api_content_raw',
q='sha1:sha1-hash')
mock_redirect.assert_called_once_with(
'/api/content/sha1:sha1-hash/raw/')
class DirectoryView(test_app.SWHViewTestCase):
render_template = False
@patch('swh.web.ui.views.browse.api')
@istest
def browse_directory_ko_bad_input(self, mock_api):
# given
mock_api.api_directory.side_effect = BadInputExc(
'Invalid hash')
# when
rv = self.client.get('/browse/directory/sha2-invalid/')
# then
self.assertEqual(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_api.api_directory.assert_called_once_with(
'sha2-invalid')
@patch('swh.web.ui.views.browse.api')
@istest
def browse_directory_empty_result(self, mock_api):
# given
mock_api.api_directory.return_value = []
# when
rv = self.client.get('/browse/directory/some-sha1/')
# then
self.assertEqual(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'), [])
mock_api.api_directory.assert_called_once_with(
'some-sha1')
@patch('swh.web.ui.views.browse.service')
@patch('swh.web.ui.views.browse.api')
@istest
def browse_directory_relative_file(self, mock_api, mock_service):
# given
stub_entry = {
'sha256': '240',
'type': 'file'
}
mock_service.lookup_directory_with_path.return_value = stub_entry
stub_file = {
'sha1_git': '123',
'sha1': '456',
'status': 'visible',
'data_url': '/api/1/content/890',
'length': 42,
'ctime': 'Thu, 01 Oct 2015 12:13:53 GMT',
'target': 'file.txt',
'sha256': '148'
}
mock_api.api_content_metadata.return_value = stub_file
mock_service.lookup_content_raw.return_value = {
'data': 'this is my file'}
# when
rv = self.client.get('/browse/directory/sha1/path/to/file/')
# then
self.assertEqual(rv.status_code, 200)
self.assert_template_used('content.html')
self.assertIsNotNone(self.get_context_variable('content'))
content = self.get_context_variable('content')
# change caused by call to prepare_data_for_view
self.assertEqual(content['data_url'], '/browse/content/890')
self.assertEqual(content['data'], 'this is my file')
mock_api.api_content_metadata.assert_called_once_with('sha256:240')
mock_service.lookup_content_raw.assert_called_once_with('sha256:240')
@patch('swh.web.ui.views.browse.service')
@patch('swh.web.ui.views.browse.api')
@istest
def browse_directory_relative_dir(self, mock_api, mock_service):
# given
mock_service.lookup_directory_with_path.return_value = {
'sha256': '240',
'target': 'abcd',
'type': 'dir'
}
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_api.api_directory.return_value = stub_directory_ls
# when
rv = self.client.get('/browse/directory/sha1/path/to/dir/')
# then
self.assertEqual(rv.status_code, 200)
self.assert_template_used('directory.html')
self.assertIsNotNone(self.get_context_variable('files'))
self.assertEqual(len(self.get_context_variable('files')),
len(stub_directory_ls))
mock_api.api_directory.assert_called_once_with('abcd')
@patch('swh.web.ui.views.browse.service')
@patch('swh.web.ui.views.browse.api')
@istest
def browse_directory_relative_not_found(self, mock_api, mock_service):
# given
mock_service.lookup_directory_with_path.side_effect = NotFoundExc(
'Directory entry not found.')
# when
rv = self.client.get('/browse/directory/some-sha1/some/path/')
# then
self.assertEqual(rv.status_code, 200)
self.assert_template_used('directory.html')
self.assertEqual(self.get_context_variable('message'),
'Directory entry not found.')
@patch('swh.web.ui.views.browse.api')
@patch('swh.web.ui.views.browse.utils')
@istest
def browse_directory(self, mock_utils, mock_api):
# 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_api.api_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_data_for_view.return_value = stub_directory_map
# when
rv = self.client.get('/browse/directory/some-sha1/')
# then
self.assertEqual(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_api.api_directory.assert_called_once_with(
'some-sha1')
mock_utils.prepare_data_for_view.assert_called_once_with(
stub_directory_ls)
class ContentWithOriginView(test_app.SWHViewTestCase):
render_template = False
@patch('swh.web.ui.views.browse.api')
# @istest
def browse_content_with_origin_content_ko_not_found(self, mock_api):
# given
mock_api.api_content_checksum_to_origin.side_effect = NotFoundExc(
'Not found!')
# when
rv = self.client.get('/browse/content/sha256:some-sha256/origin/')
# then
self.assertEqual(rv.status_code, 200)
self.assert_template_used('content-with-origin.html')
self.assertEqual(self.get_context_variable('message'),
'Not found!')
mock_api.api_content_checksum_to_origin.assert_called_once_with(
'sha256:some-sha256')
@patch('swh.web.ui.views.browse.api')
# @istest
def browse_content_with_origin_ko_bad_input(self, mock_api):
# given
mock_api.api_content_checksum_to_origin.side_effect = BadInputExc(
'Invalid hash')
# when
rv = self.client.get('/browse/content/sha256:some-sha256/origin/')
# then
self.assertEqual(rv.status_code, 200)
self.assert_template_used('content-with-origin.html')
self.assertEqual(
self.get_context_variable('message'), 'Invalid hash')
mock_api.api_content_checksum_to_origin.assert_called_once_with(
'sha256:some-sha256')
@patch('swh.web.ui.views.browse.api')
# @istest
def browse_content_with_origin(self, mock_api):
# given
mock_api.api_content_checksum_to_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.assertEqual(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_api.api_content_checksum_to_origin.assert_called_once_with(
'sha256:some-sha256')
class OriginView(test_app.SWHViewTestCase):
render_template = False
def setUp(self):
def url_for_test(fn, **args):
if fn == 'browse_revision_with_origin':
return '/browse/revision/origin/%s/' % args['origin_id']
elif fn == 'api_origin_visits':
return '/api/1/stat/visits/%s/' % args['origin_id']
self.url_for_test = url_for_test
self.stub_origin = {'type': 'git',
'lister': None,
'project': None,
'url': 'rsync://some/url',
'id': 426}
@patch('swh.web.ui.views.browse.api')
@istest
def browse_origin_ko_not_found(self, mock_api):
# given
mock_api.api_origin.side_effect = NotFoundExc('Not found!')
# when
rv = self.client.get('/browse/origin/1/')
# then
self.assertEqual(rv.status_code, 200)
self.assert_template_used('origin.html')
self.assertIsNone(self.get_context_variable('origin'))
self.assertEqual(
self.get_context_variable('message'),
'Not found!')
mock_api.api_origin.assert_called_once_with(1, None, None)
@patch('swh.web.ui.views.browse.api')
@istest
def browse_origin_ko_bad_input(self, mock_api):
# given
mock_api.api_origin.side_effect = BadInputExc('wrong input')
# when
rv = self.client.get('/browse/origin/426/')
# then
self.assertEqual(rv.status_code, 200)
self.assert_template_used('origin.html')
self.assertIsNone(self.get_context_variable('origin'))
mock_api.api_origin.assert_called_once_with(426, None, None)
@patch('swh.web.ui.views.browse.api')
@patch('swh.web.ui.views.browse.url_for')
@istest
def browse_origin_found_id(self, mock_url_for, mock_api):
# given
mock_url_for.side_effect = self.url_for_test
mock_api.api_origin.return_value = self.stub_origin
# when
rv = self.client.get('/browse/origin/426/')
# then
self.assertEqual(rv.status_code, 200)
self.assert_template_used('origin.html')
self.assertEqual(self.get_context_variable('origin'), self.stub_origin)
self.assertEqual(self.get_context_variable('browse_url'),
'/browse/revision/origin/426/')
self.assertEqual(self.get_context_variable('visit_url'),
'/api/1/stat/visits/426/')
mock_api.api_origin.assert_called_once_with(426, None, None)
@patch('swh.web.ui.views.browse.api')
@patch('swh.web.ui.views.browse.url_for')
@istest
def browse_origin_found_url_type(self, mock_url_for, mock_api):
# given
mock_url_for.side_effect = self.url_for_test
mock_api.api_origin.return_value = self.stub_origin
# when
rv = self.client.get('/browse/origin/git/url/rsync://some/url/')
# then
self.assertEqual(rv.status_code, 200)
self.assert_template_used('origin.html')
self.assertEqual(self.get_context_variable('origin'), self.stub_origin)
self.assertEqual(self.get_context_variable('browse_url'),
'/browse/revision/origin/426/')
self.assertEqual(self.get_context_variable('visit_url'),
'/api/1/stat/visits/426/')
mock_api.api_origin.assert_called_once_with(None, 'git',
'rsync://some/url')
class PersonView(test_app.SWHViewTestCase):
render_template = False
@patch('swh.web.ui.views.browse.api')
@istest
def browse_person_ko_not_found(self, mock_api):
# given
mock_api.api_person.side_effect = NotFoundExc('not found')
# when
rv = self.client.get('/browse/person/1/')
# then
self.assertEqual(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'),
'not found')
mock_api.api_person.assert_called_once_with(1)
@patch('swh.web.ui.views.browse.api')
@istest
def browse_person_ko_bad_input(self, mock_api):
# given
mock_api.api_person.side_effect = BadInputExc('wrong input')
# when
rv = self.client.get('/browse/person/426/')
# then
self.assertEqual(rv.status_code, 200)
self.assert_template_used('person.html')
self.assertEqual(self.get_context_variable('person_id'), 426)
mock_api.api_person.assert_called_once_with(426)
@patch('swh.web.ui.views.browse.api')
@istest
def browse_person(self, mock_api):
# given
mock_person = {'type': 'git',
'lister': None,
'project': None,
'url': 'rsync://some/url',
'id': 426}
mock_api.api_person.return_value = mock_person
# when
rv = self.client.get('/browse/person/426/')
# then
self.assertEqual(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_api.api_person.assert_called_once_with(426)
class ReleaseView(test_app.SWHViewTestCase):
render_template = False
@patch('swh.web.ui.views.browse.api')
@istest
def browse_release_ko_not_found(self, mock_api):
# given
mock_api.api_release.side_effect = NotFoundExc('not found!')
# when
rv = self.client.get('/browse/release/1/')
# then
self.assertEqual(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'),
'not found!')
mock_api.api_release.assert_called_once_with('1')
@patch('swh.web.ui.views.browse.api')
@istest
def browse_release_ko_bad_input(self, mock_api):
# given
mock_api.api_release.side_effect = BadInputExc('wrong input')
# when
rv = self.client.get('/browse/release/426/')
# then
self.assertEqual(rv.status_code, 200)
self.assert_template_used('release.html')
self.assertEqual(self.get_context_variable('sha1_git'), '426')
mock_api.api_release.assert_called_once_with('426')
@patch('swh.web.ui.views.browse.api')
@istest
def browse_release(self, mock_api):
# given
self.maxDiff = None
mock_release = {
"date": "Sun, 05 Jul 2015 18:02:06 GMT",
"id": "1e951912027ea6873da6985b91e50c47f645ae1a",
"target": "d770e558e21961ad6cfdf0ff7df0eb5d7d4f0754",
"target_url": '/browse/revision/d770e558e21961ad6cfdf0ff7df0'
'eb5d7d4f0754/',
"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_api.api_release.return_value = mock_release
expected_release = {
"date": "Sun, 05 Jul 2015 18:02:06 GMT",
"id": "1e951912027ea6873da6985b91e50c47f645ae1a",
"target_url": '/browse/revision/d770e558e21961ad6cfdf0ff7df0'
'eb5d7d4f0754/',
"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"
}
# when
rv = self.client.get('/browse/release/426/')
# then
self.assertEqual(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)
mock_api.api_release.assert_called_once_with('426')
class RevisionView(test_app.SWHViewTestCase):
render_template = False
@patch('swh.web.ui.views.browse.api')
@istest
def browse_revision_ko_not_found(self, mock_api):
# given
mock_api.api_revision.side_effect = NotFoundExc('Not found!')
# when
rv = self.client.get('/browse/revision/1/')
# then
self.assertEqual(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'),
'Not found!')
self.assertIsNone(self.get_context_variable('revision'))
mock_api.api_revision.assert_called_once_with('1', None)
@patch('swh.web.ui.views.browse.api')
@istest
def browse_revision_ko_bad_input(self, mock_api):
# given
mock_api.api_revision.side_effect = BadInputExc('wrong input!')
# when
rv = self.client.get('/browse/revision/426/')
# then
self.assertEqual(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('message'),
'wrong input!')
self.assertIsNone(self.get_context_variable('revision'))
mock_api.api_revision.assert_called_once_with('426', None)
@patch('swh.web.ui.views.browse.api')
@istest
def browse_revision(self, mock_api):
# given
stub_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',
'type': 'git',
'author': {
'email': 'torvalds@linux-foundation.org',
'name': 'Linus Torvalds'
},
'message': 'Linux 4.2-rc1\n',
'synthetic': False,
'directory_url': '/api/1/directory/'
'2a1dbabeed4dcf1f4a4c441993b2ffc9d972780b/',
'parent_url': [
'/api/1/revision/a585d2b738bfa26326b3f1f40f0f1eda0c067ccf/'
],
}
mock_api.api_revision.return_value = stub_revision
expected_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',
'type': 'git',
'author': {
'email': 'torvalds@linux-foundation.org',
'name': 'Linus Torvalds'
},
'message': 'Linux 4.2-rc1\n',
'synthetic': False,
'parent_url': [
'/browse/revision/a585d2b738bfa26326b3f1f40f0f1eda0c067ccf/'
],
'directory_url': '/browse/directory/2a1dbabeed4dcf1f4a4c441993b2f'
'fc9d972780b/',
}
# when
rv = self.client.get('/browse/revision/426/')
# then
self.assertEqual(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.assertIsNone(self.get_context_variable('message'))
mock_api.api_revision.assert_called_once_with('426', None)
@patch('swh.web.ui.views.browse.api')
@istest
def browse_revision_raw_message(self, mock_api):
# given
sha1 = 'd770e558e21961ad6cfdf0ff7df0eb5d7d4f0754'
# when
rv = self.client.get('/browse/revision/'
'd770e558e21961ad6cfdf0ff7df0eb5d7d4f0754/raw/')
self.assertRedirects(
rv, '/api/1/revision/%s/raw/' % sha1)
@patch('swh.web.ui.views.browse.api')
@istest
def browse_revision_log_ko_not_found(self, mock_api):
# given
mock_api.api_revision_log.side_effect = NotFoundExc('Not found!')
# when
rv = self.client.get('/browse/revision/sha1/log/')
# then
self.assertEqual(rv.status_code, 200)
self.assert_template_used('revision-log.html')
self.assertEqual(self.get_context_variable('sha1_git'), 'sha1')
self.assertEqual(
self.get_context_variable('message'),
'Not found!')
self.assertEqual(self.get_context_variable('revisions'), [])
mock_api.api_revision_log.assert_called_once_with('sha1', None)
@patch('swh.web.ui.views.browse.api')
@istest
def browse_revision_log_ko_bad_input(self, mock_api):
# given
mock_api.api_revision_log.side_effect = BadInputExc('wrong input!')
# when
rv = self.client.get('/browse/revision/426/log/')
# then
self.assertEqual(rv.status_code, 200)
self.assert_template_used('revision-log.html')
self.assertEqual(self.get_context_variable('sha1_git'), '426')
self.assertEqual(
self.get_context_variable('message'),
'wrong input!')
self.assertEqual(self.get_context_variable('revisions'), [])
mock_api.api_revision_log.assert_called_once_with('426', None)
@patch('swh.web.ui.views.browse.api')
@istest
def browse_revision_log(self, mock_api):
# given
stub_revisions = {
'revisions': [{
'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',
'type': 'git',
'author': {
'email': 'torvalds@linux-foundation.org',
'name': 'Linus Torvalds'
},
'message': 'Linux 4.2-rc1\n',
'synthetic': False,
'directory_url': '/api/1/directory/'
'2a1dbabeed4dcf1f4a4c441993b2ffc9d972780b/',
'parent_url': [
'/api/1/revision/a585d2b738bfa26326b3f1f40f0f1eda0c067ccf/'
],
}],
'next_revs_url': '/api/1/revision/1234/log/'
}
mock_api.api_revision_log.return_value = stub_revisions
# when
rv = self.client.get('/browse/revision/426/log/')
# then
self.assertEqual(rv.status_code, 200)
self.assert_template_used('revision-log.html')
self.assertEqual(self.get_context_variable('sha1_git'), '426')
self.assertTrue(
isinstance(self.get_context_variable('revisions'), map))
self.assertEqual(
self.get_context_variable('next_revs_url'),
'/browse/revision/1234/log/')
self.assertIsNone(self.get_context_variable('message'))
mock_api.api_revision_log.assert_called_once_with('426', None)
@patch('swh.web.ui.views.browse.api')
@istest
def browse_revision_log_by_ko_not_found(self, mock_api):
# given
mock_api.api_revision_log_by.side_effect = NotFoundExc('Not found!')
# when
rv = self.client.get('/browse/revision/origin/9/log/')
# then
self.assertEqual(rv.status_code, 200)
self.assert_template_used('revision-log.html')
self.assertEqual(self.get_context_variable('origin_id'), 9)
self.assertEqual(
self.get_context_variable('message'),
'Not found!')
self.assertEqual(self.get_context_variable('revisions'), [])
mock_api.api_revision_log_by.assert_called_once_with(
9, 'refs/heads/master', None)
@patch('swh.web.ui.views.browse.api')
@istest
def browse_revision_log_by_ko_bad_input(self, mock_api):
# given
mock_api.api_revision_log.side_effect = BadInputExc('wrong input!')
# when
rv = self.client.get('/browse/revision/abcd/log/')
# then
self.assertEqual(rv.status_code, 200)
self.assert_template_used('revision-log.html')
self.assertEqual(self.get_context_variable('sha1_git'), 'abcd')
self.assertEqual(
self.get_context_variable('message'),
'wrong input!')
self.assertEqual(self.get_context_variable('revisions'), [])
mock_api.api_revision_log.assert_called_once_with('abcd', None)
@patch('swh.web.ui.views.browse.api')
@istest
def browse_revision_log_by(self, mock_api):
# given
stub_revisions = [{
'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',
'type': 'git',
'author': {
'email': 'torvalds@linux-foundation.org',
'name': 'Linus Torvalds'
},
'message': 'Linux 4.2-rc1\n',
'synthetic': False,
'directory_url': '/api/1/directory/'
'2a1dbabeed4dcf1f4a4c441993b2ffc9d972780b/',
'parent_url': [
'/api/1/revision/a585d2b738bfa26326b3f1f40f0f1eda0c067ccf/'
],
}]
mock_api.api_revision_log_by.return_value = stub_revisions
# when
rv = self.client.get('/browse/revision/origin/2/log/')
# then
self.assertEqual(rv.status_code, 200)
self.assert_template_used('revision-log.html')
self.assertEqual(self.get_context_variable('origin_id'), 2)
self.assertTrue(
isinstance(self.get_context_variable('revisions'), map))
self.assertIsNone(self.get_context_variable('message'))
mock_api.api_revision_log_by.assert_called_once_with(
2, 'refs/heads/master', None)
@patch('swh.web.ui.views.browse.api')
@istest
def browse_revision_history_ko_not_found(self, mock_api):
# given
mock_api.api_revision_history.side_effect = NotFoundExc(
'Not found')
# when
rv = self.client.get('/browse/revision/1/history/2/')
# then
self.assertEqual(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'),
'Not found')
mock_api.api_revision_history.assert_called_once_with(
'1', '2')
@patch('swh.web.ui.views.browse.api')
@istest
def browse_revision_history_ko_bad_input(self, mock_api):
# given
mock_api.api_revision_history.side_effect = BadInputExc(
'Input incorrect')
# when
rv = self.client.get('/browse/revision/321/history/654/')
# then
self.assertEqual(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_api.api_revision_history.assert_called_once_with(
'321', '654')
@istest
def browse_revision_history_ok_same_sha1(self):
# when
rv = self.client.get('/browse/revision/10/history/10/')
# then
self.assertEqual(rv.status_code, 302)
@patch('swh.web.ui.views.browse.utils')
@patch('swh.web.ui.views.browse.api')
@istest
def browse_revision_history(self, mock_api, mock_utils):
# given
stub_revision = {'id': 'some-rev'}
mock_api.api_revision_history.return_value = stub_revision
expected_revision = {
'id': 'some-rev-id',
'author': {'name': 'foo', 'email': 'bar'},
'committer': {'name': 'foo', 'email': 'bar'}
}
mock_utils.prepare_data_for_view.return_value = expected_revision
# when
rv = self.client.get('/browse/revision/426/history/789/')
# then
self.assertEqual(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'), '789')
self.assertEqual(self.get_context_variable('revision'),
expected_revision)
mock_api.api_revision_history.assert_called_once_with(
'426', '789')
mock_utils.prepare_data_for_view.assert_called_once_with(stub_revision)
@patch('swh.web.ui.views.browse.api')
@istest
def browse_revision_directory_ko_not_found(self, mock_api):
# given
mock_api.api_revision_directory.side_effect = NotFoundExc('Not found!')
# when
rv = self.client.get('/browse/revision/1/directory/')
# then
self.assertEqual(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_api.api_revision_directory.assert_called_once_with(
'1', None, with_data=True)
@patch('swh.web.ui.views.browse.api')
@istest
def browse_revision_directory_ko_bad_input(self, mock_api):
# given
mock_api.api_revision_directory.side_effect = BadInputExc('Bad input!')
# when
rv = self.client.get('/browse/revision/10/directory/')
# then
self.assertEqual(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_api.api_revision_directory.assert_called_once_with(
'10', None, with_data=True)
@patch('swh.web.ui.views.browse.api')
@istest
def browse_revision_directory(self, mock_api):
# given
stub_result0 = {
'type': 'dir',
'revision': '100',
'content': [
{
'id': 'some-result',
'type': 'file',
'name': 'blah',
},
{
'id': 'some-other-result',
'type': 'dir',
'name': 'foo',
}
]
}
mock_api.api_revision_directory.return_value = stub_result0
stub_result1 = {
'type': 'dir',
'revision': '100',
'content':
[
{
'id': 'some-result',
'type': 'file',
'name': 'blah',
},
{
'id': 'some-other-result',
'type': 'dir',
'name': 'foo',
}
]
}
# when
rv = self.client.get('/browse/revision/100/directory/some/path/')
# then
self.assertEqual(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('revision'), '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_api.api_revision_directory.assert_called_once_with(
'100', 'some/path', with_data=True)
@patch('swh.web.ui.views.browse.api')
@istest
def browse_revision_history_directory_ko_not_found(self, mock_api):
# given
mock_api.api_revision_history_directory.side_effect = NotFoundExc(
'not found')
# when
rv = self.client.get('/browse/revision/123/history/456/directory/a/b/')
# then
self.assertEqual(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'), 'a/b')
self.assertEqual(self.get_context_variable('message'), 'not found')
self.assertIsNone(self.get_context_variable('result'))
mock_api.api_revision_history_directory.assert_called_once_with(
'123', '456', 'a/b', with_data=True)
@patch('swh.web.ui.views.browse.api')
@istest
def browse_revision_history_directory_ko_bad_input(self, mock_api):
# given
mock_api.api_revision_history_directory.side_effect = BadInputExc(
'bad input')
# when
rv = self.client.get('/browse/revision/123/history/456/directory/a/c/')
# then
self.assertEqual(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'), 'a/c')
self.assertEqual(self.get_context_variable('message'), 'bad input')
self.assertIsNone(self.get_context_variable('result'))
mock_api.api_revision_history_directory.assert_called_once_with(
'123', '456', 'a/c', with_data=True)
@patch('swh.web.ui.views.browse.service')
@istest
def browse_revision_history_directory_ok_no_trailing_slash_so_redirect(
self, mock_service):
# when
rv = self.client.get('/browse/revision/1/history/2/directory/path/to')
# then
self.assertEqual(rv.status_code, 301)
@patch('swh.web.ui.views.browse.service')
@istest
def browse_revision_history_directory_ok_same_sha1_redirects(
self, mock_service):
# when
rv = self.client.get('/browse/revision/1/history/1/directory/path/to')
# then
self.assertEqual(rv.status_code, 301)
@patch('swh.web.ui.views.browse.api')
@istest
def browse_revision_history_directory(self, mock_api):
# given
stub_result0 = {
'type': 'dir',
'revision': '1000',
'content': [{
'id': 'some-result',
'type': 'file',
'name': 'blah'
}]
}
mock_api.api_revision_history_directory.return_value = stub_result0
stub_result1 = {
'type': 'dir',
'revision': '1000',
'content': [{
'id': 'some-result',
'type': 'file',
'name': 'blah'
}]
}
# when
rv = self.client.get('/browse/revision/100/history/999/directory/'
'path/to/')
# then
self.assertEqual(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'), '999')
self.assertEqual(self.get_context_variable('revision'), '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_api.api_revision_history_directory.assert_called_once_with(
'100', '999', 'path/to', with_data=True)
@patch('swh.web.ui.views.browse.api')
@istest
def browse_revision_history_through_origin_ko_bad_input(self, mock_api):
# given
mock_api.api_revision_history_through_origin.side_effect = BadInputExc(
'Problem input.') # noqa
# when
rv = self.client.get('/browse/revision/origin/99'
'/history/123/')
# then
self.assertEqual(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_revision_history_through_origin.assert_called_once_with(
99, 'refs/heads/master', None, '123')
@patch('swh.web.ui.views.browse.api')
@istest
def browse_revision_history_through_origin_ko_not_found(self, mock_api):
# given
mock_api.api_revision_history_through_origin.side_effect = NotFoundExc(
'Not found.')
# when
rv = self.client.get('/browse/revision/origin/999/'
'branch/dev/history/123/')
# then
self.assertEqual(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_history_through_origin.assert_called_once_with(
999, 'dev', None, '123')
@patch('swh.web.ui.views.browse.api')
@istest
def browse_revision_history_through_origin_ko_other_error(self, mock_api):
# given
mock_api.api_revision_history_through_origin.side_effect = ValueError(
'Other Error.')
# when
rv = self.client.get('/browse/revision/origin/438'
'/branch/scratch'
'/ts/2016'
'/history/789/')
# then
self.assertEqual(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_revision_history_through_origin.assert_called_once_with(
438, 'scratch', '2016', '789')
@patch('swh.web.ui.views.browse.api')
@istest
def browse_revision_history_through_origin(self, mock_api):
# given
stub_rev = {
'id': 'some-id',
'author': {},
'committer': {}
}
mock_api.api_revision_history_through_origin.return_value = stub_rev
# when
rv = self.client.get('/browse/revision/origin/99/history/123/')
# then
self.assertEqual(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_history_through_origin.assert_called_once_with(
99, 'refs/heads/master', None, '123')
@patch('swh.web.ui.views.browse.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.assertEqual(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.browse.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.assertEqual(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.browse.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.assertEqual(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.browse.api')
@istest
def browse_revision_with_origin(self, mock_api):
# given
stub_rev = {'id': 'some-id',
'author': {},
'committer': {}}
mock_api.api_revision_with_origin.return_value = stub_rev
# when
rv = self.client.get('/browse/revision/origin/1/')
self.assertEqual(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)
@patch('swh.web.ui.views.browse.api')
@istest
def browse_revision_directory_through_origin_ko_not_found(self, mock_api):
# given
mock_api.api_directory_through_revision_origin.side_effect = BadInputExc( # noqa
'this is not the robot you are looking for')
# when
rv = self.client.get('/browse/revision/origin/2'
'/directory/')
self.assertEqual(rv.status_code, 200)
self.assert_template_used('revision-directory.html')
self.assertIsNone(self.get_context_variable('result'))
self.assertEqual(self.get_context_variable('message'),
'this is not the robot you are looking for')
mock_api.api_directory_through_revision_origin.assert_called_once_with( # noqa
2, 'refs/heads/master', None, None, with_data=True)
@patch('swh.web.ui.views.browse.api')
@istest
def browse_revision_directory_through_origin_ko_bad_input(self, mock_api):
# given
mock_api.api_directory_through_revision_origin.side_effect = BadInputExc( # noqa
'Bad Robot')
# when
rv = self.client.get('/browse/revision/origin/2'
'/directory/')
self.assertEqual(rv.status_code, 200)
self.assert_template_used('revision-directory.html')
self.assertIsNone(self.get_context_variable('result'))
self.assertEqual(self.get_context_variable('message'), 'Bad Robot')
mock_api.api_directory_through_revision_origin.assert_called_once_with(
2, 'refs/heads/master', None, None, with_data=True)
@patch('swh.web.ui.views.browse.api')
@istest
def browse_revision_directory_through_origin_ko_other(self, mock_api):
# given
mock_api.api_directory_through_revision_origin.side_effect = ValueError( # noqa
'Other bad stuff')
# when
rv = self.client.get('/browse/revision/origin/2'
'/directory/')
self.assertEqual(rv.status_code, 200)
self.assert_template_used('revision-directory.html')
self.assertIsNone(self.get_context_variable('result'))
self.assertEqual(self.get_context_variable('message'),
'Other bad stuff')
mock_api.api_directory_through_revision_origin.assert_called_once_with(
2, 'refs/heads/master', None, None, with_data=True)
@patch('swh.web.ui.views.browse.api')
@istest
def browse_revision_directory_through_origin(self, mock_api):
# given
stub_res = {'id': 'some-id',
'revision': 'some-rev-id',
'type': 'dir',
'content': 'some-content'}
mock_api.api_directory_through_revision_origin.return_value = stub_res
# when
rv = self.client.get('/browse/revision/origin/2'
'/branch/dev'
'/ts/2013-20-20 10:02'
'/directory/some/file/')
self.assertEqual(rv.status_code, 200)
self.assert_template_used('revision-directory.html')
self.assertEqual(self.get_context_variable('result'), stub_res)
self.assertIsNone(self.get_context_variable('message'))
mock_api.api_directory_through_revision_origin.assert_called_once_with(
2, 'dev', '2013-20-20 10:02', 'some/file', with_data=True)
@patch('swh.web.ui.views.browse.api')
@istest
def browse_directory_through_revision_with_origin_history_ko_not_found(
self, mock_api):
mock_api.api_directory_through_revision_with_origin_history.side_effect = NotFoundExc( # noqa
'Not found!')
# when
rv = self.client.get('/browse/revision/origin/987'
'/history/sha1git'
'/directory/')
# then
self.assertEqual(rv.status_code, 200)
self.assert_template_used('revision-directory.html')
self.assertIsNone(self.get_context_variable('result'))
self.assertEqual(self.get_context_variable('message'), 'Not found!')
self.assertEqual(self.get_context_variable('path'), '.')
mock_api.api_directory_through_revision_with_origin_history.assert_called_once_with( # noqa
987, 'refs/heads/master', None, 'sha1git', None, with_data=True)
@patch('swh.web.ui.views.browse.api')
@istest
def browse_directory_through_revision_with_origin_history_ko_bad_input(
self, mock_api):
mock_api.api_directory_through_revision_with_origin_history.side_effect = BadInputExc( # noqa
'Bad input! Bleh!')
# when
rv = self.client.get('/browse/revision/origin/798'
'/branch/refs/heads/dev'
'/ts/2012-11-11'
'/history/1234'
'/directory/some/path/')
# then
self.assertEqual(rv.status_code, 200)
self.assert_template_used('revision-directory.html')
self.assertIsNone(self.get_context_variable('result'))
self.assertEqual(self.get_context_variable('message'),
'Bad input! Bleh!')
self.assertEqual(self.get_context_variable('path'), 'some/path')
mock_api.api_directory_through_revision_with_origin_history.assert_called_once_with( # noqa
798, 'refs/heads/dev', '2012-11-11', '1234', 'some/path',
with_data=True)
@patch('swh.web.ui.views.browse.api')
@istest
def browse_directory_through_revision_with_origin_history(
self, mock_api):
stub_dir = {'type': 'dir',
'content': [],
'revision': 'specific-rev-id'}
mock_api.api_directory_through_revision_with_origin_history.return_value = stub_dir # noqa
# when
rv = self.client.get('/browse/revision/origin/101010'
'/ts/1955-11-12'
'/history/54628'
'/directory/emacs-24.5/')
# then
self.assertEqual(rv.status_code, 200)
self.assert_template_used('revision-directory.html')
self.assertEqual(self.get_context_variable('result'), stub_dir)
self.assertIsNone(self.get_context_variable('message'))
self.assertEqual(self.get_context_variable('path'), 'emacs-24.5')
mock_api.api_directory_through_revision_with_origin_history.assert_called_once_with( # noqa
101010, 'refs/heads/master', '1955-11-12', '54628', 'emacs-24.5',
with_data=True)
class EntityView(test_app.SWHViewTestCase):
render_template = False
@patch('swh.web.ui.views.browse.api')
@istest
def browse_entity_ko_not_found(self, mock_api):
# given
mock_api.api_entity_by_uuid.side_effect = NotFoundExc('Not found!')
# when
rv = self.client.get('/browse/entity/'
'5f4d4c51-498a-4e28-88b3-b3e4e8396cba/')
# then
self.assertEqual(rv.status_code, 200)
self.assert_template_used('entity.html')
self.assertEqual(self.get_context_variable('entities'), [])
self.assertEqual(self.get_context_variable('message'), 'Not found!')
mock_api.api_entity_by_uuid.assert_called_once_with(
'5f4d4c51-498a-4e28-88b3-b3e4e8396cba')
@patch('swh.web.ui.views.browse.api')
@istest
def browse_entity_ko_bad_input(self, mock_api):
# given
mock_api.api_entity_by_uuid.side_effect = BadInputExc('wrong input!')
# when
rv = self.client.get('/browse/entity/blah-blah-uuid/')
# then
self.assertEqual(rv.status_code, 200)
self.assert_template_used('entity.html')
self.assertEqual(self.get_context_variable('entities'), [])
self.assertEqual(self.get_context_variable('message'), 'wrong input!')
mock_api.api_entity_by_uuid.assert_called_once_with(
'blah-blah-uuid')
@patch('swh.web.ui.views.browse.api')
@istest
def browse_entity(self, mock_api):
# given
stub_entities = [
{'id': '5f4d4c51-5a9b-4e28-88b3-b3e4e8396cba'}]
mock_api.api_entity_by_uuid.return_value = stub_entities
# when
rv = self.client.get('/browse/entity/'
'5f4d4c51-5a9b-4e28-88b3-b3e4e8396cba/')
# then
self.assertEqual(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_api.api_entity_by_uuid.assert_called_once_with(
'5f4d4c51-5a9b-4e28-88b3-b3e4e8396cba')
class Lookup(TestCase):
@patch('swh.web.ui.views.browse.api')
@istest
def api_lookup(self, mock_api):
# given
mock_api.api_content_metadata.return_value = {'id': 'blah'}
# given
r = browse.api_lookup(mock_api.api_content_metadata, 'sha1:blah')
# then
self.assertEquals(r, {'id': 'blah'})
mock_api.api_content_metadata.assert_called_once_with('sha1:blah')
@patch('swh.web.ui.views.browse.api')
@istest
def api_lookup_not_found(self, mock_api):
# given
mock_api.api_content_filetype.side_effect = NotFoundExc
# given
r = browse.api_lookup(mock_api.api_content_filetype, 'sha1_git:foo')
# then
self.assertIsNone(r)
mock_api.api_content_filetype.assert_called_once_with('sha1_git:foo')
@patch('swh.web.ui.views.browse.api')
@istest
def api_lookup_bad_input(self, mock_api):
# given
mock_api.api_content_license.side_effect = BadInputExc
# given
r = browse.api_lookup(mock_api.api_content_license, 'sha1_git:foo')
# then
self.assertIsNone(r)
mock_api.api_content_license.assert_called_once_with('sha1_git:foo')
diff --git a/swh/web/ui/views/browse.py b/swh/web/ui/views/browse.py
index a320319de..35f08d69c 100644
--- a/swh/web/ui/views/browse.py
+++ b/swh/web/ui/views/browse.py
@@ -1,1023 +1,1032 @@
# Copyright (C) 2015-2016 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 encodings.aliases import aliases
from flask import render_template, request, url_for, redirect
from swh.core.hashutil import ALGORITHMS
from swh.core.utils import grouper
from .. import service, utils, apidoc
from ..exc import BadInputExc, NotFoundExc
from ..main import app
from . import api
hash_filter_keys = ALGORITHMS
def api_lookup(api_fn, query):
"""Lookup api with api_fn function with parameter query.
Example:
filetype = api_lookup('api_content_filetype', 'sha1:blah')
if filetype:
content['mimetype'] = filetype['mimetype']
"""
try:
return api_fn(query)
except (NotFoundExc, BadInputExc):
return None
@app.route('/origin/search/')
def search_origin():
"""
Redirect request with GET params for an origin to our fragmented URI scheme
"""
if request.method == 'GET':
data = request.args
origin_id = data.get('origin_id')
if origin_id:
return redirect(url_for('browse_origin', origin_id=origin_id))
args = ['origin_type', 'origin_url']
values = {arg: data.get(arg) for arg in args if data.get(arg)}
if 'origin_type' in values and 'origin_url' in values:
return redirect(url_for('browse_origin', **values))
@app.route('/directory/search/')
def search_directory():
"""
Redirect request with GET params for a directory to our fragmented
URI scheme
"""
def url_for_filtered(endpoint, **kwargs):
"""Make url_for ignore keyword args that have an empty string for value
"""
filtered = {k: v for k, v in kwargs.items() if kwargs[k]}
return url_for(endpoint, **filtered)
if request.method == 'GET':
data = request.args
sha1_git = data.get('sha1_git')
if sha1_git:
if 'dir_path' in data:
# dir_path exists only in requests for a revision's directory
return redirect(url_for_filtered(
'browse_revision_directory',
sha1_git=sha1_git,
dir_path=data.get('dir_path')
))
return redirect(url_for_filtered(
'browse_directory',
sha1_git=sha1_git,
path=data.get('path')
))
args = ['origin_id', 'branch_name', 'ts', 'path']
values = {arg: data.get(arg) for arg in args if data.get(arg)}
if 'origin_id' in values:
return redirect(url_for('browse_revision_directory_through_origin',
**values))
@app.route('/revision/search/')
def search_revision():
"""
Redirect request with GET params for a revision to our fragmented
URI scheme
"""
if request.method == 'GET':
data = request.args
sha1_git = data.get('sha1_git')
if sha1_git:
return redirect(url_for('browse_revision', sha1_git=sha1_git))
args = ['origin_id', 'branch_name', 'ts']
values = {arg: data.get(arg) for arg in args if data.get(arg)}
if 'origin_id' in values:
return redirect(url_for('browse_revision_with_origin', **values))
@app.route('/content/symbol/', methods=['GET'])
def search_symbol():
"""Search for symbols in contents.
Returns:
dict representing data to look for in swh storage.
"""
env = {
'result': None,
'per_page': None,
'message': '',
'linknext': None,
'linkprev': None,
}
# Read form or get information
data = request.args
q = data.get('q')
per_page = data.get('per_page')
env['q'] = q
if per_page:
env['per_page'] = per_page
if q:
try:
result = api.api_content_symbol(q)
if result:
headers = result.get('headers')
result = utils.prepare_data_for_view(result['results'])
env['result'] = result
if headers:
url = url_for('search_symbol')
if 'link-next' in headers:
next_last_sha1 = result[-1]['sha1']
if per_page:
params = (('q', q),
('last_sha1', next_last_sha1),
('per_page', per_page))
else:
params = (('q', q),
('last_sha1', next_last_sha1))
env['linknext'] = utils.to_url(url, params)
except BadInputExc as e:
env['message'] = str(e)
return render_template('symbols.html', **env)
@app.route('/content/search/', methods=['GET', 'POST'])
def search_content():
"""Search for hashes in swh-storage.
One form to submit either:
- hash query to look up in swh storage
- file hashes calculated client-side to be queried in swh storage
- both
Returns:
dict representing data to look for in swh storage.
The following keys are returned:
- search_stats: {'nbfiles': X, 'pct': Y} the number of total
queried files and percentage of files not in storage respectively
- responses: array of {'filename': X, 'sha1': Y, 'found': Z}
- messages: General messages.
TODO:
Batch-process with all checksums, not just sha1
"""
env = {'search_res': None,
'search_stats': None,
'message': []}
search_stats = {'nbfiles': 0, 'pct': 0}
search_res = None
message = ''
# Get with a single hash request
if request.method == 'POST':
# Post form submission with many hash requests
q = None
else:
data = request.args
q = data.get('q')
try:
search = api.api_search(q)
search_res = search['search_res']
search_stats = search['search_stats']
except BadInputExc as e:
message = str(e)
env['search_stats'] = search_stats
env['search_res'] = search_res
env['message'] = message
return render_template('search.html', **env)
@app.route('/browse/')
def browse():
"""Render the user-facing browse view
"""
return render_template('browse.html')
-@app.route('/api/')
-def browse_api_doc():
- """Render the API's documentation.
+@app.route('/api/1/')
+def browse_api_endpoints():
+ """Display the list of opened api endpoints.
+
"""
routes = apidoc.APIUrls.get_app_endpoints()
# Return a list of routes with consistent ordering
env = {
'doc_routes': sorted(routes.items())
}
- return render_template('api.html', **env)
+ return render_template('api-endpoints.html', **env)
+
+
+@app.route('/api/')
+def browse_api_doc():
+ """Display the API's documentation.
+
+ """
+ return render_template('api.html')
@app.route('/browse/content//')
def browse_content(q):
"""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 = {'q': q,
'message': None,
'content': None}
encoding = request.args.get('encoding', 'utf8')
if encoding not in aliases:
env['message'] = 'Encoding %s not supported.' \
'Supported Encodings: %s' % (
encoding, list(aliases.keys()))
return render_template('content.html', **env)
try:
content = api.api_content_metadata(q)
filetype = api_lookup(api.api_content_filetype, q)
if filetype:
content['mimetype'] = filetype.get('mimetype')
content['encoding'] = filetype.get('encoding')
else:
content['mimetype'] = None
content['encoding'] = None
language = api_lookup(api.api_content_language, q)
if language:
content['language'] = language.get('lang')
else:
content['language'] = None
licenses = api_lookup(api.api_content_license, q)
if licenses:
content['licenses'] = ', '.join(licenses.get('licenses', []))
else:
content['licenses'] = None
content_raw = service.lookup_content_raw(q)
if content_raw:
content['data'] = content_raw['data']
else:
content['data'] = None
ctags = api_lookup(api.api_content_ctags, q)
if ctags:
url = url_for('browse_content', q=q)
content['ctags'] = grouper((
'%s' % (
url,
ctag['line'],
ctag['line'])
for ctag in ctags
), 20)
else:
content['ctags'] = None
env['content'] = utils.prepare_data_for_view(content,
encoding=encoding)
except (NotFoundExc, BadInputExc) as e:
env['message'] = str(e)
return render_template('content.html', **env)
@app.route('/browse/content//raw/')
def browse_content_raw(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.
"""
return redirect(url_for('api_content_raw', q=q))
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//origin/')
def browse_content_with_origin(q):
"""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:
origin = api.api_content_checksum_to_origin(q)
message = _origin_seen(q, origin)
except (NotFoundExc, BadInputExc) as e:
message = str(e)
env['message'] = message
return render_template('content-with-origin.html', **env)
@app.route('/browse/directory//')
@app.route('/browse/directory///')
def browse_directory(sha1_git, path=None):
"""Show directory information.
Args:
- sha1_git: the directory's sha1 git identifier. If path
is set, the base directory for the relative path to the entry
- path: the path to the requested entry, relative to
the directory pointed by sha1_git
Returns:
The content's information at sha1_git, or at sha1_git/path if
path is set.
"""
env = {'sha1_git': sha1_git,
'files': []}
try:
if path:
env['message'] = ('Listing for directory with path %s from %s:'
% (path, sha1_git))
dir_or_file = service.lookup_directory_with_path(
sha1_git, path)
if dir_or_file['type'] == 'file':
fsha = 'sha256:%s' % dir_or_file['sha256']
content = api.api_content_metadata(fsha)
content_raw = service.lookup_content_raw(fsha)
if content_raw: # FIXME: currently assuming utf8 encoding
content['data'] = content_raw['data']
env['content'] = utils.prepare_data_for_view(
content, encoding='utf-8')
return render_template('content.html', **env)
else:
directory_files = api.api_directory(dir_or_file['target'])
env['files'] = utils.prepare_data_for_view(directory_files)
else:
env['message'] = "Listing for directory %s:" % sha1_git
directory_files = api.api_directory(sha1_git)
env['files'] = utils.prepare_data_for_view(directory_files)
except (NotFoundExc, BadInputExc) as e:
env['message'] = str(e)
return render_template('directory.html', **env)
@app.route('/browse/origin//url//')
@app.route('/browse/origin//')
def browse_origin(origin_id=None, origin_type=None, origin_url=None):
"""Browse origin matching given criteria - either origin_id or
origin_type and origin_path.
Args:
- origin_id: origin's swh identifier
- origin_type: origin's type
- origin_url: origin's URL
"""
# URLs for the calendar JS plugin
env = {'browse_url': None,
'visit_url': None,
'origin': None}
try:
origin = api.api_origin(origin_id, origin_type, origin_url)
env['origin'] = origin
env['browse_url'] = url_for('browse_revision_with_origin',
origin_id=origin['id'])
env['visit_url'] = url_for('api_origin_visits',
origin_id=origin['id'])
except (NotFoundExc, BadInputExc) as e:
env['message'] = str(e)
return render_template('origin.html', **env)
@app.route('/browse/person//')
def browse_person(person_id):
"""Browse person with id id.
"""
env = {'person_id': person_id,
'person': None,
'message': None}
try:
env['person'] = api.api_person(person_id)
except (NotFoundExc, BadInputExc) as e:
env['message'] = str(e)
return render_template('person.html', **env)
@app.route('/browse/release//')
def browse_release(sha1_git):
"""Browse release with sha1_git.
"""
env = {'sha1_git': sha1_git,
'message': None,
'release': None}
try:
rel = api.api_release(sha1_git)
env['release'] = utils.prepare_data_for_view(rel)
except (NotFoundExc, BadInputExc) as e:
env['message'] = str(e)
return render_template('release.html', **env)
@app.route('/browse/revision//')
@app.route('/browse/revision//prev//')
def browse_revision(sha1_git, prev_sha1s=None):
"""Browse the revision with git SHA1 sha1_git_cur, while optionally keeping
the context from which we came as a list of previous (i.e. later)
revisions' sha1s.
Args:
sha1_git: the requested revision's sha1_git.
prev_sha1s: an optional string of /-separated sha1s representing our
context, ordered by descending revision date.
Returns:
Information about revision of git SHA1 sha1_git_cur, with relevant URLS
pointing to the context augmented with sha1_git_cur.
Example:
GET /browse/revision/
"""
env = {'sha1_git': sha1_git,
'message': None,
'revision': None}
try:
rev = api.api_revision(sha1_git, prev_sha1s)
env['revision'] = utils.prepare_data_for_view(rev)
except (NotFoundExc, BadInputExc) as e:
env['message'] = str(e)
return render_template('revision.html', **env)
@app.route('/browse/revision//raw/')
def browse_revision_raw_message(sha1_git):
"""Given a sha1_git, display the corresponding revision's raw message.
"""
return redirect(url_for('api_revision_raw_message', sha1_git=sha1_git))
@app.route('/browse/revision//log/')
@app.route('/browse/revision//prev//log/')
def browse_revision_log(sha1_git, prev_sha1s=None):
"""Browse revision with sha1_git's log. If the navigation path through the
commit tree is specified, we intersect the earliest revision's log with the
revisions the user browsed through - ie the path taken to the specified
revision.
Args:
sha1_git: the current revision's SHA1_git checksum
prev_sha1s: optionally, the path through which we want log information
"""
env = {'sha1_git': sha1_git,
'sha1_url': '/browse/revision/%s/' % sha1_git,
'message': None,
'revisions': []}
try:
revision_data = api.api_revision_log(sha1_git, prev_sha1s)
revisions = revision_data['revisions']
next_revs_url = revision_data['next_revs_url']
env['revisions'] = map(utils.prepare_data_for_view, revisions)
env['next_revs_url'] = utils.prepare_data_for_view(next_revs_url)
except (NotFoundExc, BadInputExc) as e:
env['message'] = str(e)
return render_template('revision-log.html', **env)
@app.route('/browse/revision'
'/origin//log/')
@app.route('/browse/revision'
'/origin/'
'/branch//log/')
@app.route('/browse/revision'
'/origin/'
'/branch/'
'/ts//log/')
@app.route('/browse/revision'
'/origin/'
'/ts//log/')
def browse_revision_log_by(origin_id,
branch_name='refs/heads/master',
timestamp=None):
"""Browse the revision described by origin, branch name and timestamp's
log
Args:
origin_id: the revision's origin
branch_name: the revision's branch
timestamp: the requested timeframe for the revision
Returns:
The revision log of the described revision as a list of revisions
if it is found.
"""
env = {'sha1_git': None,
'origin_id': origin_id,
'origin_url': '/browse/origin/%d/' % origin_id,
'branch_name': branch_name,
'timestamp': timestamp,
'message': None,
'revisions': []}
try:
revisions = api.api_revision_log_by(
origin_id, branch_name, timestamp)
env['revisions'] = map(utils.prepare_data_for_view, revisions)
except (NotFoundExc, BadInputExc) as e:
env['message'] = str(e)
return render_template('revision-log.html', **env)
@app.route('/browse/revision//prev//')
def browse_with_rev_context(sha1_git_cur, sha1s):
"""Browse the revision with git SHA1 sha1_git_cur, while keeping the context
from which we came as a list of previous (i.e. later) revisions' sha1s.
Args:
sha1_git_cur: the requested revision's sha1_git.
sha1s: a string of /-separated sha1s representing our context, ordered
by descending revision date.
Returns:
Information about revision of git SHA1 sha1_git_cur, with relevant URLS
pointing to the context augmented with sha1_git_cur.
Example:
GET /browse/revision/
"""
env = {'sha1_git': sha1_git_cur,
'message': None,
'revision': None}
try:
revision = api.api_revision(
sha1_git_cur, sha1s)
env['revision'] = utils.prepare_data_for_view(revision)
except (BadInputExc, NotFoundExc) as e:
env['message'] = str(e)
return render_template('revision.html', **env)
@app.route('/browse/revision//history//')
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.
"""
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))
try:
revision = api.api_revision_history(sha1_git_root,
sha1_git)
env['revision'] = utils.prepare_data_for_view(revision)
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//')
def browse_revision_directory(sha1_git, dir_path=None):
"""Browse directory from revision with sha1_git.
"""
env = {
'sha1_git': sha1_git,
'path': '.' if not dir_path else dir_path,
'message': None,
'result': None
}
encoding = request.args.get('encoding', 'utf8')
if encoding not in aliases:
env['message'] = 'Encoding %s not supported.' \
'Supported Encodings: %s' % (
encoding, list(aliases.keys()))
return render_template('revision-directory.html', **env)
try:
result = api.api_revision_directory(sha1_git, dir_path, with_data=True)
result['content'] = utils.prepare_data_for_view(result['content'],
encoding=encoding)
env['revision'] = result['revision']
env['result'] = result
except (BadInputExc, NotFoundExc) 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//')
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
"""
env = {
'sha1_git_root': sha1_git_root,
'sha1_git': sha1_git,
'path': '.' if not path else path,
'message': None,
'result': None
}
encoding = request.args.get('encoding', 'utf8')
if encoding not in aliases:
env['message'] = 'Encoding %s not supported.' \
'Supported Encodings: %s' % (
encoding, list(aliases.keys()))
return render_template('revision-directory.html', **env)
if sha1_git == sha1_git_root:
return redirect(url_for('browse_revision_directory',
sha1_git=sha1_git,
path=path,
encoding=encoding),
code=301)
try:
result = api.api_revision_history_directory(sha1_git_root,
sha1_git,
path,
with_data=True)
env['revision'] = result['revision']
env['content'] = utils.prepare_data_for_view(result['content'],
encoding=encoding)
env['result'] = result
except (BadInputExc, NotFoundExc) as e:
env['message'] = str(e)
return render_template('revision-directory.html', **env)
@app.route('/browse/revision'
'/origin/'
'/history/'
'/directory/')
@app.route('/browse/revision'
'/origin/'
'/history/'
'/directory//')
@app.route('/browse/revision'
'/origin/'
'/branch/'
'/history/'
'/directory/')
@app.route('/browse/revision'
'/origin/'
'/branch/'
'/history/'
'/directory//')
@app.route('/browse/revision'
'/origin/'
'/ts/'
'/history/'
'/directory/')
@app.route('/browse/revision'
'/origin/'
'/ts/'
'/history/'
'/directory//')
@app.route('/browse/revision'
'/origin/'
'/branch/'
'/ts/'
'/history/'
'/directory/')
@app.route('/browse/revision'
'/origin/'
'/branch/'
'/ts/'
'/history/'
'/directory//')
def browse_directory_through_revision_with_origin_history(
origin_id,
branch_name="refs/heads/master",
ts=None,
sha1_git=None,
path=None):
env = {
'origin_id': origin_id,
'branch_name': branch_name,
'ts': ts,
'sha1_git': sha1_git,
'path': '.' if not path else path,
'message': None,
'result': None
}
encoding = request.args.get('encoding', 'utf8')
if encoding not in aliases:
env['message'] = (('Encoding %s not supported.'
'Supported Encodings: %s') % (
encoding, list(aliases.keys())))
return render_template('revision-directory.html', **env)
try:
result = api.api_directory_through_revision_with_origin_history(
origin_id, branch_name, ts, sha1_git, path, with_data=True)
env['revision'] = result['revision']
env['content'] = utils.prepare_data_for_view(result['content'],
encoding=encoding)
env['result'] = result
except (BadInputExc, NotFoundExc) 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//')
def browse_revision_with_origin(origin_id,
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,
'revision': None}
try:
revision = api.api_revision_with_origin(origin_id,
branch_name,
ts)
env['revision'] = utils.prepare_data_for_view(revision)
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//')
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:
revision = api.api_revision_history_through_origin(
origin_id,
branch_name,
ts,
sha1_git)
env['revision'] = utils.prepare_data_for_view(revision)
except (ValueError, BadInputExc, NotFoundExc) as e:
env['message'] = str(e)
return render_template('revision.html', **env)
@app.route('/browse/revision'
'/origin/'
'/directory/')
@app.route('/browse/revision'
'/origin/'
'/directory/')
@app.route('/browse/revision'
'/origin/'
'/branch/'
'/directory/')
@app.route('/browse/revision'
'/origin/'
'/branch/'
'/directory//')
@app.route('/browse/revision'
'/origin/'
'/branch/'
'/ts/'
'/directory/')
@app.route('/browse/revision'
'/origin/'
'/branch/'
'/ts/'
'/directory//')
def browse_revision_directory_through_origin(origin_id,
branch_name='refs/heads/master',
ts=None,
path=None):
env = {'message': None,
'origin_id': origin_id,
'ts': ts,
'path': '.' if not path else path,
'result': None}
encoding = request.args.get('encoding', 'utf8')
if encoding not in aliases:
env['message'] = 'Encoding %s not supported.' \
'Supported Encodings: %s' % (
encoding, list(aliases.keys()))
return render_template('revision-directory.html', **env)
try:
result = api.api_directory_through_revision_origin(
origin_id,
branch_name,
ts,
path,
with_data=True)
result['content'] = utils.prepare_data_for_view(result['content'],
encoding=encoding)
env['revision'] = result['revision']
env['result'] = result
except (ValueError, BadInputExc, NotFoundExc) as e:
env['message'] = str(e)
return render_template('revision-directory.html', **env)
@app.route('/browse/entity/')
@app.route('/browse/entity//')
def browse_entity(uuid):
env = {'entities': [],
'message': None}
try:
entities = api.api_entity_by_uuid(uuid)
env['entities'] = entities
except (NotFoundExc, BadInputExc) as e:
env['message'] = str(e)
return render_template('entity.html', **env)