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 @@ -432,3 +432,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-metdata-get') +@api_doc('/origin/') +def api_origin_intrinsic_metadata_get(request, origin_type, origin_url): + """ + .. http:get:: /api/1/origin/(origin_id)/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//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_intrinsicmetadata, 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): + """Return intrinsic metadata for origin whose origin_id matches given + origin_id. + + Args: + origin: origin's dict with keys ('type' AND 'url') + + Returns: + origin metadata. + + """ + origin_info = storage.origin_get(origin) + if not origin_info: + msg = 'Origin with type %s and url %s not found!' % \ + (origin['type'], origin['url']) + raise NotFoundExc(msg) + + origin_ids = [origin_info['origin_id']] + match = converters.idx_storage.origin_metadata_get(origin_ids)[0] + return {'metadata': 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,46 @@ 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'), + patch('swh.web.common.service.storage') + ) as (mock_idx_storage, mock_storage): + mock_idx_storage.origin_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' + } + }] + + mock_storage.origin_get.side_effect = lambda origin: { + 'origin_id': origin['id'] + } + + url = reverse('api-origin-intrinsic-metdata-get', + url_args={'origin_type': origin['type'], + 'origin_url': origin['url']}) + rv = self.client.get(url) + + self.assertEqual(rv.status_code, 200, rv.content) + self.assertEqual(rv['Content-Type'], 'application/json') + expected_data = { + 'metadata': {'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):