Our pure JavaScript Scheduler component


Post by andreas-sakellariou »

Dear Bryntum team,

I am experimenting with the Scheduler Pro trial. My project needs to access a specific remote REST API service for events, resources and other data.

The REST API has its own structure, naming, request and response format. For instance, there is no 'success' entry, the data part is named 'records', the fields returned have names as set in the DB (not the default startDate/endDate, etc). See below for a brief description of the API.

So differences exist in many places from the structure of the requests/responses and naming to the way parameters/values are passed in both ways and so on. I assume that I have to use CrudManager with a lot of customization, so please point me to an example that is the most complete and relevant to my case. Any other advice would be welcome.

UPDATE: I was able to load data by customizing eventStore and resourceStore. The problem now is how to implement insert, update, delete given the specificities of the REST API (below).

Thanks in advance

Andreas Sakellariou

Description of the API
The same mechanism is used for any DB table. Filtering and other capabilities do exist but are not described below.
Let us assume that we have to schedule room usage by employees. To load all records from the db table 'events' the endpoint would be

GET /records/events

and the response (notice the array):

{
    "records":[
        {
            "ID": 1,
            "StartDT": "2018-03-05T20:12:56Z",  // start of event
            "FinishDT": "2018-03-05T20:12:56Z",  // end of event
            "EmployeeID": 121, //foreign key -Employees table
            "RoomID": 1, // resource id
            "EmployeeName: "Smith John",
.....
(other fields of various data types}
        },
        {
            "ID": 2,
            "StartDT": "2018-04-05T20:12:56Z",  // start of event
            "FinishDT": "2018-04-05T20:12:56Z",  // end of event
            "EmployeeID": 122, //foreign key -Employees table
            "RoomID": 2, // resource id
            "EmployeeName: "Smith Helen",
.....
(other fields of various data types}
        }        
] }

To insert a record:

POST /records/events

with params passed in the body

{
            "StartDT": "2018-04-05T10:13:33Z",  // start of event
            "FinishDT": "2018-04-07T00:10:12Z",  // end of event
            "EmployeeID": 133, //foreign key -Employees table
            "RoomID": 2, // resource id
......
}

ID is not passed since it is auto-incremented by the RDBMS. The response is the number of the new ID.

To read a specific record ie with ID=221 the request would be

GET /records/events/221

with response (a single Json object):

        {
            "ID": 221,
            "StartDT": "2019-03-05T20:12:56Z",  // start of event
            "FinishDT": "2019-03-05T20:12:56Z",  // end of event
            "RoomID": 3, // resource id
            "EmployeeID": 125, //foreign key -Employees table
            "EmployeeName: "Allen Mark",
.....
(other fields of various data types}
        }

To update a specific record ie with ID=221 the endpoint would be

PUT /records/events/221

and the field values that are to be updated passed in the body

        {
            "StartDT": "2019-03-05T20:12:56Z",  // start of event
            "FinishDT": "2019-03-05T22:15:56Z",  // end of event
            "RoomID": 3, // resource id
            "EmployeeID": 144, //foreign key -Employees table
.....
(other fields that need to be updated}
        }

The number of rows affected is returned as the response (ie 1)


Post by saki »

CrudManager has not been designed around REST specification but it tries to minimize the number of server trips that are needed to work with data. CrudManager caches all changes user made during an operation and sends the minimal possible number of requests. For example, the user drags an event to another resource and another time so only one request is sent to update event data and assignment data. This is most beneficial when working with dependencies where changing on one event can cause hundreds of dependent ones to be changed too which would lead to hundreds of PUT operations with REST.

I have created a feature request to investigate the possibility to use REST with Scheduler Pro here: https://github.com/bryntum/support/issues/3615

Meanwhile you can use native Fetch API to construct your requests yourself. It is not very difficult – I have modified our Scheduler Pro Percent Done vanilla demo to show how it could be done. Just replace app.js of that example with this content to see it in action:

import '../_shared/shared.js'; // not required, our example styling etc.
import SchedulerPro from '../../lib/SchedulerPro/view/SchedulerPro.js';
import '../../lib/Scheduler/column/ResourceInfoColumn.js';

// Import the percent bar feature
import '../../lib/SchedulerPro/feature/PercentBar.js';

const syncRest = async({ action, store, records }) => {
    if (action !== 'dataset') {
        records.forEach(record => {
            fetch(`./data/${store.id}/${record.id}`, {
                method : 'PUT',
                body   : JSON.stringify(record.modificationData)
            });
        });
    }
};

const scheduler = new SchedulerPro({
    // A Project holds the data and the calculation engine for Scheduler Pro. It also acts as a CrudManager, allowing
    // loading data into all stores at once
    project : {
        autoLoad  : true,
        transport : {
            load : {
                url : './data/data.json'
            }
        },
        // This config enables response validation and dumping of found errors to the browser console.
        // It's meant to be used as a development stage helper only so please set it to false for production systems.
        validateResponse : true
    },

    appendTo          : 'container',
    startDate         : '2020-03-23',
    endDate           : '2020-03-26',
    rowHeight         : 70,
    barMargin         : 22,
    eventStyle        : 'rounded',
    resourceImagePath : '../_shared/images/users/',

    // Custom view preset, with more compact display of hours
    viewPreset : {
        base      : 'hourAndDay',
        tickWidth : 15,
        headers   : [
            {
                unit       : 'day',
                dateFormat : 'ddd DD/MM' //Mon 01/10
            },
            {
                unit       : 'hour',
                dateFormat : 'h'
            }
        ]
    },

    listeners : {
        dataChange : syncRest
    },

    features : {
        columnLines  : false,
        dependencies : false,
        // Enable the percent bar feature
        percentBar   : true
    },

    columns : [
        {
            type           : 'resourceInfo',
            text           : 'Worker',
            showEventCount : true
        }
    ]
});

Post by andreas-sakellariou »

Hi saki,

Thank you very much for your detailed answer. It took me some time to experiment with the suggested solution. I was stuck at a point where I could not load data using the project entity. The reason? I was actually using Scheduler instead of SchedulerPro something I could not imagine since the example was taken from the SchedulerPro trial setup folder!

Your suggested solution although very effective seems to bypass the default and SchedulerPro-specific way of loading/syncing. It is probaly violating the transactional nature of the intended request. Syncing should be an "all-or-nothing" transaction to preserve consistency, something that requires the use of RDBMS transactions. The latter means special handling from the backend which is not provided by typical REST APIs. If I am getting it wrong, please correct me.

Based on the above, I realize that I should develop a special backend API for the scheduler (Pro) to handle multi-table load and updates using transactions (commit/rollback).

Please comment if possible.


Post by saki »

Yes, you have understood the problem perfectly and fully. I cannot imagine an API that would implement a transactional approach for RDBMS based on a pure, strict REST.

I should develop a special backend API for the scheduler (Pro) to handle multi-table load and updates using transactions (commit/rollback).

Right. That will be the best, safest and long-term approach. The above solution would only be a makeshift if you really couldn't use anything else but REST.

IMO, for the above reasons, REST is questionably useful also in other areas. If I imagine creating an e-shop order with many items, I would also want all-or-nothing transaction which would be best achieved if the order and its items came in one request.


Post Reply