Tuesday, 31 January 2017

A few days layer ...

Not only does AppVeyor gives us continuous integration, it can give us continuous delivery as well.

So after getting the newly migrated MapGuide Maestro on GitHub hooked up to AppVeyor for CI and automated publishing of code coverage results to Coveralls, I wanted to look at how AppVeyor can be used for automating the creation of GitHub releases. The ideal scenario, is that when I decide to put out a release, I hit some button or run some command and a new GitHub release will show up a few minutes later.

It turns out, AppVeyor has a whole section on their documentation dedicated to this very subject! Some tweaks were required to the AppVeyor build system so that it also builds the API/User documentation and zip/installer packages and then by specifying these artifacts in the artifacts section of appveyor.yml, they will appear in the artifacts section of the AppVeyor build page.

What that means is that you now have access to the latest builds of MapGuide Maestro straight after AppVeyor builds and validates them. However, this is not the way I want you to access official releases of Maestro, I still want GitHub releases created for this particular case, and ideally in an automated fashion.

This is where we set up our AppVeyor configuration to deploy using the GitHub deployment provider, with the trigger being a push of a git tag. What this means in practice is when I decide to put out an official release, I make the release tag in Git, push it out to GitHub. This triggers AppVeyor to do its usual CI and artifact packaging. But because this is a release tag, it will also automatically create a corresponding GitHub release in a draft state and uploads the associated artifacts of this build with it.

From there, I can visit the draft release page and tidy up the release description and add release notes, click the publish button and voila! A new release is ready for you to download. How well is this automated process? Check out this test release and find out for yourself.

I'm really liking this well-oiled CI/CD machine, further solidifying my justification for making the move to GitHub in the first place. Oh, and as the above screenshot shows, I also got my first pull request today for Maestro too. Things are looking up!

Friday, 27 January 2017

A few hours later ...

Moving MapGuide Maestro to GitHub has already paid off!

The code is hooked up to AppVeyor for automatic continuous integration

Our unit test suites are hooked up to OpenCover, whose reports AppVeyor will automatically upload to coveralls.io.

And these services are all free!

And we get some nice badges to show for it!

So just to give some context for that coverage result: This coverage is for the MaestroAPI part of Maestro. It does not cover the WinForms application itself, which I find trying to write automated end-to-end tests (and hence coverage) to be a fruitless endeavor. It will either work or not, that's as good enough of a test as any. MaestroAPI on the other hand, is the engine that drives the application and this is something that definitely needs the test validation and coverage as it has uses outside of the Maestro application and we need objective confidence in the stability and robustness of the MaestroAPI, which these aforementioned services will give us.

Thursday, 26 January 2017

Announcing: MapGuide Maestro is moving to GitHub

After some thoughts and considerations, I've decided to move MapGuide Maestro out of the MapGuide Open Source SVN repo and out into its own GitHub repository.

The primary motivation was performance (git is way faster than svn and enables more productive development) and secondly, the "pull request" model of GitHub and other DVCSes is more conductive to external contributions than what we currently have now, which is code sitting as a sub-project in a SVN repository that requires special vetting and access for commit access to be granted.

Whereas with GitHub and friends it's just fork the repository, make your changes and send back a pull request. At no point in the above process do I ever have to get involved beyond reviewing and accepting any pull requests that comes my way. Less administrative burden on my end, less bureaucratic road blocks for external contributions on yours. Win win for everyone!

As of this post, I will cease to make any more Maestro-related commits to the SVN repo, I'll be migrating all Maestro-related issues and wiki content over to GitHub. Do not make anymore Maestro-related issues/tickets on the MapGuide Trac instance, make them on the GitHub project instead. Once the migration of issues and wiki content has completed, I'll remove Maestro from the SVN repo. Old releases will stay on the OSGeo download server, but new ones will be on GitHub.

Thursday, 19 January 2017

React-ing to the need for a modern MapGuide viewer (Part 10): The end goal of this refactoring

This post is a little backstory as to why I needed to do some behind-the-scenes refactoring.

It is so we can finally bring across this important feature from Fusion and cross off another TODO list item in the process: Support for multiple maps!

To finally support multiple maps, we had to re-structure our redux state tree so that:

1. All map-specific state (selections, layer/group toggling state, current view/scale, etc) now exist in individual branches under the mapState reducer. Each branch is keyed on the name of the respective runtime map.

2. A new active map name property is added to the configuration state branch. We then simply change our respective mapStateToProps implementations in these components to interrogate this new property to determine which map state sub-branch to read their required map state from. The beauty of this is that the state that these redux-aware components pass down to child components is mostly un-changed. They are all insulated from having to know anything about the current map.

3. To service the MapMenu component, we store the array of available map names along side the new active map name property. The entries in the MapMenu component are bound from this array. Any change from this component dispatches an action to update the active map name, and all map-related components will then auto-magically update and re-render themselves to reflect the state of the new active map.

With this set up we get a nice encapsulation of map state as everything map-related now drives off of the active map name (that the MapMenu component has the sole responsibility of toggling), as best demonstrated by this animation below

The above animation demonstrates that map-specific state like selections, layer/group visibility, current view, external base layers, etc are all perfectly isolated within their respective redux state branches and the mere act of switching maps is just deciding which particular state branch we consider to be the "active" map. It is not possible for states in other maps to "bleed over" into the current map. We never got this kind of encapsulation or safety guarantees with the current Fusion implementation and no doubt is the cause of many bugs with Fusion and AppDefs with multiple Map Groups.

There's still some aspects of the viewer that still need verification under a multi-map configuration, but for the most part this is working pretty well.

Wednesday, 11 January 2017

React-ing to the need for a modern MapGuide viewer (Part 9): It's like a Christmas tree!

After figuring out how to make an accordion and getting the 0.7 release out the door, I needed to do some necessary refactoring of how we're currently modelling application state in redux to make my intended marquee feature of the next release possible.

After the refactoring, I saw some noticeable sluggishness in the viewer across all templates. This is where my choice to build this viewer on top of React has been validated (once again for the umpteenth time!), as it comes with top-notch developer tool support. So I flicked over to the React Developer Tools, activated update tracing and lo and behold ...

The viewer lit up like a Christmas tree, without any user interaction! What this was showing was that some React component was constantly updating and re-rendering itself and the sluggishness I was experiencing was in part due to that.

So it was clear that the refactoring caused some component to constantly be updating. But what?

That's where I turned to a new feature in the latest 15.4 release of React, which is by simply appending react_perf to the query string

It activates performance profiling in React itself. I then switched to the Timeline tab in the Chrome devtools and recorded 5 seconds of this "idle" activity

And we see there is activity that corresponds to this constant updating/re-rendering. If we drill down to one of these hot spots.

We finally identify the culprit:

  • It is the map viewer component
  • Something is causing the busy count to constantly go up and down. We store busy count as redux state so that the Navigator (aka. Zoom slider) component can listen on to determine whether to show/hide the "busy" animating indicator at the top of the component
The incrementBusyWorker and decrementBusyWorker functions are registered handlers for the imageloadstart and imageloadend events provided by OpenLayers MapGuide image sources. The only way these events would constantly fire from OpenLayers would be if something was constantly refreshing the map.

Which meant the possible suspect location is most likely in the component's componentWillReceiveProps lifecycle hook function which we use to interact with OpenLayers in response to component prop changes.

I stuck a breakpoint here to see if it was being hit while "idle", and it did!

While paused in this breakpoint, I evaluated the above two expressions to see what was being compared

And there was the problem! My visibility difference check was insufficient as it only did a shallow object reference comparison. The possible cause for this behaviour? My refactoring and over-zealous use of the new object spread operator in TypeScript 2.1 for shallow state cloning in my various redux reducers exposed and brought this problem into the light.

The fix was two parts. Firstly, to replace these (obviously incorrect) checks with helper functions that not only check the references (a null/undefined to set reference transition or vice versa is still a legit difference), but the actual object properties themselves.

Secondly, the layerGroupVisibility prop was a nested object prop that was being created in our component's connect function. This is actually a bad thing, which upon further reading makes perfect sense as returning new objects made in connect() will break the shallow object comparison that redux will do against a previous connect-invocation to short-circuit unnecessary component re-rendering. The fix here is to flatten this particular prop by replacing the layerGroupVisibility prop with the 4 individual array props and update all prop usages/references in the component code accordingly.

With this change in place, turning on React update tracing again shows that it no longer flashes around like a Christmas tree.

So what were the lessons learned here?
  • Component props and state, and how you structure and compare them are important when it comes to performance. The endless articles out there on the art of properly implementing shouldComponentUpdate underscores how important this fact is.
  • On the same vein, Redux has its own set of best practices for performance. This is good reference.
  • React's developer tooling is top-notch, and allows for performance issues like this one to be easily identified and debuggable.
Now to make sure this major refactoring didn't regress any other stuff.

Saturday, 7 January 2017

Announcing: mapguide-react-layout 0.7.1

Here's a quick bug fix release to address the following issues:

The base layer switcher now has a "NONE" option just like its Fusion counterpart

Thanks to some insights from the Blueprint devs, the Accordion component in the Slate and Maroon templates now initially shows the Task Pane when loaded as I had originally intended.

Finally, the scale display dropdown (shown when you have tiled maps) should now properly work when selecting a fractional scale.

Oh, and here's something I left out of the new features for 0.7. The viewer options now actually does something instead of being an empty placeholder. You can use it to toggle feature tooltips on/off should you not have a MapTip present in your toolbar.


Tuesday, 3 January 2017

Announcing: mapguide-react-layout 0.7

It's a new year and the releases keep on coming!

Here's a summary of changes since the last release

Blueprint as UI foundation

I've adopted Blueprint as the UI foundation for mapguide-react-layout. This gives us a rich and cohesive toolbox of various React UI components and key components that allow us to port over the remaining Fusion templates across. Various UIs such as QuickPlot, Measure, Selection Panel, etc have got the Blueprint facelift.


All Fusion Templates available

Thanks to components provided by Blueprint, we now have the key ingredients to bring across the remaining Fusion templates.

Other Changes

The BasemapSwitcher widget has been ported over

An initial cut of the Geolocation widget has also been ported across

The scale display is also editable

For tiled maps, this turns into a dropdown

Also the zoom slider, it better reflects reality in terms of positioning, especially for tiled maps.

And much more is available from the release notes from the download link below.


Announcing: mapguide-rest 1.0 RC5

Here's a long awaited new release of mapguide-rest.

This release is PHP 5.6 compatible (so it will work with MapGuide Open Source 3.1, that bundles PHP 5.6) and includes various fixes that can be found in the referenced change log


So ... what's stopping me from putting an end to these endless 1.0 RC releases?

I need to answer (and commit to it) the age old question of: What is the best strategy for versioning REST APIs? Currently, I'm leaning towards the "put the version in the URL" approach. If you have played around with the mapguide-rest addin for Maestro, that is what the "v1" in the url represents when you connect to mapguide-rest from Maestro.

The bits I still have to figure out is once the "v1" API is set in stone, how do we evolve the API afterwards. I need a solid plan for post-v1 API evolution in place before I can slap the 1.0 final label.