Add support for relative URLs in simple metadata responses (#721)

## Summary

This PR adds support for relative URLs in the simple JSON responses. We
already support relative URLs for HTML responses, but the handling has
been consolidated between the two. Similar to index URLs, we now store
the base alongside the metadata, and use the base when resolving the
URL.

Closes #455.

## Test Plan

`cargo test` (to test HTML indexes). Separately, I also ran `cargo run
-p puffin-cli -- pip-compile requirements.in -n
--index-url=http://localhost:3141/packages/pypi/+simple` on the
`zb/relative` branch with `packse` running, and forced both HTML and
JSON by limiting the `accept` header.
This commit is contained in:
Charlie Marsh 2023-12-27 09:53:21 -04:00 committed by GitHub
parent ae83a74309
commit 007f52bb4e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 285 additions and 134 deletions

View file

@ -0,0 +1,34 @@
use serde::{Deserialize, Serialize};
use url::Url;
#[derive(Debug, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)]
pub struct BaseUrl(Url);
impl BaseUrl {
/// Parse the given URL. If it's relative, join it to the current [`BaseUrl`]. Allows for
/// parsing URLs that may be absolute or relative, with a known base URL.
pub fn join_relative(&self, url: &str) -> Result<Url, url::ParseError> {
match Url::parse(url) {
Ok(url) => Ok(url),
Err(err) => {
if err == url::ParseError::RelativeUrlWithoutBase {
self.0.join(url)
} else {
Err(err)
}
}
}
}
}
impl From<Url> for BaseUrl {
fn from(url: Url) -> Self {
Self(url)
}
}
impl std::fmt::Display for BaseUrl {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}

View file

@ -1,8 +1,10 @@
pub use base_url::*;
pub use direct_url::*;
pub use lenient_requirement::*;
pub use metadata::*;
pub use simple_json::*;
mod base_url;
mod direct_url;
mod lenient_requirement;
mod metadata;

View file

@ -1,8 +1,10 @@
use chrono::{DateTime, Utc};
use pep440_rs::VersionSpecifiers;
use serde::{de, Deserialize, Deserializer, Serialize};
use std::str::FromStr;
use chrono::{DateTime, Utc};
use serde::{de, Deserialize, Deserializer, Serialize};
use pep440_rs::VersionSpecifiers;
use crate::lenient_requirement::LenientVersionSpecifiers;
#[derive(Debug, Clone, Deserialize)]
@ -10,13 +12,13 @@ pub struct SimpleJson {
pub files: Vec<File>,
}
/// A single (remote) file belonging to a package, generally either a wheel or a source dist.
/// A single (remote) file belonging to a package, either a wheel or a source distribution.
///
/// <https://peps.python.org/pep-0691/#project-detail>
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct File {
// Not PEP 691 compliant alias used by pypi
// Non-PEP 691-compliant alias used by PyPI.
#[serde(alias = "data_dist_info_metadata")]
pub dist_info_metadata: Option<DistInfoMetadata>,
pub filename: String,