Using Araxis Merge in a simple parallel development scenario

Using Araxis Merge in a simple parallel development scenario

The following document describes the steps used that cover a standard parallel development scenario across two streams.

 

 

The attached document describes a practical 6 step scenario working on both the mainline and patch development and how Araxis Merge can be used to help assist the merge conflicts.

 

Step 1: Main Line Development (Version 1.0)   
Step 2: Work on a Branch Patch Release (Version 1.1)   
Step 3: Make a patch modification (Version 1.1)   
Step 4: Main Line Development (Version 2.0)   
Step 5: Merge the branch modifications into the mainline   
Step 6: Review the merge   

Please see the attached document for further details.

Continue reading
73 Hits
0 Comments

COMING SOON: Silk Test Workbench Essentials (SLK430)

COMING SOON: Silk Test Workbench Essentials (SLK430)

This new 1-day course has been developed as a fully-fledged Instructor Led Training course (ILT). The course includes progressive learning modules, including a hands-on lab experience to accelerate learning.

As well as developing a fully featured test scenario, learning is further enhanced and re-enforced through participation in a fully featured and expanded wrap up lab exercise and assessment.

Lab exercises are systematically themed, providing fully annotated instruction. Each module allows the student to build out live test scenarios that harness the major technologies employed in automated functional testing offered by Silk Test Work Bench using user friendly interfaces, designed to get your team building smooth, efficient automated tests, without the need for in-depth scripting knowledge.

For more information on the course, see the following course outline.

 

The following modules are covered:

• Module 1: Course introduction
• Module 2: Introduction to Silk Test Workbench
• Module 3: Creating a Silk Test Workbench project
• Module 4: Managing Application Configurations
• Module 5: Recording a test 
• Module 6: Object recognition
• Module 7: Adding verification
• Module 8: Play back tests and analyze results
• Module 9: Testing Web applications on mobile browsers
• Module 10: Example: Cross-browser testing
• Module 11: Wrap-up Exercises
• Wrap-up Assessment

Recent Comments
Paul Caruana
You are welcome Raj
Monday, 15 October 2018 8:08 AM
Raj Khanna J
Great post paul keep up the good work and cheers.
Saturday, 13 October 2018 10:10 AM
Continue reading
113 Hits
2 Comments

New Course: Silk4J Scripting Essentials (SLK420)

New Course: Silk4J Scripting Essentials (SLK420)

Featuring a brand-new Instructor Led Training Course aimed for developers who wish to harness the power of Java in Eclipse, using the Silk4J plugin.

The following modules are covered:

• Module 1: Course introduction
• Module 2: Introduction to Silk4J
• Module 3: Creating a Silk4J project
• Module 4: Managing Application Configurations
• Module 5: Recording a test 
• Module 6: Object recognition
• Module 7: Adding verification
• Module 8: Play back tests and analyze results
• Module 9: Testing Web applications on mobile browsers
• Module 10: Example: Cross-browser testing
• Module 11: Wrap-up Exercises
• Wrap-up Assessment

See more information on the Course and the Course Outline for more information.

Continue reading
125 Hits
0 Comments

COMING SOON: SILK4J Scripting Essentials (SLK420)

COMING SOON: SILK4J Scripting Essentials (SLK420)

This new 1-day course has been developed as a fully-fledged Instructor Led Training course (ILT). The course includes progressive learning modules, including a hands-on lab experience to accelerate learning.

As well as developing a fully featured test scenario, learning is further enhanced and re-enforced through participation in a fully featured and expanded wrap up lab exercise and assessment.

Lab exercises are systematically themed, providing fully annotated instruction. Each module allows the student to build out live test scenarios that harness the major technologies employed in automated functional testing offered by Silk Test leveraged with the programming power of Java using the Eclipse IDE.

For more information on the course, see the following course outline.

Continue reading
183 Hits
0 Comments

New Course: Silk4Net C# Scripting Essentials (SLK410)

New Course: Silk4Net C# Scripting Essentials (SLK410)

Featuring a brand new Instructor Led Training Course aimed for developers who wish to harness the power of Visual Studio (C#), using the Silk4Net plugin.

 

The following modules are covered:

 

• Module 1: Course introduction
• Module 2: Introduction to Silk4NET
• Module 3: Creating a Silk4NET project
• Module 4: Managing Application Configurations
• Module 5: Recording a test 
• Module 6: Object recognition
• Module 7: Adding verification
• Module 8: Play back tests and analyze results
• Module 9: Testing Web applications on mobile browsers
• Module 10: Example: Cross-browser testing
• Module 11: Wrap-up Exercises
• Wrap-up Assessment

 

See more information on the Course and the Course Outline for more information.

 

Continue reading
149 Hits
0 Comments

New Course: Silk4Net Scripting Essentials (SLK400)

New Course: Silk4Net Scripting Essentials (SLK400)

Featuring a brand new Instructor Led Training Course aimed for developers who wish to harness the power of both Test Director and Visual Studio (VB.net), using the Silk4Net plugin.

The following modules are covered:

• Module 1: Course introduction
• Module 2: Introduction to Silk4NET
• Module 3: Creating a Silk4NET project
• Module 4: Managing Application Configurations
• Module 5: Recording a test 
• Module 6: Object recognition
• Module 7: Adding verification
• Module 8: Play back tests and analyze results
• Module 9: Testing Web applications on mobile browsers
• Module 10: Example: Cross-browser testing
• Module 11: Wrap-up Exercises
• Wrap-up Assessment

See more information on the Course and the Course Outline for more information.

Continue reading
257 Hits
0 Comments

Reporting with Complex Systems

“A little conflict can create a lot of activity.” –Richie Norton

The conflict we’re addressing today is that more and more systems, especially modern SaaS systems, are moving to different authorization protocols.

  • Authorization Micro Services: “I need to generate a token automatically and pass that token into all of my subsequent RESTful web service calls. It’s not necessarily a proper protocol, but that’s how you work with our APIs.”
  • Data Abstraction: We use this in SBM as TS-IDS. The item identifier that might come back from a web service call may not actually be the human-readable identifier for an item ID. We have internal identifiers that come back, and the actual values are stored in a different table.
  • Lack of Data Consistency: Depending on the system that you’re querying, you might get back an all-lowercase new value, a camel-case new value, or all caps. Every system stores these items a little differently.

And there are several other conflicts or challenges that we have with external systems when we bring all of these together. So how do we handle these types of challenges?

We can do direct database reporting: Within SBM, we provide several different methods of reporting against systems:

  • With the Data Service, we can actually connect directly to a database and add a web services layer on top of these databases that may not actually have web services exposed.
  • We can do the same thing with Advanced Reporting—we actually pull these and you’re able to use our reporting engine to get back the actual data with a SQL query.
  • We can also use ModScript and RESTful Web Services. This is an interesting option, because most people don’t think about the ModScript engine in terms of being able to actually query other systems and use it to report all of that data coming back. How can we actually do that?

Let’s start by diving into what ModScript is. ModScript was first introduced in SBM 11.2 or 11.3. With every SBM release, we’ve been improving ModScript and adding more features and functionality.


ModScript is based on a scripting language called ChaiScript, which is based on ECMAScript, which is the basis of JavaScript. It executes on the server side, so when you think of client/server execution, it doesn’t take up any resources on the browser side—all of those requests are done from the server itself.

ModScript has contextual awareness, so it knows things like whether you’re trying to execute the script in the context of a transition, if you’re directly invoking the script, or if it’s being invoked by a URL. Since we can invoke these scripts via a URL , we can return back HTML, but we can also return back JSON—and JSON is the de facto standard for web service calls when you’re using the REST protocol.

There are several uses for Modscript:

  • We can do server-side integrations and processing data
  • We can utilize external DLLs
  • We can perform database-level integrations with SBM
  • We can execute external Command Line commands
  • We can also insert dynamic HTML and JavaScript as well as generate JSON

This works when we use ModScript as a proxy, so everything is as-a-service. As shown in the diagram below, ModScript can be invoked by an SBM URL. The script then executes, and that script is written so that we request the authorization token. The destination system would then send back our authorization token, and we would perform the RESTful web service call to retrieve the dataset with that token in place in the payload. The destination system would send back the dataset that we need, and our script would process that data. Finally, ModScript returns the data to the user.

When we say “The script processes the data,” this processing can happen a few different ways. If the service sends back everything exactly the way we need it and we just need to be able to handle that authorization – great! In some cases, we may want to format the data, or we have any of those data consistency issues that we mentioned above. For example, you might want to change a lowercase “low priority” field title to a camel case to match your other data. Or you may want to trim off some system value that’s appended to the end. In our demo, we go through how we would do that within the ModScript itself.

Check out our webinar recording for Reporting with Complex Systems here, which includes a great demo of ModScript used as a proxy integrated with Micro Focus Service Management Automation X (SMA-X). We’ll generate an authentication token and normalize some data being returned to what we would see out of SBM. We’ll also show you how we return JSON from an already existing report.

See our Discovering SBM page for all of our webinars covering a range of SBM features and functionality. And, as always, we want to hear from you! Tell us what you want to hear more about or anything you’d like to see addressed in an upcoming session.

Recent Comments
Raj Khanna J
Great article very helpful https://www.webdschool.com/graphic-design-courses-in-chennai.html
Saturday, 13 October 2018 10:10 AM
Paul Caruana
Good article
Wednesday, 29 August 2018 9:09 AM
shiney bratt
great!
Wednesday, 05 September 2018 10:10 AM
Continue reading
313 Hits
4 Comments

COMING SOON: Silk4Net Scripting Essentials

COMING SOON: Silk4Net Scripting Essentials

Coming soon, a new fully featured course for Silk Test.

 

This new 1-day course has been developed as a fully-fledged Instructor Led Training course (ILT). The course includes progressive learning modules, including a hands-on lab experience to accelerate learning.

As well as developing a fully featured test scenario, learning is further enhanced and re-enforced through participation in a fully featured and expanded wrap up lab exercise and assessment.

Lab exercises are systematically themed, providing fully annotated instruction. Each module allows the student to build out live test scenarios that harness the major technologies employed in automated functional testing offered by Silk Test.

 

 

Recent Comments
Daniel Nolan
x
Wednesday, 25 July 2018 10:10 AM
Denis Bill
Great
Wednesday, 29 August 2018 5:05 AM
Canon Printer Support Number
Fantastic
Wednesday, 19 September 2018 12:12 PM
Continue reading
913 Hits
3 Comments

UPDATED COURSE: 200 DA FOR RELEASE ENGINEERS

UPDATED COURSE: 200 DA FOR RELEASE ENGINEERS

Following the release of DA 6.2, 200 DA for Release Engineers  has been updated.

This course update contains the following additions and benefits:

 

  • New 6.2 features covered:
    • UI updates including Process Designer

    • Deployment Packages

  • End of module quiz assessments added:

    • Re-inforce learning

    • Provide additional group interaction

  • Additional lab exercise content covering:

    • Fail and Debug scenario  added

    • Improved UI navigation to harness UI improvements

    • Added Application Gates informationally and as a practical exercise

    • Added Pipelines informationally and as a practical exercise

    • Improved diagnostic checks

    • Limited step repeat scenarios – allow complex exercises to be completed more efficiently

    • Introduced systematic 15 point method for a new and fully functioning DA Application creation module

    • Deployment technology update including DA and CM Deployment comparison

       

  • Exercise improvements: more real life role-play work scenarios, simplified procedures

  • Content improvements: reformatting, improved layout and additional visual aids to understanding.

     


More information on the DA200 course can be found in the Course Outline.


More information on DA courses can be found in the:  Courseware Catalogue

 

 

 

 

 

 

 

 

 

Continue reading
756 Hits
0 Comments

Release Train for Solution Business Manager

Recently, we've formalized the release train for Solutions Business Manager (SBM) to be as predictable and agile as possible. By holding to a predictable release schedule, our hope is that customers will be better able to plan resources and schedule upgrades in a repeatable and standardized way. In addition, by releasing new versions often, new customer innovation and corrections to non-critical customer reported defects can be distributed more quickly.

Quarterly Releases

Our goal is to release a new version of SBM once a quarter. To achieve this cadence, we expect to generally alternate between minor and maintenance (update) versions. For each minor release (e.g. 11.3) we expect to release an update release in the following quarter (e.g. 11.3.1). This will then be following by a minor release the next quarter (e.g. 11.4) and the cycle repeats.

A minor release is defined as a release that may introduce new features or deprecate legacy features, but will not introduce significant architectural changes to the product. An update version may introduce enhancements to existing features but typically will not include completely new features. As needed, we may release a major release instead of a minor release which would include architectural changes, but this is not expected to happen often.

Codenames for releases

Generally, SBM uses code names for releases prior to achieving code complete. To make it easy to understand the cadence of SBM, code names are in alphabetical order and follow a theme. The theme for SBM is "cities in the United States". So, for example, we've had releases codenamed "Aurora", "Babylon", "Chattanooga", "Dillsburg" and "Eugene". Because the releases are in alphabetical order, you can always tell if a release comes before or after another - i.e. Dillsburg came after Chattanooga and before Eugene.

Upgrading has become routine

Improvements since the SBM 11.0 release have made upgrades efficient and quick to perform. For example, over the last 6 releases (releases from 11.0 to 11.4), Micro Focus has upgraded our internal production version of SBM in under 2 hours including both the upgrade and post-upgrade testing. Because it is much easier to upgrade than in the 10.x series, it is our hope that customers will stay current on the latest release and be able to participate in the quarterly release cadence.

 

We hope that this new predictable release train, coupled with easy identification and efficient upgrades, will enable customers to take full advantage of every release and the new capabilities provided in each version.

Continue reading
409 Hits
0 Comments

What's new in SBM 11.4?

Solutions Business Manager 11.4 released on May 24th, 2018. This minor release is intended to augment orchestration options, ease administration, improve navigation, enhance developer flexibility, and address customer ideas and defects. Here are some of the highlights from the release. 

New Application Administrator Interface

  What it is: The SBM Application Administrator user interface now leverages HTML5 functionality that results in an improved user experience for administrators including enhanced navigation, direct access to analytics and statistics, a tree view for projects, fewer clicks to view project or user details, and a modern look and feel.

  Why we did it: As has been widely communicated, Flash based technology is no longer considered secure enough for today's product offerings. By moving from Flash to HTML5, these security concerns are addressed. In addition, since all of the facets of Application Administrator had to be converted, we took the time to review navigation and launch points within SBM to improve how administrators use SBM. Finally, there's a trend today toward flat, monochromatic interfaces being driven primarily in the mobile marketplace, so we took this opportunity to embrace this trend so that Application Administrator feels in line with other parts of SBM.

New Scheduler Feature

What it is: The new Scheduler feature enables administrators to define and schedule jobs to perform tasks like run scripts, execute Web services, send email, call REST services, or raise orchestration events at a specific date and time or on a recurring basis.

Why we did it: Adding a native scheduler to SBM dramatically broadens the options available to a process designer by allowing asynchronous actions to be taken on a consistent basis. Rather than waiting for an event to be raised or external source to connect with SBM, SBM can now initiate such polling actions. For example, SBM can check with another solution to determine if unexpected changes have occurred and then notify stakeholders of the situation - even using the E-mail transition feature within SBM to allow a transaction to be performed remotely.

 

 Improved Runtime Statistics 

  What it is: Several new report types have been added to Runtime Statistics that provide usage statistics for Applications, Orchestrations, and Reports for a given time period including access counts, execution counts and run counts.

  Why we did it: There are two primary drivers for these enhancements. First off, statistics can be used to better understand how not only SBM as a whole is being used but also how an individual process app or report is being utilized. Rarely used items can be identified for sunsetting, or updating of commonly used items can be business justified. Secondly, administrators need to have tools to monitor unexpected resource use of the SBM system. By either watching real-time statistics or by setting thresholds, an administrator can be proactive about ensuring that SBM runs efficiently. If thresholds are set, SBM will even notify the administrator of an exceeded threshold and attach a link to a screenshot which will show a graph of the affected resource when the threshold was exceeded.

 Work Center navigation improvement

What it is: Administrators can now specify which categories to display or hide in the Work Center side menu for users.

Why we did it: For users not taking full advantage of all the views, reports, folders and dashboards available through SBM, we heard feedback that it was preferred to hide unused categories. By enabling these categories to be hidden per application group, administrators can ensure that the available categories will grow as the capabilities become required.

 

Other enhancements

Many new enhancements were first suggested on Serena Central. Those introduced in SBM 11.4 have now been marked as Delivered.

  • Work Center Search Improvements: Users can now select which work item fields are displayed in Work Center search results and search for archived items from Work Center.
  • Report Name Used for Exported Report: When you export a report to Microsoft Excel, the default file name is now the report name instead of tmtrack.xlsx.
  • SBM ModScript Improvements:  Several new functions and usability improvements have been added to SBM ModScript. The SBM ModScript documentation has been improved and expanded with new examples, detailed usage notes, and consistent formatting. In addition, a new blog series that provides background and training has been posted to Serena Central.
  • Command line orchestrations: Orchestrations can now be invoked via the command line - either at a command prompt or via a script.
  • "Where used" improvement: In Composer, the "Where Used" capability that shows where any design element is used in a process app and in referenced applications now searches workflow and transition overrides.

 In addition, Over 240 Customer-Reported Issues were fixed. A list of defects fixed in this version can be found in the Knowledge Base.

Important notice about User WorkSpace

In SBM version 10.1.3, Serena introduced a new paradigm in user experience with the SBM Work Center.  SBM User Workspace remained available in the product to enable organizations to transition from SBM User Workspace to SBM Work Center over a period of several releases. However, SBM 11.4 will be the final minor release to include the User Workspace shell. Customers are encouraged to adopt Work Center to interact with SBM in future releases.

Obtaining the New Version

You can download Micro Focus SBM 11.4 from Micro Focus SupportLine.

Learn More

You can view the recording for the What’s New in SBM 11.4 webcast that happend on May 30th. A demo of many of the above features was part of the presentation.

Continue reading
743 Hits
0 Comments

SBM ModScript, Part 12 - Class Inheritance

After looking into ChaiScript classes, I discovered that ChaiScript does not currently support class inheritance. This is common for scripting languages. However, as ChaiScript is such a powerful language, I was wondering how hard it would be to implement it. I came up with the following two options.

Option 1

Option 1 was updated on June 11, 2018 to introduce the "this.This" object, which allows for methods in the base class to invoke overrides on the child class.

def _getBaseVarName( obj ) {
  return "_${obj.get_type_name()}_base";
}

def _getBase( obj ) {
  return eval("obj.${_getBaseVarName(obj)}");
}

def _setThis( obj, ancestor ) {
  ancestor.This := obj;
  if ( eval( "!ancestor.${_getBaseVarName(ancestor)}.is_var_undef()" ) ) {
    _setThis( obj, ancestor._getBase() );
  }
}

def _addMethod( obj, funcname, containedFunc ) {
  var pts = containedFunc.get_param_types();
  
  // since we checked arity above, we know there will be at least 2 entries, so this is safe
  if ( pts[1].is_type_undef() || pts[1].name() == "Dynamic_Object" ) { // if parameter is for a Dynamic_Object class, this is true
    // ensure there are no guards on the parameters (pts[0] is return, pts[1] is "this"
    if ( pts.size() > 2 ) {
      for( var ii = 2; ii < pts.size(); ++ii ) {
        if ( pts[ii].name() != "Object" ) {
          return false;
        }
      }
    }
    
    var paramStr = ""
    for ( var ii = 1; ii < containedFunc.get_arity(); ++ii ) {
      if ( ii != 1 ) {
        paramStr += ',';
      }
      paramStr += "v${ii}";
    }
    eval( "def ${obj.get_type_name()}::${funcname}(${paramStr}){ this._getBase().${funcname}(${paramStr}); }" );
    return true;
  }
  return false;
}

def _verifyAndAddMethod( obj, base, funcname, func ) {
  var sParamVals = "";
  for ( var x = 1; x < func.get_arity(); ++x ) {
    sParamVals += ",0";
  }
  
  if ( eval("call_exists( func, base ${sParamVals} );") && // method exists on base object
       !eval("call_exists( func, obj ${sParamVals} );") && // method does not exist on this object (override)
       !func.has_guard() &&                                // method may not have guards
       !func.get_contained_functions()[0].has_guard() &&   // method may not have guards
       funcname != "This" &&                               // ignore "This" attribute
       !_addMethod( obj, funcname, func ) ) {
    for ( containedFunc : func.get_contained_functions() ){
      if ( !containedFunc.get_contained_functions().empty() && _addMethod( obj, funcname, containedFunc ) ) {
        break;
      }
    }
  }
}

def _setup( obj ) {
  _setThis( obj, obj );
}

def _inherit( obj, base ) {
  obj._getBase() = base;
  _setup( obj );
  
  global _inheritance;
  if ( _inheritance.is_var_undef() ) {
    _inheritance = Map();
  }
  
  // only set up this class inheritance once
  if ( _inheritance.count( obj.get_type_name() ) == 0 ) {
    _inheritance[obj.get_type_name()] = base.get_type_name();
    
    // must invoke get_functions every time, cannot filter and save list, as class definitions might occur after we 
    // hit this for the first time
    for ( func : get_functions() ) {
      if ( func.second.get_arity() == -1 ) {
        for ( inner : func.second.get_contained_functions() ) {
          if ( inner.get_arity() > 0 && !inner.get_contained_functions().empty() ) {
            _verifyAndAddMethod( obj, base, func.first, inner );
          }
        }
      }
      else if ( func.second.get_arity() > 0 && !func.second.get_contained_functions().empty() ) {
        _verifyAndAddMethod( obj, base, func.first, func.second );
      }
    }
  }
}
Usage:
class Base {
  attr id;
  def Base() { _setup( this ); }
  def Base( ID ) { _setup( this ); this.id = ID; }
  def go( x ){ print("go(x) | dynamicType: ${this.This.get_type_name()} | x: ${x} | id: ${this.This.id}"); }
  def go2( x ){ print( "go2(x) | hardcodedType: Base | x: ${x} | id: ${this.This.id}" ); }
  def go3( x, y ){ print( "go3(x,y) | hardcodedType: Base | x: ${x} | x: ${y} | id: ${this.This.id}" ); }
};

class Child {
  def Child() { 
    _inherit( this, Base() );
  }
  
  def Child( ID ) {
    _inherit( this, Base( ID ) );
  }
  
  // override that (optionally) invokes base
  def go2( x ){ print( "Child: ${x}: ${this.This.id}" ); this._getBase().go2( x ); }
};

class GrandChild {
  def GrandChild() { 
    _inherit( this, Child() );
  }
  
  def GrandChild( ID ) {
    _inherit( this, Child( ID ) );
  }
  
  // override that (optionally) invokes _base
  def go3( x, y ){ print( "go3(x,y) | dynamicType: ${this.get_type_name()} | x: ${x} | x: ${y}" );  this._getBase().go3( x,y ); }
};
What Does It Do?
  • In any given base class (class which does not inherit from another class, but will be inherited from), invoke "_setup(this);".
    • This will set up a "this.This" attribute on the object.
  • In the constructors of child classes, invoke "_inherit( this, )".
    • The class of the base object is used to determine the parent class of this object. It is important to use the same base class for every constructor for the child class. 
    • You can invoke any of the parent's constructors, allowing you to initialize the parent object as desired.
  • Behind the scenes of the _inherit() function
    • It will look at all the attributes and methods of the parent class and, if it doesn't find an override in the child class, it will define a method in the child class that invokes the method from the parent class. As such, any given child class object will have access to all the attributes and methods of the parent object.
    • The base object passed to _inherit is stored as a member on the child object. It is accessible via the "this._getBase()" method. This allows you to invoke the parent class method directly if desired.
    • Each object of type GrandChild will look like this (simplified):
      • GrandChild class
        • this = theGrandChildObj
        • this.This = theGrandChildObj
        • this._getBase() = theChildObj (Child class)
          • this = theChildObj
          • this.This = theGrandChildObj
          • this._getBase() = theBaseObj (Base class)
            • this = theBaseObj
            • this.This = theGrandChildObj
            • this._getBase() = undefinedObj
    • As you can see, in the GrandChild constructor, we create an object of type Child (which gets stored in the "this._getBase()" location). The Child constructor creates an object of type Base which gets stored in the Child object's "this._getBase()" location. Therefore, in any given method, you can call the parent object's version of the method by invoking this._getBase().method().
    • Each object will point to itself via the "this" attribute. Be careful when you use "this", as it is safer to use "this.This" to ensure polymorphic overrides are invoked.
    • Each object will point to the actual object via the "this.This" attribute. As such, any given method can polymorphically invoke the methods of the child object.
      • Do NOT use "this.This" when invoking "this._getBase()".
  • Watch out for guards on methods or parameters. It is just too complex to support class method overrides with guards, so they are not supported in this implementation. Also, each class is still separate as far as the ChaiScript class engine is concerned, so a function like "foo( Base bar )" will not recognize an object of type "Child". As long as you leave the guards out, all objects should act polymorphically correct.
Notes
  • Make sure to invoke _inherit() in every constructor on the child class, and pass in an object from the parent class.
  • Make sure to invoke _setup() in every class intended for usage as a base class. This will set up the "this.This" pointer, which allows the base class methods to invoke the overridden methods in the child class.
  • Attributes and Methods added to the parent class after a child class object has been created will never be visible to objects of the child class.
  • No classes in the class hierarchy may have methods with guards on the parameters or on the functions.
  • Objects of the child class will not convert to the base class, do not try to pass an object of type Child to a function like foo( Base bar ).
  • When invoking a method from the base class, code inside the base class method will treat the "this" object as if it is of the base class. Calls in the base method to other methods that have overrides in the child will not invoke the child override if you use the "this" object. To get the child methods to invoke from base methods, use the "this.This" object.
  • Classes built in to ModScript cannot be inherited from.

 

Option 2

There is another option, which has different benefits and drawbacks. The biggest drawback is that you cannot use the ChaiScript "class" syntax.

global _classInheritence_ = ["VirtualClass" : ""];
global _vtable_ = Map();

// All "classes" will be instances of VirtualClass, but with a different
// value for _classname_. It knows how to look up the stuff for the
// fake "class".
class VirtualClass{
  attr _classname_;
  def VirtualClass( sClass ) { this._classname_ = sClass; }
  
  // a method for invoking the function in the parent class
  def _callBase( classname, funcName, params ) {
    // verify "classname" is base of "this"
    var classnameSearch = this._classname_;
    while ( !classnameSearch.empty() ) {
      if ( classnameSearch == classname ) {
        break;
      }
      classnameSearch = _classInheritence_[classnameSearch];
    }
    if ( classnameSearch.empty() ) {
      Ext.LogErrorMsg( __FILE__ + ":" + __LINE__ + ": could not find base class ${classname} for ${this._classname_}");
      throw(0);
    }
    var f = _findVMethod( "${funcName}:${params.size()+1}", classname );
    var p2 = [this];
    params.for_each( back_inserter( p2 ) );
    return call( f, p2 );
  }
}

def _findVMethod( sFuncWithArrity, classname ) {
  var virtFuncArrity = _vtable_.at( sFuncWithArrity );
  var classnameSearch = classname;
  while ( !classnameSearch.empty() ) {
    if ( virtFuncArrity.count( classnameSearch ) == 1 ) {
      return virtFuncArrity[ classnameSearch ];
    }
    classnameSearch = _classInheritence_[classnameSearch];
  }
  Ext.LogErrorMsg( __FILE__ + ":" + __LINE__ + ": could not find function ${sFuncWithArrity} for class ${classname}" );
  throw(0);
}

def VClass( sClass, sParentClass ) {
  if ( _classInheritence_.count(sClass) == 0 ) {
    //eval( "global ${sClass} = fun(){ return VirtualClass( \"${sClass}\" ); };" );
    eval( "def ${sClass}(){ return VirtualClass( \"${sClass}\" ); }" );
    _classInheritence_[sClass] = sParentClass;
  }
}

def VClass( sClass ) {
  VClass( sClass, "VirtualClass" );
}

def VMethod( sClass, sFunc, f ) {
  if ( f.get_arity() < 1 ) {
    return;
  }
  else if ( f.has_guard() ) {
    Ext.LogErrorMsg( __FILE__ + ":" + __LINE__ + ": registerVirtual failed for function ${sFunc}, virtual function cannot have guard" );
    throw(0);
  }
  else {
    // check the params of f, reject if any params have a guard
    for ( p : f.get_param_types() ){
      if ( p.name() != "Object" ) {
        Ext.LogErrorMsg( __FILE__ + ":" + __LINE__ + ": registerVirtual failed for function ${sFunc}, virtual function cannot have guard on parameter" );
        throw(0);
      }
    }
  }
  
  var sFuncWithArrity = "${sFunc}:${f.get_arity()}";
  
  if ( _vtable_.count( sFuncWithArrity ) == 0 ) {
    _vtable_[sFuncWithArrity] = Map();
    var sParams = "";
    for ( var v = 1; v < f.get_arity(); ++v ) {
      if ( v != 1 ) {
        sParams += ',';
      }
      sParams += "v${v}";
    }
    
    // add a method to the VirtualClass that will look up the method for this "class"
    eval( 
"def VirtualClass::${sFunc}(${sParams}){ 
  /* _findVMethod throws if not found */
  var f = _findVMethod( \"${sFuncWithArrity}\", this._classname_ );
  return f( this ${sParams.empty() ? "" : ","} ${sParams} );
}" );
  }
  
  _vtable_[sFuncWithArrity][sClass] = f;
}

def VClassAttr( sClass, sMember ) {
  VMethod( sClass, sMember, eval( "fun(this){ return this._${sClass}_${sMember}; }" ) );
}
Usage:
VClass( "Base" );
VMethod( "Base", "go", fun(this, x){ print( "${this._classname_}: ${x}" ); } );
VMethod( "Base", "go2", fun(this, x){ print( "Base: ${x}" ); } );
VClassAttr( "Base", "id" );

VClass( "Child", "Base" );
VMethod( "Child", "go2", fun(this, x){ 

// optional call to base
this._callBase( "Base", "go2", [x] );
} ); VClass( "GrandChild", "Child" ); VMethod( "GrandChild", "go2", fun(this, x){ this._callBase( "Base", "go2", [x] ); } );

 

Notes
  • All objects are instances of the VirtualBase class
    • All methods and attributes for each class are added to the _vtable_ Map.
    • Each object has an attribute that tells it which class it is.
    • Function calls will look up the method for the current class, and if no override is found, will search up the class tree to find the method or attribute.
    • As opposed to Option 1, when invoking a method from the base class, code inside the base class method will treat the object as if it is of the child class. Calls in the base method to other methods that have overrides in the child will invoke the child override. As such, there is no need for a "this.This" attribute.
  • No virtual methods may have methods with guards on the parameters or on the functions.
  • All objects are of type VirtualBase, do not try to pass an object of type Child to a function like foo( Base bar ), but you can pass any objects from this hierarchy to a function like  foo( VirtualBase bar ).
  • Classes built in to ModScript cannot be inherited from.
  • Option 2 is faster than Option 1.
  • this._callBase() requires that you tell it the name of the base class, and it checks to ensure that the base class you select is an ancestor of the current class.

 

SBM ModScript Blog Series

Continue reading
554 Hits
0 Comments

DA 6.2: Evaluation Tutorial - Deployment Packages

DA 6.2: Evaluation Tutorial - Deployment Packages

A new feature in Deployment Automation 6.2. Deployment Packages allow you to orchestrate a series of Application or Component Deployments within a single Deployment Package process.

 

The attached Evaluation Guide allows you to create a deployment package from scratch using some simple JSON Applications.

 

Step 1: Create the first application 
Step 2: Create the second application 
Step 3: Create a Deployment Package 
Step 4: Test and run the Deployment Package

 

See the Evaluation Guide for more details.

 

Please check out the related video.

 

 

 

Tags:
Continue reading
281 Hits
0 Comments

SBM ModScript, Part 11 - Transitions

In ModScript, you can create objects based on Primary and Auxiliary tables. These objects can be used to Submit new items into those tables. Also, you can read items from the tables and then transition them. In Part 2, we used ProjectBasedRecord.QuickTransition() to transition a master item when all related items were closed.

 

To submit, first create an object for the desired table. When submitting, the current user must have privilege to submit into the desired aux table or project. The following methods are available for submitting:

  • VarRecord.StartSubmitToAux()
    • Start the submission process for the aux table. Fields will be initialized to default values.
  • VarRecord.FinishSubmitToAux()
    • After a call to VarRecord.StartSubmitToAux(), populate field values as desired, then invoke VarRecord.FinishSubmitToAux() to complete the submission process.
  • VarRecord.QuickSubmitToAux()
    • If no fields need to be set by the script, you can just run the submit directly.
  • ProjectBasedRecord.StartSubmitToProject()
    • Start the submission process for the project, project can be specified by project TS_ID, project internal name, or project UUID. Fields will be initialized to default values.
  • ProjectBasedRecord.FinishSubmitToProject()
    • After a call to ProjectBasedRecord.StartSubmitToProject(), populate field values as desired, then invoke ProjectBasedRecord.FinishSubmitToProject() to complete the submission process.
  • ProjectBasedRecord.QuickSubmitToProject()
    • If no fields need to be set by the script, you can just run the submit directly.

 

To execute a transition on an item, the object must first be created and then read.  ModScript can only invoke Regular, Update, and Delete transitions (not Post, Subtask, etc). The current user must have the privilege to execute the selected transition. The following methods are available for executing transitions:

  • VarRecord.GetTransitions()
    • Allows the script to get a list of the transitions that are currently available on the item. Only Regular, Update, and Delete transitions will be listed.
  • VarRecord.StartTransition()
    • Starts a transition on the item. The transition can be specified by transition TS_ID, internal name, or UUID. The default Update transition can be specified by sending 0. The default values will be set for fields. 
  • VarRecord.StartTransitionWithLock()
    • This method is the same as VarRecord.StartTransition() except that it assumes you have already established a record lock by invoking AppRecord.Lock() on this item.
  • VarRecord.FinishTransition()
    • After invoking VarRecord.StartTransition(), set the field values as desired, then complete the transition by invoking this method.
  • VarRecord.QuickTransition()
    • If no field values need to be set, the item can be transitioned in a single command. The transition can be specified by transition TS_ID, internal name, or UUID.

 

Notes
  • Each of these functions returns bool.  If false is returned, check Shell.GetLastErrorMessage() for more information.
  • It is possible for the transition commands to throw an exception. This will exit the script unless you wrap the call in a try/catch block.
  • AppRecord.Add(), AppRecord.Update(), AppRecord.UpdateWithLock(), and AppRecord.Delete() can be used to modify Primary/Aux items, but they will not invoke the workflow transitions.

 

SBM ModScript Blog Series

Continue reading
554 Hits
0 Comments

SBM ModScript Blog Series

SBM ModScript was introduced in SBM 11.3. This blog series is intended to supplement the SBM ModScript Guide with background information and detailed use cases.  You can find the latest version of the documentation here

  1. An Introduction to ModScript
    • Background on SBM ModScript
  2. Transition Related Items
    • An example script containing several key features of SBM ModScript, including:
      • Function definitions
      • Regular expressions
      • List iteration
      • Defining constants
      • Transitioning SBM items
      • Reading lists of SBM items
      • Using the from_json() function
      • Multi-line comments
  3. Adding Methods to a Class
    • How to use the script engine's ability to extend classes to add new methods.
  4. JSON
    • How to use the script engine's to_json() and from_json() utility functions.
  5. Algorithms and Lambdas
    • How to use the script engine's built-in algorithms, along with the optional use of lambdas (anonymous functions).
  6. Invoking DLLs
    • How to interact with custom DLLs.
  7. REST Call Into ModScript
    • How to invoke a ModScript via a URL.
  8. REST Callouts
    • How to invoke a REST endpoint from ModScript.
  9. SQL Queries
    • An introduction to the SQL interaction available in ModScript.
  10. Regular Expressions
    • How to use regular expressions in ModScript.
  11. Transitions
    • A review of how to invoke transitions on Primary/Auxiliary items in ModScript
  12. Class Inheritence
    • An experimental approach to class inheritance.
Recent Comments
Daniel Nolan
x
Friday, 22 June 2018 10:10 AM
Paul Thompson
Yea! I can't explain how frustrated I've been trying to learn ModScript. I find the syntax to be obscure and counter-intuitive, ... Read More
Wednesday, 23 May 2018 4:04 AM
Continue reading
753 Hits
2 Comments

SBM ModScript, Part 10 - Regular Expressions

ModScript has the ability to execute regular expressions on strings. The interface for this is the Regex class. In the following example, we create a regular expression that will match any string that starts with "t" (the default options make this case-insensitive). We will then read a full list of users and fill a Vector with users whose loginid starts with "t". Finally, we iterate our Vector and write the users we found to the output stream.

def AppRecord::GetLogin() {
	return this.GetFieldValue("LOGINID").to_string();
}

var users = Ext.CreateAppRecordList( Ext.TableId("TS_USERS") );
users.Read();

var regex = Regex();
regex.Compile( "^t" );

var out = [];
filter( users, bind( fun( iuser, innerRegex ){ return innerRegex.Matches( user.GetLogin() ); }, _, regex ), back_inserter( out ) );

for_each( out, fun( user ){ Ext.WriteStream( user.GetLogin() ); } );

Step by step:

  • The first thing should look familiar, we talked about adding methods to an existing class in Part 3. In this case, we are making a function that makes it easy to pull the LOGINID out of the User, getting it as a string. 
  • The call to Ext.CreateAppRecordList(), passing in the return value of a call to Ext.TableId(), should be pretty familiar from previous examples. In this case, we are building a list which will let us read rows from the Users table. Then, we call the AppRecordList.Read() method to read the entire Users table (this might not be a great idea on systems with lots of users, but it works well in my simple example). 
  • We create a Regex() and compile it with a simple "starts with t" rule. Since we pass no options into Regex.Compile(), we get the default, which is case-insensitive.
  • We create an output Vector to hold the records that match our regular expression.
  • We invoke the "filter" algorithm. In Part 5 we talked about algorithms, including filter, bind, and back_inserter.
    • filter() - Loops through the range for container "users", invokes the function passed in, for each object where the function returns true, it invokes the second function.
    • users - this is the container to iterate.
    • bind( fun( user, innerRegex ){ return innerRegex.Matches( user.GetLogin() ); }, _, regex ) - bind returns a function for filter to invoke when iterating the users container
      • When the returned function is invoked by the "filter" algorithm, passing in a user from the users container, that value will be passed to the function as the first parameter, which is indicated by the underscore in the call to bind()
      • Also, bind will pass our regex object to the inner function as the second parameter, indicated by the "regex" after the "_" passed to bind.
      • Finally, the inner function will use the regex to indicate to the "filter" call whether this user matches our regular expression.
    • back_inserter( out ) - Adds the matched values to the "out" Vector
      • When the filter function finds a match, it invokes this function, which will append the user object onto the "out" Vector.
  • We assume you want to do something with the filtered list of users. In this case, I invoke the "for_each" algorithm, which will invoke my lambda function on each item in the Vector. In this case, it will print out the matching users' LOGINIDs.

 

Regular Expressions With Groups

Above, we saw a simple regular expression and a simple call to Regex.Matches(). However, we can also use more complex regular expressions, including group capture.

var regex = Regex();
regex.Compile( "(\\d+)(\\w+)" );

regex.Matches( "123abc" );
for ( var i = 0; i < regex.GroupCount(); ++i ) {
	Ext.WriteStream( regex.GroupVal(i) );
}

In this example, we have a regular expression with two groups. First, we expect 1 or more digits, followed by 1 or more word-characters. When we invoke Regex.Match() on a string, the Regex object will remember the groups that it matched, and you can access them via Regex.GroupCount() and Regex.GroupVal(). Regex.GroupVal( 0 ) will always be the text matched by the entire expression. After that, the rest of the groups will be indexed in the order they were captured in the string. The above example gives the output:

123abc
123
abc

 

Regular Expressions: MatchesAgain

After calling Regex.Matches(), you can continue finding matches by invoking Regex.MatchesAgain(). Below, we'll print out each letter in the string, one by one. The regular expression will match any non-digit (\d), non-non-word (\W) character. We do not need grouping parens because the Regex.GroupVal( 0 ) call will always give us the full string that was matched by the regular expression. One thing to note, ChaiScript does not give us a do-while loop, so instead you'll see a while(true)-if-break loop, which is exactly the same paradigm.

var regex = Regex();
regex.Compile( "[^\\d\\W]" );

if ( regex.Matches( "123abc" ) ) {
  while (true) {
	Ext.WriteStream( regex.GroupVal( 0 ) );
	if ( !regex.MatchesAgain() ) { break; }
  } 
}

 

Regular Expressions: ReplaceAll

Finally, the ModScript Regex class has the ability to use regular expression matching to replace values in strings, returning a modified string with all matches replaced. Also, you can use $ notation to use the matched value, or a matching group number, in the replacement: $0 is the entire matched value, $1 would be the first captured group in the match, etc). 

var regex = Regex();
regex.Compile( "[^\\d\\W]" );

Ext.WriteStream( regex.ReplaceAll( "123abc", "(\$0)" ) );

 

Output:

123(a)(b)(c)

What happened? I replaced each matching value, in this case the a, b, and c, with a value of the matched text wrapped with parenthesis. As such, "a" became "(a)", etc. You do not need to use groups in the replacement value, you can replace each letter with "D" if you wish. Keep in mind that if you are not trying to use a dollar group-identifier in the replacement string, you will want to escape the dollar symbol with a double backslash \\.

 

Notes:
  • It is important to remember that most regular expressions have backslashes in them. ChaiScript uses backslash in string literals to identify special characters like newline: \n and tab: \t. As such, all backslashes that are intended for the regular expression need to be double-backslash \\.
  • As the dollar $ symbol is important in regular expressions, it is important to remember that it also means something in ChaiScript. If ChaiScript finds a ${...} in the string, it will try to invoke the value inside the curly braces as if it were string-injected-script. This could be pretty messy if you accidentally mixed it with a regular expression. It is wise to ChaiScript-escape the $ in the string with a single backslash. If you also are trying to regular-expression-escape the dollar, you may need \\\$.
  • The Regex.Compile() function takes an optional second parameter, which is used to indicate options. The default is a case-insensitive, single-line regular expression. To shut off case-insensitive but keep the other options, pass 0. To set the options you like, pass the options to the second parameter, connected with the ChaiScript bitwise-or operator | . 
    • RegexOptionBitsConstants.IGNORECASE
    • RegexOptionBitsConstants.MULTILINE
    • RegexOptionBitsConstants.DOT_MATCHES_ALL

 

SBM ModScript - Table of Contents

Continue reading
530 Hits
0 Comments

SBM ModScript, Part 9 - SQL Queries

In ModScript, we can read queries from the SBM database. One way to do this is to read items from a table, where each item is based on that specific table's schema. This is what the AppRecord, VarRecord, and ProjectBasedRecord classes (and child classes) are for. When you create one with Ext.CreateAppRecord(), Ext.CreateVarRecord(), or Ext.CreateProjectBasedRecord(), you always pass in the table ID that the record will be based on, so for the life of that object, it will be associated with that table and its schema. Also, we have the AppRecordList (and child classes), which is created with Ext.CreateAppRecordList(), and which also will be bound to a specific table ID on creation. Thus, all items in the AppRecordList will be objects of that tables' type.

All these classes can be used to read records from the SBM table for which the class is bound, using the following functions:

  • AppRecord.Read()
    • Read an item by TS_ID or name.
  • AppRecord.ReadByColumn()
    • Read an item by a column from that table's schema
  • AppRecord.ReadByColumnAndColumn()
    • Use two column values to identify the item to read.
  • AppRecord.ReadWithWhere()
    • Read the item with any SQL where clause. This function can use SQL bind parameters.
  • AppRecord.ReadByUUID()
    • Read an item by TS_UUID
  • AppRecordList.Read()
    • Read an entire table, this could potentially use a lot of resources.
  • AppRecordList.ReadByColumn()
    • Read a list of items by a column from that table's schema
  • AppRecordList.ReadByColumnAndColumn()
    • Use two column values to identify the items to read.
  • AppRecordList.ReadWithWhere()
    • Read the list of items with any SQL where clause. This function can use SQL bind parameters.

When using the ReadWithWhere() functions, I'd encourage the usage of SQL bind parameters. When trying to build SQL using dynamic values, you need to worry that the values you are pulling from fields or other sources could have embedded single-quotes ( ticks: ' ), which can break the SQL. These values would need to be encoded if added directly to the SQL query. However, if instead you put a question mark in the SQL, you can bind the value to it using a Vector of bind values. These bind values do not need to have the ticks encoded, which simplifies your script. In Part 2 of this series, you saw me use SQL bind parameters:

containerList.ReadWithWhere(
    "TS_ID in (select TS_SOURCERECORDID from TS_USAGES where TS_FIELDID=? and TS_RELATEDRECORDID=?)",
    [ Pair(DBTypeConstants.INTEGER, relational.GetId()),
      Pair(DBTypeConstants.INTEGER, Shell.Item().GetId()) ] );

Each entry in the Vector is a Pair, with the first value being the data type for the value, and the second value being the value I am binding to my SQL. 

 

AppDb Queries

ModScript also allows for SQL queries that are not tied directly to tables. The Shell.Db() method returns an AppDb object which points to the current SBM AE schema. It has the following functions which allow for free SQL to be executed, (each take an optional Vector of SQL bind parameters):

  • AppDb.ReadIntWithSQL()
    • Returns a single integer read from the database.
  • AppDb.ReadTextWithSQL()
    • Returns a single string read from the database.
  • AppDb.ReadIntegersWithSQL()
    • Fills a Vector with integers read from the database.
  • AppDb.ReadIntegerPairsWithSQL()
    • Fills a Vector with Pairs of integers read from the database.
  • AppDb.ReadTextValsWithSQL()
    • Fills a Vector with strings read from the database.

With these, you can execute any SQL you want as long as the output of the SQL can be bound to as a single int, varchar, a list of ints, list of varchars, or a list of pairs of ints. In 11.4, we added AppDb.ReadDynaSQL(), which allows you to specify any number of columns, with their column types, which makes you truly free to execute any SQL you want.

 

SQL Performance

Keep in mind that queries can be slow. In SBM, we try to identify any slow queries built into our product, and we add database indexes to mitigate the performance slowdown. However, we can't do this for queries that you add, so you will probably want to watch for places where you should add your own indexes to the database to help speed up your queries.

 

SBM ModScript - Table of Contents

Continue reading
623 Hits
0 Comments

New On-Demand Course 3165: ZENworks 2017 Architect Academy:

New On-Demand  Course 3165: ZENworks 2017 Architect Academy:

Overview

 

In this course, you learn about the tasks required to evaluate a customer’s network environment, design a ZENworks solution, and then configure the initial ZENworks production implementation. You are provided videos, documents, and other resources to help you through the learning process. In addition, you are provided tools to complete your ZENworks Architect design tasks.

 

 

 

 

 

Modules included

 

The course currently includes the following key phases in completing the overall task of architecting and rolling out a ZENworks implementation:

 

 

 

  • Phase 1: Design the ZENworks System

    • Describe the ZENworks System

    • Access the Environment

    • Design the ZENworks System

    • Create the Strategies

    • Document the Design

  • Phase 2: Prepare the ZCM Environment

    • Create the Project Documentation

    • Prepare the Network Infrastructure

    • Configure the Network Environment

    • Acquire the Media and Certificates

    • Set Up the Appliance Environment

  • Phase 3: Install the Zenworks System

    • Install the Primary Servers

    • Implement the Satellite Servers

  • Phase 4: Update the ZENworks System

    • Plan the Zenworks Update Rollout

    • Deploy the Update

    • Perform Post Update Tasks

  • Phase 5: Upgrade the ZENworks System

    • Plan the ZENworks Upgrade Rollout

    • Deploy the Upgrade

    • Perform Post Upgrade Tasks

 

 

For more information and insights on the course, you can view it in On-demand by going to:

https://www.microfocus.com/ondemand/courses/zenworks-2017-architect-academy-3165/

 

Continue reading
414 Hits
0 Comments

SBM ModScript, Part 8 - REST Callouts

In ModScript, we can make calls to external REST APIs. Being able to pull in data or send data to a REST API really grows the ability to build integrations with ModScript. In my example, I use the experimental SBM feature Data Service. Data Service allows us to create a connection to a database, which can be the current SBM database or any other database via an ODBC DSN, and pre-configure an SQL query that can be requested via REST. The SQL query can have runtime parameters bound to them from URL parameters.

 

SBM Data Service

The SBM Data Service is an experimental feature. As such, it must be enabled in the TS_SYSTEMSETTINGSNAMESPACED table:

update TS_SYSTEMSETTINGSNAMESPACED set TS_LONGVALUE=1 where TS_NAME='EnableDataServices'

Next, edit DataServiceConf.xml in SBM\Application Engine\bin. Read the big comment in the xml file to get more information about the different types of connections you can use in Data Service.  In my example, I create a DSN-based Connection, even though I could just use a local-SBM connection. The reason I do this is because I think it is likely that my reader will really want to pull data from a different database rather than pull data from the SBM database. ModScript has powerful SQL querying features for querying the SBM database, and probably will not need to invoke a REST service to do it. However, ModScript cannot peek into a non-SBM database using its SQL query features; as such, it is a more likely use case to invoke Data Services via REST to query a separate database. However, to keep the example simple, I use the DSN-based Connection to peek back into the SBM database, as I know it is a database that my readers have on-site.

<DataServices>
  <Connection name="ModScriptService" >
    <DSN_String>DSN=SBM_AE;UID=sa;PWD=serena123!</DSN_String>
  </Connection>
  <DataService name="ModScriptQuery1" connection="ModScriptService">
    <columns>
      <column display="ID" column="TS_ID" type="number-integer" />
      <column display="Name" column="TS_TITLE" type="text" len="512" />
      <column display="Submitter" column="TS_SUBMITTER" type="number-integer" />
      <column display="SubmitDate" column="TS_SUBMITDATE" type="datetime" />
    </columns>
    <query>
      <queryText>select TS_ID, TS_TITLE, TS_SUBMITTER, TS_SUBMITDATE from {namespacePrefix}USR_MODSCRIPTRESTCALL {nolockhint} where TS_ACTIVEINACTIVE={0} and TS_SUBMITTER in ({1})</queryText>
      <params>
        <param index="0" type="int" name="active"/>
        <param index="1" type="intArray" name="submitter"/>
      </params>
    </query>
  </DataService>
</DataServices>

In this example, we search the primary table USR_MODSCRIPTRESTCALL for items where TS_ACTIVEINACTIVE is equal to the value passed on the URL, and TS_SUBMITTER is equal to the value passed on the URL. We return the TS_ID, TS_TITLE, TS_SUBMITTER, and TS_SUBMITDATE. This query could be much more complex. It could create a temp table, insert data into it, then join to that data to collate it all into the desired output. However, this example is not about fancy SQL queries, so we keep it simple.

The DataServiceConf.xml file is only processed on AE startup to keep the service fast. Unfortunately, this means IIS has to be reset any time you change the file. So, after an IIS reset, a call to the DataService URL will set up the query, bind the URL parameters to the query, and execute it.

http:///workcenter/tmtrack.dll?jsonpage&command=dataservice&service=ModScriptQuery1&active=0&submitter=8

The example output:

{
	"data": [{
		"ID": 1,
		"Name": "abc",
		"Submitter": 8,
		"SubmitDate": "\/DATE(1525900070000)\/"
	}, {
		"ID": 14,
		"Name": "def",
		"Submitter": 8,
		"SubmitDate": "\/DATE(1525914739000)\/"
	}],
	"result": {
		"type": "OK",
		"msg": ""
	}
}

 

ModScript

In this example, the ModScript will be fairly simple. It requests to data from Data Service and injects it into the HTML form as a JavaScript variable. I added this script as a pre-transition action on the submit transition of my workflow. What this will do is show the submitter all other items they submitted into this table that are still active. Attached is my example application.

var rest = Ext.CreateAppRecord(Ext.TableId("TS_RESTDATASOURCE"));
rest.Read("DataServiceModScriptQuery1RESTDataSource");

var result = "";
if ( !rest.Get( result, [Pair("submitter", Shell.User().GetId()), Pair("active",0)] ) ) {
	// write an error to Event Viewer
	Ext.LogErrorMsg("Rest call failed in script " + __FILE__ + ":\n" + Shell.GetLastErrorMessage() );

	Shell.RedoMessage() = "Rest call failed";
}
else {
	var resultObj = result.from_json();
	Ext.WriteStream( "" );
}

 

The Form

On the form, I added an HTML/JavaScript Widget, with the following contents. It simply iterates the JavaScript variable injected by the ModScript and writes the values as an unordered list.

<script type="text/javascript">
document.write("<ul>");
 
for ( var i in itemsISubmittedThatAreStillOpen ) \{
    document.write("<li>");  
    document.write(itemsISubmittedThatAreStillOpen[i].Name + ": Submitted on " + new Date( parseInt( itemsISubmittedThatAreStillOpen[i].SubmitDate.match(/\d+/)[0] )));
    document.write("</li>");
}
 
document.write("</ul>");
</script>

Is my example a little contrived? Sure, the form could have invoked Data Service directly, but we are pretending that the ModScript did something important with the JSON before writing it to the form. Also, in many use cases, there is no form involved. Instead, ModScript will invoke a REST API to write data to some integration, or use ModScript to pull data from an external source into a field on the item. Also, ModScript could have written a more complex JavaScript into the page to do all the stuff we do on the form. However, writing JavaScript from inside ModScript is not very fun, as you have to be sure to encode everything correctly. Quoted text can be really annoying. Instead, use ModScript to write the data onto the form can be pretty simple, and then the JavaScript can take it from there. In a more complex example, I would move the JavaScript to its own file, simply exposing a simple function for the HTML/JavaScript widget to invoke (this makes the JavaScript reusable, easier to write using Composer's syntax highlighter, and easier to get to).

Contrived or not, we can see ModScript in action, directly invoking a REST call and doing something with the data.

 

 What's new in SBM 11.4

ModScript in 11.3.1 has the ability to make REST calls. In 11.4, we greatly increased the flexibility of the REST call functionality. Features added in SBM 11.4 for REST callouts:

  • Custom URL path parameters
    • 11.3.1 allowed the scripter to add and change URL parameters (values after the ? in the URL). 11.4 extends this to the URL path (values after the protocol, server, and port but before the ?). This allows the scripter to add a single REST Data Source that points to the protocol/server/port to whatever REST API you are interacting with, then add URL path values to invoke the REST functions required.
  • 11.3.1 allowed the scripter to invoke REST calls via POST and GET, but 11.4 extends this to PUT, DELETE, and any custom HTTP REST verb desired.
  • 11.4 allows the scripter to add any custom header to the HTTP call.
  • 11.4 allows the scripter to get the headers from the HTTP result.
  • 11.4 allows the scripter to bypass the SBM Proxy if desired.
    • SBM Proxy adds functionality such as the ability to support OATH2, but sometimes it might be desired to bypass the SBM Proxy to avoid any complexity added.
    • Bypassing can help with debugging a REST call that is having a problem (see if the problem is due to SBM Proxy or not).
  • As a side note, not related to REST but tangential to this example: In 11.3.1, ModScript has functions like Db.ReadIntegersWithSQL(), but the column types are rigid (you cannot read an int, 2 strings, and a double), which might make using Data Service look good for custom queries on the SBM database. 11.4 has Db.ReadDynaSQL(), allowing any number of columns to be returned in a query, making for extremely flexible database querying of the SBM AE schema. As such, you would only need Data Service for querying external databases. 

 

SBM ModScript - Table of Contents

Recent Comments
Oliver Kraus
Don, looks like the link to the next part is missing in this part, Oliver
Monday, 11 June 2018 8:08 AM
Don Inghram
Thanks, Oliver. All posts link back to the ModScript Blog Series table of contents.
Monday, 11 June 2018 1:01 PM
Continue reading
603 Hits
2 Comments

SBM ModScript, Part 7 - REST Call Into ModScript

Sometimes, we want more information on a custom form, but we can't figure out how to get it. The answer might be a call to SBM JSON API using a REST widget. However, if you just can't seem to find a non-scripty way to get the data you want, consider invoking ModScript from the form. I have put together a sample application based on 11.3.1 (see 11.4 example below for updated version) which shows how you could do this. In my example, the process app has a Contact field, and I want to show more information about that contact and the company that they are part of. To do this, I wrote a ModScript that can be invoked via the Direct URL context. It requires that the contact ID be passed in, either as a URL parameter or as a JSON value in the body of the HTTP call. You wouldn't really need to provide flexibility like that, but here we are trying to give an example of both so that we can really see how to send data to ModScript in the Direct URL context.

 

The ModScript Script
var contactID = 0;
var method = "";
if ( !Shell.PostData().to_string().trim().empty() ){
	contactID = Shell.PostData().to_string().from_json()["contact"];
	method = "JSON";
}
else {
	contactID = Shell.Params()["contact"].to_string().to_int();
	method = "URLParam";
}

Here, you see that we first look at the Shell's "PostData()" value. This will hold the body of the HTTP request, as long as the request is a POST, the HTTP request "Content-Type" is "application/json", and the data length is less than our maximum allowed in the "ScriptPostDataMax" system setting (which defaults to 10 MB). So, first, the ModScript checks to see if we have any data in Shell.PostData(). If so, it casts the value to a string (most values in the Shell are Variant), then invoke from_json() on the string. Assuming that the JSON passed in was { "contact": 123 }, this should give us a Map with a single entry of "contact". So, the script immediately requests the value "contact" from the Map. Of course, the data sent in to ModScript could be quite complex, in which case you could catch the return value of "from_json()" and then process it.

If we did not get anything in Shell.PostData(), we then look in the Shell.Params(), which is a Dictionary of the URL parameters passed to this Direct URL call. In this case, we expect to find a value "contact" as a URL parameter. Values in Dictionaries are also Variant, so we need to cast it. Unfortunately, until 11.4, we do not have a direct Variant.to_int(), so we use Variant.to_string(), then use string.to_int() to finally get an integer.

Next, we want to read the Contact using the ID passed in:

var contactRec = Ext.CreateVarRecord( Ext.TableId("TS_CONTACTS") );
if ( contactID == 0 || !contactRec.Read( contactID ) ) {
	Ext.WriteStream("{}");
}

This is pretty straight forward. Create a VarRecord object for interacting with the "TS_CONTACTS" table. If we were not passed a contactID that we can read, we return an empty JSON object (the JavaScript consumer of this output expects JSON, so be sure to send valid JSON, even when there is an error).

else {
	var companyName = "";
	var companyAddress = "";
	var companyFld = contactRec.Fields().FindSysField( 200 ); // 200 is the syscode for the Contact's Company field.
	var companyID = Variant();
	if ( !companyFld.is_var_null() && fun( f, v ){ f.GetDbValue(v); return v; }( companyFld, companyID ) != 0 ) {
		var temp = Variant();
		companyFld.GetDisplayValue( temp );
		companyName = temp.to_string();
		var companyRec = Ext.CreateVarRecord( Ext.TableId( "TS_COMPANIES" ) );
		companyRec.Read( companyID );
		companyAddress = (companyRec.GetFieldValue("ADDRESS1") + ", " + companyRec.GetFieldValue("CITY") + ", " + companyRec.GetFieldValue("STATE") + " " + companyRec.GetFieldValue("ZIPCODE")).to_string();
	}

	Ext.WriteStream( [
                       "name" : contactRec.GetName(), 
                       "email" : contactRec.GetFieldValue("EMAIL").to_string(),
                       "company" : companyName,
                       "companyAddress" : companyAddress,
                       "method" : method
                      ].to_json() );
}

Here, we see a few important things. First, after calling VarFieldList.FindField() or VarFieldList.FindSysField(), you may have a null object (if the field could not be found). It is important to test for null using "is_var_null()". Next, you see a lambda which takes a Field and a Variant, it invokes Field.GetDbValue() and returns the value. This is because, until 11.4, ModScript did not have a version of Field.GetDbValue() that returned the value directly to the caller. Instead, I need a way to call Field.GetDbValue() after the check for is_var_null() but while still inside the "if" (or I could do nested "if" statements, I chose the lambda). Immediately after the lambda is declared, we invoke it with our Field and Variant, and let it give us the value that we can test.

Finally, we have the Company ID and we can read the Company to get the address. I put all of this together into a ModScript Map, then invoke "to_json()" to get a nice JSON string that JSON-encodes the embedded text values and formats the return value.

 

The JavaScript Script

On the JavaScript side, I have two examples to show both a POST with a JSON body and a GET with the data on the URL. Both put the results onto the custom form in the

location specified. The JavaScript writes an unordered list of information using the returned JSON. Keep in mind that in SBM, the jQuery object is called "jQuerySBM", not "$".

 
function processModScriptResult( data, updateLoc ) {
    console.log(data);
    var p = JSON.parse(data);
    $("#"+updateLoc).html("<ul><li>" + p.name + "</li><li><a href=\"mailto:" + p.email + "\">" + p.email + "</a></li><li>" + p.company + "</li><li>" + p.companyAddress + "</li></ul>");
}

function invokeModScriptBody( val, updateLoc ) {
    $.ajax({
        contentType: 'application/json',
        accept: 'application/json',
        type: 'POST',
        data: JSON.stringify({ "contact": val }),
        url: "tmtrack.dll?ScriptPage&ScriptName=ModScriptDirectURL",
        success: function(data){ processModScriptResult(data, updateLoc); }
    });
}
 
function invokeModScriptParam( val, updateLoc ) {
    $.ajax({
        contentType: 'application/json',
        accept: 'application/json',
        type: 'GET',
        url: "tmtrack.dll?ScriptPage&ScriptName=ModScriptDirectURL&contact=" + val,
        success: function(data){ processModScriptResult(data, updateLoc); }
    });
}

 

The Form

To pull it all together, I created a State form for my one and only workflow state. In the form's properties, I selected my custom JavaScript. I also ensured that "Include jQuery plugin" was checked. I added an HTML/Javascript widget, with the following HTML and JavaScript:

<script type="text/javascript">
  invokeModScriptParam( {Contact[ID]}, "ModScriptInsertLocation1" );
  invokeModScriptBody( {Contact[ID]}, "ModScriptInsertLocation2" );
</script>
<p id="ModScriptInsertLocation1"></p>
<p id="ModScriptInsertLocation2"></p>

This invokes my two examples, and injects a couple locations in the HTML for the JavaScript to write out the results.

 

What's New in 11.4

I wrote this application to work in 11.3.1. In 11.4, we could have simplified our script. Also, we could bind the JSON output directly to a REST Grid Widget on a form. I  have updated the sample application with the changes. A few notable differences:

  • Ext.SetContentType( "application/json" );
    • In 11.4, ModScript can set the declared Content-Type for the Direct-URL context. This is important as the REST Grid Widget does not allow you to bind to a request that returns a Content-Type of "text/html".
  • The JSON returned is now a JSON array so that the REST Grid Widget recognizes it.
  • Since we are using the REST Grid, we don't really need the HTML/JavaScript widget or the JavaScript file. However, I kept them so we can see all the different options. The JavaScript doesn't need to call JSON.parse(data), the new Content-Type of "application/JSON" indicates to the underlying engine that it should parse the JSON and give us an object. The script was changed to access index 0 of the array, as the JSON is now an array with our object inside.
  • To use the REST Grid Widget, I added an Endpoint that points to the ModScript call that we had been making via JavaScript. Be sure to edit the endpoint in Application Repository to point to your AE server. The REST Grid Widget can bind the Contact ID to the URL parameter.
  • Other cleanup in ModScript:
    • Invoke "Variant.to_int()" directly
    • Use "VarRecord.GetDbValueInt()" to get the field value as an integer
    • Setting our companyName string = Variant directly (no longer need to invoke Variant.to_string() when assigning a Variant value to a string).
    • 11.4 has VarRecord.GetFieldValueString(), but it still requires the string to be passed in rather than returned directly to the caller (this is so that it can return false if the field is not found). I added a function to the Field class in my script that will return the string value directly to the caller, which makes it easier to concatenate the address string. Only do this if you are SURE all the fields will be found:
      • def VarRecord::GetFieldValueString( s ) {
        	var ret = "";
        	this.GetFieldValueString( s, ret );
        	return ret;
        }

 

SBM ModScript - Table of Contents

Recent comment in this post
Oliver Kraus
Good example (although I prefer plain JS), I did not know that Shell has a PostData() member function. Good to know.Thanks, Olive... Read More
Monday, 11 June 2018 7:07 AM
Continue reading
657 Hits
1 Comment

Recent Tweets