Application development tutorial

This tutorial explains how to write a simple application for GAChain.

The goal

The application starts simple and grows in complexity as the tutorial progresses.

The final version of the app stores simple messages (strings) in a table with a timestamp and a message sender’s account identifier. A user can access the list of messages and add new messages from the app’s page. The app’s page can be accessed from the ecosystem menu.

Part 1: The environment

Govis

Govis is a single unified client for GAChain. Govis provides functionality for all user and ecosystem roles. Application developers can develop and test applications in Govis. Ecosystem administrators use Govis to manage ecosystems. Users can interact with ecosystem apps via Govis.

For this tutorial, you’ll be writing contract code, page template code and performing all other actions in the Govis client. Govis also provides a way to retrieve, save and execute contract code, manage data structures (tables), assign access rights, and create applications.

Each node has its own Govis client instance.

Part 2: The contract

Your first application will start as a simple “Hello, World!” application.

Note

The application stores a single string in a table. It doesn’t have any user interface.

Founder’s account

The “root” privileges for an ecosystem are available to accounts with the Developer role. By default, this role has access to all operations. In a new ecosystem, Admin role is assigned to the founder’s account. You must use this account to introduce major changes to the ecosystem, such as creating new apps and tables.

To login to the ecosystem with founder’s account.

New app

Once you are logged as ecosystem’s founder, you can create a new app.

To create a new app:

  1. Go to the Developer tab.

  2. From the menus on the left, select Application.

  3. In the Applications interface, select Create.

  4. Specify the name of your app in the Name field.

  5. In the Change conditions specify true.

    The true value will make it possible for anyone to change the app.

    Another option is to specify ContractConditions("MainCondition"). This will forbid application changes to anyone except the founder.

  6. Your app will appear in the list of apps. Click specify app name field to make it active.

    Note

    Selecting apps in the Developer tab makes it easier to navigate resources related to the selected app. It has no effect on the ecosystem. All ecosystem apps will still be available, no matter which one is selected.

New table

To store data, the application needs a table. Create this table from Govis.

To create a table:

  1. On the Developer tab, select Application - name > Tables.

    This will display all tables for the selected app. If the list will be empty, because of your app doesn’t have any tables yet.

  2. Click Create.

    Govis will display the Create table view.

  3. Specify a name for your table in the Name field.

    This tutorial uses apptable name for the table.

  4. Add a column. Name it message and set its type to Text.

    As a result, the table must have two columns: id (predefined), and message. You’ll add more columns later.

    ../_images/app-tut-table.png
  5. For write permissions, specify true in every field.

    This will allow anyone to perform inserts and updates on the table, and to add columns, read entry data.

    As an option, you can restrict writing permissions to the founder account. In this case, specify ContractConditions("MainCondition") in this field.

New contract

Contract code sections

Every contract has three sections see more: Structure of the contract.

Creating a new contract

  1. On the Developer tab, select Application - name > Contracts.

    This will display all contracts for the selected app. The list for your new app will be empty.

  2. Click Create.

    A new contract template will open in the editor.

An empty contract template looks like this:

contract ... {
    data {

    }
    conditions {

    }
    action {

    }
}

Contract name

To start, give a name to your contract.

contract AppContract {

Data section

Fill the data section.

In the example below, Message is the name of the variable, string is its type.

data {
    Message string
}

Condition section

Fill the conditions section. The single validation condition is that the specified string must not be empty. If Message length is 0, the contract will generate an alert with the defined message upon execution.

conditions {
    // avoid writing empty strings
    if Size($Message) == 0 {
        error "Message is empty"
    }
}

Action section

Fill the action section. The single action is writing the Message to the table.

action {
    DBInsert("apptable", {message: $Message})
}

Full contract code

Below is the full contract code for this part.

All GAChain contracts are constructed like this and always contain data, conditions, and action sections.

contract AppContract {
    data {
        Message string
    }
    conditions {
        // avoid writing empty strings
        if Size($Message) == 0 {
            error "Message is empty"
        }
    }
    action {
        DBInsert("apptable", {message: $Message})
    }
}

Save & execute

The contract is ready for testing:

  1. In the Editor menu, click Save.

    This updates the contract code. The updated version becomes available to all the network nodes.

  2. In the Editor menu, click Execute.

    This displays the Execute contract view.

  3. In the Execute contract view, enter the input parameters for the contract.

    The contract has one parameter, Message, so specify Message in Key and Hello, World in Value.

    ../_images/app-tut-execute.png
  4. Click Exec.

    The results will be displayed on the right.

If the string was added successfully, the results will contain the block number of the transaction that introduced the change, and the result code.

{
   "block": "31",
   "result": null
}

Part 3: The interface

After the contract is working, it’s time to expand it into something more useful. In this part, you’ll be implementing the UI and extra functionality.

Note

The app stores strings in a table, like entries in a log. Every string has an author and a timestamp.

A user can view the stored list of strings from the application page, which is a simple table at this point.

New columns

Just like before, edit the table from the Developer tab > Application - name > Tables view.

Add the following columns to the apptable table:

  • author of type Number with Update set to true.

    This field will store the identifier of the author’s account.

  • timestamp of type Date/Time with Update set to true.

Updated contract

Update the contract code to handle author IDs and timestamps.

Author IDs are identifers of the ecosystem accounts. Timestamps are the date and time of the contract execution in the Unix time format.

Both of these values are provided by the predefined variables. Since there is no need to input or validate the predefined variables, changes are needed only in the action section.

Change the contract so that the author’s ID and the timestamp are written to the table when a message is added. The author’s ID is defined by $key_id, the timestamp is defined by $time.

action {
    DBInsert("apptable", {message: $Message, author: $key_id, timestamp: $time})
}

The page

For this part, the application’s interface is a simple page that displays information stored in the table.

Just like all other resources, UI pages can be created in Govis:

  1. Navigate to Developer tab > Application - name > Pages.

  2. Click Create.

    A visual editor will open in the new tab.

Designer’s view

The default page is empty. Fortunately, you can use predefined structures to fill the page quickly.

../_images/app-tut-designer.png

Create a basic table with header:

  1. In the view selector on the right, click Designer.

    The view will switch to the visual editor.

  2. From the menu on the left, select Table With Header and drag it to the page.

    A table with several elements will appear.

Developer’s view

User interfaces for GAChain are written in Gastyle. You’ll need to write code for the page, so switch to the developer’s view.

../_images/app-tut-developer.png

To switch to the developer’s view:

  1. In the view selector on the right, click Developer.

    The view will switch to the code editor with the page code.

Get data from the table

At the moment, the page template does nothing. Then change the code, so that the page displays data from the apptable table.

  1. To request data from a table, use the DBFind function.

    The function call in the following example gets data from the apptable table, puts it into the src_table source, and orders it by the timestamp field. The src_table source is later used as a source of data for the table view on the page.

    DBFind(Name: apptable, Source: src_table).Columns(Columns: "author,timestamp,message").Order(timestamp)
    
  2. To display data from the src_table source, specify it as a source along with a list of headers in the Table function.

    Table(Columns: "AUTHOR=author,TIME=timestamp,MESSAGE=message", Source: src_table)
    
  3. In the view selector on the right, Click Preview to check that the data is displayed correctly.

Full page code

Below is the full page code for this part. This basic page will be expanded later.

DBFind(Name: apptable, Source: src_table).Columns(Columns: "author,timestamp,message").Order(timestamp)

Div(Class: panel panel-primary) {
    Div(Class: panel-heading, Body: Table block)
    Table(Columns: "AUTHOR=author,TIME=timestamp,MESSAGE=message", Source: src_table)
    Div(Class: panel-footer text-right) {
        Button(Class: btn btn-primary, Contract: ContractName, Body: More)
    }
}

Save the page

Click Save to save the page:

  1. Specify AppPage or any other name for a page in the Name field.
  2. Leave the Menu option at default_menu.
  3. In Change Conditions specify true.
  4. Click Confirm.

Part 4: The app

In the previous parts you’ve created a contract, a table to store data, and a basic UI page to display this data.

In this part, you’ll be finalizing the app, so it looks and behaves like an actual application.

The menu

A page is always linked to a menu. For example, the the default_page page that is displayed on the Home tab is linked to the default ecosystem menu, default_menu.

Because the tutorial app is small (just one page), there is no need to create an individual menu for it. A new menu item in the default menu will be enough.

Note

You can define what menu is displayed for the page by editing page properties in Developer tab > Application - name > Pages. For example, if your app has several pages, you may want to create a menu to navigate between these pages and assign it to all pages of your app.

Add a menu item

Just like all other resources, menus can be created and edited in Govis:

  1. Navigate to Developer tab > Menu.

    ../_images/app-tut-menu-list.png
  2. Click default_menu entry name.

    A visual editor will open in the new tab.

  3. Add a new menu item to the end of the template. This menu item will open the app’s page. The icon is from the FontAwesome icon set.

    MenuItem(Title:Messages, Page:AppPage, Icon:"fa fa-envelope")
    
  4. Click Save.

Test the new menu item

Check that the new menu item works:

  1. Open the Home tab.

  2. Click Refresh in the menu.

    A new item titled Messages will appear.

    ../_images/app-tut-menu-messages.png
  3. Click Messages.

    The app’s page will open.

Sending messages

Buttons in GAStyle can execute contracts and open pages, depending on the arguments.

The Button function has two arguments for contracts:

  • Contract

    Name of the contract that must be activated.

  • Params

    Input parameters for the contract.

Form

To send data to contracts, add a form to the app’s page. This form must have an input field for the message, and a button that will activate the AppContract contract.

Below is an example of such form. It is enclosed in its own Div. Place it after the Div element that holds the table view. The Input field of this form has a defined name, message_input. This name is used by the button to send Message parameter value to the contract. Finally, Val function is used to obtain the value of the input field.

Div(Class: panel panel-primary) {
  Form() {
        Input(Name: message_input, Class: form-control, Type: text, Placeholder: "Write a message...", )
        Button(Class: btn btn-primary, Body: Send, Contract: AppContract, Params: "Message=Val(message_input)")
  }
}

Test this new functionality by sending messages. You may notice that the table doesn’t refresh when a new message is sent. This is addressed page refresh.

Table navigation

The default table view on the page will display only 25 first entries. Add a simple navigation that will allow users to navigate all table entries.

Variables

This navigation requires two variables to store the table view state:

  • #table_view_offset#

    This variable stores the current table view offset.

    Navigation buttons will pass this as a parameter when reloading the page.

  • #record_count#

    This variable stores the total number of entries in the table.

    This value will be calculated.

Record count

To calculate #record_count#, modify the existing DBFind function call. The variable specified in the .Count() call will store the record count.

DBFind(Name: apptable, Source: src_table).Columns(Columns: "author,timestamp,message").Order(timestamp).Count(record_count)

Table offset

The table view offset must be passed to the page when it is opened. If #table_view_offset# is not passed, it is assumed to be 0.

Add the following code to the top of the page template.

If(GetVar(table_view_offset)){
}.Else{
    SetVar(table_view_offset, 0)
}

Modify the DBFind function call again. This time it must use the new table view offset.

DBFind(Name: apptable, Source: src_table).Columns(Columns: "author,timestamp,message").Order(timestamp).Count(record_count).Offset(#table_view_offset#)

Button code

Locate the Div function call that defines the footer, Div(Class: panel-footer text-right). Add the button code to it.

Div(Class: panel-footer text-right) {

}

The Previous button will be displayed only if there is at least one Next to go back to. The new table view offset for the page, offset_previous is calculated when the button is added. Parameters are passed to the reopened page in the PageParams parameter.

If(#table_view_offset# >= 25) {
    SetVar(offset_previous, Calculate(#table_view_offset# - 25))
    Button(Class: btn btn-primary, Body: Previous, Page: AppPage, PageParams:"table_view_offset=#offset_previous#")
}

The Next button will be displayed only if the total record count is more than what is displayed on the page. The new table view offset for the page, offset_next is calculated when the button is added. Parameters are passed to the reopened page in the PageParams parameter.

If(#record_count# >= Calculate(#table_view_offset# + 25)) {
    SetVar(offset_next, Calculate(#table_view_offset# + 25))
    Button(Class: btn btn-primary, Body: Next, Page: AppPage, PageParams:"table_view_offset=#offset_next#")
}
../_images/app-tut-navigation.png

After the buttons are added, save the page and test it from the Home > Messages menu item.

Page refresh

One final functionality that must be implemented is the automatic update of the table located on the page. When a user sends a new message, it must be displayed in the table.

You can implement this by making the Send button re-open the current page in addition to executing the contract. The #table_view_offset# parameter must be passed to the page without changes.

Add Page and PageParams arguments to Send button code like demonstrated below.

Button(Class: btn btn-primary, Body: Send, Contract: AppContract, Params: "Message=Val(message_input)", Page:AppPage, PageParams:"table_view_offset=#table_view_offset#")

Full page code

This part introduced many changes to the application page template. Below is the full code for the app page.

If(GetVar(table_view_offset)){
}.Else{
    SetVar(table_view_offset, 0)
}

DBFind(Name: apptable, Source: src_table).Columns(Columns: "author,timestamp,message").Order(timestamp).Count(record_count).Offset(#table_view_offset#)

Div(Class: panel panel-primary) {
 Div(Class: panel-heading, Body: Table block)
 Table(Columns: "AUTHOR=author,TIME=timestamp,MESSAGE=message", Source: src_table)
 Div(Class: panel-footer text-right) {

  If(#table_view_offset# >= 25) {
    SetVar(offset_previous, Calculate(#table_view_offset# - 25))
    Button(Class: btn btn-primary, Body: Previous, Page: AppPage, PageParams:"table_view_offset=#offset_previous#")
  }

  If(#record_count# >= Calculate(#table_view_offset# + 25)) {
    SetVar(offset_next, Calculate(#table_view_offset# + 25))
    Button(Class: btn btn-primary, Body: Next, Page: AppPage, PageParams:"table_view_offset=#offset_next#")
  }

 }
}

Div(Class: panel panel-primary) {
  Form() {
        Input(Name: message_input, Class: form-control, Type: text, Placeholder: "Write a message...", )
        Button(Class: btn btn-primary, Body: Send, Contract: AppContract, Params: "Message=Val(message_input)", Page:AppPage, PageParams:"table_view_offset=#table_view_offset#")
  }
}

Conclusion

This tutorial stops at the point where you have the basic application for your ecosystem. It doesn’t explain other important topics for application developers like layout styles, access rights management and interaction between apps and resources. Please consult the rest of the documentation for more information about these advanced topics.