Skip to content

Commit 472c2af

Browse files
authored
chore: migrate motoko/superheroes to icp-cli (#1387)
1 parent 09af6a8 commit 472c2af

27 files changed

Lines changed: 504 additions & 700 deletions

‎.github/workflows/superheroes.yml‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
name: superheroes
2+
3+
on:
4+
push:
5+
branches: [master]
6+
pull_request:
7+
paths:
8+
- motoko/superheroes/**
9+
- .github/workflows/superheroes.yml
10+
11+
concurrency:
12+
group: ${{ github.workflow }}-${{ github.ref }}
13+
cancel-in-progress: true
14+
15+
jobs:
16+
motoko-superheroes:
17+
runs-on: ubuntu-24.04
18+
container: ghcr.io/dfinity/icp-dev-env-motoko:0.3.2
19+
env:
20+
ICP_CLI_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
21+
steps:
22+
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
23+
- name: Deploy and test
24+
working-directory: motoko/superheroes
25+
run: |
26+
icp network start -d
27+
icp deploy
28+
make test

‎motoko/superheroes/.devcontainer/devcontainer.json‎

Lines changed: 0 additions & 20 deletions
This file was deleted.

‎motoko/superheroes/.gitignore‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
node_modules/
2+
frontend/dist/
3+
frontend/src/bindings/
4+
.icp/cache/

‎motoko/superheroes/BUILD.md‎

Lines changed: 0 additions & 113 deletions
This file was deleted.

‎motoko/superheroes/Makefile‎

Lines changed: 41 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,43 @@
1-
.PHONY: all
2-
all: build
3-
4-
.PHONY: node_modules
5-
.SILENT: node_modules
6-
node_modules:
7-
npm install
8-
9-
.PHONY: deploy
10-
.SILENT: deploy
11-
deploy: node_modules
12-
dfx deploy
13-
141
.PHONY: test
15-
.SILENT: test
16-
test: deploy
17-
$(eval ID := $(shell dfx canister call superheroes create '(record {name = "Superman"; superpowers = vec { "invulnerability"; "superhuman strength" }})' | tr -d '()'))
18-
dfx canister call superheroes read '($(ID))' \
19-
| grep 'Superman' && echo 'PASS'
20-
dfx canister call superheroes update '($(ID), record {name = "Superman"; superpowers = vec { "invulnerability"; "superhuman strength"; "flight"; "x-ray vision" }})' \
21-
| grep 'true' && echo 'PASS'
22-
dfx canister call superheroes read '($(ID))' \
23-
| grep 'x-ray vision' && echo 'PASS'
24-
dfx canister call superheroes delete '($(ID))' \
25-
| grep 'true' && echo 'PASS'
26-
dfx canister call superheroes delete '($(ID))' \
27-
| grep 'false' && echo 'PASS'
28-
dfx canister call superheroes read '($(ID))' \
29-
| grep 'null' && echo 'PASS'
302

31-
.PHONY: clean
32-
.SILENT: clean
33-
clean:
34-
rm -fr .dfx
35-
rm -fr node_modules
3+
# Tests capture the ID from create so they are idempotent across re-runs
4+
# (the backend uses an auto-incrementing counter, so the ID differs each run).
5+
test:
6+
@echo "=== Test 1: create a superhero ==="
7+
@set -e; \
8+
out=$$(icp canister call backend create '(record { name = "Superman"; superpowers = vec { "invulnerability"; "superhuman strength" } })'); \
9+
echo "$$out"; \
10+
id=$$(echo "$$out" | grep -oE '[0-9]+' | head -1); \
11+
[ -n "$$id" ] || (echo "FAIL: could not parse created ID" && exit 1); \
12+
echo "Created ID: $$id"; \
13+
echo "PASS"; \
14+
\
15+
echo "=== Test 2: read the created superhero ==="; \
16+
result=$$(icp canister call --query backend read "($$id : nat32)"); \
17+
echo "$$result"; \
18+
echo "$$result" | grep -q 'Superman' && echo "PASS" || (echo "FAIL" && exit 1); \
19+
\
20+
echo "=== Test 3: update the superhero ==="; \
21+
result=$$(icp canister call backend update "($$id : nat32, record { name = \"Superman\"; superpowers = vec { \"invulnerability\"; \"superhuman strength\"; \"flight\"; \"x-ray vision\" } })"); \
22+
echo "$$result"; \
23+
echo "$$result" | grep -q 'true' && echo "PASS" || (echo "FAIL" && exit 1); \
24+
\
25+
echo "=== Test 4: read updated superhero to verify state change ==="; \
26+
result=$$(icp canister call --query backend read "($$id : nat32)"); \
27+
echo "$$result"; \
28+
echo "$$result" | grep -q 'x-ray vision' && echo "PASS" || (echo "FAIL" && exit 1); \
29+
\
30+
echo "=== Test 5: delete the superhero ==="; \
31+
result=$$(icp canister call backend delete "($$id : nat32)"); \
32+
echo "$$result"; \
33+
echo "$$result" | grep -q 'true' && echo "PASS" || (echo "FAIL" && exit 1); \
34+
\
35+
echo "=== Test 6: delete non-existent superhero returns false ==="; \
36+
result=$$(icp canister call backend delete "($$id : nat32)"); \
37+
echo "$$result"; \
38+
echo "$$result" | grep -q 'false' && echo "PASS" || (echo "FAIL" && exit 1); \
39+
\
40+
echo "=== Test 7: read deleted superhero returns null ==="; \
41+
result=$$(icp canister call --query backend read "($$id : nat32)"); \
42+
echo "$$result"; \
43+
echo "$$result" | grep -q 'null' && echo "PASS" || (echo "FAIL" && exit 1)

‎motoko/superheroes/README.md‎

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,51 @@
1-
# CRUD example
1+
# Superheroes CRUD
22

3-
This example demonstrates how to build a CRUD application on ICP using Motoko and React.
3+
This example demonstrates how to build a CRUD (Create, Read, Update, Delete) application on ICP using Motoko and React. The backend canister stores superhero records (a name and a list of superpowers), and exposes four methods: `create`, `read`, `update`, and `delete`. The React frontend provides a simple UI for interacting with each operation.
44

5-
## Deploying from ICP Ninja
5+
## Build and deploy from the command line
66

7-
When viewing this project in ICP Ninja, you can deploy it directly to the mainnet for free by clicking "Run" in the upper right corner. Open this project in ICP Ninja:
7+
### Prerequisites
88

9-
[![](https://icp.ninja/assets/open.svg)](https://icp.ninja/i?g=https://github.com/dfinity/examples/motoko/superheroes)
9+
- Node.js
10+
- icp-cli: `npm install -g @icp-sdk/icp-cli @icp-sdk/ic-wasm`
11+
- ic-mops: `npm install -g ic-mops`
1012

11-
## Build and deploy from the command-line
13+
### Install
1214

13-
### 1. [Download and install the IC SDK.](https://internetcomputer.org/docs/building-apps/getting-started/install)
15+
Clone the example project:
1416

15-
### 2. Download your project from ICP Ninja using the 'Download files' button on the upper left corner, or [clone the GitHub examples repository.](https://github.com/dfinity/examples/)
17+
```bash
18+
git clone https://github.com/dfinity/examples
19+
cd examples/motoko/superheroes
20+
```
21+
22+
### Deploy and test
23+
24+
```bash
25+
icp network start -d
26+
icp deploy
27+
make test
28+
icp network stop
29+
```
1630

17-
### 3. Navigate into the project's directory.
31+
`make test` creates a superhero, exercises all CRUD operations, and deletes it — using the returned ID for all subsequent calls, so tests are idempotent and can be re-run multiple times without redeploying.
1832

19-
### 4. Deploy the project to your local environment:
33+
The frontend is served by the asset canister. To run the Vite dev server with hot reload during frontend development:
2034

35+
```bash
36+
npm run dev
2137
```
22-
dfx start --background --clean && dfx deploy
38+
39+
## Updating the Candid interface
40+
41+
The `backend/backend.did` file defines the backend canister's public interface. The frontend TypeScript bindings are auto-generated from this file during the frontend build.
42+
43+
If you modify the backend's public API, regenerate the `.did` file using the Motoko compiler:
44+
45+
```bash
46+
$(mops toolchain bin moc) --idl -o backend/backend.did backend/app.mo
2347
```
2448

2549
## Security considerations and best practices
2650

27-
If you base your application on this example, it is recommended that you familiarize yourself with and adhere to the [security best practices](https://internetcomputer.org/docs/building-apps/security/overview) for developing on ICP. This example may not implement all the best practices.
51+
If you base your application on this example, it is recommended that you familiarize yourself with and adhere to the [security best practices](https://docs.internetcomputer.org/guides/security/overview) for developing on ICP. This example may not implement all the best practices.

motoko/superheroes/src/superheroes/Main.mo renamed to motoko/superheroes/backend/app.mo

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import Map "mo:core/Map";
22
import Nat32 "mo:core/Nat32";
33

4-
persistent actor Superheroes {
4+
actor Superheroes {
55

66
public type SuperheroId = Nat32;
77

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
type SuperheroId = nat32;
2+
3+
type Superhero = record {
4+
name : text;
5+
superpowers : vec text;
6+
};
7+
8+
service : {
9+
create : (Superhero) -> (SuperheroId);
10+
read : (SuperheroId) -> (opt Superhero) query;
11+
update : (SuperheroId, Superhero) -> (bool);
12+
delete : (SuperheroId) -> (bool);
13+
}

‎motoko/superheroes/dfx.json‎

Lines changed: 0 additions & 28 deletions
This file was deleted.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
<title>Superheroes</title>
7+
</head>
8+
<body>
9+
<div id="app"></div>
10+
<script type="module" src="/src/main.jsx"></script>
11+
</body>
12+
</html>

0 commit comments

Comments
 (0)