Skip to content

Commit

Permalink
[node-js-release-info] add .fetch_all() method to retrieve the list o…
Browse files Browse the repository at this point in the history
…f supported configurations
  • Loading branch information
busticated committed Oct 21, 2023
1 parent 0cfb362 commit b8dd23e
Show file tree
Hide file tree
Showing 2 changed files with 441 additions and 89 deletions.
192 changes: 103 additions & 89 deletions crates/node-js-release-info/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ mod os;
mod arch;
mod error;
mod ext;
mod specs;
mod url;

use std::string::ToString;
use semver::Version;
#[cfg(feature = "json")]
use serde::{Serialize, Deserialize};
pub use crate::os::NodeJSOS;
Expand Down Expand Up @@ -248,7 +248,8 @@ impl NodeJSRelInfo {
self.clone()
}

/// Fetches Node.js metadata from the [releases download server](https://nodejs.org/download/release/)
/// Fetches Node.js metadata for specified configuration from the
/// [releases download server](https://nodejs.org/download/release/)
///
/// # Examples
///
Expand All @@ -266,29 +267,10 @@ impl NodeJSRelInfo {
/// }
/// ```
pub async fn fetch(&mut self) -> Result<Self, NodeJSRelInfoError> {
self.version = match Version::parse(self.version.as_str()) {
Err(_) => return Err(NodeJSRelInfoError::InvalidVersion(self.version.clone())),
Ok(v) => v.to_string(),
};

let info_url = self.url_fmt.info(&self.version);
let res = match reqwest::get(info_url.as_str()).await {
Err(e) => return Err(NodeJSRelInfoError::HttpError(e)),
Ok(r) => r,
};

// TODO (busticated): handle 5xx errors
if res.status().as_u16() >= 400 {
return Err(NodeJSRelInfoError::UnrecognizedVersion(self.version.clone()));
}

let body = match res.text().await {
Err(e) => return Err(NodeJSRelInfoError::HttpError(e)),
Ok(b) => b,
};

let version = specs::validate_version(self.version.as_str())?;
let specs = specs::fetch(&version, &self.url_fmt).await?;
let filename = self.filename();
let info = body.lines().find(|&line| {
let info = specs.lines().find(|&line| {
line.contains(filename.as_str())
});

Expand All @@ -303,6 +285,56 @@ impl NodeJSRelInfo {
Ok(self.to_owned())
}

/// Fetches Node.js metadata for all supported configurations from the
/// [releases download server](https://nodejs.org/download/release/)
///
/// # Examples
///
/// ```rust
/// use node_js_release_info::{NodeJSRelInfo, NodeJSRelInfoError};
///
/// #[tokio::main]
/// async fn main() -> Result<(), NodeJSRelInfoError> {
/// let info = NodeJSRelInfo::new("20.6.1");
/// let all = info.fetch_all().await?;
/// assert_eq!(all.len(), 16);
/// assert_eq!(all[1].version, "20.6.1");
/// assert_eq!(all[1].filename, "node-v20.6.1-darwin-arm64.tar.gz");
/// assert_eq!(all[1].sha256, "d8ba8018d45b294429b1a7646ccbeaeb2af3cdf45b5c91dabbd93e2a2035cb46");
/// assert_eq!(all[1].url, "https://nodejs.org/download/release/v20.6.1/node-v20.6.1-darwin-arm64.tar.gz");
/// Ok(())
/// }
/// ```
pub async fn fetch_all(&self) -> Result<Vec<NodeJSRelInfo>, NodeJSRelInfoError> {
let version = specs::validate_version(self.version.as_str())?;
let specs = specs::fetch(&version, &self.url_fmt).await?;
let specs = match specs::parse(&version, specs) {
Some(s) => s,
None => {
return Err(NodeJSRelInfoError::UnrecognizedVersion(version.clone()));
}
};

let mut all: Vec<NodeJSRelInfo> = vec![];
for (os, arch, ext, sha256, filename) in specs.into_iter() {
let version = version.clone();
let mut info = NodeJSRelInfo {
os,
arch,
version,
ext,
filename,
sha256,
..Default::default()
};

info.url = info.url_fmt.pkg(&info.version, &info.filename);
all.push(info);
}

Ok(all)
}

fn filename(&self) -> String {
let arch = self.arch.to_string();
let ext = self.ext.to_string();
Expand All @@ -319,7 +351,7 @@ impl NodeJSRelInfo {

#[cfg(test)]
mod tests {
use mockito::{Server, Mock};
use mockito::Server;
use super::*;

fn is_thread_safe<T: Sized + Send + Sync + Unpin>() {}
Expand Down Expand Up @@ -495,11 +527,10 @@ mod tests {
#[tokio::test]
#[should_panic(expected = "called `Result::unwrap()` on an `Err` value: UnrecognizedVersion(\"1.0.0\")")]
async fn it_fails_to_fetch_info_when_version_is_unrecognized() {
let version = "1.0.0";
let mut info = NodeJSRelInfo::new(version);
let mut info = NodeJSRelInfo::new("1.0.0");
let mut server = Server::new_async().await;
let mock = setup_server_mock(version, &mut info, &mut server)
.with_body(get_fake_info())
let mock = specs::setup_server_mock(&info.version, &mut info.url_fmt, &mut server)
.with_body(specs::get_fake_specs())
.with_status(404)
.create_async()
.await;
Expand All @@ -511,11 +542,10 @@ mod tests {
#[tokio::test]
#[should_panic(expected = "called `Result::unwrap()` on an `Err` value: UnrecognizedConfiguration(\"node-v20.6.1-linux-x64.zip\")")]
async fn it_fails_to_fetch_info_when_configuration_is_unrecognized() {
let version = "20.6.1";
let mut server = Server::new_async().await;
let mut info = NodeJSRelInfo::new(version).linux().zip().to_owned();
let mock = setup_server_mock(version, &mut info, &mut server)
.with_body(get_fake_info())
let mut info = NodeJSRelInfo::new("20.6.1").linux().zip().to_owned();
let mock = specs::setup_server_mock(&info.version, &mut info.url_fmt, &mut server)
.with_body(specs::get_fake_specs())
.create_async()
.await;

Expand All @@ -525,11 +555,10 @@ mod tests {

#[tokio::test]
async fn it_fetches_node_js_release_info() {
let version = "20.6.1";
let mut info = NodeJSRelInfo::new(version);
let mut info = NodeJSRelInfo::new("20.6.1");
let mut server = Server::new_async().await;
let mock = setup_server_mock(version, &mut info, &mut server)
.with_body(get_fake_info())
let mock = specs::setup_server_mock(&info.version, &mut info.url_fmt, &mut server)
.with_body(specs::get_fake_specs())
.create_async()
.await;

Expand All @@ -543,11 +572,10 @@ mod tests {

#[tokio::test]
async fn it_fetches_node_js_release_info_when_ext_is_msi() {
let version = "20.6.1";
let mut info = NodeJSRelInfo::new(version).arm64().msi().to_owned();
let mut info = NodeJSRelInfo::new("20.6.1").arm64().msi().to_owned();
let mut server = Server::new_async().await;
let mock = setup_server_mock(version, &mut info, &mut server)
.with_body(get_fake_info())
let mock = specs::setup_server_mock(&info.version, &mut info.url_fmt, &mut server)
.with_body(specs::get_fake_specs())
.create_async()
.await;

Expand All @@ -559,53 +587,39 @@ mod tests {
assert_eq!(info.sha256, "9471bd6dc491e09c31b0f831f5953284b8a6842ed4ccb98f5c62d13e6086c471");
}

fn setup_server_mock(version: &str, info: &mut NodeJSRelInfo, server: &mut Server) -> Mock {
info.url_fmt.host = server.host_with_port();
info.url_fmt.protocol = "http:".to_string();
server.mock("GET", info.url_fmt.info_pathname(version).as_str())
}

fn get_fake_info() -> &'static str {
"ea52b4feaf917e08cd2c729c1186585fcacef07c261a01310c91333b9e41d93c node-v20.6.1-aix-ppc64.tar.gz
9471bd6dc491e09c31b0f831f5953284b8a6842ed4ccb98f5c62d13e6086c471 node-v20.6.1-arm64.msi
d8ba8018d45b294429b1a7646ccbeaeb2af3cdf45b5c91dabbd93e2a2035cb46 node-v20.6.1-darwin-arm64.tar.gz
9c61b0d60fce962244d5e54549dc912e28b3c5f5e23149bfd15f66f8f7269129 node-v20.6.1-darwin-arm64.tar.xz
365ec544c6596f194afff9a613554abfc68d4a2274181b7651386d9a11cf5862 node-v20.6.1-darwin-x64.tar.gz
9b10c16670781e3a5af722656d28f264cdd8ebb3140f62692b33813100391349 node-v20.6.1-darwin-x64.tar.xz
d8271461ced2887f65af413949caee19db3e80d22bbefdaf01252ca998570052 node-v20.6.1-headers.tar.gz
60963e3ee60b6739e97e0c7b8ffb25848a82649c0c277af728400c570fd9db6d node-v20.6.1-headers.tar.xz
d38fe2e41e3fe8ae81b517b4cf49521f500e181e54f4c3d05e2b2d691a57b2ca node-v20.6.1-linux-arm64.tar.gz
6823720796b287465bb4aa8e7611143322ffd6cbdb9c6e3b149576f6d87953bf node-v20.6.1-linux-arm64.tar.xz
459510281ea51cf5d89fc666e36fbba80793ae4b90c3a7f89dd6666c65c825b3 node-v20.6.1-linux-armv7l.tar.gz
9dbd4fd7f804a28de91ffb8792df6e89bbb4f934fccd013624b3dabf8bf809ac node-v20.6.1-linux-armv7l.tar.xz
ca00f1aa8b2535fa167258cf5f2cfce4b79d83c442dd5e46f5e17d6a5749ec0f node-v20.6.1-linux-ppc64le.tar.gz
27884935b025b6676e4b8737f334673ee825947d0baef61aa0326374597aeb05 node-v20.6.1-linux-ppc64le.tar.xz
4a3f29cfc8a7ed1e9e44fcacb78e2fbaa3ce01be1efc4971a42710ad1e9e45d1 node-v20.6.1-linux-s390x.tar.gz
3968d629989b6de16b8872b6d7ee6e6cdf1204def99c43412a6ee28203ed0022 node-v20.6.1-linux-s390x.tar.xz
26dd13a6f7253f0ab9bcab561353985a297d927840771d905566735b792868da node-v20.6.1-linux-x64.tar.gz
591f9f274104f266a8cf085d2c7d5d2848ba73b98ae323d501db2d4c4b7026e5 node-v20.6.1-linux-x64.tar.xz
d9acf82d9576dd0350c8e66b55f6fc2750fa9f4aa23d6453ffc58e32af995894 node-v20.6.1.pkg
0053c09a01b1b355bca5af82927cae376124c13d74fa53567f08f4cfb085e6aa node-v20.6.1.tar.gz
3aec5e728daa38800c343b129221d3488064a2529a39bb5467bc55be226c6a2b node-v20.6.1.tar.xz
337549faf397deb0da3bccd4e27db45a619d89de4ea12830d16d9dfaded8e92c node-v20.6.1-win-arm64.7z
0e62045bfc9d7c38360bd7da152c75ed82087242d5e4b401fa23a439588d36f6 node-v20.6.1-win-arm64.zip
c6cfe7824770a266a30bee8c33f485d0e89b94254c682250a239d83adfb7ce77 node-v20.6.1-win-x64.7z
88371914f1f75d594bb367570e163cf5ecebeb514fd54cc765093819ebb0ed48 node-v20.6.1-win-x64.zip
87d631b294a25386400d0f44d227330da62a1326e2a4fbb98bda3d7c431257f1 node-v20.6.1-win-x86.7z
578cff623601aa8878a035f06edbf69190338ee3b345e7a096e804cb80c4ce24 node-v20.6.1-win-x86.zip
5c2616da46728dd1326645c7db114e78ad87138a258c0724a035269258c23509 node-v20.6.1-x64.msi
cb83586af83182187e760b7e01aa7c7b2bacb521d60ceefed3ac6fc62c222449 node-v20.6.1-x86.msi
7cc3240fd7ce7926eef1cbbad33b033f7c5d97b3f3e527d65ff1e2c3f7638a11 win-arm64/node.exe
deb027ded744371657811cfe52e774881ea928d36779924af84aa9a7a31104d2 win-arm64/node.lib
dcb6b4bc6f2a78bf0f759853b59e94ddbe9ad6b9f32d24fdcf590d74c6350bc2 win-arm64/node_pdb.7z
bdcd574e99646ec4a03bb13b3661c957f5a7ca837f5c33827075c4262d449689 win-arm64/node_pdb.zip
5b824f3a375cca06dfd7dc70fa341a6ef8bb0b2e912358d8602a0c7ad273b9a4 win-x64/node.exe
d275cfc4d637d2feaf4c39e1a5f5cd84f5b474fa713c15013e940c329feed13b win-x64/node.lib
fea6c0fcff45739a6e5af9843ec45455c97ff8677167bd649fd48cbef59ca52d win-x64/node_pdb.7z
bc13f5e63c1510cd41f82dc20725f40bbfa378252e09a00a8531cddabbf1b106 win-x64/node_pdb.zip
837db0d8fb7fa194ebe23cd34ac7bedc02d1132de67cf4f147d694574be5cc4e win-x86/node.exe
a0738dec64427ae73eeb1d036081652c1c0223a679a63e0459c2af667f284f58 win-x86/node.lib
516ac820f05eb8478be541ac12386c3b5b5c07624f73934bcf0b11a3fcdb1c95 win-x86/node_pdb.7z
9b68f3e1f1717a2f6a090e1679f8cc627566ed064c657c35eddd0dba9484e310 win-x86/node_pdb.zip"
#[tokio::test]
async fn it_fetches_all_supported_node_js_configurations() {
let mut info = NodeJSRelInfo::new("20.6.1");
let mut server = Server::new_async().await;
let mock = specs::setup_server_mock(&info.version, &mut info.url_fmt, &mut server)
.with_body(specs::get_fake_specs())
.create_async()
.await;

let all = info.fetch_all().await.unwrap();
mock.assert_async().await;

assert_eq!(all.len(), 16);
assert_eq!(all[1].version, "20.6.1");
assert_eq!(all[1].os, NodeJSOS::Darwin);
assert_eq!(all[1].arch, NodeJSArch::ARM64);
assert_eq!(all[1].ext, NodeJSPkgExt::Targz);
assert_eq!(all[1].filename, "node-v20.6.1-darwin-arm64.tar.gz");
assert_eq!(all[1].sha256, "d8ba8018d45b294429b1a7646ccbeaeb2af3cdf45b5c91dabbd93e2a2035cb46");
assert_eq!(all[1].url, "https://nodejs.org/download/release/v20.6.1/node-v20.6.1-darwin-arm64.tar.gz");
}

#[tokio::test]
#[should_panic(expected = "called `Result::unwrap()` on an `Err` value: UnrecognizedVersion(\"1.0.0\")")]
async fn it_fails_to_fetch_all_supported_node_js_configurations_when_version_is_unrecognized() {
let mut info = NodeJSRelInfo::new("1.0.0");
let mut server = Server::new_async().await;
let mock = specs::setup_server_mock(&info.version, &mut info.url_fmt, &mut server)
.with_body(String::from(""))
.create_async()
.await;

info.fetch_all().await.unwrap();
mock.assert_async().await;
}
}
Loading

0 comments on commit b8dd23e

Please sign in to comment.