Announcement

Collapse
No announcement yet.
X
  • Filter
  • Time
Clear All
new posts

    Workflow examples

    Hi,

    We would like to add workflow/wizard functionality into our existing SmartGWT based application. I have searched forum and found those threads requesting workflow examples, but not a single line of working example code:
    So, I would like to share what I have learned so far.

    I have created a simple flow starting with fetching process state data from client-only DataSource.
    Next node is a script task which logs data.
    Then, there is a user task which displays a form in the window and allows user to enter data.
    Modified data is processed by a gateway, which compares to specified criteria.
    Then it goes to error flow and back to user form or, if data matches criteria (field1='value1')), to node saving data and finishing process.

    Here is index.html js example code:
    Code:
    <!DOCTYPE html>
    
    <html>
        <head>
    
            <meta http-equiv="content-type" content="text/html; charset=UTF-8">
    
            <title>Client</title>
    
        </head>
    
        <body>
    
            <script type="text/javascript">
                var isomorphicDir = "isomorphic/";
            </script>
    
            <script src="isomorphic/system/modules-debug/ISC_Core.js">          </script>
            <script src="isomorphic/system/modules-debug/ISC_Foundation.js">    </script>
            <script src="isomorphic/system/modules-debug/ISC_Containers.js">    </script>
            <script src="isomorphic/system/modules-debug/ISC_Grids.js">         </script>
            <script src="isomorphic/system/modules-debug/ISC_Forms.js">         </script>
            <script src="isomorphic/system/modules-debug/ISC_RichTextEditor.js"></script>
            <script src="isomorphic/system/modules-debug/ISC_Calendar.js">      </script>
            <script src="isomorphic/system/modules-debug/ISC_DataBinding.js">   </script>
            <script src="isomorphic/system/modules-debug/ISC_Workflow.js">      </script>
    
            <script src="isomorphic/skins/Graphite/load_skin.js"></script>
    
            <script type="text/javascript">
    var ds = isc.DataSource.create({
        ID: "Test",
        dataURL: "test.data.xml",
        dataFormat: "xml",
        dataProtocol: "postXML",
        fields: [
            {name: "id", type: "text", primaryKey: true},
            {name: "field1", type: "text"},
            {name: "field2", type: "text"},
        ],
        clientOnly: true
    });
    var process = isc.Process.create({
        //state: {field1: "valueA", field2: "valueB"},
        elements : [
            isc.ServiceTask.create({
                ID: "data",
                dataSource: ds,
                outputFieldList: ["id", "field1", "field2"],
                operationType: "fetch",
                criteria: {field1: "valueA"},
                nextElement: "script"
            }),
            isc.ScriptTask.create({
                ID: "script",
                inputFieldList: ["id", "field1", "field2"],
                execute: function(input, inputRecord) {
                    isc.logEcho(inputRecord, "ScriptTask");
                },
                nextElement: "form"
            }),
            isc.UserTask.create({
                ID: "form",
                inputFieldList: ["id", "field1", "field2"],
                outputFieldList: ["id", "field1", "field2"],
                targetView: isc.Window.create({
                    ID: "wnd",
                    width: 300,
                    height: 120,
                    items: [isc.DynamicForm.create({
                        ID: "df",
                        fields:[
                            {name:"id", type:"text", hidden: true},
                            {name:"field1", type:"text"},
                            {name:"field2", type:"text"},
                            {title:"Submit", type:"submit"}
                        ],
                    })]
                }),
                targetForm: "df",
                nextElement: "gate"
            }),
    //      isc.XORGateway.create({ ID: "gate", inputFieldList: "field1", criteria: advancedCriteria, failureElement: "errorFlow", nextElement: "goodFlow" }),
            isc.DecisionGateway.create({
                ID: "gate",
                inputFieldList: ["id", "field1","field2"],
                criteriaMap: {goodFlow: {field1: "value1"}, errorFlow: {field1: "value2"}},
                defaultElement: "errorFlow"
            }),
            isc.ScriptTask.create({
                ID:"goodFlow",
                inputFieldList: ["id", "field1", "field2"],
                execute: function(input, inputRecord) {
                    isc.warn("goodFlow - save");
                    isc.Window.getById("wnd").hide();
                },
                nextElement: "save"
            }),
            isc.ServiceTask.create({
                ID: "save",
                inputFieldList: ["id", "field1", "field2"],
                dataSource: ds,
                operationType: "update",
            }),
            isc.ScriptTask.create({
                ID:"errorFlow",
                inputFieldList: ["id", "field1", "field2"],
                execute: function(input, inputRecord) {
                    isc.warn("errorFlow - try again");
                },
                nextElement: "script"
            })
        ]
    });
    
    process.start();
            </script>
    
        </body>
    </html>

    and MainEntryPoint.java gwt example code:

    Code:
    package pl.com.tech4.client;
    
    import com.google.gwt.core.client.EntryPoint;
    import com.smartgwt.client.data.Criteria;
    import com.smartgwt.client.data.DataSource;
    import com.smartgwt.client.data.Record;
    import com.smartgwt.client.data.fields.DataSourceTextField;
    import com.smartgwt.client.types.DSDataFormat;
    import com.smartgwt.client.types.DSOperationType;
    import com.smartgwt.client.types.DSProtocol;
    import com.smartgwt.client.util.SC;
    import com.smartgwt.client.util.workflow.DecisionGateway;
    import com.smartgwt.client.util.workflow.ProcessElement;
    import com.smartgwt.client.util.workflow.ScriptTask;
    import com.smartgwt.client.util.workflow.ServiceTask;
    import com.smartgwt.client.util.workflow.UserTask;
    import com.smartgwt.client.util.workflow.XORGateway;
    import com.smartgwt.client.widgets.Window;
    import com.smartgwt.client.widgets.form.DynamicForm;
    import com.smartgwt.client.widgets.form.fields.SubmitItem;
    import com.smartgwt.client.widgets.form.fields.TextItem;
    import java.util.HashMap;
    
    public class MainEntryPoint implements EntryPoint {
    
        @Override
        public void onModuleLoad() {
    
            layout();
        }
    
        private void layout() {
    
            DataSource ds = new DataSource();
            ds.setID("Test");
            ds.setDataURL("test.data.xml");
            ds.setDataFormat(DSDataFormat.XML);
            ds.setDataProtocol(DSProtocol.POSTXML);
            DataSourceTextField id = new DataSourceTextField("id");
            id.setPrimaryKey(true);
            DataSourceTextField field1 = new DataSourceTextField("field1");
            DataSourceTextField field2 = new DataSourceTextField("field2");
            ds.setFields(id, field1, field2);
            ds.setClientOnly(true);
    
            com.smartgwt.client.util.workflow.Process process = new com.smartgwt.client.util.workflow.Process() {
                @Override
                public void finished(Record state) {
                    SC.logWarn("Process finished.");
                }
            };
            process.setID("process");
    
            ServiceTask dataTask = new ServiceTask();
            dataTask.setID("data");
            dataTask.setDataSource(ds);
            dataTask.setOutputFieldList("id", "field1", "field2");
            dataTask.setOperationType(DSOperationType.FETCH);
            dataTask.setCriteria(new Criteria("field1", "valueA"));
            dataTask.setNextElement("script");
    
            ScriptTask scriptTask = new ScriptTask() {
                @Override
                public Object execute(Object input, Record inputRecord) {
                    SC.logEcho(inputRecord.getJsObj(),"scriptTask");
                    return input;
                }
            };
            scriptTask.setID("script");
            scriptTask.setNextElement("form");
            scriptTask.setInputFieldList("id", "field1", "field2");
    
            UserTask formTask = new UserTask();
            formTask.setID("form");
            formTask.setNextElement("gate");
            formTask.setInputFieldList("id", "field1", "field2");
            formTask.setOutputFieldList("id", "field1", "field2");
            final Window wnd = new Window();
            wnd.setWidth(300);
            wnd.setHeight(120);
            DynamicForm df = new DynamicForm();
            df.setID("df");
            TextItem idItem = new TextItem("id");
            idItem.setHidden(true);
            TextItem field1Item = new TextItem("field1");
            TextItem field2Item = new TextItem("field2");
            SubmitItem submit = new SubmitItem("submit");
            df.setFields(idItem, field1Item, field2Item, submit);
            wnd.addItem(df);
            formTask.setTargetView(wnd);
            formTask.setTargetForm(df);
    
    //        XORGateway gateway = new XORGateway();
    //        gateway.setCriteria(criteria);
    //        gateway.setNextElement("goodFlow");
    //        gateway.setFailureElement("errorFlow");
            DecisionGateway gateway = new DecisionGateway();
            gateway.setID("gate");
            HashMap criteriaMap = new HashMap();
            criteriaMap.put("goodFlow", new Criteria("field1", "value1"));
            criteriaMap.put("errorFlow", new Criteria("field1", "value2"));
            gateway.setCriteriaMap(criteriaMap);
            gateway.setDefaultElement("errorFlow");
    
            ScriptTask goodFlowTask = new ScriptTask() {
                @Override
                public Object execute(Object input, Record inputRecord) {
                    SC.warn("goodFlow - save");
                    wnd.hide();
                    return input;
                }
            };
            goodFlowTask.setID("goodFlow");
            goodFlowTask.setInputFieldList("id", "field1", "field2");
            goodFlowTask.setNextElement("save");
    
            ServiceTask saveTask = new ServiceTask();
            saveTask.setID("save");
            saveTask.setDataSource(ds);
            saveTask.setInputFieldList("id", "field1", "field2");
            saveTask.setOperationType(DSOperationType.UPDATE);
    
            ScriptTask errorFlowTask = new ScriptTask() {
                @Override
                public Object execute(Object input, Record inputRecord) {
                    SC.warn("errorFlow - try again");
                    return input;
                }
            };
            errorFlowTask.setID("errorFlow");
            errorFlowTask.setInputFieldList("id", "field1", "field2");
            errorFlowTask.setNextElement("script");
    
            ProcessElement[] elements = new ProcessElement[7];
            elements[0] = dataTask;
            elements[1] = scriptTask;
            elements[2] = formTask;
            elements[3] = gateway;
            elements[4] = goodFlowTask;
            elements[5] = saveTask;
            elements[6] = errorFlowTask;
    
    //        process.setElements(elements);//does not work
            process.getOrCreateJsObj();
            process.setAttribute("elements", elements, true);
    
            //Record state = new Record();
            //state.setAttribute("field1Item", "value2");
            //process.setState(state);
    
            process.start();
    
        }
    
    }
    Data file for client-only ds test.data.xml:
    Code:
    <response>
        <startRow>0</startRow>
        <endRow>1</endRow>
        <totalRows>2</totalRows>
        <data>
            <Test>
                <id>1</id>
                <field1>valueA</field1>
                <field2>valueB</field2>
            </Test>
            <Test>
                <id>2</id>
                <field1>valueC</field1>
                <field2>valueD</field2>
            </Test>
        </data>
        <status>STATUS_SUCCESS</status>
    </response>
    I have been working with SmartClient 12.1d dated 2019.12.01.

    Comments welcome.

    MichalG

    #2
    many thanks!

    Comment


      #3
      Hello, many thanks for your sample, I managed to find the time to try your same sample in my application with one of my dataSources. Now it's more clear how it works.

      BTW, did you ever use it for something useful in your applications? I mean, I'm not sure I see the advantage of using it with a simple sample like this, and also what Isomorphic says here:

      https://forums.smartclient.com/forum...184#post256184
      The Workflow system is really a target for visual tools
      makes me wonder if it is really useful outside of Reify.
      But probably it's me that I'm still not seeing the big picture of this API (which seems way more rich than the first time I saw it).
      Last edited by claudiobosticco; 15 Oct 2020, 07:18.

      Comment


        #4
        We see it as principally something for visual tools - although we’ve worked hard to make the XML workflow format nearly as expressive as code, it’s still slightly less so.

        Some people who are not using visual tools to edit workflows may prefer it because it could allow someone who can’t really write code to write limited bits of logic by writing XML.

        In Reify of course there is a full visual workflow editor, with all kinds of intelligence built in; we have seen non-programmers build complex branching logic - the kinds of thing you would definitely assume would require a developer.

        Comment


          #5
          I can't comment Reify usage - no experience in it.

          We found workflow subsystem quite flexible, especially together with xml definition.

          We just prepared few user assistants based on workflow and have plans to include them in next production version.

          They are targeted for tasks which would be done by users occasionally using our system, for example "failure report" which ask step by step where crash occurred, what broke, any photo and so on...

          Other target we hope workflow would help us is customized usage made upon customer's request provided and distributed as xml/js extensions to production version.

          Comment


            #6
            MichalG, thanks again for chiming in.

            Since you aren’t using visual tools to author it, what do you see as the principle value of workflow for these scenarios?

            Do you have non-programmers authoring it? Or perhaps reading it?

            Or are you trying to restrict extensions so they can only do certain things (that is, only things that can be expressed in a workflow)?

            Comment


              #7
              A bigger picture of our development environment is:

              An UML tool is a central point where project is created. Everything from menu, fields and panels definitions, buttons and reports are modeled using hierarchical tagged values of project elements.

              We have got an export plugin to project structure which prepares xml definitions of datasources, panels and so on. Those are loaded and interpreted by our SGWT based client application. SGWT client app shows user interface build from xml definition. Client app is not modified or customized in any way, at least between major versions. All customizations are made through diff changes of core system as it goes out from project.

              The point about workflow for us is its flexibility when defined in xml. We have got standard panels (grid above, for below linked together), but we lack more flexible user interface solutions from time to time and workflow seems to be good tool for that.

              We also see workflow useful when building wizards better adjusted to mobile usage: forms with one or just a few fields at a time, grids with just a few columns etc.

              Mobility is probably the most often asked feature of our potential customers now.

              As far as non-programmers of ours are concerned: they describe their requirements very traditional way (project management tracking system), but they have very good acquaintance of possible interface solutions. Then, programmers analyze it and implement. So far we find it quicker and result better quality.

              Comment


                #8
                Originally posted by Isomorphic View Post
                In Reify of course there is a full visual workflow editor, with all kinds of intelligence built in; we have seen non-programmers build complex branching logic - the kinds of thing you would definitely assume would require a developer.
                Hello, I recall that you said something about workflows in Reify, but did you mean that one could build a workflow in Reify? I don't see how. Or did you mean that Reify uses workflows for its functionalities?

                Comment


                  #9
                  claudiobosticco,

                  regarding SmartClient (not Reify): there is a whole chapter in the SmartClient docs about workflows, but I haven't seen them in action, yet, as the showcase does not include an example. It also does not load ISC_Workflow.js from #1.

                  Isomorphic: A sample showing a workflow in action would be great!

                  Best regards
                  Blama

                  Comment


                    #10
                    In Reify, if you go to the Events tab for any component, you can make the event take a standard Action (example: bind RecordClick to form.editSelectedRecord()) or you can pick "Workflow..." and you enter the Workflow editor.

                    Workflows are built out of a library of standard tasks, including DataSource operations, operations on components (like populating a form field) and decisions for branching workflows.

                    So you can visually design things like a workflow that checks if there is already a customer with a given name, and if there is, proceeds to the next step, otherwise, tells the user this is a new customer and drops them into a form for creating a customer record, with the name filled in.

                    It's very easy to use; it's meant for non-developers, and we have seen non-technical Product Managers successfully build complete branching workflows like the one just described.

                    If you want to add a Workflow editor to your product or application, we do offer that. It's called "Reify Embedded":

                    https://www.reify.com/reify-embedded

                    You can add the entirety of Reify to your application, as an extension engine, or any part of it, for example, just the Workflow Editor.

                    Reify is built as a SmartClient/SmartGWT component - you can literally do "new Reify()" and of course "new WorkflowEditor()".

                    So the concept is: you just add an "Extend.." button to your application, and that drops you into Reify, pre-populated with your application's DataSources and perhaps some of your custom components. With very little effort, you have just added a way for non-developers to build custom screens on the fly, including push-button, managed deployments.

                    You can do it at any granularity, as well. You could add a context menu like "Add custom UI here.." so that people can insert Reify-built functionality anywhere, such as adding a Button that executes a Reify workflow, or adding a new Tab that shows a Reify-built report. Or, if you start maintaining some of the application as Component XML, you can add the ability to customize those parts of the existing UI in Reify.

                    Of course, you don't need to offer this power directly to the end users. What a lot of customers seem to want is to add Reify to their applications as an admin-only tool. So, if they have their product deployed at a customer's site, they can go in and customize it, or add new capabilities.

                    Same thing with a SaaS offering. Imagine you have a customer or prospect and they need something customized or added, and you just do it and say "OK, how about now?".

                    The possibilities are pretty mind-blowing. We would greatly appreciate your help spreading the word, because there are a lot of people who would love to have capabilities like this, and they simply don't know that it's possible - they don't think that something like Reify exists.

                    Finally, your application doesn't need to be based on SmartClient to use Reify Embedded. You can just deliver Reify as a separate, self-contained system (e.g. in a Docker container) which talks to your app's data services via HTTP. So any product, no matter the technology stack, or whether it's installed or SaaS, can become extensible via Reify with just a modicum of effort.

                    Comment


                      #11
                      Originally posted by Isomorphic View Post
                      In Reify, if you go to the Events tab for any component, you can make the event take a standard Action (example: bind RecordClick to form.editSelectedRecord()) or you can pick "Workflow..." and you enter the Workflow editor.
                      Thanks for pointing me out to the Workflow editor in Reify, as I didn't noticed it by myself.

                      I just noticed that the component library lacks Calendars and Timelines: is there a problem with having them in Reify?

                      Comment


                        #12
                        No fundamental problem, they just require a bit of "hardening" to work perfectly in an editor. Basically, to work in a visual editor, a component needs to be able to never crash even if some of its required properties aren't provided, and then allow the necessary properties to be set in any order. So Calendar and Timeline just need a bit of polish in that area, and that work is already scheduled.

                        Note that, for third-party components, the same problem arises, so we've built this whole "Placeholder" system to allow Reify to edit components that aren't polished enough for visual editing:

                        https://smartclient.com/reifyOnSite/...stomComponents

                        This could be used to make Calendar and Timeline editable now, if you wanted.

                        Comment

                        Working...
                        X