in my app have rust backend and react frontend so want to be able to upload a wasm file upload the chunks and hash them to get wasm hash and upload wasm_chunks and hash dont know what i am doing wrong but in frontend getting error
index-6c91d4d2.js:62 Uncaught (in promise) Error: Invalid vec nat8 argument: {}
at index-6c91d4d2.js:62:6456
at index-6c91d4d2.js:52:16370
at Array.map (<anonymous>)
at zipWith (index-6c91d4d2.js:52:16359)
at encode$1 (index-6c91d4d2.js:62:6397)
at g (index-6c91d4d2.js:81:3543)
at a.h [as create_release] (index-6c91d4d2.js:81:3954)
at fA (index-6c91d4d2.js:81:6398)
at Object.Nb (index-6c91d4d2.js:37:9858)
at Tb (index-6c91d4d2.js:37:10016)
my backend code
use ic_cdk::export::candid::{CandidType, Deserialize};
use ic_cdk_macros::*;
use serde::Serialize;
use std::cell::RefCell;
use std::collections::BTreeMap;
use sha2::{Sha256, Digest};
#[derive(Clone, Debug, CandidType, Deserialize, Serialize)]
struct Release {
id: String,
version: String,
features: String,
wasm_file: Vec<u8>,
wasm_hash: String,
}
thread_local! {
static RELEASES: RefCell<BTreeMap<String, Release>> = RefCell::default();
}
fn hash_wasm_file(wasm_file: &[u8]) -> String {
let mut hasher = Sha256::new();
hasher.update(wasm_file);
let result = hasher.finalize();
format!("{:x}", result)
}
fn chunk_wasm_file(wasm_file: &[u8]) -> Vec<Vec<u8>> {
let chunk_size = 1024 * 1024; // 1MiB
let chunks = wasm_file
.chunks(chunk_size)
.map(|chunk| chunk.to_vec())
.collect();
chunks
}
#[update]
fn create_release(id: String, version: String, features: String, wasm_file: Vec<u8>) -> String {
let chunks = chunk_wasm_file(&wasm_file);
let wasm_hash = hash_wasm_file(&wasm_file);
for chunk in chunks {
}
let release = Release {
id: id.clone(),
version,
features,
wasm_file,
wasm_hash,
};
RELEASES.with(|releases| {
releases.borrow_mut().insert(id.clone(), release);
});
id
}
#[query]
fn get_release(release_id: String) -> Option<Release> {
RELEASES.with(|releases| releases.borrow().get(&release_id).cloned())
}
#[update]
fn update_release(id: String, version: String, features: String, wasm_file: Vec<u8>) -> String {
let chunks = chunk_wasm_file(&wasm_file); // Chunk the wasm file
let wasm_hash = hash_wasm_file(&wasm_file); // Hash the wasm file
for chunk in chunks {
// Upload each chunk using the `upload_chunk` method
}
let release = Release {
id: id.clone(),
version,
features,
wasm_file,
wasm_hash,
};
RELEASES.with(|releases| {
releases.borrow_mut().insert(id.clone(), release);
});
id
}
#[update]
fn delete_release(release_id: String) -> String {
RELEASES.with(|releases| {
releases.borrow_mut().remove(&release_id);
});
"Release deleted".to_string()
}
#[query]
fn get_all_releases() -> Vec<Release> {
RELEASES.with(|releases| releases.borrow().values().cloned().collect())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_create_and_get_release() {
let id = "test".to_string();
let version = "1.0.0".to_string();
let features = "test features".to_string();
let wasm_file = vec![1, 2, 3, 4, 5];
let created_id = create_release(id.clone(), version.clone(), features.clone(), wasm_file.clone());
assert_eq!(created_id, id);
let release = get_release(id.clone()).unwrap();
assert_eq!(release.id, id);
assert_eq!(release.version, version);
assert_eq!(release.features, features);
assert_eq!(release.wasm_file, wasm_file);
}
#[test]
fn test_update_release() {
let id = "test".to_string();
let version = "1.0.0".to_string();
let features = "test features".to_string();
let wasm_file = vec![1, 2, 3, 4, 5];
create_release(id.clone(), version.clone(), features.clone(), wasm_file.clone());
let updated_version = "1.0.1".to_string();
let updated_id = update_release(id.clone(), updated_version.clone(), features.clone(), wasm_file.clone());
assert_eq!(updated_id, id);
let release = get_release(id.clone()).unwrap();
assert_eq!(release.version, updated_version);
}
#[test]
fn test_delete_release() {
let id = "test".to_string();
let version = "1.0.0".to_string();
let features = "test features".to_string();
let wasm_file = vec![1, 2, 3, 4, 5];
create_release(id.clone(), version.clone(), features.clone(), wasm_file.clone());
let message = delete_release(id.clone());
assert_eq!(message, "Release deleted".to_string());
let release = get_release(id.clone());
assert!(release.is_none());
}
#[test]
fn test_get_all_releases() {
let id1 = "test1".to_string();
let version1 = "1.0.0".to_string();
let features1 = "test features 1".to_string();
let wasm_file1 = vec![1, 2, 3, 4, 5];
let id2 = "test2".to_string();
let version2 = "1.0.0".to_string();
let features2 = "test features 2".to_string();
let wasm_file2 = vec![6, 7, 8, 9, 10];
create_release(id1.clone(), version1.clone(), features1.clone(), wasm_file1.clone());
create_release(id2.clone(), version2.clone(), features2.clone(), wasm_file2.clone());
let releases = get_all_releases();
assert_eq!(releases.len(), 2);
assert!(releases.iter().any(|release| release.id == id1));
assert!(releases.iter().any(|release| release.id == id2));
}
}
my frontend
import { useState } from 'react';
import { addRelease_backend } from 'declarations/addRelease_backend';
import InputField from './InputField';
function App() {
const [releaseId, setReleaseId] = useState('');
const [version, setVersion] = useState('');
const [features, setFeatures] = useState('');
const [releases, setReleases] = useState([]);
const [wasm_file, setWasm_file] = useState(null);
function handleFileChange(file) {
setWasm_file(file);
}
function handleCreate(event) {
event.preventDefault();
addRelease_backend.create_release(releaseId, version, features, wasm_file).then((id) => {
setReleaseId(id);
});
return false;
}
function handleGet(event) {
event.preventDefault();
addRelease_backend.get_release(releaseId).then((release) => {
setVersion(release.version);
setFeatures(release.features);
});
return false;
}
function handleUpdate(event) {
event.preventDefault();
addRelease_backend.update_release(releaseId, version, features).then((id) => {
setReleaseId(id);
});
return false;
}
function handleDelete(event) {
event.preventDefault();
addRelease_backend.delete_release(releaseId).then(() => {
setReleaseId('');
setVersion('');
setFeatures('');
});
return false;
}
function handleGetAll(event) {
event.preventDefault();
addRelease_backend.get_all_releases().then((allReleases) => {
setReleases(allReleases);
});
return false;
}
return (
<main className="p-4">
<img src="/logo2.svg" alt="DFINITY logo" />
<br />
<br />
<form action="#" onSubmit={handleCreate} className="justify-center mx-auto flex space-y-5">
<InputField
id="releaseId"
label="Release ID"
type="text"
onChange={(e) => setReleaseId(e.target.value)}
/>
<InputField
id="version"
label="Release Version"
type="text"
onChange={(e) => setVersion(e.target.value)}
/>
<InputField
id="features"
label="Features"
type="text"
onChange={(e) => setFeatures(e.target.value)}
/>
<InputField
id="wasmFile"
label="Wasm File"
type="file"
onChange={(e) => handleFileChange(e.target.files[0])}
/>
</form>
<div className="flex space-x-4 justify-center mx-auto mt-10">
<button onClick={handleCreate} className="bg-blue-500 text-white p-2 rounded hover:bg-blue-700">
Create Release
</button>
<button onClick={handleGet} className="bg-green-500 text-white p-2 rounded hover:bg-green-700">
Get Release
</button>
<button onClick={handleUpdate} className="bg-yellow-500 text-white p-2 rounded hover:bg-yellow-700">
Update Release
</button>
<button onClick={handleDelete} className="bg-red-500 text-white p-2 rounded hover:bg-red-700">
Delete Release
</button>
<button onClick={handleGetAll} className="bg-purple-500 text-white p-2 rounded hover:bg-purple-700">
Get All Releases
</button>
</div>
<section id="releases" className="mt-4 justify-center mx-auto">
{JSON.stringify(releases)}
</section>
</main>
);
}
export default App;