diff --git a/Dockerfile b/Dockerfile index 457285c..58d80d8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,7 +14,8 @@ EOT RUN curl -sSf https://raw.githubusercontent.com/WasmEdge/WasmEdge/master/utils/install.sh | bash FROM buildbase AS build -COPY . . +COPY Cargo.toml orders.json update_order.json . +COPY src ./src # Build the Wasm binary RUN --mount=type=cache,target=/usr/local/cargo/git/db \ --mount=type=cache,target=/usr/local/cargo/registry/cache \ diff --git a/README.md b/README.md index 3d487b4..1dd7e9c 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ The easiest way to get started is to use a version of Docker with Wasm WASI supp docker compose up ``` -This will build the Rust source code, run the Wasm server, and startup a MySQL backing database. See the [Dockerfile](Dockerfile) and [docker-compose.yml](docker-compose.yml) files. You can jump directly to the [CRUD tests](#crud-tests) section to interact with the web service. +This will build the Rust source code, run the Wasm server, and startup a MySQL backing database. It also starts a very basic web interface (available at http://localhost:8090). See the [Dockerfile](Dockerfile) and [docker-compose.yml](docker-compose.yml) files. You can jump directly to the [CRUD tests](#crud-tests) section to interact with the web service. However, if you want to build and run the microservice app step by step on your own system. Follow the detailed instructions below. diff --git a/client/index.html b/client/index.html new file mode 100644 index 0000000..d07c384 --- /dev/null +++ b/client/index.html @@ -0,0 +1,102 @@ + + + + Demo App + + + + +
+
+

Loading...

+
+ +
+

Welcome to the Demo!

+

This application is served using nginx for the website, Wasm for the backend, and MariaDB for the database.

+ +
+ There are currently no orders to display! +
+ + + + + + + + + + + + + + + +
IdProduct IdQuantityAmountShippingTaxAddress
+ +
+ +
+
+
+
+

+ +

+ +
+
+
+ + +
The ID of the order
+
+
+ + +
The ID of the product
+
+
+ + +
How many of the product?
+
+
+ + +
The total amount
+
+
+ + +
The total amount of tax
+
+
+ + +
The total amount for shipping
+
+
+ + +
Where to send the order
+
+ + +
+
+
+
+
+
+
+ +
+ + + + + \ No newline at end of file diff --git a/client/js/app.js b/client/js/app.js new file mode 100644 index 0000000..abaeb4b --- /dev/null +++ b/client/js/app.js @@ -0,0 +1,135 @@ +(function() { + let orders = null; + const appLoadingEle = document.getElementById("app-loading-display"); + const orderWrapperEle = document.getElementById("order-display"); + const orderEmptyTextEle = document.getElementById("order-empty-text"); + const orderTableEle = document.getElementById("order-table"); + const orderTableBodyEle = document.querySelector("#order-table tbody"); + const addOrderEle = document.getElementById("add-order-wrapper"); + const addOrderForm = document.getElementById("add-order-form"); + + const orderIdField = document.getElementById("order-id"); + const productIdField = document.getElementById("product-id"); + const quantityField = document.getElementById("quantity"); + const amountField = document.getElementById("amount"); + const taxField = document.getElementById("tax"); + const shippingField = document.getElementById("shippingAmount"); + const shippingAddressField = document.getElementById("shippingAddress"); + + function fetchOrders() { + fetch("http://localhost:8080/orders") + .then(r => r.json()) + .then(r => orders = r) + .then(renderOrders) + .catch((e) => { + init(); + }); + } + + function init() { + fetch("http://localhost:8080/init") + .then(() => fetchOrders()) + .catch((e) => displayError(e)); + } + + function renderOrders() { + appLoadingEle.classList.add("d-none"); + orderWrapperEle.classList.remove("d-none"); + addOrderEle.classList.remove("d-none"); + + if (orders.length === 0) { + orderEmptyTextEle.classList.remove("d-none"); + orderTableEle.classList.add("d-none"); + return; + } + + orderEmptyTextEle.classList.add("d-none"); + orderTableEle.classList.remove("d-none"); + + while (orderTableBodyEle.firstChild) { + orderTableBodyEle.removeChild(orderTableBodyEle.firstChild); + } + + orders.forEach((order) => { + const orderId = order.order_id; + + const row = document.createElement("tr"); + + row.appendChild(createCell(order.order_id)); + row.appendChild(createCell(order.product_id)); + row.appendChild(createCell(order.quantity)); + row.appendChild(createCell(order.amount)); + row.appendChild(createCell(order.shipping)); + row.appendChild(createCell(order.tax)); + row.appendChild(createCell(order.shipping_address)); + + const actionCell = document.createElement("td"); + + const deleteButton = document.createElement("button"); + deleteButton.classList.add(...["btn","btn-sm","btn-danger"]); + deleteButton.innerText = "Delete"; + + deleteButton.addEventListener("click", (e) => { + e.preventDefault(); + deleteOrder(orderId); + }); + + actionCell.appendChild(deleteButton); + + row.appendChild(actionCell); + + orderTableBodyEle.appendChild(row); + }); + } + + function createCell(contents) { + const cell = document.createElement("td"); + cell.innerText = contents; + return cell; + } + + function deleteOrder(orderId) { + fetch(`http://localhost:8080/delete_order?id=${orderId}`) + .then(() => fetchOrders()); + } + + function displayError(err) { + alert("Error:" + err); + } + + function onAddFormSubmit(e) { + e.preventDefault(); + + const data = { + order_id : parseFloat(orderIdField.value), + product_id : parseFloat(productIdField.value), + quantity : parseFloat(quantityField.value), + amount : parseFloat(amountField.value), + shipping : parseFloat(shippingField.value), + tax : parseFloat(taxField.value), + shipping_address : shippingAddressField.value, + }; + + fetch("http://localhost:8080/create_order", { + method: "POST", + body: JSON.stringify(data), + headers: { "Content-type": "application/json" }, + }).then(() => fetchOrders()) + .then(() => resetAddOrderForm()); + + alert("Order added"); + } + + function resetAddOrderForm() { + orderIdField.value = ""; + productIdField.value = ""; + quantityField.value = ""; + amountField.value = ""; + shippingField.value = ""; + taxField.value = ""; + shippingAddressField.value = ""; + } + + fetchOrders(); + addOrderForm.addEventListener("submit", onAddFormSubmit); +})(); \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 8490d4d..8b068ef 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,4 +1,10 @@ services: + client: + image: nginx:alpine + ports: + - 8090:80 + volumes: + - ./client:/usr/share/nginx/html server: image: server build: