Skip to content

Commit 08a0755

Browse files
committed
Merge branch 'mr/cardao/add-e3.npm-and-e3.maven' into 'master'
Add e3.npm and e3.maven modules See merge request it/e3-core!70
2 parents 839f57a + 8e1a855 commit 08a0755

File tree

2 files changed

+190
-0
lines changed

2 files changed

+190
-0
lines changed

src/e3/maven.py

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
from __future__ import annotations
2+
3+
import json
4+
import requests
5+
6+
from e3.log import getLogger
7+
8+
logger = getLogger("e3.maven")
9+
10+
11+
class MavenLink:
12+
def __init__(self, group: str, name: str, version: str) -> None:
13+
"""Maven download link metadata.
14+
15+
Note:
16+
-----
17+
Checksum is not handled here because maven provides file checksum directly
18+
in the HTTP request header using: x-checksum-md5 or x-checksum-sha1
19+
The request URL is not provided, because the code will compute it depending on
20+
the package group/name/version.
21+
22+
:param group: The package group
23+
:param name: The package name
24+
:param version: The package version
25+
"""
26+
filename = f"{name}-{version}.jar"
27+
self.filename = filename
28+
self.package_group = group
29+
self.package_name = name
30+
self.version = version
31+
self.url = f"https://repo1.maven.org/maven2/{group}/{name}/{version}/{filename}"
32+
33+
34+
class MavenLinksParser:
35+
def __init__(self) -> None:
36+
"""Create the MavenLinksParser."""
37+
self.links: list[MavenLink] = []
38+
39+
def _raise_missing_key(self, key: str) -> None:
40+
raise KeyError(
41+
f"Maven links parser failed: key {key!r} is missing from the HTTP answer"
42+
)
43+
44+
def feed(self, data: str) -> MavenLinksParser:
45+
"""See HTMLParser.feed."""
46+
docs = json.loads(data).get("response", {}).get("docs")
47+
if not docs:
48+
self._raise_missing_key("response:docs")
49+
50+
for pkgmeta in docs:
51+
for k in ("g", "a", "v"):
52+
if k not in pkgmeta:
53+
self._raise_missing_key(f"response:docs:{k}")
54+
55+
group = pkgmeta["g"]
56+
name = pkgmeta["a"]
57+
version = pkgmeta["v"]
58+
self.links.append(MavenLink(group, name, version))
59+
return self
60+
61+
62+
class Maven:
63+
def __init__(self, url: str = "https://search.maven.org/solrsearch/select") -> None:
64+
"""Initialize Maven manager class.
65+
66+
:param url: The package search URL
67+
"""
68+
self.url = url
69+
self.cache: dict[str, list[MavenLink]] = {}
70+
71+
def fetch_project_links(
72+
self,
73+
group: str,
74+
name: str,
75+
*,
76+
headers: dict[str, str | bytes | None] | None = None,
77+
) -> list[MavenLink]:
78+
"""Fetch list of resources for a given Maven package.
79+
80+
:param name: Maven package name
81+
:param headers: To add additionnal headers to the HTTP request. Can be mandatory
82+
depending on the situation.
83+
:return: a list of dict containing the link to each resource along with
84+
some metadata
85+
"""
86+
if name not in self.cache:
87+
logger.debug(f"fetch {name} links from {self.url}")
88+
request = requests.get(
89+
f"{self.url}?q="
90+
f"g:%22{group}%22+AND+a:%22{name}%22&core=gav&rows=20&wt=json",
91+
headers=headers,
92+
)
93+
request.raise_for_status()
94+
95+
# Update cache
96+
self.cache[name] = MavenLinksParser().feed(request.text).links
97+
return self.cache[name]

src/e3/npm.py

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
from __future__ import annotations
2+
3+
import json
4+
import requests
5+
6+
from e3.log import getLogger
7+
8+
logger = getLogger("e3.npm")
9+
10+
11+
class NPMLink:
12+
def __init__(self, name: str, version: str, url: str, checksum: str) -> None:
13+
"""NPM download link metadata.
14+
15+
:param name: The package name
16+
:param version: The package version
17+
:param url: The download url
18+
:param checksum: The sha1 checksum of the package
19+
"""
20+
self.filename = f"{name}-{version}.tgz"
21+
self.package_name = name
22+
self.version = version
23+
self.url = url
24+
self.checksum = checksum
25+
26+
27+
class NPMLinksParser:
28+
def __init__(self) -> None:
29+
"""Create the NPMLinksParser."""
30+
self.links: list[NPMLink] = []
31+
32+
def _raise_missing_key(self, key: str) -> None:
33+
raise KeyError(
34+
f"NPM links parser failed: key {key!r} is missing from the HTTP answer"
35+
)
36+
37+
def feed(self, data: str) -> NPMLinksParser:
38+
"""Feed this parser with retrieved JSON data.
39+
40+
.. seealso: :meth:`html.parser.HTMLParser.feed`
41+
"""
42+
versions = json.loads(data).get("versions")
43+
if not versions:
44+
self._raise_missing_key("versions")
45+
46+
for version, val in versions.items():
47+
for k in ("name", "dist"):
48+
if k not in val:
49+
self._raise_missing_key(f"versions:{version}:{k}")
50+
51+
dist = val["dist"]
52+
for k in ("tarball", "shasum"):
53+
if k not in dist:
54+
self._raise_missing_key(f"versions:{version}:dist:{k}")
55+
56+
self.links.append(
57+
NPMLink(
58+
val["name"],
59+
version,
60+
dist["tarball"],
61+
dist["shasum"],
62+
)
63+
)
64+
return self
65+
66+
67+
class NPM:
68+
def __init__(self, url: str = "https://registry.npmjs.org/") -> None:
69+
"""Initialize NPM manager class.
70+
71+
:param url: The package search URL
72+
"""
73+
self.url = url if url.endswith("/") else f"{url}/"
74+
self.cache: dict[str, list[NPMLink]] = {}
75+
76+
def fetch_project_links(
77+
self, name: str, *, headers: dict[str, str | bytes | None] | None = None
78+
) -> list[NPMLink]:
79+
"""Fetch list of resources for a given NPM package.
80+
81+
:param name: NPM package name
82+
:param headers: To add additionnal headers to the HTTP request. Can be mandatory
83+
depending on the situation.
84+
:return: a list of dict containing the link to each resource along with
85+
some metadata
86+
"""
87+
if name not in self.cache:
88+
logger.debug(f"fetch {name} links from {self.url}")
89+
request = requests.get(f"{self.url}{name}", headers=headers)
90+
request.raise_for_status()
91+
# Update cache
92+
self.cache[name] = NPMLinksParser().feed(request.text).links
93+
return self.cache[name]

0 commit comments

Comments
 (0)