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 @@ -433,3 +433,47 @@ enrich_fn=partial(_enrich_origin_visit, with_origin_url=True, with_origin_visit_url=False)) + + +@api_route(r'/origin/(?P[a-z]+)/url/(?P.+)' + '/intrinsic-metadata', 'api-origin-intrinsic-metadata-get') +@api_doc('/origin/') +def api_origin_intrinsic_metadata_get(request, origin_type, origin_url): + """ + .. http:get:: /api/1/origin/(origin_type)/url/(origin_url)/intrinsic-metadata + + Get intrinsic metadata of a software origin. + + :param string origin_type: the origin type (possible values are ``git``, ``svn``, + ``hg``, ``deb``, ``pypi``, ``ftp`` or ``deposit``) + :param string origin_url: the origin url + + :>jsonarr dict metadata: instrinsic metadata of the origin (as a JSON-LD/CodeMeta dictionary) + + :reqheader Accept: the requested response content type, + either ``application/json`` (default) or ``application/yaml`` + :resheader Content-Type: this depends on :http:header:`Accept` header of request + + **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//intrinsic-metadata` + """ # noqa + ori_dict = { + 'type': origin_type, + 'url': origin_url + } + + error_msg = 'Origin with type %s and URL %s not found' % ( + ori_dict['type'], ori_dict['url']) + + return api_lookup( + service.lookup_origin_intrinsic_metadata, ori_dict, + notfound_msg=error_msg, + enrich_fn=_enrich_origin) 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 @@ -289,6 +289,28 @@ return results +def lookup_origin_intrinsic_metadata(origin_dict): + """Return intrinsic metadata for origin whose origin_id matches given + origin_id. + + Args: + origin_dict: origin's dict with keys ('type' AND 'url') + + Returns: + origin metadata. + + """ + origin_info = storage.origin_get(origin_dict) + if not origin_info: + msg = 'Origin with type %s and url %s not found!' % \ + (origin_dict['type'], origin_dict['url']) + raise NotFoundExc(msg) + + origin_ids = [origin_info['id']] + match = idx_storage.origin_intrinsic_metadata_get(origin_ids)[0] + return match['metadata'] + + def lookup_person(person_id): """Return information about the person with id person_id. 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 @@ -326,6 +326,39 @@ mock_idx_storage.origin_intrinsic_metadata_search_fulltext \ .assert_called_with(conjunction=['Jane Doe'], limit=100) + @given(origin()) + def test_api_origin_intrinsic_metadata_get(self, origin): + with patch('swh.web.common.service.idx_storage') as mock_idx_storage: + mock_idx_storage.origin_intrinsic_metadata_get \ + .side_effect = lambda origin_ids: [{ + 'from_revision': ( + b'p&\xb7\xc1\xa2\xafVR\x1e\x95\x1c\x01\xed ' + b'\xf2U\xfa\x05B8'), + 'metadata': {'author': 'Jane Doe'}, + 'id': origin['id'], + 'tool': { + 'configuration': { + 'context': ['NpmMapping', 'CodemetaMapping'], + 'type': 'local' + }, + 'id': 3, + 'name': 'swh-metadata-detector', + 'version': '0.0.1' + } + }] + + url = reverse('api-origin-intrinsic-metadata-get', + url_args={'origin_type': origin['type'], + 'origin_url': origin['url']}) + rv = self.client.get(url) + + mock_idx_storage.origin_intrinsic_metadata_get \ + .assert_called_once_with([origin['id']]) + self.assertEqual(rv.status_code, 200, rv.content) + self.assertEqual(rv['Content-Type'], 'application/json') + expected_data = {'author': 'Jane Doe'} + self.assertEqual(rv.data, expected_data) + @patch('swh.web.common.service.idx_storage') def test_api_origin_metadata_search_invalid(self, mock_idx_storage):