Skip to content
This repository has been archived by the owner on Sep 21, 2024. It is now read-only.

feat: add Content-Length headers when writing to a sphere. #582

Merged
merged 2 commits into from
Jan 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion rust/noosphere-core/src/context/content/write.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,18 @@ where
let body_cid =
BodyChunkIpld::store_bytes(&bytes, self.sphere_context_mut().await?.db_mut()).await?;

self.link(slug, content_type, &body_cid, additional_headers)
let header = (
Header::ContentLength.to_string(),
format!("{}", bytes.len()),
);
let additional_headers = if let Some(mut headers) = additional_headers {
headers.push(header);
headers
} else {
vec![header]
};

self.link(slug, content_type, &body_cid, Some(additional_headers))
.await
}

Expand Down
66 changes: 63 additions & 3 deletions rust/noosphere-core/src/context/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -256,22 +256,82 @@ mod tests {
use crate::{
authority::{generate_capability, generate_ed25519_key, Access, SphereAbility},
context::{
HasMutableSphereContext, HasSphereContext, SphereContentWrite, SpherePetnameWrite,
AsyncFileBody, HasMutableSphereContext, HasSphereContext, SphereContentWrite,
SpherePetnameWrite,
},
data::{ContentType, LinkRecord, LINK_RECORD_FACT_NAME},
data::{ContentType, Header, LinkRecord, LINK_RECORD_FACT_NAME},
helpers::{make_valid_link_record, simulated_sphere_context},
tracing::initialize_tracing,
view::Sphere,
};

use noosphere_storage::{MemoryStorage, SphereDb};
use noosphere_storage::{MemoryStorage, SphereDb, Storage};
use serde_json::json;
use ucan::{builder::UcanBuilder, crypto::KeyMaterial, store::UcanJwtStore};

#[cfg(target_arch = "wasm32")]
use wasm_bindgen_test::wasm_bindgen_test;

#[cfg(target_arch = "wasm32")]
wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);

#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
#[cfg_attr(not(target_arch = "wasm32"), tokio::test)]
async fn it_sets_content_length_and_type_on_write() -> Result<()> {
initialize_tracing(None);

let (mut sphere_context, _) = simulated_sphere_context(Access::ReadWrite, None).await?;

check_headers(
&mut sphere_context,
"note",
ContentType::Text,
"hello".as_ref(),
"5",
)
.await?;

check_headers(
&mut sphere_context,
"note",
ContentType::Json,
json!({"foo":true}).to_string().as_ref(),
"12",
)
.await?;

async fn check_headers<C, S, F>(
sphere_context: &mut C,
slug: &str,
content_type: ContentType,
content: F,
expected_length: &str,
) -> Result<()>
where
C: HasMutableSphereContext<S>,
S: Storage + 'static,
F: AsyncFileBody,
{
let db = sphere_context.sphere_context().await?.db().clone();
let link = sphere_context
.write(slug, &content_type, content, None)
.await?;
let memo = link.load_from(&db).await?;
assert_eq!(
memo.headers,
vec![
(
Header::ContentLength.to_string(),
expected_length.to_owned()
),
(Header::ContentType.to_string(), content_type.to_string())
]
);
Ok(())
}
Ok(())
}

#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
#[cfg_attr(not(target_arch = "wasm32"), tokio::test)]
async fn it_validates_slug_names_when_writing() -> Result<()> {
Expand Down
4 changes: 4 additions & 0 deletions rust/noosphere-core/src/data/headers/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ use std::{convert::Infallible, fmt::Display, ops::Deref, str::FromStr};

/// Well-known headers in the Noosphere
pub enum Header {
/// The content length (in bytes) of associated binary data
ContentLength,
/// Content-type, for mimes
ContentType,
/// A proof, typically a UCAN JWT
Expand Down Expand Up @@ -34,6 +36,7 @@ impl FromStr for Header {

fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s.to_lowercase().as_str() {
"content-length" => Header::ContentLength,
"content-type" => Header::ContentType,
"file-extension" => Header::FileExtension,
"proof" => Header::Proof,
Expand All @@ -52,6 +55,7 @@ impl Deref for Header {

fn deref(&self) -> &Self::Target {
match self {
Header::ContentLength => "Content-Length",
Header::ContentType => "Content-Type",
Header::Proof => "Proof",
Header::Author => "Author",
Expand Down
3 changes: 2 additions & 1 deletion swift/Tests/SwiftNoosphereTests/NoosphereTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -188,11 +188,12 @@ final class NoosphereTests: XCTestCase {
var pointer = file_header_names.ptr!;

// NOTE: "hello" is only given once even though there are two headers with that name
assert(name_count == 3)
assert(name_count == 4)

let expected_headers = [
["foo", "bar"],
["hello", "world"],
["Content-Length", String(file_bytes.count)],
["Content-Type", "text/subtext"]
]

Expand Down
Loading