diff --git a/swh/web/api/views/origin.py b/swh/web/api/views/origin.py --- a/swh/web/api/views/origin.py +++ b/swh/web/api/views/origin.py @@ -15,25 +15,62 @@ from swh.web.api.views.utils import api_lookup +DOC_RETURN_ORIGIN = ''' + :>json string origin_visits_url: link to in order to get information + about the visits for that origin + :>json string url: the origin canonical url + :>json string type: the type of software origin (deprecated value; + types are now associated to visits instead of origins) + :>json number id: the origin unique identifier (deprecated value; + you should only refer to origins based on their URL) +''' + +DOC_RETURN_ORIGIN_ARRAY = \ + DOC_RETURN_ORIGIN.replace(':>json', ':>jsonarr') + +DOC_RETURN_ORIGIN_VISIT = ''' + :>json string date: ISO representation of the visit date (in UTC) + :>json str origin: the origin canonical url + :>json string origin_url: link to get information about the origin + :>jsonarr string snapshot: the snapshot identifier of the visit + :>jsonarr string snapshot_url: link to + :http:get:`/api/1/snapshot/(snapshot_id)/` in order to get + information about the snapshot of the visit + :>json string status: status of the visit (either **full**, + **partial** or **ongoing**) + :>json number visit: the unique identifier of the visit +''' + +DOC_RETURN_ORIGIN_VISIT_ARRAY = \ + DOC_RETURN_ORIGIN_VISIT.replace(':>json', ':>jsonarr') + +DOC_RETURN_ORIGIN_VISIT_ARRAY += ''' + :>jsonarr number id: the unique identifier of the origin + :>jsonarr string origin_visit_url: link to + :http:get:`/api/1/origin/(origin_url)/visit/(visit_id)/` + in order to get information about the visit +''' + + def _enrich_origin(origin): if 'id' in origin: o = origin.copy() o['origin_visits_url'] = reverse( - 'api-1-origin-visits', url_args={'origin_id': origin['id']}) + 'api-1-origin-visits', url_args={'origin_url': origin['url']}) return o return origin def _enrich_origin_visit(origin_visit, *, - with_origin_url, with_origin_visit_url): + with_origin_link, with_origin_visit_link): ov = origin_visit.copy() - if with_origin_url: + if with_origin_link: ov['origin_url'] = reverse('api-1-origin', - url_args={'origin_id': ov['origin']}) - if with_origin_visit_url: + url_args={'origin_url': ov['origin']}) + if with_origin_visit_link: ov['origin_visit_url'] = reverse('api-1-origin-visit', - url_args={'origin_id': ov['origin'], + url_args={'origin_url': ov['origin'], 'visit_id': ov['visit']}) snapshot = ov['snapshot'] if snapshot: @@ -46,7 +83,7 @@ @api_route(r'/origins/', 'api-1-origins') @api_doc('/origins/', noargs=True) -@format_docstring() +@format_docstring(return_origin_array=DOC_RETURN_ORIGIN_ARRAY) def api_origins(request): """ .. http:get:: /api/1/origins/ @@ -60,13 +97,7 @@ :query int origin_count: The maximum number of origins to return (default to 100, can not exceed 10000) - :>jsonarr number id: the origin unique identifier - :>jsonarr string origin_visits_url: link to in order to get information - about the visits for that origin - :>jsonarr string type: the type of software origin (possible values - are ``git``, ``svn``, ``hg``, ``deb``, ``pypi``, ``npm``, ``ftp`` - or ``deposit``) - :>jsonarr string url: the origin canonical url + {return_origin_array} {common_headers} {resheader_link} @@ -98,26 +129,43 @@ return response -@api_route(r'/origin/(?P[0-9]+)/', 'api-1-origin') @api_route(r'/origin/(?P[a-z]+)/url/(?P.+)/', 'api-1-origin') +@api_route(r'/origin/(?P.+)/get/', 'api-1-origin') +@api_route(r'/origin/(?P[0-9]+)/', 'api-1-origin') @api_doc('/origin/') -@format_docstring() +@format_docstring(return_origin=DOC_RETURN_ORIGIN) def api_origin(request, origin_id=None, origin_type=None, origin_url=None): """ + .. http:get:: /api/1/origin/(origin_url)/get/ + + Get information about a software origin. + + :param string origin_url: the origin url + + {return_origin} + + {common_headers} + + **Allowed HTTP Methods:** :http:method:`get`, :http:method:`head`, + :http:method:`options` + + :statuscode 200: no error + :statuscode 404: requested origin can not be found in the archive + + **Example:** + + .. parsed-literal:: + + :swh_web_api:`origin/git/url/https://github.com/python/cpython/` + .. http:get:: /api/1/origin/(origin_id)/ Get information about a software origin. :param int origin_id: a software origin identifier - :>json number id: the origin unique identifier - :>json string origin_visits_url: link to in order to get information - about the visits for that origin - :>json string type: the type of software origin (possible values are - ``git``, ``svn``, ``hg``, ``deb``, ``pypi``, ``npm``, ``ftp`` or - ``deposit``) - :>json string url: the origin canonical url + {return_origin} {common_headers} @@ -137,16 +185,17 @@ Get information about a software origin. + .. warning:: + + This endpoint is deprecated. You should use + :http:get:`/api/1/origin/(origin_url)/get/` instead. + :param string origin_type: the origin type (possible values are ``git``, ``svn``, ``hg``, ``deb``, ``pypi``, ``npm``, ``ftp`` or ``deposit``) :param string origin_url: the origin url - :>json number id: the origin unique identifier - :>json string origin_visits_url: link to in order to get information - about the visits for that origin - :>json string type: the type of software origin - :>json string url: the origin canonical url + {return_origin} {common_headers} @@ -168,11 +217,8 @@ 'url': origin_url } ori_dict = {k: v for k, v in ori_dict.items() if ori_dict[k]} - if 'id' in ori_dict: - error_msg = 'Origin with id %s not found.' % ori_dict['id'] - else: - error_msg = 'Origin with type %s and URL %s not found' % ( - ori_dict['type'], ori_dict['url']) + error_msg = 'Origin %s not found.' % \ + (ori_dict.get('id') or ori_dict['url']) return api_lookup( service.lookup_origin, ori_dict, @@ -183,7 +229,7 @@ @api_route(r'/origin/search/(?P.+)/', 'api-1-origin-search') @api_doc('/origin/search/') -@format_docstring() +@format_docstring(return_origin_array=DOC_RETURN_ORIGIN_ARRAY) def api_origin_search(request, url_pattern): """ .. http:get:: /api/1/origin/search/(url_pattern)/ @@ -201,11 +247,7 @@ :query boolean with_visit: if true, only return origins with at least one visit by Software heritage - :>jsonarr number id: the origin unique identifier - :>jsonarr string origin_visits_url: link to in order to get information - about the visits for that origin - :>jsonarr string type: the type of software origin - :>jsonarr string url: the origin canonical url + {return_origin_array} {common_headers} @@ -253,7 +295,7 @@ @api_route(r'/origin/metadata-search/', 'api-1-origin-metadata-search') @api_doc('/origin/metadata-search/', noargs=True, need_params=True) -@format_docstring() +@format_docstring(return_origin_array=DOC_RETURN_ORIGIN_ARRAY) def api_origin_metadata_search(request): """ .. http:get:: /api/1/origin/metadata-search/ @@ -268,12 +310,7 @@ :query int limit: the maximum number of found origins to return (bounded to 100) - :>jsonarr number origin_id: the origin unique identifier - :>jsonarr dict metadata: metadata of the origin (as a - JSON-LD/CodeMeta dictionary) - :>jsonarr string from_revision: the revision used to extract these - metadata (the current HEAD or one of the former HEADs) - :>jsonarr dict tool: the tool used to extract these metadata + {return_origin_array} {common_headers} @@ -302,11 +339,42 @@ } +@api_route(r'/origin/(?P.*)/visits/', 'api-1-origin-visits') @api_route(r'/origin/(?P[0-9]+)/visits/', 'api-1-origin-visits') @api_doc('/origin/visits/') -@format_docstring() -def api_origin_visits(request, origin_id): +@format_docstring( + return_origin_visit_array=DOC_RETURN_ORIGIN_VISIT_ARRAY) +def api_origin_visits(request, origin_id=None, origin_url=None): """ + .. http:get:: /api/1/origin/(origin_url)/visits/ + + Get information about all visits of a software origin. + Visits are returned sorted in descending order according + to their date. + + :param str origin_url: a software origin URL + :query int per_page: specify the number of visits to list, for + pagination purposes + :query int last_visit: visit to start listing from, for pagination + purposes + + {common_headers} + {resheader_link} + + {return_origin_visit_array} + + **Allowed HTTP Methods:** :http:method:`get`, :http:method:`head`, + :http:method:`options` + + :statuscode 200: no error + :statuscode 404: requested origin can not be found in the archive + + **Example:** + + .. parsed-literal:: + + :swh_web_api:`origin/https://github.com/hylang/hy/visits/` + .. http:get:: /api/1/origin/(origin_id)/visits/ Get information about all visits of a software origin. @@ -322,18 +390,7 @@ {common_headers} {resheader_link} - :>jsonarr string date: ISO representation of the visit date (in UTC) - :>jsonarr number id: the unique identifier of the origin - :>jsonarr string origin_visit_url: link to - :http:get:`/api/1/origin/(origin_id)/visit/(visit_id)/` in order to - get information about the visit - :>jsonarr string snapshot: the snapshot identifier of the visit - :>jsonarr string snapshot_url: link to - :http:get:`/api/1/snapshot/(snapshot_id)/` in order to get - information about the snapshot of the visit - :>jsonarr string status: status of the visit (either **full**, - **partial** or **ongoing**) - :>jsonarr number visit: the unique identifier of the visit + {return_origin_visit_array} **Allowed HTTP Methods:** :http:method:`get`, :http:method:`head`, :http:method:`options` @@ -348,15 +405,22 @@ :swh_web_api:`origin/1/visits/` """ result = {} - origin_id = int(origin_id) + if origin_url: + origin_query = {'url': origin_url} + notfound_msg = 'No origin {} found'.format(origin_url) + url_args_next = {'origin_url': origin_url} + else: + origin_query = {'id': int(origin_id)} + notfound_msg = 'No origin {} found'.format(origin_id) + url_args_next = {'origin_id': origin_id} per_page = int(request.query_params.get('per_page', '10')) last_visit = request.query_params.get('last_visit') if last_visit: last_visit = int(last_visit) def _lookup_origin_visits( - origin_id, last_visit=last_visit, per_page=per_page): - all_visits = get_origin_visits({'id': origin_id}) + origin_query, last_visit=last_visit, per_page=per_page): + all_visits = get_origin_visits(origin_query) all_visits.reverse() visits = [] if not last_visit: @@ -369,11 +433,11 @@ for v in visits: yield v - results = api_lookup(_lookup_origin_visits, origin_id, - notfound_msg='No origin {} found'.format(origin_id), + results = api_lookup(_lookup_origin_visits, origin_query, + notfound_msg=notfound_msg, enrich_fn=partial(_enrich_origin_visit, - with_origin_url=False, - with_origin_visit_url=True)) + with_origin_link=False, + with_origin_visit_link=True)) if results: nb_results = len(results) @@ -387,7 +451,7 @@ result['headers'] = { 'link-next': reverse('api-1-origin-visits', - url_args={'origin_id': origin_id}, + url_args=url_args_next, query_params=query_params) } @@ -398,12 +462,38 @@ return result +@api_route(r'/origin/(?P.*)/visit/(?P[0-9]+)/', + 'api-1-origin-visit') @api_route(r'/origin/(?P[0-9]+)/visit/(?P[0-9]+)/', 'api-1-origin-visit') @api_doc('/origin/visit/') -@format_docstring() -def api_origin_visit(request, origin_id, visit_id): +@format_docstring(return_origin_visit=DOC_RETURN_ORIGIN_VISIT) +def api_origin_visit(request, visit_id, origin_url=None, origin_id=None): """ + .. http:get:: /api/1/origin/(origin_url)/visit/(visit_id)/ + + Get information about a specific visit of a software origin. + + :param str origin_url: a software origin URL + :param int visit_id: a visit identifier + + {common_headers} + + {return_origin_visit} + + **Allowed HTTP Methods:** :http:method:`get`, :http:method:`head`, + :http:method:`options` + + :statuscode 200: no error + :statuscode 404: requested origin or visit can not be found in the + archive + + **Example:** + + .. parsed-literal:: + + :swh_web_api:`origin/https://github.com/hylang/hy/visit/1/` + .. http:get:: /api/1/origin/(origin_id)/visit/(visit_id)/ Get information about a specific visit of a software origin. @@ -413,16 +503,7 @@ {common_headers} - :>json string date: ISO representation of the visit date (in UTC) - :>json number origin: the origin unique identifier - :>json string origin_url: link to get information about the origin - :>jsonarr string snapshot: the snapshot identifier of the visit - :>jsonarr string snapshot_url: link to - :http:get:`/api/1/snapshot/(snapshot_id)/` in order to get - information about the snapshot of the visit - :>json string status: status of the visit (either **full**, - **partial** or **ongoing**) - :>json number visit: the unique identifier of the visit + {return_origin_visit} **Allowed HTTP Methods:** :http:method:`get`, :http:method:`head`, :http:method:`options` @@ -437,13 +518,15 @@ :swh_web_api:`origin/1500/visit/1/` """ + if not origin_url: + origin_url = service.lookup_origin({'id': int(origin_id)})['url'] return api_lookup( - service.lookup_origin_visit, int(origin_id), int(visit_id), + service.lookup_origin_visit, origin_url, int(visit_id), notfound_msg=('No visit {} for origin {} found' - .format(visit_id, origin_id)), + .format(visit_id, origin_url)), enrich_fn=partial(_enrich_origin_visit, - with_origin_url=True, - with_origin_visit_url=False)) + with_origin_link=True, + with_origin_visit_link=False)) @api_route(r'/origin/(?P[a-z]+)/url/(?P.+)' @@ -480,8 +563,7 @@ 'url': origin_url } - error_msg = 'Origin with type %s and URL %s not found' % ( - ori_dict['type'], ori_dict['url']) + error_msg = 'Origin with URL %s not found' % ori_dict['url'] return api_lookup( service.lookup_origin_intrinsic_metadata, ori_dict, diff --git a/swh/web/common/origin_visits.py b/swh/web/common/origin_visits.py --- a/swh/web/common/origin_visits.py +++ b/swh/web/common/origin_visits.py @@ -35,15 +35,20 @@ from swh.web.common import service - cache_entry_id = 'origin_%s_visits' % origin_info['id'] + if 'url' in origin_info: + origin_url = origin_info['url'] + else: + origin_url = service.lookup_origin(origin_info)['url'] + + cache_entry_id = 'origin_visits_%s' % origin_url cache_entry = cache.get(cache_entry_id) if cache_entry: last_visit = cache_entry[-1]['visit'] - new_visits = list(service.lookup_origin_visits(origin_info['id'], + new_visits = list(service.lookup_origin_visits(origin_url, last_visit=last_visit)) if not new_visits: - last_snp = service.lookup_latest_origin_snapshot(origin_info['id']) + last_snp = service.lookup_latest_origin_snapshot(origin_url) if not last_snp or last_snp['id'] == cache_entry[-1]['snapshot']: return cache_entry @@ -52,7 +57,7 @@ per_page = service.MAX_LIMIT last_visit = None while 1: - visits = list(service.lookup_origin_visits(origin_info['id'], + visits = list(service.lookup_origin_visits(origin_url, last_visit=last_visit, per_page=per_page)) origin_visits += visits @@ -105,10 +110,9 @@ visits = get_origin_visits(origin_info) if not visits: - if 'type' in origin_info and 'url' in origin_info: + if 'url' in origin_info: message = ('No visit associated to origin with' - ' type %s and url %s!' % (origin_info['type'], - origin_info['url'])) + ' url %s!' % origin_info['url']) else: message = ('No visit associated to origin with' ' id %s!' % origin_info['id']) @@ -119,9 +123,8 @@ if len(visit) == 0: if 'type' in origin_info and 'url' in origin_info: message = ('Visit for snapshot with id %s for origin with type' - ' %s and url %s not found!' % - (snapshot_id, origin_info['type'], - origin_info['url'])) + ' url %s not found!' % + (snapshot_id, origin_info['url'])) else: message = ('Visit for snapshot with id %s for origin with' ' id %s not found!' % @@ -133,9 +136,9 @@ visit = [v for v in visits if v['visit'] == int(visit_id)] if len(visit) == 0: if 'type' in origin_info and 'url' in origin_info: - message = ('Visit with id %s for origin with type %s' + message = ('Visit with id %s for origin with' ' and url %s not found!' % - (visit_id, origin_info['type'], origin_info['url'])) + (visit_id, origin_info['url'])) else: message = ('Visit with id %s for origin with id %s' ' not found!' % (visit_id, origin_info['id'])) @@ -168,9 +171,9 @@ return visit else: if 'type' in origin_info and 'url' in origin_info: - message = ('Visit with timestamp %s for origin with type %s ' + message = ('Visit with timestamp %s for origin with ' 'and url %s not found!' % - (visit_ts, origin_info['type'], origin_info['url'])) + (visit_ts, origin_info['url'])) else: message = ('Visit with timestamp %s for origin with id %s ' 'not found!' % (visit_ts, origin_info['id'])) diff --git a/swh/web/common/service.py b/swh/web/common/service.py --- a/swh/web/common/service.py +++ b/swh/web/common/service.py @@ -212,8 +212,7 @@ """Return information about the origin matching dict origin. Args: - origin: origin's dict with keys either 'id' or - ('type' AND 'url') + origin: origin's dict with keys either 'id' or 'url' Returns: origin information as dict. @@ -221,11 +220,8 @@ """ origin_info = storage.origin_get(origin) if not origin_info: - if 'id' in origin and origin['id']: - msg = 'Origin with id %s not found!' % origin['id'] - else: - msg = 'Origin with type %s and url %s not found!' % \ - (origin['type'], origin['url']) + msg = 'Origin %s not found!' % \ + (origin.get('id') or origin['url']) raise NotFoundExc(msg) return converters.from_origin(origin_info) @@ -814,11 +810,11 @@ return storage.stat_counters() -def _lookup_origin_visits(origin_id, last_visit=None, limit=10): +def _lookup_origin_visits(origin_url, last_visit=None, limit=10): """Yields the origin origin_ids' visits. Args: - origin_id (int): origin to list visits for + origin_url (str): origin to list visits for last_visit (int): last visit to lookup from limit (int): Number of elements max to display @@ -827,8 +823,10 @@ """ limit = min(limit, MAX_LIMIT) - yield from storage.origin_visit_get( - origin_id, last_visit=last_visit, limit=limit) + for visit in storage.origin_visit_get( + origin_url, last_visit=last_visit, limit=limit): + visit['origin'] = origin_url + yield visit def lookup_origin_visits(origin_id, last_visit=None, per_page=10): @@ -841,28 +839,28 @@ Dictionaries of origin_visit for that origin """ - lookup_origin({'id': origin_id}) visits = _lookup_origin_visits(origin_id, last_visit=last_visit, limit=per_page) for visit in visits: yield converters.from_origin_visit(visit) -def lookup_origin_visit(origin_id, visit_id): +def lookup_origin_visit(origin_url, visit_id): """Return information about visit visit_id with origin origin_id. Args: - origin_id: origin concerned by the visit + origin (str): origin concerned by the visit visit_id: the visit identifier to lookup Yields: The dict origin_visit concerned """ - visit = storage.origin_visit_get_by(origin_id, visit_id) + visit = storage.origin_visit_get_by(origin_url, visit_id) if not visit: - raise NotFoundExc('Origin with id %s or its visit ' - 'with id %s not found!' % (origin_id, visit_id)) + raise NotFoundExc('Origin %s or its visit ' + 'with id %s not found!' % (origin_url, visit_id)) + visit['origin'] = origin_url return converters.from_origin_visit(visit) diff --git a/swh/web/tests/api/views/test_origin.py b/swh/web/tests/api/views/test_origin.py --- a/swh/web/tests/api/views/test_origin.py +++ b/swh/web/tests/api/views/test_origin.py @@ -30,7 +30,8 @@ mock_get_origin_visits.side_effect = ValueError(err_msg) - url = reverse('api-1-origin-visits', url_args={'origin_id': 2}) + url = reverse( + 'api-1-origin-visits', url_args={'origin_url': 'http://foo'}) rv = self.client.get(url) self.assertEqual(rv.status_code, 400, rv.data) @@ -47,7 +48,8 @@ mock_get_origin_visits.side_effect = StorageDBError(err_msg) - url = reverse('api-1-origin-visits', url_args={'origin_id': 2}) + url = reverse( + 'api-1-origin-visits', url_args={'origin_url': 'http://foo'}) rv = self.client.get(url) self.assertEqual(rv.status_code, 503, rv.data) @@ -65,7 +67,8 @@ mock_get_origin_visits.side_effect = StorageAPIError(err_msg) - url = reverse('api-1-origin-visits', url_args={'origin_id': 2}) + url = reverse( + 'api-1-origin-visits', url_args={'origin_url': 'http://foo'}) rv = self.client.get(url) self.assertEqual(rv.status_code, 503, rv.data) @@ -94,7 +97,7 @@ (all_visits[1]['visit'], all_visits[2:4])): url = reverse('api-1-origin-visits', - url_args={'origin_id': origin_id}, + url_args={'origin_url': new_origin['url']}, query_params={'per_page': 2, 'last_visit': last_visit}) @@ -106,11 +109,53 @@ for expected_visit in expected_visits: origin_visit_url = reverse( 'api-1-origin-visit', - url_args={'origin_id': origin_id, + url_args={'origin_url': new_origin['url'], 'visit_id': expected_visit['visit']}) snapshot_url = reverse( 'api-1-snapshot', url_args={'snapshot_id': expected_visit['snapshot']}) + expected_visit['origin'] = new_origin['url'] + expected_visit['origin_visit_url'] = origin_visit_url + expected_visit['snapshot_url'] = snapshot_url + + self.assertEqual(rv.data, expected_visits) + + @given(new_origin(), visit_dates(3), new_snapshots(3)) + def test_api_lookup_origin_visits_by_id(self, new_origin, visit_dates, + new_snapshots): + + origin_id = self.storage.origin_add_one(new_origin) + new_origin['id'] = origin_id + for i, visit_date in enumerate(visit_dates): + origin_visit = self.storage.origin_visit_add(origin_id, visit_date) + self.storage.snapshot_add(origin_id, origin_visit['visit'], + new_snapshots[i]) + + all_visits = list(reversed(get_origin_visits(new_origin))) + + for last_visit, expected_visits in ( + (None, all_visits[:2]), + (all_visits[1]['visit'], all_visits[2:4])): + + url = reverse('api-1-origin-visits', + url_args={'origin_url': new_origin['url']}, + query_params={'per_page': 2, + 'last_visit': last_visit}) + + rv = self.client.get(url) + + self.assertEqual(rv.status_code, 200, rv.data) + self.assertEqual(rv['Content-Type'], 'application/json') + + for expected_visit in expected_visits: + origin_visit_url = reverse( + 'api-1-origin-visit', + url_args={'origin_url': new_origin['url'], + 'visit_id': expected_visit['visit']}) + snapshot_url = reverse( + 'api-1-snapshot', + url_args={'snapshot_id': expected_visit['snapshot']}) + expected_visit['origin'] = new_origin['url'] expected_visit['origin_visit_url'] = origin_visit_url expected_visit['snapshot_url'] = snapshot_url @@ -128,6 +173,39 @@ self.storage.snapshot_add(origin_id, origin_visit['visit'], new_snapshots[i]) url = reverse('api-1-origin-visit', + url_args={'origin_url': new_origin['url'], + 'visit_id': visit_id}) + + rv = self.client.get(url) + self.assertEqual(rv.status_code, 200, rv.data) + self.assertEqual(rv['Content-Type'], 'application/json') + + expected_visit = self.origin_visit_get_by(origin_id, visit_id) + + origin_url = reverse('api-1-origin', + url_args={'origin_url': new_origin['url']}) + snapshot_url = reverse( + 'api-1-snapshot', + url_args={'snapshot_id': expected_visit['snapshot']}) + + expected_visit['origin'] = new_origin['url'] + expected_visit['origin_url'] = origin_url + expected_visit['snapshot_url'] = snapshot_url + + self.assertEqual(rv.data, expected_visit) + + @given(new_origin(), visit_dates(3), new_snapshots(3)) + def test_api_lookup_origin_visit_by_id(self, new_origin, visit_dates, + new_snapshots): + + origin_id = self.storage.origin_add_one(new_origin) + new_origin['id'] = origin_id + for i, visit_date in enumerate(visit_dates): + origin_visit = self.storage.origin_visit_add(origin_id, visit_date) + visit_id = origin_visit['visit'] + self.storage.snapshot_add(origin_id, origin_visit['visit'], + new_snapshots[i]) + url = reverse('api-1-origin-visit', url_args={'origin_id': origin_id, 'visit_id': visit_id}) @@ -138,11 +216,12 @@ expected_visit = self.origin_visit_get_by(origin_id, visit_id) origin_url = reverse('api-1-origin', - url_args={'origin_id': origin_id}) + url_args={'origin_url': new_origin['url']}) snapshot_url = reverse( 'api-1-snapshot', url_args={'snapshot_id': expected_visit['snapshot']}) + expected_visit['origin'] = new_origin['url'] expected_visit['origin_url'] = origin_url expected_visit['snapshot_url'] = snapshot_url @@ -156,6 +235,27 @@ max_visit_id = max([v['visit'] for v in all_visits]) url = reverse('api-1-origin-visit', + url_args={'origin_url': origin['url'], + 'visit_id': max_visit_id + 1}) + + rv = self.client.get(url) + + self.assertEqual(rv.status_code, 404, rv.data) + self.assertEqual(rv['Content-Type'], 'application/json') + self.assertEqual(rv.data, { + 'exception': 'NotFoundExc', + 'reason': 'Origin %s or its visit with id %s not found!' % + (origin['url'], max_visit_id+1) + }) + + @given(origin()) + def test_api_lookup_origin_visit_not_found_by_id(self, origin): + + all_visits = list(reversed(get_origin_visits(origin))) + + max_visit_id = max([v['visit'] for v in all_visits]) + + url = reverse('api-1-origin-visit', url_args={'origin_id': origin['id'], 'visit_id': max_visit_id + 1}) @@ -165,8 +265,8 @@ self.assertEqual(rv['Content-Type'], 'application/json') self.assertEqual(rv.data, { 'exception': 'NotFoundExc', - 'reason': 'Origin with id %s or its visit with id %s not found!' % - (origin['id'], max_visit_id+1) + 'reason': 'Origin %s or its visit with id %s not found!' % + (origin['url'], max_visit_id+1) }) @given(origin()) @@ -179,7 +279,25 @@ expected_origin = self.origin_get(origin) origin_visits_url = reverse('api-1-origin-visits', - url_args={'origin_id': origin['id']}) + url_args={'origin_url': origin['url']}) + + expected_origin['origin_visits_url'] = origin_visits_url + + self.assertEqual(rv.status_code, 200, rv.data) + self.assertEqual(rv['Content-Type'], 'application/json') + self.assertEqual(rv.data, expected_origin) + + @given(origin()) + def test_api_origin_by_url(self, origin): + + url = reverse('api-1-origin', + url_args={'origin_url': origin['url']}) + rv = self.client.get(url) + + expected_origin = self.origin_get(origin) + + origin_visits_url = reverse('api-1-origin-visits', + url_args={'origin_url': origin['url']}) expected_origin['origin_visits_url'] = origin_visits_url @@ -198,7 +316,7 @@ expected_origin = self.origin_get(origin) origin_visits_url = reverse('api-1-origin-visits', - url_args={'origin_id': origin['id']}) + url_args={'origin_url': origin['url']}) expected_origin['origin_visits_url'] = origin_visits_url @@ -218,8 +336,7 @@ self.assertEqual(rv['Content-Type'], 'application/json') self.assertEqual(rv.data, { 'exception': 'NotFoundExc', - 'reason': 'Origin with type %s and url %s not found!' % - (new_origin['type'], new_origin['url']) + 'reason': 'Origin %s not found!' % new_origin['url'] }) @given(origin()) @@ -395,7 +512,7 @@ for expected_origin in expected_origins: expected_origin['origin_visits_url'] = reverse( 'api-1-origin-visits', - url_args={'origin_id': expected_origin['id']}) + url_args={'origin_url': expected_origin['url']}) self.assertEqual(rv.data, expected_origins) diff --git a/swh/web/tests/api/views/test_revision.py b/swh/web/tests/api/views/test_revision.py --- a/swh/web/tests/api/views/test_revision.py +++ b/swh/web/tests/api/views/test_revision.py @@ -109,7 +109,7 @@ self.assertEqual(rv['Content-Type'], 'application/json') self.assertEqual(rv.data, { 'exception': 'NotFoundExc', - 'reason': 'Origin with id %s not found!' % + 'reason': 'Origin %s not found!' % unknown_origin_id_}) @given(origin()) @@ -222,7 +222,7 @@ self.assertEqual(rv['Content-Type'], 'application/json') self.assertEqual(rv.data, { 'exception': 'NotFoundExc', - 'reason': 'Origin with id %s not found!' % + 'reason': 'Origin %s not found!' % unknown_origin_id_ }) diff --git a/swh/web/tests/common/test_origin_visits.py b/swh/web/tests/common/test_origin_visits.py --- a/swh/web/tests/common/test_origin_visits.py +++ b/swh/web/tests/common/test_origin_visits.py @@ -80,7 +80,6 @@ visit_id=visit_id) exception_text = cm.exception.args[0] self.assertIn('Visit with id %s' % visit_id, exception_text) - self.assertIn('type %s' % origin_info['type'], exception_text) self.assertIn('url %s' % origin_info['url'], exception_text) visit = get_origin_visit(origin_info, visit_id=2)