diff --git a/app.py b/app.py index c22d80a..9958f73 100644 --- a/app.py +++ b/app.py @@ -3,8 +3,7 @@ from fastapi import FastAPI from starlette.responses import Response -from npm_deps.models import NPMPackageVersion -from npm_deps.package_version import get_package_version +from npm_deps.package import get_package app = FastAPI(title="NpmDepsService", version="1.0") @@ -15,5 +14,5 @@ async def health() -> Response: @app.get("/package/{name}/{version}", tags=["package"]) -async def get_package(name: str, version: str) -> NPMPackageVersion: - return await get_package_version(name, version) +async def get_package_version(name: str, version: str): # type: ignore[no-untyped-def] + return await get_package(name, version) diff --git a/npm_deps/models.py b/npm_deps/models.py index b470d23..a3f2554 100644 --- a/npm_deps/models.py +++ b/npm_deps/models.py @@ -1,4 +1,10 @@ -from pydantic import BaseModel +from pydantic.v1 import BaseModel + + +class VersionedPackage(BaseModel): + name: str + version: str + dependencies: list["VersionedPackage"] | None = None class NPMPackageVersion(BaseModel): @@ -10,3 +16,6 @@ class NPMPackageVersion(BaseModel): class NPMPackage(BaseModel): name: str versions: dict[str, NPMPackageVersion] + + +VersionedPackage.update_forward_refs() diff --git a/npm_deps/package.py b/npm_deps/package.py new file mode 100644 index 0000000..2e1b6d2 --- /dev/null +++ b/npm_deps/package.py @@ -0,0 +1,37 @@ +import requests +from nodesemver import min_satisfying + +from npm_deps.models import VersionedPackage +from npm_deps.package_request import request_package + + +async def get_package(name: str, range: str | None = "*") -> VersionedPackage: + package_json = requests.get(f"https://registry.npmjs.org/{name}").json() + versions = list(package_json["versions"].keys()) + version = min_satisfying(versions, range) + version_record = package_json["versions"][version] + + package = VersionedPackage( + name=package_json["name"], + version=version_record["version"], + ) + dependencies = version_record.get("dependencies", {}) + package.dependencies = [ + await get_package(name, version) for name, version in dependencies.items() + ] + return package + + +async def get_package_version(name: str, range: str) -> VersionedPackage: + package_json = await request_package(name) + versions = list(package_json["versions"].keys()) + version = min_satisfying(versions, range) + deps_list = [] + for d in package_json["dependencies"]: + package = VersionedPackage(name=d, version=package_json["dependencies"][d]) + deps_list.append(package) + return VersionedPackage( + name=name, + version=version, + dependencies=deps_list, + ) diff --git a/tests/e2e/test_app.py b/tests/e2e/test_app.py index 1a3b286..319a2f4 100644 --- a/tests/e2e/test_app.py +++ b/tests/e2e/test_app.py @@ -11,13 +11,22 @@ def test_healthcheck(): assert response.status_code == HTTPStatus.NO_CONTENT -def test_get_package_by_name_and_version(): +def test_get_package(): client = TestClient(app) - response = client.get("/package/react/16.3.0") + response = client.get("/package/minimatch/3.1.2") assert response.status_code == HTTPStatus.OK - assert response.json().get("name") == "react" - assert response.json().get("version") == "16.3.0" - assert response.json().get("dependencies") is not None + assert response.json().get("name") == "minimatch" + assert response.json().get("version") == "3.1.2" + assert response.json().get("dependencies") == [ + { + "name": "brace-expansion", + "version": "1.1.11", + "dependencies": [ + {"name": "balanced-match", "version": "1.0.2", "dependencies": []}, + {"name": "concat-map", "version": "0.0.1", "dependencies": []}, + ], + } + ] def test_get_package_by_name():