#S:EXTERNAL=rust=hello_me.rs
Welcome back to another tutorial in the Core Concepts series.
The app we have built so far returns a constant value however for more complex applications it would be useful to be able to store some data.
Today you will learn how to do this by adding an entry type to your zome. Remember an entry is a piece of data in your source chain that has been validated.
We will add the following:
person
entry type that stores information about a person.create_person
for your UI to create and store a person entry.retrieve_person
for your UI to retrieve a person entry.This tutorial builds on the previous tutorial so go back and complete that if you haven't already.
Start by writing a test so it's easy to see when your app is working:
Open up cc_tuts/test/index.js
.
This is how we left the testing scenario in the Hello Test tutorial:
#S:MODE=test #S:EXTERNAL=javascript=hello_me.js=test #S:SKIP
#S:INCLUDE,HIDE
The new tests go below t.deepEqual(result, { Ok: 'Hello Holo' })
.
The following test will create an entry with the name "Alice", retrieve the same entry and check that it has the name "Alice".
Add a call to the create_person
function with a person whose name is Alice:
Check that the result of the call is Ok:
Add a call to the retrieve_person
function with the address from the last call:
Check that this call is Ok as well:
This is the actual result we want at the end of the test. Check that the entry at the address is indeed named Alice
:
#S:HIDE
Your test should now look like this:
#S:CHECK=javascript=test
Obviously these tests will fail right now. Can you guess what the first failure will be? Let's have a look.
Enter the nix-shell if you don't have it open already:
Run the test:
!!! note "Run in nix-shell
"
bash hc test
!!! failure "The test fails on the create_person function because it doesn't exist yet:"
"Holochain Instance Error: Zome function 'create_person' not found in Zome 'hello'"
Note that this test might actually get stuck because we haven't put in the required functions yet. Press
ctrl-c
to exit a stuck test.
Open up your zomes/hello/code/src/lib.rs
file.
To add an entry into your source chain start by telling Holochain what kinds of entry exist.
First we'll create a struct
to define the shape of the data.
In a moment we will add a Person
struct, but this is where to put it:
#S:SKIP
Add the following lines.
Allow this struct to be easily converted to and from JSON:
#S:INCLUDE
Represent a person as a struct:
Represent their name as a String:
Look for the following lines inside the hello_zome
mod.
#S:HIDE
Add the person_entry_def
function, which tells Holochain about the person entry type:
Add the entry!
macro that lets you easily create a ValidatingEntryType
:
Give it the same name as the Person
struct, just to be consistent. Entry types are usually in lowercase.
Add the name and description of the entry:
Entries of this type are just for this agent's eyes only, so set the entry sharing to private:
Add the validation_package
function that says what is needed to validate this entry:
Add the validation
function that validates this entry.
It returns that this entry is always Ok as long as it fits the shape of the Person
struct:
Now you can create actual person
entries and store them on your source chain.
A note on validation: Validation is very important. It is the "rules of the game" for your Holochain app. It is meaningful to emphasize that although we are returning
Ok(())
that we are still validating that the data type checks as aPerson
with aname
property containing aString
. Essentially this rule says the person entry must be in this format.
use
statementsIn the above code we have used a few types and macros that are not mentioned anywhere else. So the Rust compiler doesn't know where to find them yet.
Add the following use
statements:
#S:EXTRA
#S:CHECK=rust
Now you need a way for your UI to actually create a person entry. Holochain has a concept called hc_public
which is a way of telling the runtime make this function available to call from outside this zome.
Add the following lines below the previous person_entry_def
function.
Add a public function that takes a Person
and returns a result with an Address
:
#S:INCLUDE
Create an entry from the person argument:
Commit the entry to your local source chain:
Return the Ok
result with the new person entry's address:
#S:EXTRA
#S:CHECK=rust
Check for compile errors again:
!!! note "Run in nix-shell
"
bash hc package
Lastly you need a way for your UI to get a person entry back from the source chain.
Add the following lines below the create_person
function.
Add a public retrieve_person
function that takes an Address
and returns a Person
:
Get the entry from your local storage, asking for it by address, and convert it to a Person type:
#S:HIDE,INCLUDE
In Rust the last line is always returned. You do not need to explicitly say
return
. Just leave off the;
.
#S:CHECK=rust
Instead of directly compiling, you can run the test you wrote at the start (the test always compiles before it runs):
!!! note "Run in nix-shell
"
bash hc test
!!! success "If everything went smoothly you will see:" ```bash # tests 5 # pass 5
# ok
```
Now that the backend is working you can modify the UI to interact with zome functions you created. First let's do some housekeeping and move the JavaScript from the previous tutorial into its own file.
Go to the GUI project folder that you created in the Hello GUI tutorial:
Create a new hello.js
file, open it in your favorite editor, and open the index.html
alongside it.
Move the everything inside the <script>
tag into the hello.js
:
#S:SKIP,MODE=gui
Add the src
attribute to the <script>
tag:
In your index.html
start by adding the HTML elements to create a person.
Look for the previous 'say hello' elements.
Below them, give the section a heading:
Add a text box so the user can enter their name:
Add a button that calls a (yet to be written) JavaScript function called create_person
:
Add a span with the id address_output
so you can render the result of this call:
??? question "Check your index.html:" ```html <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Hello GUI</title>
<meta name="description" content="GUI for a Holochain app" />
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/gh/kognise/water.css@latest/dist/dark.min.css"
/>
</head>
<body>
<button onclick="hello()" type="button">Say Hello</button>
<div>Response: <span id="output"></span></div>
<h3>Create a person</h3>
<input type="text" id="name" placeholder="Enter your name :)" />
<button onclick="create_person()" type="button">Submit Name</button>
<div>Address: <span id="address_output"></span></div>
<script
type="text/javascript"
src="hc-web-client/hc-web-client-0.5.1.browser.min.js"
></script>
<script type="text/javascript" src="hello.js"></script>
</body>
</html>
```
hello.js
fileLet's write the create_person
function that will call your zome.
Add the create_person
function:
Get the text box by its ID name
and save the current text value into the name variable:
Wait for the connection and then make a zome call:
Call create_person
in your hello
zome and pass in the name variable as part of a person structure, then write the result to the console:
??? question "Check your hello.js:" ```javascript // Connect var holochain_connection = holochainclient.connect({ url: 'ws://localhost:3401', });
// Render functions
function show_output(result) {
var span = document.getElementById('output');
var output = JSON.parse(result);
span.textContent = ' ' + output.Ok;
}
// Zome calls
function hello() {
holochain_connection.then(({callZome, close}) => {
callZome('test-instance', 'hello', 'hello_holo')({args: {}}).then(result =>
show_output(result),
);
});
}
function create_person() {
const name = document.getElementById('name').value;
holochain_connection.then(({callZome, close}) => {
callZome('test-instance', 'hello', 'create_person')({
person: {name: name},
}).then(result => console.log(result));
});
}
```
Go ahead and test your first call.
Open a new terminal window and enter the nix-shell:
Run the server:
!!! note "Run in nix-shell
"
bash python -m SimpleHTTPServer
In your other terminal window (the one with your backend code) package and run your zome:
!!! note "Run in nix-shell
"
bash hc package
bash hc run -p 3401
Now that both your UI server and your Holochain conductor server are running, open up a browser and go to 0.0.0.0:8000
. You should see the HTML elements you created:
Open the developer console, enter your name, and press the "Submit Name" button. You should something similar to this:
The address you see will probably be different, because you typed in your own name.
Now we're going to show the address on the page rather than the developer console.
But first, a bit of refactoring. If you make the show_ouput
function more generic, then you can reuse it for each element that shows the output for a zome function.
Pass in the element's ID so that the function can be reused:
Go back to your browser and refresh the page. This time when you enter your name and press Submit Name, you will see the address show up:
Back in the index.html
file now and under the create person section, add a new header:
Add a text box so the user can enter the address that is returned from the create_person
function:
Add a button that calls the (yet to be written) retrieve_person
JavaScript function:
Add a span with the ID person_output
to display the person that is returned from the retrieve_person
function:
hello.js
fileAdd the retrieve_person
function to call the zome function of the same name and show its response:
Get the value from the address_in
text box:
Wait for the connection and then make a zome call:
Call the retrieve_person
public zome function, passing in the address. Then pass the result to show_person
:
Add the show_person
function. It is very similar to show_output
except that you need to show the name.
Finally go and test this out at 0.0.0.0:8000
.
You should see somehting like this:
Well done! You have stored and retrieved data from a private source chain all using a GUI.