Refactoring the SNH Data Table Widget, Part IV

“Testing is an infinite process of comparing the invisible to the ambiguous in order to avoid the unthinkable happening to the anonymous.”
James Bach

Last time, we tested a number of the features of the refactored SNH Data Table collection, but there is still much to do before we can bundle this all up into a new Update Set and stuff it out on Share. Now that we know that the initial version that I put out there is missing a critical component, I’d like to wrap this up and replace it with the refactored version, so let’s get to it.

To test the bulk actions and other clicks that do not result in navigating to a new portal page, I took my original Button Click Handler Example widget, renamed it the Table Click Handler Example widget, and then reworked it to handle all four of the clickable features, reference links, aggregate columns, buttons and icons, and bulk actions. Here is the new Client script for the repurposed widget:

function (spModal, $rootScope) {
	var c = this;
	var eventNames = {
		referenceClick: 'data_table.referenceClick',
		aggregateClick: 'data_table.aggregateClick',
		buttonClick: 'data_table.buttonClick',
		bulkAction: 'data_table.bulkAction'
	};

	$rootScope.$on(eventNames.referenceClick, function(e, parms) {
		displayClickDetails(eventNames.referenceClick, parms);
	});

	$rootScope.$on(eventNames.aggregateClick, function(e, parms) {
		displayClickDetails(eventNames.aggregateClick, parms);
	});

	$rootScope.$on(eventNames.buttonClick, function(e, parms) {
		displayClickDetails(eventNames.buttonClick, parms);
	});

	$rootScope.$on(eventNames.bulkAction, function(e, parms) {
		displayClickDetails(eventNames.bulkAction, parms);
	});

	function displayClickDetails(eventName, parms) {
		var html = '<div>'; 
		html += ' <table>\n';
		html += '  <tbody>\n';
		html += '   <tr>\n';
		html += '    <td class="text-primary">Event: &nbsp;</td>\n';
		html += '    <td>' + eventName + '</td>\n';
		html += '   </tr>\n';
		html += '   <tr>\n';
		html += '    <td class="text-primary">Table: &nbsp;</td>\n';
		html += '    <td>' + parms.table + '</td>\n';
		html += '   </tr>\n';
		html += '   <tr>\n';
		html += '    <td class="text-primary">Sys ID: &nbsp;</td>\n';
		html += '    <td>' + parms.sys_id + '</td>\n';
		html += '   </tr>\n';
		html += '   <tr>\n';
		html += '    <td class="text-primary">Config: &nbsp;</td>\n';
		html += '    <td><pre>' + JSON.stringify(parms.config, null, 4) + '</pre></td>\n';
		html += '   </tr>\n';
		html += '   <tr>\n';
		html += '    <td class="text-primary">Item(s): &nbsp;</td>\n';
		html += '    <td><pre>' + JSON.stringify(parms.record, null, 4) + '</pre></td>\n';
		html += '   </tr>\n';
		html += '  </tbody>\n';
		html += ' </table>\n';
		html += '</div>';
		spModal.alert(html);
	}
}

This will allow me to test a number of things, all with the same companion widget, which should save a little bit of time. The original button_test page has a number of options in the configuration, so let’s pull that guy up and click around and see what we can find out.

Original button_test page

One thing that I noticed right away was that the master checkbox was not working. It seems that when I rebuilt the core SNH Data Table widget from the latest version of the stock Data Table widget, I neglected to paste back in the following added function:

$scope.masterCheckBoxClick = function() {
	for (var i in c.data.list) {
		c.data.list[i].selected = c.data.master_checkbox;
	}
};

Putting that back solved that problem, but then I also discovered that I needed to add spModal to the function arguments so that the error message would come up when you tried to select a bulk action from the drop-down without selecting any of the records in the table. Once I got all that out of the way, I got back to testing the buttons and icons.

Clicking on the Status Check icon

So now when I click on a button or icon, the alert pops up with all of the information that is passed with the broadcast message, which gives you an idea of what you have to work with in your companion widget to take whatever action that you would like to take based on that information. We should be able to do the same thing when selecting one or more rows and then choosing a bulk action.

Selecting a bulk action

Looks like that is working as well, so I think things are looking pretty good at this point. There is another old test page that we can pull called snh_data_table that has a companion widget for one of the icons.

snh_data_table test page

Once again, we will need to update the Client script in the click handler widget to adapt to our new approach to click handling.

function(spModal, $rootScope) {
	var c = this;
	var eventNames = {
		referenceClick: 'data_table.referenceClick',
		aggregateClick: 'data_table.aggregateClick',
		buttonClick: 'data_table.buttonClick',
		bulkAction: 'data_table.bulkAction'
	};

	$rootScope.$on(eventNames.buttonClick, function(e, parms) {
		if (parms.config.name == 'icon') {
			spModal.open({widget: 'snh-user-profile', widgetInput: {sys_id: parms.sys_id}}).then(function() {
				//
			});
		}
	});
}

Since the button is configured to launch a new page and the icon is configured to pop up a modal dialog, we need to check to make sure that the button click is for the icon and not the button. Other than that, it is very similar to the other examples.

Clicking on the icon to bring up the modal dialog

So that works. Very nice. Obviously, there is a lot more that we could do here to check out every little thing, but I think that we have covered most of the high points, and given that the version that is out on Share right now contains a pretty significant flaw, I think I would like to roll the dice and toss this out there as is to resolve that issue. Hopefully, I will not miss any important artifacts this time! Here is the Update Set for those of you who would like to check it out. As always, please feel free to leave any feedback of any kind in the comments. Thanks!

Refactoring the SNH Data Table Widget, Part III

“More than the act of testing, the act of designing tests is one of the best bug preventers known.”
Boris Beizer

Now that we have refactored the various SNH Data Table widgets and added the missing support for clickable aggregate columns and conditional buttons and icons, it’s time to run everything through its paces and make sure that it all works. To begin, let’s just see if the stuff that was working is still working now that we have made all of these changes to the artifacts and added these new features. A good place to start would be with the User Directory, as that little project utilizes a number of different components that were affected by the changes.

Primary User Directory page

Well, that seems to work, still. Changing perspectives and selecting different states all seem to function as they should as well, as does paging through the data and sorting on the various columns. So far so good. This tests out the SNH Data Table from URL Definition widget as well as the underlying core SNH Data Table widget. In the User Directory, the Department column and the Location column reference links are mapped to two other pages that were built using the SNH Data Table from JSON Configuration widget, and clicking on the value in one of those columns should test out both the reference link handling and that other wrapper widget.

Department Roster page

So far, so good. Clicking on a Location value should test out the reference link handling from this wrapper widget, and also bring up yet another page using the second of the three wrapper widgets.

Location Roster page

Once again, everything seems to be behaving as it should using the two wrapper widgets tested so far. The third wrapper widget, the SNH Data Table from Instance Definition widget, is not used in the User Directory, but we have other test scenarios set up for that one. Before we move on, though we can go ahead and test one more thing while we are working with the User Directory: conditional icons. In the User Admin perspective, there is an icon defined to edit the user’s data. We can add a quick condition to that icon configuration and see how that works out. Just for testing purposes, let’s try the following expression:

item.department.display_value == 'Sales'

If that works, the edit icon should only appear for users who are assigned to the Sales department.

User Directory User Admin perspective with conditional icon

Well, that seems to work as well. Good deal. While we are here, we might as well click on the icon and see if the button click handling works as well as the reference link handling that we tested earlier.

User Profile edit page

And it would seem that it does. That’s about all that we can squeeze out of the User Directory, but we still have a lot more to test, including the third wrapper widget and the other new feature, the clickable aggregate columns. Plus, we still have to do regression testing for bulk actions and clicks that result in a modal pop-up box instead of branching to a new portal page. That’s a lot to get through, so let’s keep plowing ahead.

Our third aggregate column test page was built on the SNH Data Table from Instance Definition widget, so let’s pull that guy up and see how things look.

Aggregate Column test page #3

Well, that all seems to work, and the Network group has two Incidents, so we just need another page to which we can link to display those Incidents. That’s easy enough to do with yet another aggregate column test page using the SNH Data Table from Instance Definition widget, a table name of Incident and a filter of active=true^assignment_group={{sys_id}}.

Aggregate Column test page #5

So all of three of the wrapper widgets seem to work, which by default tests out the core widget, and direct links from reference columns, buttons, and aggregate columns all navigate to the appropriate pages. That just leaves bulk actions and modal pop-ups to be tested, both of which will require companion widgets to listen for the associated broadcast messages. For that, we will have to hunt down or create some companion widgets for testing purposes, which might get a little involved, so let’s jump into that next time out.

Aggregate List Columns, Part XI

“When obstacles arise, you change your direction to reach your goal; you do not change your decision to get there.”
Zig Ziglar

Last time, I completed the changes that I wanted to make to support the ability to click on an aggregate column value and see a list of the records represented by the value. I fully intended to test everything out this time and wrap this up; however, once I started testing things out, I began to realize that there were some inconsistencies in the approach taken for the aggregate columns compared to that taken with the buttons and icons. I don’t really like seeing that, so now I am contemplating scrapping the whole thing and starting over. I’m not fully there just yet, but I really don’t like have two different solutions to virtually the same objective.

To begin my testing, I built a simple modal pop-up widget and then edited the original AggregateTestConfig Script Include to contain an action property with a value of broadcast. That produced the desired clickable link on the aggregate column, but I noticed that there was no tool tip on mouse-over like there is with the buttons and icons. That, of course, is because I did not add the hint property to the aggregate column specification object like there is in the button/icon specification object. That seems like an easy fix, but the other thing that I did not like was that the column was still a link when the value was 0. Since there is nothing to see when the value is 0, it seems to me that that column shouldn’t be clickable unless there are records to view. That should also be an easy fix, so I set both of those concerns aside and moved on to testing the other option, linking to a new page.

Not wanting to disturb my earlier testing, I pulled up the AggregateTestConfig2 Script Include to use the second test page for this effort. You may recall that the second test page is a list of sys_user_grmember table records, not sys_user table records. To make this work for counting up the related records, we added the optional source property to the aggregate column specification object, but the code that I added to support the clickable links did not reference that property. That needed to be addressed as well, but I still wanted to do some testing, so I decided to swap over to the AggregateTestConfig3 Script Include to use the third existing test page instead of the second. That allowed me to complete my testing, and everything seemed to work as it should, but in digging around in the code, I found a couple of things that really disturbed my sense of The Way Things Ought To Be.

For one, to provide a link for buttons and icons, we use the property page_id. To accomplish the same thing for aggregate columns, the property name is action. The first is simply the ID of an existing Portal Page, selected from an sn-record-picker of Portal Pages. The second is a URL query string that includes the ID of the desired Portal Page as well as any other parameters that you might want to include in your URL. Both achieve the desired objective, but again, I do not like to see two different approaches to the same issue in the same component. It does work, but I don’t like it.

The other thing that I discovered is that the code that I added to handle the link to the new page was in the core SHN Data Table widget, while the similar code for the buttons and icons was located in the various wrapper widgets. I am sure that I copied all of that from the original click handler for the entire row, but I am not sure why it is in the wrapper widgets, where it has to be replicated in each and every one, instead of in the core widget, where it would seem to belong. Maybe there was a reason for that when the stock items were first constructed, but I do not know what that reason might have been.

In the midst of all of that, I came across this conversation, which included a link to this article. While I happen to share the article author’s concerns about cloning stock widgets, and he does make a number of valid points in his reply to the original poster, some modifications that you would like to make are so extensive that it makes little sense to attempt to retain whatever might be left of the original artifact. Still, his approach of embedding the original widget underneath your enhancements, which is essentially what the wrapper widgets do with the core data table widget, is an intriguing idea. I still have to study the sample to see how that might be adapted to what I have been trying to do, but I think it might be worth a closer look.

All of things these piled on top of one another make me think that maybe I want to back up the truck and take another shot at this from a different perspective. I definitely want to be able to click on a non-zero aggregate column and see a list of the records represented there, either in a modal pop-up on a new page, but maybe the way that I jumped into this was just a little too hasty. I think I have to do a little more digging around before I fully commit to what I have started here. Maybe this could be done a little better than I have it now. Or maybe not. Hopefully, I can figure all of that out relatively soon and we can still wrap this up and put it behind us.

Aggregate List Columns, Part VI

“There are three qualities that make someone a true professional. These are the ability to work unsupervised, the ability to certify the completion of a job or task and, finally, the ability to behave with integrity at all times.”
Subroto Bagchi

Last time, we wrapped up the changes for the SNH Data Table from Instance Definition widget, and fixed a little issue with the core SNH Data Table widget in the process. Today, we need to do the same for the remaining wrapper widget, SNH Data Table from URL Definition. As with the previous widget, there isn’t that much code here that needs to be addressed, but I did come across these lines in the widget’s Server script:

copyParameters(data, ['p', 'o', 'd', 'filter', 'buttons', 'refpage', 'bulkactions']);
copyParameters(data, ['relationship_id', 'apply_to', 'apply_to_sys_id']);

It looks like I need to add the aggregates parameter to the list, so I threw that in and collapsed everything into a single line.

copyParameters(data, ['p', 'o', 'd', 'filter', 'aggregates', 'buttons', 'refpage',
	'bulkactions', 'relationship_id', 'apply_to', 'apply_to_sys_id']);

That was the only thing that I could find, so the next thing that I did was to clone the original test page once again and swap out the table widget for the one that I wanted to test. The problem with testing this one, though, is that all of the options that drive the process are URL parameters, and I did not want to type or paste in that huge URL every time I wanted to test. To eliminate that, I added an HTML widget above the table with a link that would contain the URL needed for testing. This way, all I needed to do to perform a test would be to click on that link.

Now I need to build a test URL using the same parameters that we used for the previous widget testing. Here is the list:

table: sys_user_group
filter: typeCONTAINS1cb8ab9bff500200158bffffffffff62
fields: name,manager
order_by: name
maximum_entries: 7
aggregates: [{ "label": "Members", "name": "members", "heading": "Members", "table": "sys_user_grmember", "field": "group", "filter": "user.active=true" },{ "label": "Incidents", "name": "incidents", "heading": "Incidents", "table": "incident", "field": "assignment_group", "filter": "active=true" },{ "label": "Catalog Tasks", "name": "sc_tasks", "heading": "Catalog Tasks", "table": "sc_task", "field": "assignment_group", "filter": "active=true" }]
refpage: {"sys_user": "user_profile"}

Using all of that to create a URL for the test page results in this lengthy string:

/sp?id=aggregate_test_4&table=sys_user_group&filter=typeCONTAINS1cb8ab9bff500200158bffffffffff62&fields=name,manager&order_by=name&maximum_entries=7&aggregates=[{"label":"Members","name":"members","heading":"Members","table":"sys_user_grmember","field":"group","filter":"user.active%3Dtrue"},{"label":"Incidents","name":"incidents","heading":"Incidents","table":"incident","field":"assignment_group","filter":"active%3Dtrue"},{"label":"CatalogTasks","name":"sc_tasks","heading":"CatalogTasks","table":"sc_task","field":"assignment_group","filter":"active%3Dtrue"}]&refpage={"sys_user":"user_profile"}

Once I constructed the URL, I added the following HTML to the HTML widget that I inserted on the page:

<div class="text-center" style="padding: 15px;">
  <a href="/sp?id=aggregate_test_4&amp;table=sys_user_group&amp;filter=typeCONTAINS1cb8ab9bff500200158bffffffffff62&amp;fields=name,manager&amp;order_by=name&amp;maximum_entries=7&amp;aggregates=[{&quot;label&quot;:&quot;Members&quot;,&quot;name&quot;:&quot;members&quot;,&quot;heading&quot;:&quot;Members&quot;,&quot;table&quot;:&quot;sys_user_grmember&quot;,&quot;field&quot;:&quot;group&quot;,&quot;filter&quot;:&quot;user.active%3Dtrue&quot;},{&quot;label&quot;:&quot;Incidents&quot;,&quot;name&quot;:&quot;incidents&quot;,&quot;heading&quot;:&quot;Incidents&quot;,&quot;table&quot;:&quot;incident&quot;,&quot;field&quot;:&quot;assignment_group&quot;,&quot;filter&quot;:&quot;active%3Dtrue&quot;},{&quot;label&quot;:&quot;CatalogTasks&quot;,&quot;name&quot;:&quot;sc_tasks&quot;,&quot;heading&quot;:&quot;CatalogTasks&quot;,&quot;table&quot;:&quot;sc_task&quot;,&quot;field&quot;:&quot;assignment_group&quot;,&quot;filter&quot;:&quot;active%3Dtrue&quot;}]&amp;refpage={&quot;sys_user&quot;:&quot;user_profile&quot;}">
    <button class="btn btn-primary">Click to Test</button>
  </a>
</div>

Now, all I needed to do was to bring up the Service Portal and pull up the page.

First look at the fourth test page

OK, that’s not all I needed to do. I also needed to click on the button to invoke that long URL with all of the parameters included.

Fourth test page after clicking on the Click to Test button

There, that’s better. So, it looks like everything seems to be in working order. That takes care of the last of the wrapper widgets. We still need to update the configuration file editor to support the new aggregate columns, which should be relatively straightforward, so maybe we can jump right into that next time out, wrap this whole thing up, and put out a new Update Set.

Collaboration Store, Part LVI

“Ideas don’t happen in isolation. You must embrace opportunities to broadcast and then refine your ideas through the energy of those around you.”
Scott Belsky

Now that we have released the latest version of the Collaboration Store Update Sets for testing purposes, we should take a quick look ahead to see where things might go from here. Obviously, the first order of business is to validate the existing build and work to put out that 1.0 version with the current feature set. However, it won’t hurt to take a moment and look a little bit beyond that to see what may lie ahead for this app. This first version accomplishes the three main goals of providing the ability to set-up an instance, publish an app, and install an app, but there are a lot things missing that would be helpful and/or really nice to have. So let’s take a look at that list.

Images

One of the things that has disturbed me for quite some time is the fact that the logo image for an app is not included in the Update Set when you select the Publish to Update Set… option on the Custom Application form. We use that very same function to move an app into the Store, so our process suffers from the same shortcoming. We should be able to add some extra processing to our workflow, however, that could work around this issue. One of the things that I thought might provide a solution would be to copy the image file attached to the app and attach the copy to the Collaboration Store application record. Then during the installation process, we could copy the image from the application record over to the installed app once the application installation process was completed. That would also provide the opportunity to display the application logo on the application form, which could be done in much the same way as the user photo is displayed on the User Profile form.

Speaking of images, I have also thought from the beginning that each instance in the community should have its own image as well, and that all of the applications shared by that instance should include the providing instance’s image as well as the specific image of the application. This would provide additional visual clues when searching through the apps in the store.

Conditionals and UI Policies

What you can and cannot do with certain records and fields needs to be tied to the original owner of the artifact. For example, the Install button should not appear on version records for your own applications. Conversely, the Publish to Collaboration Store link should not appear on applications that are not your own. And all of the form fields on records that did not originate in your instance should be protected, as you should not be allowed to make changes to records that are not your own. Also, there are certain fields that should never be updated under any circumstances (such as the version number), as these are determined by background processes and should never be editable. There is a lot of work that needs to be done in this area, and it probably should be done before the initial 1.0 version is released to the general public.

Shopping

Version 1.0 was all about getting the mechanics to work, making sure that a scoped app on one instance could be shared, stored, distributed, and installed on some other instance. Once testing establishes that all of those things work as they should, though, it will be time to start looking into setting up a much better experience for locating an app that looks interesting or meets a specific purpose. Images will help create a more visual, catalog shopping type of searching, but so will key words and categories and even marketing tools and programmatic hints such as “People who installed this app also installed these apps:”. User ratings, comments, error reports, and other feedback would also help improve the shopping experience. Also, statistics on how many instances have installed the app and which versions were in use would be useful information when looking at available options. So many possibilities; so little time.

Activity Tracking

From the onset of this project I have always thought that there should be some kind of running log of the activities within each instance, particularly the Host. I set up a table for this early on when I was first setting up the tables for the app, but there is no code in this version to write to this table for any reason. The next major release, if there is such a thing, should really complete this functionality and log every movement of data between the Host and the Clients. Maybe a simple logActivity function could be crafted to accept certain arguments and then all you would have to do to add logging would be to add a single line of code to call that shared function. That sounds like a project, though, but it needs to be done at some point.

Code Consolidation

A while back I created a series of shared function to replace a collection of cloned functions so that I would not clone them all for yet a third time. In my new use case, I utilized the shared functions that were designed to work in all three places, but I was too lazy to go back and refactor the original two sections of code where I had done the initial cloning. I really need to take the time to go back and rework the original and that first copy to have both of them use the redesigned functions that were designed to work for all of the places where I needed to ship instance data, application data, version data, and XML Update Set attachments from one instance to another. It works the way that it is right now, but for future maintenance (and just decent coding standards), that all needs to be addressed.

Additional Artifacts

This initial version allows you to share Scoped Applications between unrelated instances in the same community (Clients of the same Host). That is a good start, but it would also be nice to be able to share single artifacts or global Update Sets or any other component or collection developed on the Now Platform. That was a little more than I was willing to take on at the onset of this project, but now that the sharing of apps between instances seems to be a reality, it would be nice to branch out a little and start adding more stuff that could be shared. Once again, I seem to have more ideas than I do free time. Still, it would be nice to do more than just whole apps.

First Things First

Still, the original goal was to see if we could just make this work, and although it seems like everything is functioning as it should, every little thing needs to be tested out. Hopefully, a few brave souls are actually busy doing that very thing while I type this out, so maybe we will get some much needed feedback relatively soon and see if this thing actually works for anyone other than myself! As always, any feedback of any kind is always appreciated. Thanks in advance to those of you who have actually pulled this down and given things a go.

Collaboration Store, Part LV

“A person who tries has an advantage over the person who wishes.”
Utibe Samuel Mbom

Theoretically, all of the artifacts are complete for this initial version of the Collaboration Store, but now that we have crossed that threshold, we need to really shake everything out before we can build that final 1.0 version that will be solid enough for public consumption. Before we do that, though, we should probably back up just a bit and explain the general purpose of the app, and go through a little bit of a user guide for the folks that might be joining this party a little more recently.

The concept for the initial version of the app is pretty simple. It allows developers from one ServiceNow instance to share Scoped Applications with developers on a different instance. There are three primary functions included in the app: 1) the initial set-up process, 2) the application publishing (sharing) process, and 3) the application installation process. One instance in the community needs to be designated as the Host instance, and all other instances are considered Client instances. The Host instance needs to be set up first, as the set-up process for a Client instance requires communication with the identified Host. Once an application has been pushed to the Host, the Host instance then distributes that application to all of the Clients in the community. Once any instance has a version of a shared application, developers can then install that application on their own instance.

Initial Set-up

Once all of the artifacts have been installed, the first thing that you need to do on your instance is to run the set-up process. To enter the set-up process, select Collaboration Store Set-up from the Collaboration Store section of the left-hand nav. You will need to be in the Collaboration Store scope, and if you are not already in that scope, you will be prompted to make the switch.

Initial set-up screen

The form is fairly self-explanatory, but you need to make sure that you have selected the correct Installation Type, and if you are setting up a Client instance, you will want to make sure that you have entered the correct Host instance ID, which is the first element of the URL of the instance, the unique portion that precedes the .service-now.com portion of the instance address. Also, you will want to enter a valid email address to which you have access, as an email will be sent to that address with a verification code, which you will need to enter on the next screen.

Email verification screen

Once the email has been validated, the set-up process commences and the final screen of the set-up process appears when the set-up process is complete.

Set-up completion screen

Application Publishing

Once your instance has been set up, you should now be able to publish applications to the store. This is accomplished through a new link that the application has added to the Scoped Application form. A while ago I created a scoped app called Simple Webhook when I was playing around with outbound Webhooks, and we can use that as an example to give this thing a go. To publish the app to the store, pull up the application form and select the Publish to Collaboration Store link down at the bottom of the form.

Publishing a scoped application

Clicking on this link will pop up the Publish to Collaboration Store dialog where you will enter the details of this version of the application.

Publish to Collaboration Store dialog

Clicking on the Publish button will then bring up the Export to XML progress bar while the Update Set is converted to an XML file.

Export to XML progress bar

Clicking on the Done button will then bring up another dialog box where you can see the progress of all of the other steps involved in creating the Collaboration Store records, attaching the XML file, and sending all of the artifacts over to the Host instance.

Publish to Collaboration Store completion dialog

Clicking on the Done button here will close the dialog and reload the form, completing the process. Once the last artifact reaches the Host, that will trigger a background process that will distribute the app to all of the other instances in the community. You don’t need to worry about that, though; that all goes on behind the scenes and takes care of itself. If you are testing, however, and hopefully you are (the more, the merrier!), you will want to check all of the other instances in the community to ensure that all of the artifacts arrived safely and in good condition. That’s the whole point of the app, obviously, so we want to make sure that it all works as intended.

Now if things do take a wrong turn somewhere along the line, there is another background job that runs on the Host to check with all of the Clients each day and sends over any missing artifacts to keep everything in sync. That’s another thing that we will want to test out thoroughly, which may require introducing some intentional errors just to see if the daily sync process finds and corrects them.

Application Installation

Once another instance has shared an app, you should be able to install it on your own instance, which is accomplished in this version of the app by navigating to the version record for the version that you want to install and clicking on the Install button. This should kick off a series of events starting with the conversion of the XML file attachment back into an Update Set, Previewing that Update Set, correcting any errors detected in the Preview, then Committing the Update Set and updating the Collaboration Store data to reflect the installation of the app. Once again, using our Simple Webhook app as example, let’s pull up the version record on a different instance and click on that Install button.

Installing a shared application

After clicking on the Install button, you will first see the XML file being uploaded to the server.

Update Set XML file being uploaded to the server

Once the XML file has been uploaded and converted back into an Update Set, you will see the Preview progress bar.

Preview progress bar

Once the Preview has been completed, you will see the Commit progress bar, which looks very similar to the Preview progress bar.

Commit progress bar

Once the Commit has been completed, the Collaboration Store records will be updated and then you will be returned to the version record, where the Install button will no longer appear, since this version has now been installed.

Version record after installation

Also, if you preview the associated application record, you will see that the Collaboration Store application has been linked to the installed application and the installed application’s version matches the latest version of the app.

Application record linked to installed application

Let the Testing Begin!

OK, that’s it, the initial set-up process, the application publishing process, and the application installation process. Seems pretty simple for something that took 55 episodes to complete, but there is a lot going on under the hood behind all of those little pop-up screens. Of course, if you want to do any testing, you will need something to test, so here are the artifacts that you will need to install, in the order in which they need to be installed, if you want to take this out for a spin:

  • If you have not done so already, you will need to install the most recent version of the snh-form-field, tag, which is needed for the initial set-up widget. You can find that here.
  • Once you have the snh-form-field tag installed, you can install the newest Scoped Application Update Set.
  • And then once the application has been installed, you can install the Update Set for the additional global components that could not be included in the Scoped Application.

Once you have everything installed, you can go through the initial set-up process and then you should be good to publish and install shared applications. As usual, all feedback is welcome and encouraged. Please pull it down and give it a try, and please let me know what you find, good, bad, or indifferent. If we get any comments, we will take a look at those next time out.

Collaboration Store, Part XLVII

“Relinquish your attachment to the known, step into the unknown, and you will step into the field of all possibilities.”
Deepak Chopra

While we wait patiently for the testing results of the instance sync process to come trickling in, we should go ahead and get started on the application installation process. As I laid out earlier, there are a couple of different ways to go here, and I really do not like either one. However, as much as I would like to have come up with a preferable third alternative, nothing has really come to mind, despite the fact that I have been dragging out the instance sync process for much longer than I should have while I searched in vain for a better solution. We have to keep pushing forward, though, so enough waiting. Let’s get into it.

I have decided to go with the client-side approach, mainly because I don’t want to get involved with the security issues related to attempting to emulate a user session on the server side. Both approaches involve quite a bit more hackery than I really care to include in something that is supposed to be a viable product to be distributed and used by others, but at this point, I don’t see any other way. The client-side approach involves downloading the Update Set XML file from the server, and then presenting it back as if it were a local file uploaded to the import XML process. So, to start with, we will need a Script Include function that will deliver the XML to the client side. Since this will be a client callable Script Include, we will want to create a new one rather than add another function to our existing utilities. We will call this new Script Include ApplicationInstaller, and check the Client callable checkbox when we create it. Our lone function will be called getXML and it will accept a single parameter for the sys_id of the attachment record, and then use that sys_id to fetch the record and return both the file name and the file contents in a JSON string response.

var ApplicationInstaller = Class.create();
ApplicationInstaller.prototype = Object.extendsObject(global.AbstractAjaxProcessor, {

	getXML: function() {
		var answer = {};
		var sysId = this.getParameter("attachment_id");
		if (sysId) {
			var sysAttGR = new GlideRecord('sys_attachment');
			if (sysAttGR.get(sysId)) {
				var gsa = new GlideSysAttachment();
				answer.fileName = sysAttGR.getDisplayValue('file_name');
				answer.xml = gsa.getContent(sysAttGR);
			}
		}
		return JSON.stringify(answer);
	},

    type: 'ApplicationInstaller'
});

There’s nothing too extraordinary about the code here. We do leverage our old friend the GlideSysAttachment to grab the contents of the file, but that’s nothing that we have not already done before. Other than that, it’s pretty basic stuff.

Now that we have a way to fetch the name and contents of the attached file, we will need a way to launch the process, which will either be a clone of the existing upload.do page or the original upload.do page with a little creative hackery to bend it to our will. I hate to mess with the original, but I also don’t want to create an entire second copy if I don’t have to. But let’s not get too far ahead of ourselves just yet. Right now, we just need to build a launcher for the process, and we can easily change the page that we launch once we decide which way to go.

To launch the installation process, we can add a UI Action to the version form, which we can simply label Install. We will want to make this action conditional, since we would not want to see this option on a version that has already been installed. At this point, we don’t need to worry about the code behind the action; we can just create it and see how it renders out on the page.

New UI Action “Install”

After saving the new action, we can pull up a version page and see how it looks.

New UI Action rendered on the version page

Now we need to add some code to make the action actually do something. Basically, we just want to jump over to whatever version of the upload.do page we end up pursuing, but before we do that we need to include a parameter. The client callable Script Include that we just created takes an attachment record sys_id as a parameter, but out action is sitting on the version record, not the attachment record. We will need to hunt down the attachment record to pull out the sys_id and then we can pass that along to our destination so that it can be used to make the GlideAjax call to our Script Include function. Here is the UI Action script to make all of that happen.

var attachmentGR = new GlideRecord('sys_attachment');
attachmentGR.addQuery('table_name', 'x_11556_col_store_member_application_version');
attachmentGR.addQuery('table_sys_id', current.sys_id);
attachmentGR.addQuery('content_type', 'CONTAINS', 'xml');
attachmentGR.query();
if (attachmentGR.next()) {
	action.setRedirectURL('/upload.do?attachment_id=' + attachmentGR.getUniqueValue());
} else {
	gs.addErrorMessage('No Update Set XML file found attached to this version');
}

Once again, this is pretty basic stuff that doesn’t require a whole lot of explanation. We query the attachment table for an XML file attached to the current record, and if we find it, we construct a URL and jet off to that page. If not, we throw up an error message, but that really shouldn’t happen unless something has gone horribly wrong somewhere along the line.

So now we have built enough parts to get us to the page that will actually fetch the XML from the server and then push it back up again. I need to make a decision as to whether I should hack up the original upload.do page or just use it as a guide to make one of my own. I’ll need to give that some thought, so let’s pause for now and we’ll jump into that next time out.

Collaboration Store, Part XLVI

“Fundamentally you can’t do your own QA. it’s a question of seeing you own blind spots.”
Ron Avitzur

Last time, we wrapped up all of the coding for the process that will keep all of the Client instances in sync with the Host instance. I have been dragging this process out a little bit in the hopes of coming across a much better approach to the application installation process, as I am not all that happy with the solutions that have come to mind so far. But we have finally come to the end of the instance sync build, so it’s time to release another Update Set and do a little testing. After that, I’m going to have to plow ahead on the application installation process whether a new and better idea has revealed itself or not (so far, not!). And as long as we are going to be doing a little testing, we might as well do a little regression testing as well on the initial set-up process and the application publication process, just to make sure that we haven’t mangled something up with all of this additional code. It would be nice to know that everything still works.

As with our other recent releases, you will need three items, installed in the following order, to be able to give this all a spin:

  • If you have not done so already, you will need to install the most recent version of SNH Form Fields, which you can find here.
  • Once you have SNH Form Fields installed, you can install the new Scoped Application Update Set.
  • And then once the application has been installed, you can install the Update Set for the additional global components that could not be included in the Scoped Application.

If this is a new installation, then you can test out the initial set-up process when you set up your instance. You can find information on testing the set-up process here.

If this is a new installation, or if you have not yet published any applications, you will want to go through the process of publishing a few applications from as many of your instances as possible to fully test out the instance sync process. You can test the set-up process with a single Host instance, but to fully test the application publishing process or the instance sync process, you will need a few Client instances in your community as well. You can find information on testing the application publishing process here.

Assuming that you have successfully installed all of the parts, have successfully gone through the initial set-up process for your Host and Client instances, and have published a few apps from multiple sources, you are now ready to test the instance sync process. But how would we do that, you ask. Well, there are a number of things that can be done, starting with just making sure that the process runs on the Host instance on schedule and doesn’t do anything at all on any of the Client instances. The best way to verify that is to install the latest version everywhere and then go into the Flow Designer after 24 hours and pull up the flow and check out the execution history. If everything is working as it should, the process should run every day, and on the Host instance, there should be as many iterations of the loop as there are active Client instances in the community.

You don’t necessarily have to wait for the process to run on schedule, though, if you want to some more in-depth testing. You can use the Test button on the flow to run the process as many times as you would like to test out various scenarios. Here are just a a few of the things that you can try, just to see how things work out.

  • Delete one of the Client instance records on one of the Clients and then run the sync process to see if the Client and all of its applications have been restored on that instance.
  • Delete one of the application records on one of the Clients and then run the sync process to see if the application and all of its versions have been restored on that instance.
  • Delete one of the version records on one of the Clients and then run the sync process to see if the version and its attached Update Set XML file have been restored on that instance.
  • Delete an attached Update Set XML file on one of version records on one of the Clients and then run the sync process to see if the attachment has been restored on that instance.

You can also take a look at the System Logs to see the breadcrumbs left behind by the process to see if they are all accurate and meaningful. Any suggestions or helpful hints in this area would also be quite helpful. Basically, you just want to create a situation where one or more Client instances is out of sync with the Host, and then either let the process run as scheduled or manually kick start it in the Flow Designer, and then check to make sure that everything is back in sync again.

Once again, I would like to express my appreciation to all of those who have assisted with the testing in the past, and to anyone who has not participated in the past, but would like to join in, welcome to the party! You all have my sincere gratitude for your efforts, and as always, any and all comments are welcome and appreciated. If you tried everything out and were not able to uncover any of the bugs that are surely buried in there somewhere, let me know that as well. It is always welcome news to find out that things are working as they should! Thanks to all of you for your help.

Next time, if there are no test results to review, we will finally get into the application installation process.

Collaboration Store, Part XXXV

“The good news about computers is that they do what you tell them to do. The bad news is that they do what you tell them to do.”
James Barton

Well, the test results are starting to trickle in, and one of the issues looks rather important, so we need to take a look at that before we jump into our next major effort. The problem with the scoped System Properties came up earlier, and I thought that I had found a way to deal with the issue, but obviously there is still a problem where the initial set-up is concerned. Basically, if you are not in the Collaboration Store scope, then the set-up fails because the updates to the scoped System Properties are not allowed. My idea of moving the update logic to a global component did not resolve this issue, as the problem is related to the active scope of the user rather than the scope of the component.

So, it seems that the answer to the problem is to ensure that the user is in the correct scope before allowing them proceed with the set-up. Fortunately, there is already an area in ServiceNow where that very check is made, and there is even an option in the error message to switch over to the correct scope. You can see that when you bring up a scoped app and you are not in the scope of that application.

Sample error message for incorrect scope on the scoped application form

So it would seem that we could snag the HTML for that message and throw it up in the top of the HTML for the initial set-up widget with some kind of ng-hide so that it only appears if you are in the wrong scope. The first thing that we would need to do, then, is to figure out how to detect the user’s scope and then set up some boolean variable in the widget to indicate whether or not the user is in the correct scope to proceed with the set-up. Looking at the GlideSession API, it seems like the getCurrentApplicationId() method is just what we need. So I added this line to the top of the server script of the widget:

data.validScope = (gs.getCurrentApplicationId() == '5b9c19242f6030104425fcecf699b6ec');

Then, to prevent the user from submitting the form when in the wrong scope, I modified the ng-disabled tag on all of the submit buttons from this:

ng-disabled="!(form1.$valid)"

… to this:

ng-disabled="!(form1.$valid) || !c.data.validScope"

Now all I needed to do was to grab a copy of the HTML source from the application form and paste it in at the top of the HTML for the widget and see if the link would still work. Unfortunately, the link in the existing code relies on the GlideURL object, which is not available in Service Portal widgets, so we are going to have to hack that up a little bit to get around that problem. Here is the existing onclick script:

window.location.href=new GlideURL('change_current_app.do').addParam('app_id', '5b9c19242f6030104425fcecf699b6ec').addParam('referrer', window.location.pathname + window.location.search + window.location.hash).getURL();

Basically, this code just builds a URL, so it seems as if we could just go ahead and build the URL manually and hard-code the link. After doing a little tinkering around to see just what the URL actually was, I came up with this value for our circumstances:

change_current_app.do?app_id=5b9c19242f6030104425fcecf699b6ec&referrer=%24sp.do%3Fid%3Dcollaboration_store_setup

So, my final HTML, including the ng-hide based on my new widget variable, turned out to be this:

<div id="nav_message" class="outputmsg_nav" ng-hide="c.data.validScope">
  <img role="presentation" src="images/icon_nav_info.png">
  <span class="outputmsg_nav_inner">
    &nbsp;
    The <strong>Collaboration Store Set-up</strong> cannot be completed because <strong>Collaboration Store</strong> is not selected in your application picker.
    <a onclick="window.location.href='change_current_app.do?app_id=5b9c19242f6030104425fcecf699b6ec&referrer=%24sp.do%3Fid%3Dcollaboration_store_setup'">
      Switch to <strong>Collaboration Store</strong>
    </a>.
  </span>
</div>

Now all I needed to do was to bring up the set-up widget while in the wrong scope and see how it looked and how the new link worked out once I gave it a try.

Initial set-up screen with error message and link when in the incorrect scope

With the hard-coded link in the onclick function, everything seems to work as intended. The user is placed in the correct scope, the form is reloaded, and the error message goes away. This should finally resolve this annoying issue once and for all (let’s hope!).

The other issue that was reported was that the provider field on the application record was not populated when publishing a new version of an application. It was not clear from the comment whether the missing data was on the Client instance, the Host instance, or both, but I was unable to recreate any of those conditions in any of my testing. I am going to need a little more detail on this one before I can address it, so if anyone encounters this error in any of their testing, please leave a detailed message in the comments so that we can get it resolved.

There is still the potential for more feedback to come, but this particular issue seems to be rather critical, so it’s time to release a new Update Set. While I am at it, I think I will take a different approach on the global components and make an actual Update Set for that as well instead of just exporting the XML for the one global Script Include. This way, I can also include the app’s logo image, which is always missing from the XML generated for a scoped application. Here are the two new Update Sets:

As always, all feedback is welcome, and not just to report issues. If you give this a try and everything actually works, I would love to hear about that as well. Next time out, we will either deal with any more issues to come to light, or we will jump into the next big challenge, which will be to install an app published to the store.

Special Note to Testers

Set-up Process – If you want to test the set-up process, you will need to set up the Host instance first. You cannot set up a Client instance until there is a valid Host instance, as the Client instance set-up process requires access to a valid Host instance. What to look for: check to make sure that all instances in the community have the same list of instances; select Collaboration Store -> Member Organizations to pull up the list of instances in each instance to verify that all instances have the exact same list of all instances in the community.

Application Publishing Process – If you want to test the application publishing process, you will need to go through the set-up process first, and then you can publish an application to the store. This can be done with a single Host instance, but to fully test all of the functionality, you will need at least two Client instances in addition to the Host. What to look for: once you publish the application, check to make sure that all instances in the community have the newly published version of the application, including the XML Update Set attachment; select Collaboration Store -> Member Organizations to pull up the list of instances, then check the Related List of applications under the appropriate instance for the application, and then click on the app to check the Related List of versions under the application.

Application Installation Process – If you want to test the application installation process, you have jumped ahead of the class, as that portion of the app has not yet been developed. However, even though the development of the official installation process has not even been started, if you really want to install an app that has been shared with your instance, you can always download the XML attachment on the version record, import it back into your instance, and go through the normal XML Update Set installation process that you would go through for any other imported Update Set. But again, that’s getting a little ahead of where things stand right at the moment with project’s development.

One More Thing

As mentioned earlier, there is currently no error recovery built into the system at this time. This is definitely something near the top of the we-really-need-to-do-this list, but it is not there right now. What that means is that if you are doing any kind of testing and one or more of the instances in your community is off-line or unavailable for some reason, things will fail and those instances will not get the needed updates. One day we will definitely need to fix that, but for now, if that happens, that’s not a bug in the software to be reported; it’s to be expected until we build in some kind of error recovery into the product.

Also, if you end up testing things and don’t have any issues to report, then by all means, report that, too! You don’t have to have encounter a problem to post your results. If everything worked out as expected, we would definitely love to hear that as well. As always, all feedback of any kind is very much appreciated.

Collaboration Store, Part XXXIV

“The key is not to prioritize what’s on your schedule, but to schedule your priorities.”
Stephen Covey

So far, we have completed the first two of the three primary components of the project, the initial set-up process and the application publication process. The last of the three major pieces will be the process that will allow you to install an application obtained from the store. Before we dive straight into that, though, we should pause to take a quick look at what we have, and what still needs to be done in order to make this a viable product. At this point, you can install all of the prerequisites and then grab the latest Update Set, install it, and go through the set-up process to create either a Host or Client instance. Once you get through all of that, you are ready to publish any local Scoped Application to the store, which will then be shared with all other instances in your Collaboration Store community.

What you cannot do, just yet, is to find an application published to the store by some other instance and install it on your own instance. That’s the missing third leg of the stool that we will need to take on next. But that is not all that is left to be done. Once we get the basics to work, there are quite a number of other things to address before one could consider this to be truly usable. Some things are just annoyances, but others are definite features that you would have to consider essential for a complete product.

Speaking of annoyances, one of the things that I really don’t like is that when you publish an app to XML for distribution, the resulting Update Set XML does not include the app’s logo image. Clearly it is a part of the app, and if you push an app to an internal store and pull it down into another instance, it comes complete with the logo, so why they left that out of the XML is a mystery to me. I don’t like that for a couple of reasons: 1) when you pull down the XML for this app, you do not get the logo, and 2) when we use the XML to publish an app to the store, the logo is missing there as well. I have seen people complain about this, but I have not, as yet, seen a solution. I would really like to address that, both for my own apps as well as for the process that we are using in this one.

Speaking of logos, another feature that I would like to have is to provide the ability for each instance to have its own distinctive logo image, so that everything from that particular instance could be tagged with that image as a way to visually identify where the app originated. That’s not a critical feature, which is why I did not include it initially, but it has always been something that I felt should be a part of the process, particularly when you start thinking about ways to browse the store and find what you are looking for. That’s definitely on the We-will-get-around-to-it-one-day list.

Browsing the store is another thing that will need some attention at some point. Right now, we just want to prove that we can set-up the app, publish an application, and install an application published by someone else. Those are the fundamental elements of the app. But once we get all of that worked out, being able to hunt through the store to find what you want will be another important capability to build out. We’re not done with the fundamentals just yet, so we don’t want to put too much energy into that issue right at the moment, but at some point, we will need to create a user-friendly way to easily find what you need.

That, of course, leads into things like searchable keywords, tags, user ratings, reviews, and the like, but now is not the time to head down that rabbit hole. Still, there are a lot of possibilities here, and this could turn into a life-long project in and of itself. That’s probably not a good thing, though!

Anyway, we won’t get anything done if we don’t focus, so we need to stay on task and figure out the application installation process. Once again, there are several options available, but the nicest one seems to be the process that you go through to install an app from an internal store. That’s basically a one-click operation and then the app is installed. Unfortunately, that particular page is neither Jelly nor AngularJS, so you can’t just peek under the hood and see the magic like you can with so many other things on the Now Platform. Another option would be to hack up a copy of the Import XML Action on the Update Set list page to push in the attached XML from a published app version, but that only takes things so far; you still have to Preview the Update Set, resolve any issues, and then manually issue the Commit. It would be much nicer if we could just push a button and have the app installation process run in the background and notify you when it was completed. Obviously, we have some work to do here to come up with the best way to go about this, and we had better figure that out relatively soon. Next time, if we are not dealing with test results from the last release, we will need to start building this out.