Next training February 27. Next free webinar March 6.

Develop a fully functional business application within an hour with CUBA Platform (with English subtitles)


Description

In the course of this webinar you will create a fully functional bike mechanic workshop application from scratch. The webinar covers:

  • Defining data model and creating the database
  • Auto-generating CRUD UI
  • Generic filters
  • Security subsystem, including role-based and row-level security
  • Audit
  • Development beyond CRUD:
    • Integration with an IDE
    • Business logic implementation
    • Customization and hot deploy
  • REST API

Functional Specification

The application should meet the following requirements:

  1. Store customers with their name, mobile phone and email
  2. Record information about orders: price for repair and time spent by mechanic
  3. Keep track of spare parts in stock
  4. Automatically calculate price based on spare parts used and time elapsed
  5. Control security permissions for screens, CRUD operations and attributes of records
  6. Perform Audit of critical data changes
  7. Provide API for a mobile client to place an order

The data model is shown in the picture below:

Data model

Creating a New Project

  1. Start Cuba Studio server
  2. Create a new project workshop, select the latest platform version
  3. Select HSQL database type and IntelliJ IDEA as Java IDE

Now our project is properly configured and everything is ready to start working on the data model.

Defining Data Model and Creating the Database

According to the ER diagram, we will create 4 entities: Client, Mechanic, SparePart and Order and one enumeration OrderStatus.

Client Entity

  1. To create a new entity, open the Data Model section of the left-hand side navigation panel and click New -> Entity. Set Class name: Client and click OK.
  2. To create a new entity attribute, click the New button under the Attributes table. Enter the name of the attribute: name. Click Add.
  3. Similarly add other attributes: phoneNumber (String, mandatory, length=20, unique) and email (String, length=50, unique).
  4. Go to the INSTANCE NAME tab of the entity designer screen and select name and phoneNumber fields one by one. Click OK in the top-right corner to save the entity.

Mechanic Entity

  1. In the same way create the Mechanic entity.
  2. Add the user attribute which is a link to the standard CUBA User entity.
    • Name: user
    • Attribute type: Association
    • Type: User [sec$User]
    • Cardinality: MANY_TO_ONE
    • Mandatory: Yes
  3. Add the hourlyRate attribute (BigDecimal, mandatory).
  4. Set name as the entity's instance name and save the new entity.

SparePart Entity

  1. Create the SparePart entity with the following attributes:
    • title (String, mandatory, unique);
    • description (String, unlimited length);
    • price (BigDecimal, mandatory).
  2. Set title as an instance name.

OrderStatus Enum

For the Order entity, we'll need to create the OrderStatus enum first. The status will be stored in the DB as numeric values.

  1. Open the Data Model section of the left-hand side navigation panel and click New -> Enumeration.
  2. Enter the name: OrderStatus.
  3. Select Integer id type, then add the enum values:
    • NEW (10),
    • IN_PROGRESS(20),
    • READY(30).
  4. Click OK to save the enumeration.

Order Entity

  1. Create the Order entity with the following attributes:
    • client (many-to-one association to the Client entity, mandatory);
    • mechanic (many-to-one association to the Mechanic entity, mandatory);
    • description (String, unlimited length);
    • hoursSpent (Integer);
    • amount (BigDecimal);
    • parts (many-to-many association to the SparePart entity);
    • status (enum of OrderStatus type).
  2. Set the instance name for the Order entity to its description attribute and save the new entity.

Generate DB Scripts and Create the Database

  1. Click the Generate DB scripts at the bottom of the Data Model section; Studio will generate a script to create tables and constraints.
  2. Click Save and close, and Studio will save the scripts into a special directory of our project, so we will be able to access them if needed.
  3. Invoke the Run -> Create database action from the menu to create a database. CUBA Studio warns us that the old DB will be deleted, click OK.

Auto-generated CRUD UI

Under CRUD or standard UI we understand screens that allow you to browse the list of records (browser) and create/edit one record (editor).

SparePart CRUD UI

  1. Open the Data Model section of the left-hand side navigation panel and select the SparePart entity and click New -> Generic UI Screen.
  2. On the Generic UI Templates page select the Entity browser and editor screens template and click Create.
  3. Studio has generated two separate screens: browser and editor. You can edit them in WYSIWYG screen designer by clicking Edit.

Visual components that tightly work with data (tables, dropdowns, etc) are data-aware and connect to the database through datasources. Datasources use standard JPQL queries to load data.

For the sparepart-browse.xml screen, the sparePartsDs is set to the datasource property of the main table, and result list of its query will be automatically shown in this table.

Client CRUD UI

Similarly to SparePart, create the standard browser and editor screen for Client.

Mechanic CRUD UI

Mechanic differs from the entities we have just created UI for. It has relation to the system User entity, which is used in CUBA for security reasons. So, our datasource should load the user field along with the simple fields of the Mechanic entity.

For that purpose, CUBA uses views - an XML description of what entity attributes should be loaded from the database.

  1. On the Generic UI Templates page, click the plus button to create a new view, and the View designer screen will appear.
  2. Studio has automatically generated the view name: mechanic-view, keep it as we will use it for both browser and editor.
  3. Our view extends the _local one, which is the system view that includes all the simple (local) attributes. By default, all screens use _local view for standard screen generation. It means that references to other entities will not be loaded and shown by default.
  4. Select user attribute and specify the _minimal system view for it; _minimal view includes only attributes that are used for the entity's instance name. This view will give us enough information to show an entity in the browser's table and editor's field group.
  5. Click OK to save the view.
  6. Select mechanic-view for both browser and editor and create the screens.

Order CRUD UI

  1. Open Generic UI Templates page to create the screens for the Order entity.
  2. Create the new order-view: specify the _minimal view for client and mechanic attributes, also select title and description for the parts attribute.
  3. Select order-view for both browser and editor and create the screens.

First Launch

Our application is done, of course, to a first approximation. Let's compile and launch it!

  1. Invoke the Run -> Start application server action from the menu. Studio will deploy a local Tomcat instance in the project subdirectory, deploy the compiled application there and launch it.
  2. Open the application by clicking a link in the bottom part of the Studio.
  3. CUBA application welcomes us with the login screen. It's a part of the security module, integrated into the platform. Default login and password are already set on the screen, so just click Submit.
  4. After successful login you can see the main window of your application, which can be customised from the Studio, similarly to any other screen.
  5. Open Application - Orders from the menu.
    The standard browser screen appears. It contains a generic data filter on top, a table of records with buttons on top, performing standard CRUD actions. There are more standard actions available in the platform, for example, export to excel.
    Click Create.
  6. This is the autogenerated editor screen for the Order entity. Let's fill up the form. Click […] to select a client.
  7. There are no clients in the system yet, so the client browser is empty. Click Create and add a new client to the system. Double click the client record we have just created or simply click Select on the bottom part of the client browser. Create the Mechanic field in the same way.
  8. Complete entering data for the order using the following values and click OK

Our CRUD application is ready, so we have successfully completed items 1-3 of the functional specification!

Generic Filter

Now let's examine how data filter works. The CUBA generic filter enables you to filter data by all the fields of the screen main entity, all the entities it refers to as well as their fields. It also gives you the ability to create custom filtering rules based on JPQL queries and to save them for repeated use.

You can find the full version of this part of lab with screenshots on workshop wiki on GitHub.

See more on filters in the corresponding tutorial.

Security Setup

The platform has built-in functionality to manage users and access rights. This functionality is available from the Administration menu.

The CUBA platform security model is role-based and controls CRUD permissions for entities, attributes, menu items and screen components and supports custom access restrictions.

All security settings can be configured at runtime. There is also an additional facility to control row level access.

You can find the full version of this part of lab with screenshots on workshop wiki on GitHub.

See more on security settings in our tutorials: Access Groups, User Roles and Access Rights, and User Substitution.

Audit

There is a requirement in our functional specification to track critical data changes. CUBA Platform has a built-in mechanism to track entity changes, which you can configure to track operations with critical system data.

Keeping track of our application, it happens when one day someone has accidentally erased the order description. It is not appropriate to call the client on the phone, apologize and ask them to repeat the what needs to be done.

You can see how this can be avoided on this workshop wiki.

First Launch Conclusion

While spending short time exploring the CUBA Platform and its features we have already finished the following requirements from the functional specification:

  1. Store customers with their name, mobile phone, and email
  2. Record information about orders: price for repair and time spent by mechanic
  3. Keep track of spare parts in stock
  4. Automatically calculate price based on spare parts used and time elapsed
  5. Control security permissions for screens, CRUD operations, and records’ attributes
  6. Perform Audit of critical data changes
  7. Provide API for a mobile client to place an order

Note that we didn't even type any line of source code, everything has been provided by the platform or scaffolded by the Studio. Let's move forward towards business logic.

Development beyond CRUD

Integration with IDE and Project Structure

Let's have a look how our project looks from inside. Keep your application up and running and follow the steps:

  1. Launch IntelliJ IDEA. The IDE should be up and running to enable integration with the CUBA Studio. If you don’t have the CUBA Plugin installed, please install it, cause it is used as a communication bridge between the Studio and IDE.
  2. Go to the Project properties section in Studio.
  3. Click the IDE button. The Studio will generate project files and open the project in the IDE.
  4. Move to the IDE and press Alt+1 to see the project structure. By default, any project consists of 4 modules: global, core, web, gui.
    • global - data model classes
    • core - middle tier services
    • gui - common component interfaces; screens and components for both Web and Desktop clients and
    • web - screens and components for Web client

Customization of Existing Screens and Hot deploy

In this chapter, we will polish our Orders browser and editor by adding logic into the controller and changing the user interface.

Initialization of a New Record in Editor

Let's solve the first problem with an empty status of the newly created orders. A new order should be created with the NEW order status pre-set.

  1. Go to the Generic UI section of the navigation panel in the CUBA Studio.
  2. Select the order-edit.xml screen.
  3. Click the IDE button on top of the section. Screen descriptor appears in your IDE. You can make any changes right from the source code because The Studio and an IDE has two ways synchronization.
  4. Hold Ctrl button and click on OrderEdit in the class attribute of the XML descriptor to navigate to its implementation.
  5. Override method initNewItem() and set status OrderStatus.NEW to the passed order:
    
        public class OrderEdit extends AbstractEditor<Order> {
            @Override
            protected void initNewItem(Order item) {
                super.initNewItem(item);
                item.setStatus(OrderStatus.NEW);
            }
        }
      
  6. We haven't stopped our application and it is still up and running in the browser. Open/Reopen Application - Orders screen.
  7. Click Create.
  8. We see our changes, although we haven't restarted the server. The CUBA Studio automatically detects and the hot-deploys changes, except for the data model, which saves a lot of time while UI development and business logic development.

Adding Standard Excel Action to the Orders Browser

The standard screens contain Create, Edit, and Remove actions by default. Let's add an action to export the order list to Excel, which is also a standard action you can use out of the box. You can follow the link to learn more about standard actions.

  1. Open the order-browse.xml screen in the Studio.
  2. Select table component, go to properties panel.
  3. Click the edit button in the actions property.
  4. Add a new action row to the list.
  5. Specify id as excel for this action.
  6. Click OK.
  7. Adding Standard Excel Action to the Orders Browser.
  8. Add a new button to the button panel (drag and drop it from the components palette into the hierarchy of components.
  9. Select ordersTable.excel action for the button using properties panel.
  10. Save the screen by clicking OK in the top-right corner.
  11. Let's use the magic of hot deploy once again. Open/Reopen the Orders screen.
  12. Click Excel to export your orders to an XSL file.

Adding Custom Behaviour

Our mechanics are yelling that it's too annoying to go to the Order editor every time they want to change the order status. They would like to click a button and set the corresponding status from the browser.

  1. Open the order-browse.xml screen in Studio.
  2. Add a new button to the button panel (drag and drop it into the hierarchy of components).
  3. Set the following properties for the button:
    • id: btnNewStatus
    • caption: Set as New
  4. Click the [>>] button on the right side of the invoke field.
  5. The Studio will generate a method, that will be called on button click. Press Ctrl+I to save the changes and open the screen controller in your IDE.
  6. CUBA extends the standard spring injection mechanism with the ability to inject CUBA related infrastructure and UI components. We will need to inject the datasource from our screen:
    
    @Inject
    private CollectionDatasource<Order, UUID> ordersDs; 
    
  7. Let's implement the onBtnNewStatusClick method, that was created by the Studio:
    
    public void onBtnNewStatusClick(Component source) {
    Order selectedItem = ordersDs.getItem();
    	if (selectedItem != null) {
           selectedItem.setStatus(OrderStatus.NEW);
           ordersDs.commit();
       }
    }
    
  8. Open/Reopen the Orders screen to see the mystery of hot deploy. Now our mechanics can work with maximum productivity.

Business Logic Development

As the next step, let's add business logic to our system to calculate the order price when we save it in the edit screen. The amount will be based on the spare parts price and time spent by the mechanic.

  1. To use mechanic hourly rate and prices for parts, we'll need to load this attribute, so we need to add it to the order-view. In order to change the view switch to the Studio, open the Entities section of the Studio navigation panel, select the order-view item and click Edit.
  2. Include the hourlyRate and price for parts attributes to the view.
  3. Click OK to save the view. From now we will have access to the hourlyRate and price attributes from the orders screens (editor and browser).
  4. Go to the Middleware section in the Studio and click New - Service.
  5. Change the last part of Interface name to OrderService.
  6. Click OK. The interface will be located in the global module, its implementation - in the core module. The service will be available for invocation for all clients that are connected to the middle tier of our application (web-client, portal, mobile clients or integration with third-party applications).
  7. Select the OrderService item in the navigation panel and click IDE.
  8. In Intellij IDEA, we'll see the service interface, let's add the amount calculation method to it:
    
    BigDecimal calculateAmount(Order order);
    
  9. Go to OrderServiceBean using the green navigation icon at the left and implement the caclulateAmount method:
    
    @Service(OrderService.NAME)
    public class OrderServiceBean implements OrderService {
    
        @Override
        public BigDecimal calculateAmount(Order order) {
            BigDecimal amount = new BigDecimal(0);
            if (order.getHoursSpent() != null) {
                amount = amount.add(new BigDecimal(order.getHoursSpent())
                   .multiply(order.getMechanic().getHourlyRate()));
            }
            if (order.getParts() != null) {
                for (SparePart part : order.getParts()) {
                   amount = amount.add(part.getPrice());
               }
           }
           return amount;
       }
    }
    
  10. Go back to Studio. Select the order-edit.xml screen in the Generic UI section of the navigation panel and click IDE.
  11. Go to the screen controller (OrderEdit class). Add OrderService field to class and annotate it with @Inject annotation:
    
    @Inject
    private OrderService orderService;
    
  12. Override the preCommit() method and invoke the calculation method of OrderService:
    @Override
    protected boolean preCommit() {
       Order order = getItem();
       order.setAmount(orderService.calculateAmount(order));
       return super.preCommit();
    }
    
  13. Restart your application using the Run - Restart application action from Studio.
  14. Open Application - Orders from the menu. Open editor screen for any order.
  15. Set Hours Spent. Click OK to save order.

We can see a newly calculated value of the amount in the table.

Now we can automatically calculate the price based on spare parts used and time elapsed as it was mentioned in our functional specification!

REST API

Often enterprise systems have more than one client. In our example, we have web client for our back office staff. In the future, we could come with an idea of having a portal and/or a mobile application for our customers. For this purpose, CUBA provides developers with the generic REST API.

REST API is enabled by default in the web client, thus we can use it right now for a simple web page that will show the list of recent orders for a logged in user.

Our page will consist of login form and list of recent orders.

  1. Create orders.html and orders.js in web module: /modules/web/web/VAADIN/orders.html.
  2. Add jquery-3.1.1.min.js and bootstrap.min.css prerequisites.
  3. Let's implement our page. Our HTML form will consist of 2 fields: login and password. We will send login request to REST API on Submit button click:
    
     <!DOCTYPE html>
       <html lang="en">
           <head>
               <script type="text/javascript" src="./jquery-3.1.1.min.js"></script>
               <script type="text/javascript" src="orders.js"></script>
               <link rel="stylesheet" href="bootstrap.min.css"/>
           </head>
           <body>
               <div style="width: 300px; margin: auto;">
                   <h1>Bike Workshop</h1>
    
                   <div id="loggedInStatus" style="display: none" class="alert alert-success">
                       Logged in successfully
                   </div>
                   <div id="loginForm">
                       <div class="form-group">
                           <label for="loginField">Login:</label>
                           <input type="text" class="form-control" id="loginField">
                       </div>
                       <div class="form-group">
                           <label for="passwordField">Password:</label>
                           <input type="password" class="form-control" id="passwordField">
                       </div>
                       <button type="submit" class="btn btn-default" onclick="login()">Submit</button>
                   </div>
    
                   <div id="recentOrders" style="display: none">
                       <h2>Orders</h2>
                       <ul id="ordersList"></ul>
                   </div>
               </div>
           </body>
       </html>
    
  4. We will send login request according to OAuth protocol:
    
    var oauthToken = null;
    
       function login() {
           var userLogin = $('#loginField').val();
           var userPassword = $('#passwordField').val();
           console.log ( '#someButton was clicked' );
           $.post({
               url: 'http://localhost:8080/app/rest/v2/oauth/token',
               headers: {
                   'Authorization': 'Basic Y2xpZW50OnNlY3JldA==',
                   'Content-Type': 'application/x-www-form-urlencoded'
               },
               dataType: 'json',
               data: {grant_type: 'password', username: userLogin, password: userPassword},
               success: function (data) {
                   oauthToken = data.access_token;
                   $('#loggedInStatus').show();
                   $('#loginForm').hide();
                   loadRecentOrders();
               }
           })
       }
    	
  5. If we successfully logged in, then we load the list of recent orders:
    
    function loadRecentOrders() {
       $.get({
           url: 'http://localhost:8080/app/rest/v2/entities/workshop$Order?view=_local',
           headers: {
               'Authorization': 'Bearer ' + oauthToken,
               'Content-Type': 'application/x-www-form-urlencoded'
           },
           success: function (data) {
               $('#recentOrders').show();
               $.each(data, function (i, order) {
                   $('#ordersList').append("
  6. " + order.description + "
  7. "); }); } }); }
  8. And if we try to log in using default login: admin and password: admin we will see our orders list:

    REST API example

That was the last requirement from the functional specification. Now the system is ready for production!

Conclusion

This is a very small application for bicycle workshop management. It is simple but can be applied to a real local workshop.

You can run it in the production environment (including clouds) as is, and it will be suitable for its purpose.

You can add much more functionality using CUBA additional modules, and this enables you to grow your application to a big strong solution.

We have many more features!

In this session covers just a few features of CUBA, but the platform has many more...

If you want to learn more about additional modules and components just take a look at CUBA documentation:
https://www.cuba-platform.com/manual

Questions?

Visit our forum:

https://www.cuba-platform.com/discuss/