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

feat(xkcd)!: add simple UI to XKCD actor #233

Merged
merged 1 commit into from
Sep 13, 2023
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
2 changes: 1 addition & 1 deletion actor/xkcd/.gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
target
build
/build
76 changes: 75 additions & 1 deletion actor/xkcd/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 6 additions & 4 deletions actor/xkcd/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "xkcd"
version = "0.1.7"
authors = [ "wasmcloud Team" ]
version = "0.2.0"
authors = ["wasmcloud Team"]
edition = "2021"

[lib]
Expand All @@ -11,12 +11,14 @@ crate-type = ["cdylib", "rlib"]
async-trait = "0.1"
futures = "0.3"
serde_bytes = "0.11"
serde_json ="1.0"
serde = {version = "1.0", features = ["derive"]}
serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] }
wasmbus-rpc = "0.13"
wasmcloud-interface-httpserver = "0.10"
wasmcloud-interface-httpclient = "0.9"
wasmcloud-interface-numbergen = "0.9"
rust-embed = "8.0.0"
mime_guess = "2.0.4"

[profile.release]
# Optimize for small code size
Expand Down
5 changes: 5 additions & 0 deletions actor/xkcd/Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# examples/actor/xkcd

.PHONY: build
PROJECT = xkcd
VERSION = $(shell cargo metadata --no-deps --format-version 1 | jq -r '.packages[] .version' | head -1)
REVISION = 2
Expand All @@ -20,6 +21,10 @@ ACTOR_ID = $(shell make actor_id)
HTTPSERVER_PROVIDER_ID = VAG3QITQQ2ODAOWB5TTQSDJ53XK3SHBEIFNK4AYJ5RKAX2UNSCAPHA5M
HTTPCLIENT_PROVIDER_ID = VCCVLH4XWGI3SGARFNYKYT2A32SUYA2KVAIV2U2Q34DQA7WWJPFRKIKM

build:
cd ui && npm run build
wash build


link:
# link to httpserver and httpclient
Expand Down
45 changes: 8 additions & 37 deletions actor/xkcd/README.md
Original file line number Diff line number Diff line change
@@ -1,49 +1,20 @@
# Xkcd comic generator

This example actor demonstrates three capability providers:
- wasmcloud:httpserver for responding to http requests
- wasmcloud:httpclient for fetching http resources from the internet

- wasmcloud:httpserver for responding to http requests
- wasmcloud:httpclient for fetching http resources from the internet
- wasmcloud:builtin:numbergen for generating a random number.

When the actor runs, open a web browser to the http port
(selected when linking to the httpserver)
## Prerequisites

Each time you refresh the page, you should get a random xkcd comic!
[Install wash](https://wasmcloud.com/docs/installation)

To use this actor:
_These instructions will be simpler when the providers
are published to a public registry_
## Running this example

```shell
# start wasmcloud host, nats, and a registry
# (use the docker-compose.yml file from the sdk)
# docker-compose up -d

# compile and generate signed wasm file for this actor
make
# push to local registry and start the actor
make push
make start

# install and start httpserver provider
cd path/to/capability-providers/httpserver-rs
make
make push
make start
make inspect
# get the provider id (starting with 'V') and paste it into the Makefile for HTTPSERVER_PROVIDER_ID

# install and start httpclient provider
cd path/to/capability-providers/httpclient
make
make push
make start
make inspect
# get the provider id (starting with 'V') and paste it into the Makefile for HTTPCLIENT_PROVIDER_ID

# you should now be able to link them
make link

wash up -d
wash app deploy ./wadm.yaml
```

Once all providers and the actor are started and linked,
Expand Down
80 changes: 50 additions & 30 deletions actor/xkcd/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,22 @@
//! then requests metadata for that comic from the xkcd site,
//! and generates and html page with the title and image url from the metadata.
//!
use serde::Deserialize;
use std::collections::HashMap;

use serde::{Deserialize, Serialize};
use serde_json::json;

use wasmbus_rpc::actor::prelude::*;
use wasmcloud_interface_httpclient::{HttpClient, HttpClientSender, HttpRequest as CHttpRequest};
use wasmcloud_interface_httpserver::{HttpRequest, HttpResponse, HttpServer, HttpServerReceiver};
use wasmcloud_interface_numbergen::random_in_range;

// the highest numbered comic available. (around 2705 as of Nov 30, 2022)
mod ui;
use ui::Asset;

// the highest numbered comic available. (around 2705 as of Sep 4, 2023)
// xkcd comics are numbered continuously starting at 1
const MAX_COMIC_ID: u32 = 2705;
const MAX_COMIC_ID: u32 = 2822;

#[derive(Debug, Default, Actor, HealthResponder)]
#[services(Actor, HttpServer)]
Expand All @@ -24,17 +30,45 @@ struct XkcdActor {}
#[async_trait]
impl HttpServer for XkcdActor {
async fn handle_request(&self, ctx: &Context, req: &HttpRequest) -> RpcResult<HttpResponse> {
// all the work happens inside handle_inner.
// The purpose of this wrapper is to catch any errors generated
// by the inner function and turn them into a valid HttpResponse.
Ok(self
.handle_inner(ctx, req)
.await
.unwrap_or_else(|e| HttpResponse {
body: json!({ "error": e.to_string() }).to_string().into_bytes(),
status_code: 500,
..Default::default()
}))
match req.path.trim_start_matches('/') {
// Handle requests to retrieve comic data
"comic" =>
// all the work happens inside handle_inner.
// The purpose of this wrapper is to catch any errors generated
// by the inner function and turn them into a valid HttpResponse.
{
Ok(self
.handle_inner(ctx, req)
.await
.unwrap_or_else(|e| HttpResponse {
body: json!({ "error": e.to_string() }).to_string().into_bytes(),
status_code: 500,
..Default::default()
}))
}
ui_asset_path => {
let path = if ui_asset_path.is_empty() {
"index.html"
} else {
ui_asset_path
};
// Request for UI asset
Ok(Asset::get(path)
.map(|asset| {
let mut header = HashMap::new();
if let Some(content_type) = mime_guess::from_path(path).first() {
header
.insert("Content-Type".to_string(), vec![content_type.to_string()]);
}
HttpResponse {
status_code: 200,
header,
body: Vec::from(asset.data),
}
})
.unwrap_or_else(|| HttpResponse::not_found()))
}
}
}
}

Expand All @@ -59,22 +93,8 @@ impl XkcdActor {
// and build html page
let info = serde_json::from_slice::<XkcdMetadata>(&resp.body)
.map_err(|e| tag_err("decoding metadata", e))?;
let html = format!(
r#"<!DOCTYPE html>
<html>
<head>
<title>Your XKCD random comic</title>
</head>
<body>
<h1>{}</h1>
<img src="{}"/>
</body>
</html>
"#,
&info.title, &info.img
);
let resp = HttpResponse {
body: html.into_bytes(),
body: serde_json::to_vec(&info).unwrap_or_default(),
..Default::default()
};
Ok(resp)
Expand All @@ -83,7 +103,7 @@ impl XkcdActor {

/// Metadata returned as json
/// (this is a subset of the full metadata, but we only need two fields)
#[derive(Deserialize)]
#[derive(Deserialize, Serialize)]
struct XkcdMetadata {
title: String,
img: String,
Expand Down
5 changes: 5 additions & 0 deletions actor/xkcd/src/ui.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
use rust_embed::RustEmbed;

#[derive(RustEmbed)]
#[folder = "./ui/build"]
pub struct Asset;
20 changes: 20 additions & 0 deletions actor/xkcd/ui/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage

# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local

npm-debug.log*
yarn-debug.log*
yarn-error.log*
6 changes: 6 additions & 0 deletions actor/xkcd/ui/.prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"arrowParens": "avoid",
"trailingComma": "es5",
"singleQuote": true,
"semi": true
}
Loading
Loading