Saturday, 30 November 2019

Dreamforce 2019

Dreamforce 2019

Updated 01/12/2019 to add that Evergreen is in closed developer preview in Spring 20.

It’s now over a week since Dreamforce 2019 officially finished, the jet lag is starting to fade and those afflicted by show fever are coming to terms with the fact that a lot of what they saw isn’t going to be available for a while.

Keynote

Marc Benioff’s keynote was somewhat different from previous years, reflecting the sheer breadth of the Salesforce product set. Instead of lengthy demonstrations of specific clouds, we got quick snippets and pointed at the keynote for the cloud in question. How long before it’s just a page of short links that we read through together?

There was an additional demonstration, when a protestor stood up and started reading a speech that only those around him could hear.

This was clearly expected, as Benioff didn’t miss a beat and told him he had 30 seconds and a timer appeared on screen. This would have been the time to grab the mike and make a few killer points, but the plucky protestor simply continued reading from his script without amplification in a room that holds around 15,000 people. Definitely a missed opportunity in my opinion.

Developers

There are some cool features on their way, especially for developers, and below are three that really caught my attention.

1. Evergreen

The coolest announcement was Evergreen. (As is traditional for Salesforce, this term has already been in use for a while, originally denoting a CPQ subscription with no end date).From a developer perspective, it now means serverless functions, written in open programming languages, invoked from Apex, /Flows or triggered by Events. This kind of thing has always been possible (and Mick Wheeler's talk on Microservices inside Salesforce with Platform Events and Change Data Capture demonstrated exactly this) but the secret sauce for Evergreen is (per the Salesforce blog post) :

Evergreen is a seamless part of the Salesforce platform and no extra authentication or networking setup is required. 

The demo that I was given in the Trailhead zone made it look very simple (no surprise there), with Node microservice code in a subdirectory of the force-app/main/default directory, everything configured through Salesforce setup, and the deployment handled by Salesforce tooling. A fair few questions were met with the response “we haven’t decided exactly how that will be done yet”, but as an early preview it was quite impressive. Evergreen goes into closed developer preview in Spring 20. Pre-release orgs should be available in a couple of weeks, but often these don’t have all features enabled from the word go, so we might end up having to wait until the release lands around February. UPDATE 01/12/2019 - I’ve been informed by product management that Evergreen will be in closed developer preview in Spring 20, which means you’d have to be nominated to take part - contact Salesforce if you think you have a good use case, and if you don’t, get used to waiting!

Evergreen feels to me like an admission that the Salesforce platform has been taken about as far as it can be. Those of us working with large and complex implementations have found ourselves spending more of our time battling governor limits (particularly CPU time) and while going asynchronous can certainly help, that usually requires a degree of orchestration and also brings limits of it’s own. By making Evergreen a seamless part of Salesforce, where developers really don’t have to care that much about exactly where their code runs, it sounds like the product team have given us a good mechanism to scale. Of course we haven’t seen the pricing yet, and there’s probably an element of smoke and mirrors about the demo, but so far it looks very good.

Andy Fawcett’s influence was clearly making itself felt here, as these functions will also be packageable by ISVs!

2. Data Mask

As part of the ongoing privacy wars, Salesforce will soon have native protection of data copied from production when a sandbox is refreshed, via Data Mask. This provides three options for data protection:

  1. Anonymization (which we used to call scrambling), where the data is converted to meaningless values.
  2. Pseudonymization, where the field is left with a readable, indicative value, but unrelated to the original value. For example, if the field is a phone number, the real data will be replaced with something that looks very much like a valid phone number, but isn’t. 
  3. Deletion, where the contents of the field are removed.

This is another area where we’ve been able to do this kind of thing for a while, but never completely. For example, we could easily run an apex class after refresh to protect the data, but if field history tracking is enabled then we’d still end up being able to view the original values. We can turn off field history tracking via the metadata API, but then the original data is unprotected for a short while, which means we have to stop everyone logging in until we’ve taken care of all this, which is another brick in the wall. Something that takes care of all this under the hood is a great improvement.

According to the Salesforce Developers blog post, Data Mask will be generally available “next month” which at the time of writing means December 2019.

3. Open Source Lightning Base Components

50 odd of the Lightning Base Components have been open sourced, with more to come. Initially this will be interesting to understand the implementation details and see if we are anywhere near the standard approach. The Github repo makes it clear that contributions are not welcome right now, but I’m sure in the fullness of time they’ll open up the firehose and have to deal with a torrent of pull requests.

If you don’t see your favourite component in the repo, that means it isn’t open sourced yet. Don’t panic, it’s a work in progress.

There’s  a Trailhead for that

In the continuing story of Trailhead eating the world, the developer and admin keynotes had a call to action in the form of a trailmix:

One More Thing

The theatre sessions weren’t recorded this year, just the breakout sessions in Moscone West, so if you presented a theatre session, you can replay it for your local developer group without too many people having a sense of deja vu. 

Related Posts

 

Thursday, 7 November 2019

Dreamforce - It's a Marathon, Not a Sprint

Dreamforce - It’s a Marathon, Not a Sprint

At the time of writing (Nov 7th 2019) Dreamforce is 12 days away. This year will be my 10th consecutive year attending, which doesn’t seem possible but here we are. In spite of my best efforts, I have learned a few things over the years which may be of use to those heading for their first event.

Packing List

Comfortable Shoes

While this feelst like a hackneyed trope, it really is one of the best pieces of advice, especially if you are exhibiting. You’ll be on your feet a huge amount of time, especially once you factor in breakfast sessions and parties, so make sure you aren’t in agony from a couple of hours in. If you need to put on smart shoes (to present, for example), pack them in your rucksack and switch into them only for as long as you need them.

Battery Charger

While you might think your phone has plenty of charge, Dreamforce days are long days. Yes there are charging points, but do you really want to stand there for 45 minutes just charging your phone. It’s also a great way to make new friends if you have some charge to spare!

Space in your Case

You will pick up swag, water bottles and your Dreamforce backpack. Make sure you have enough room to get them home. Also, remember that when picking up your Dreamforce backpack, you may now have two backpacks to manhandle. This is surprisingly easy to forget and it gets real old real quick fighting your luggage all the time.

Exhibiting is Tough

The first year I went to Dreamforce was, like so much of my career, just dumb luck. BrightGen had decided to take a stand, and one of the sales team who was due work the stand took up an offer elsewhere. So a couple of weeks out I was suddenly going to Dreamforce, but on a Booth Staff ticket. This ticket allows you entry to the expo and keynote broadcast rooms and that’s about it, so any ideas I had about attending sessions were not to be. Not that I would have had any spare time anyway, as for some reason everyone was really interested in talking to us about service management contracts even though we were the other side of the world with an 8 hour time difference.

Working a stand is a hard job at Dreamforce, as it’s a multi-day event. Early starts and late finishes are the norm and you have to make sure you are ready to pitch your wares at a moments notice. Don’t underestimate the cumulative effect of four long days on your feet, typically followed by Salesforce or customer events, so you’re still on duty. This is one trip I really wouldn’t want to recreate.

Fitting in Sessions is Tough

One piece of advice I always give to those attending for the first time - don’t sign up for too many sessions, as you won’t get to them. However many you think you’ll be able to make, it will be less, for a variety of reasons including the following.

Moving Buildings Takes Time

Even if it seems like they are really close to each other, such as moving from Moscone West to the Hilton, which as the crow flies is about 50 yards. However, you have to get out of your breakout room, which will take a couple of minutes, longer if you have questions for the presenter. Then you’ll have to get down one or two escalators - if multiple sessions or a keynote have kicked out at this time, you’ll be queuing there for a while. Getting out onto the street from ground level is a snip, but then you have to cross the road. If it’s anything other than early in the morning you’ll find a few thousand people with the same idea, so you’ll have to wait a while.Then you’ll funnel in to the Hilton, figure out where the room is and if you are early enough, join the queue of registered attendees. If you haven’t allowed enough time you’ll find that general admission has been opened up and there are no seats left. Suddenly your planned-to-the-second agenda is blown to pieces and it’s just after lunch on the first day.

If the buildings are a long way apart (Rincon Center to Moscone, for example) it has taken me 30 minutes to get between them around lunchtime. Don’t forget that you are in the middle of a big city, so you have all the standard delays that come with that plus Dreamforce traffic on top.

You Will See People You Know

Even if everything else goes to plan, you’ll bump into someone you know, either in person or online, and stop to chat with them. When you finish chatting you’ll realise it is now 10 minutes into your next session, which is in a different building to the one you’ve been chatting in. This happens to me all the time.

Keynotes Are Busy

Marc Benioff’s keynote is crazy busy, and if you are planning to attend that in person then you really don’t want have anything else to do for at least an hour before. The queues will be huge, and the metal detectors won’t help.

Anything involving American politics will be popular - Hillary Clinton and Michelle Obama both generated queues around the block, which made any kind of movement, towards or away from the keynote, very difficult. I expect Barack Obama to be like this but multiplied several times.

Getting out of the main keynote rooms takes ages, as there are usually 5,000-15,000 people trying to funnel through 4 doors onto a few escalators. If you want to get anywhere quickly, either leave a few minutes early or wait 10-15 minutes.

The Trailhead Zone is Awesome

Once you get in there you won’t want to leave. But you’ll need to, in order to go to the next session that you’ve tried to cram in. So leave yourself some time to explore. I spend a lot of my time here.

Get to the Trailhead Zone Early

Most of the hands-on areas fill up really quickly, so if you don’t want to spend more time queueing, get there as soon as it opens. It also means that there will be plenty of swag once you’ve completed your challenge/trail/quest/whatever it is called this year. The lines for things like spinning the admin wheel of fortune, headshots, t-shirts etc are usually short or empty, so if you move quickly you can cover a lot of attractions in a short time.

And Stay There

As I mentioned earlier, I spend a lot of my time here. Not particularly for the hands on side of things, but for the theatre sessions. I usually prefer these to the full on breakout sessions as they are more bite sized and I can get a quick introduction to something I haven’t worked on before and if it piques my interest, I’ll then go off somewhere and learn more about it. There’s always a talk going on somewhere and you can just pitch up and start listening if something takes your fancy.

Don’t Forget your Badge and Lanyard

10 years ago you could get around with just the badge in your wallet, especially for the after parties. That’s no longer the case and you typically need the whole thing everywhere. Remember that you don’t have to wear it all the time on the street to advertise the fact that you are a tourist. 

You aren’t out with your Friends

By this I don’t mean that people are unfriendly or unhelpful, quite the reverse given the well-publicised Ohana community spirit. What I mean is that you aren’t out on the lash with a bunch of mates who will think it hilarious if you get smashed and cause a scene. You’ll be among colleagues, customers and partners who will expect you to behave in a professional manner. It’s very easy to undo a huge amount of work and hard-won goodwill with a single drunken episode, so always remember you need to stay in control.  Even if you don’t care about the effects on your own reputation and career, nobody else is there to watch you make an idiot of yourself.

It’s a Marathon not a Sprint

Dreamforce is four days, so don’t kill yourself trying to do everything on day one. This goes double for the parties - you won’t get much return on the investment for your trip if you are hungover all the time, and this will definitely reduce the sessions you can make it to (or stick around in!). If the lines are long in the Trailhead zone, get an early night and turn up first thing the the next morning - you’ll be glad you did.

Don’t feel bad about taking a time out whenever you need to. While there are mindfulness/quiet zones at the event, getting away from it all for a little while can be a nice change of pace. I usually walk down to the Embarcadero area and spend a few minutes looking at the bridges, boats and water.

Above all, have fun - don’t get so hung up on trying to do everything that you don’t enjoy the experience.

Don’t Miss Sessions

  • Mine
    I’m on at the Developer Theatre at 3:45 on Wednesday 19th, taking about UI Testing with Selenium and NodeJS.

  • Marc Benioff’s Keynote
    All the big announcements come at this keynote - there are plenty of overflow rooms if you can’t get there in person, and it will be streamed live.

  • Developer Keynote
    If you are a developer you’ll definitely want to see this one. Arrive early for decent seats.

  • Trailhead Keynote
    Always a riot and one of the loudest keynotes you’ll come across. Sit down the front to be deafened by excited MVPs! 

 

 

Wednesday, 30 October 2019

Auto-completing a Signature Capture Flow

Auto-completing a Signature Capture Flow

Introduction

One of the requests I’ve received from a couple of people around embedding Signature Capture is around automatically navigating once the signature has been captured. One request from the POV of users forgetting to click the Next/Finish button after saving the signature image, and one from the POV of not letting the user navigate further until they have captured a signature image. Luckily both of these can be handled via the same mechanism.

Taking Over the Flow Footer

Aura components that implement the lightning:availableForFlowScreens interface can manage navigation for the screen element they are embedded in. However, you do have to configure the screen so that the footer is hidden:

Once this is done, you have to control navigation via your aura component or the user will be stuck on that flow screen component for eternity.

Handling the Navigation

A lightning component that implements the lightning:availableForFlowScreens interface automatically receives the v.availableActions attribute, which lists the available navigation actions (prev/next etc). This is a collection of strings documented here. Executing a navigation action is as simple as accessing another implicit attribute, v.navigateFlow, and executing this with the desired action:

let navigate=component.get("v.navigateFlow");
navigate(‘FINISH');

Signature Capture With Navigation

In order to carry out the flow-specific navigation I’ve created a new component (SigCapFlowWithFinish). The component wraps an embedded SignatureCapture component and replicates its attributes so that these can be exposed in the flow builder. It also provides a handler for the SignatureCapturedEvt, fired when the user saves the signature image:

<aura:handler event="BGSIGCAP:SignatureCapturedEvt" action="{!c.handleCaptured}"/>

The handler for this event iterates the available actions and if it finds a next or previous, executes that. (Note that you can’t have both next and previous, so there’s no need to worry about ordering).

let flowAction=null;
let availableActions=component.get('v.availableActions');
for (let idx=0; idx<availableActions.length && null==flowAction; idx++) {
    let availAction=availableActions[idx];
    if ('NEXT'==availAction) {
        flowAction=availAction;
    }
    else if ('FINISH'==availAction) {
        flowAction=availAction
    }
}

if (null!=flowAction) {
    let navigate=component.get("v.navigateFlow");
    navigate(flowAction);
}

So when the user saves the signature, the flow auto-finishes or moves to the next element, and until they save the signature they can’t move forward. Here’s a quick video showing this for a flow launched from a contact record:

 

Share the Code, Share the Love 

You can find the component and it’s associated flow in the Signature Capture Samples repository, and here are direct links to the flow and component.

Related Posts

Saturday, 7 September 2019

Mentz - The Story Continues

Mentz - The Story Continues

Introduction

It’s been over three months since I launched Mentz on an unsuspecting Salesforce ecosystem, and the results have far exceeded my expectations. I’d have been quite happy with a couple of people attempting the challenges that I mentored myself, but it’s fair to say we are well past that. At the time of writing (September 2019) we have 90 mentees, 24 mentors and 45 solutions that have been mentored. The standard of mentoring is incredible - a lot of very smart people are putting a lot of effort into helping others in their development journey.

Release, Review, Repeat

  • Creating and maintaining the tooling around Mentz has been an interesting aspect for me, not least because of how wrong I’ve been about some of it. 
  • My original plan was to have two stages in a solution lifecycle - mentoring and publication. A mentee would iterate on their solution based on mentor feedback and when they were completely happy with it, publish it for the wider mentee community to see and comment on. This was pretty much entirely unsuccessful and just caused confusion about where to post solutions. We now have a single place where solutions are published which anyone can access.
  • Solutions were originally uploaded as chatter files until one of the Mentors asked me to turn on "Allow Inclusion of Code Snippets from UI” - now if the solution fits int the 10k chatter message limit it is uploaded as a snippet, which is a lot easier to respond to (thanks Adam Lasek).
  • The challenges typically involved a class with multiple methods to be built out. While I always created my own (unpublished) reference solutions, as I’d come up with the scenario it didn’t take me very long. When a few solutions stacked up and I mentored them on a weekend, it took me almost all day! So I created a couple of short challenges to see what kind of reception they would get.
  • I used to regularly post into the Mentor group to let everyone know if there were any solutions awaiting a response. This always lead to apologetic replies from the Mentors, which wasn’t what I was after at all - I just wanted to avoid them having to poll the org to see if there was anything they could help with. I replaced this with a lightning web component rendered in in the Mentors group home page that listed any unanswered solutions, which seems to have helped.

There have also been some other changes to try to make things easier/more interesting:

  • A Suggestion Box repository for mentees (or anyone really) to suggest challenges they’d like to take
  • The ability to lock a solution to a single mentor. The idea here is that a mentor claims a solution and is the only one (aside from the original author) that can respond to it. I haven’t turned this on yet as AFAIK we’ve only had one instance where two mentors were working on responses to the same solution at the same time.
  • Mentee and Mentor leaderboards - people seem to like these so I’ve added them to the group home pages. I’m at the top of the Mentor leaderboard, but only because I mop up any solutions that haven’t had a response after a few days. I don’t have to do this, but I do feel a sense of responsibility having enticed mentees to join.
  • The Mentz Salesforce CLI plugin now has a challenges topic that lists the available challenges (optionally including those already completed) and clones the repo for the user:
            $ sfdx mentz:publish --targetusername myOrg@example.com --all

              Select a challenge
              1) COLLECTION SIMPLE 1
              2) CONDITIONAL SIMPLE 1 (Completed)
              0) Quit

              Choose a challenge: 1
              Cloning repository = https://github.com/mentzbb/SimpleCollections1
              ...
             Done

What’s Next

As I wrote in my original Mentz blog post:

If things do take off, I don’t want to handle everything out of a single org myself, as that will limit scale. Instead I'll make the code available as a package so that others can host their own instance of Mentz, in their own org. We'll all use a common set of challenges, but the actual mentoring will be distributed.

With 90 mentees it feels like my first org is getting pretty close to as big as I want it to get, so it’s time to test the waters and see if anyone else wants to host Mentz. Here’s a few things to think about before you jump in:

  • This will be a developer edition setup by me that you then take over - this isn’t (only) because I’m a megalomaniac, but more because I want to go through the setup a few times to get it documented before leaving people to face it on their own. The Mentz code will be deployed as unpackaged code  - it might be packaged up in the future, but at present it won’t add much and just adds more work for me :) It does also allow me to keep an eye on the standard of mentoring, as it’s been stellar so far and I’d like to keep it up there.
  • There’s not very much housekeeping - mostly it’s emailing the Mentee/Mentor requestors and then executing a lightning action to convert the request to a user. I usually do half a dozen or so a week.
  • The challenges are the same for every Mentee, all that changes is the org they post solutions to and who mentors.
  • If you host a Mentz instance, be prepared to act as the Mentor of last resort - this doesn’t happen often, but I think it’s quite important to make sure that posts are getting answered. I usually allow about a week for the mentors to dive in and then start picking things up myself, usually over the weekend.
  • Lots of people will register an interest and never even login - remember that this is Mentz where we do what we want, so this is absolutely fine. Never try to make people do anything, although it’s okay to check from time to time to make sure they aren’t trying and struggling/failing.
  • You need to have some reach to attract Mentees/Mentors - I’d imagine it’s a bit dispiriting to announce this is happening and receive zero interest :)

If this sounds like the sort of thing you’d be interested in, fill in the short form at https://bobbuzz.me.uk/MentzHosting - it will almost certainly take me a week or two to do anything so don’t panic if you don’t hear back quickly.

Related Posts

 

 

Sunday, 1 September 2019

Parallel Apex Unit Tests and Salesforce CLI Plugins

Parallel Apex Unit Tests and Salesforce CLI Plugins

 

Introduction

In the Salesforce Winter 20 release notes was something I’ve been looking forward to for a few years - the ability to turn off parallel Apex unit test execution. By default parallel unit test are enabled (somewhat confusingly, by the fact that the Disable Parallel Apex Testing option is not checked):

 

and this typically results in a number of failures in my automated test runner package, as the tests can’t get exclusive access to write to the account others object tables.

This setting is one of the last items that I have to manually turn on when creating a scratch org, and that has been annoying me for a while. I could automate it via Selenium, but that feels like overkill, so I was very pleased to see the new Apex Settings metadata type. Among other features, this has the enableDisableParallelApexTesting field which allows me to check or uncheck the Disable Parallel Apex Testing checkbox, albeit via a metadata API deployment. The release notes also mentioned the to-be-deprecated OrgPreferenceSettings metadata object, and it turns out that this also has a mechanism for turning off parallel testing via the DisableParallelApexTesting setting, so there was no need for me to wait until Winter 20 as long as I could switch between the two mechanisms based on the API version the org is at.

Salesforce CLI Plugin

Regular readers of this blog will know that I’m a huge fan of the Salesforce Cli - I use it all the time and when I’m looking to do anything around developer tooling I always try to create a CLI plugin to host it. This was no different, although it presented a couple of challenges that I hadn’t taken on before:

  • Determining the API version that the org is at. If this is 46 or less (not sure how it would be possible to be on an earlier version of the API than Summer 19, but if Salesforce ever allow it I wanted to be covered) I need to deploy an OrgPreferences metadata type, 47 or higher I need to deploy an ApexSettings.
  • Metadata deployment from inside a plugin. 

After I scaffolded a new bbsfdx plugin and copied the commands/hello/org.ts example command to bb/test/parallel.ts, I set about solving them.

Determine the API Version

Whenever I’m doing anything new with a CLI plugin, my first port of call is the reference documentation for the Salesforce DX Core Library, and I wasn’t disappointed. I can find the API version for the org via the Connection.retrieveMaxAPIVersion function. Getting a Connection object is simple in a CLI plugin - just specify the requiresUsername property as true and a connection comes up with the rations via the org property supplied by the plugin. Getting the target API version is as simple as:

const conn = this.org.getConnection();
const apiVersion = await conn.retrieveMaxApiVersion();

So far so good.

Metadata Deployment

The simplest way to do this is to execute an existing Salesforce CLI deployment command, either force:source:deploy or force:mdapi:deploy, but I’m not a fan of this approach. Spawning a process to execute a Salesforce CLI command from within a CLI plugin seems clunky and inelegant, and it binds me to a command that I don’t control and which may be retired. I should be able replicate anything the standard commands do as I have access to the same underlying libraries.

This time the core library docs weren’t much help - there was a metadata property on the Connection object, but it didn’t have any detail, so time to look elsewhere. The Core library is built on Shinichi Tomita’s JSforce library, so I headed over to the docs for that. The API reference for the Metadata class was exactly what I was looking for, specifically the deploy method.

To deploy metadata, I need a zip file containing a manifest (package.xml) and the metadata files themselves in the directory structure mandated by the metadata API. In order to achieve this, I create a temporary directory and write the appropriate information depending on the API version (stored as a float value in the fltVersion variable):

let packageFile=join(targetDir, 'package.xml');
let packageContents='<?xml version="1.0" encoding="UTF-8"?>\n' + 
    '<Package xmlns="http://soap.sforce.com/2006/04/metadata">\n' +
    ' <types>\n' + 
    '   <name>Settings</name>\n';

let fltVersion=parseFloat(apiVersion);

if (fltVersion>46) {
  packageContents+='    <members>Apex</members>\n' + 
                   '  </types>\n' +
                   '  <version>47.0</version>\n' + 
                   '</Package>';
  let apex=join(settingsDir, 'Apex.settings');
  writeFileSync(apex, 
        '<?xml version="1.0" encoding="UTF-8"?>\n' + 
        '<ApexSettings xmlns="http://soap.sforce.com/2006/04/metadata">\n' +
        '  <enableDisableParallelApexTesting>' + attr + '</enableDisableParallelApexTesting>\n' +
        '</ApexSettings>\n'
          );
}
else {
  packageContents+='    <members>OrgPreference</members>\n' + 
                   '  </types>\n' +
                   '  <version>46.0</version>\n' + 
                   '</Package>';
  let orgPref=join(settingsDir, 'OrgPreference.settings');
  writeFileSync(orgPref, 
    '<?xml version="1.0" encoding="UTF-8"?>\n' + 
    '<OrgPreferenceSettings xmlns="http://soap.sforce.com/2006/04/metadata">\n' + 
    '  <preferences>\n' + 
    '     <settingName>DisableParallelApexTesting</settingName>\n' + 
    '     <settingValue>' + attr + '</settingValue>\n' + 
    '  </preferences>\n' + 
    '</OrgPreferenceSettings>\n'
  );
}

writeFileSync(packageFile, packageContents);

Now I have my directory structure, I need to zip it. Searching on npm for zip packages returns a lot of results, so how to choose? I bounced around sites like stack exchange to see what others were using and eventually settled on compressing for a couple of reasons. First, it supports other compression files than zip, and I might need that flexibility in the future, and second it has a really simple API and is already promisified. After installing it into my plugin node modules and importing it into the parallel.ts file, generating a zip file is a couple of lines:

const zipFile=join(tmpDir, 'pkg.zip');
await compressing.zip.compressDir(targetDir, zipFile);

Getting there. Back to the docs for the metadata deploy function, it wants a zip stream rather than a filename, so I create one of those and add the code to deploy the metadata;

let zipStream=createReadStream(zipFile);
let result=await conn.metadata.deploy(zipStream, {});

The deploy function returns information about the deployment job, so I then enter a loop to poll for the status until it is finished:

let done=false;

let deployResult:DeployResult;
while (!done) {
  deployResult=await conn.metadata.checkDeployStatus(result.id);
  done=deployResult.done;
  if (!done) {
    this.ux.log(deployResult.status + messages.getMessage('sleeping'));
    await new Promise(sleep => setTimeout(sleep, 5000));
  }
}

and there it is - a plugin to enable or disable parallel Apex unit testing in under a couple of hundred lines of Typescript (and obviously a ton of existing node modules that allow me to stand on the shoulders of giants). 

Where’s the Code?

The full code for the plugin can be found in the Github repository at : https://github.com/keirbowden/bbsfdx 

The plugin itself is published on npm at : https://www.npmjs.com/package/bbsfdx - it has been tested on MacOS.

To install the plugin into your version of sfdx, execute:

sfdx plugins:install bbsfdx

Related Posts

Monday, 26 August 2019

Salesforce Unlocked Packages and Supplementary Code

Salesforce Unlocked Packages and Supplementary Code

Introduction

Unlocked packages have been Generally Available since the Winter 19 release, so almost a year at the time of writing (August 2019). Unlike 1.0 packaging, source of truth is the version controlled source, rather than the contents of a packaging org. In fact the only time you need anything other than a scratch org is if you are namespacing your package, in which case you’ll have to create a developer edition to annex the namespace. In packaging 2.0 the contents are determined by the code on the filesystem where you run the packaging commands and the Salesforce DX project configuration file (sfdx-project.json).

I’m not going to go into the detail of creating and publishing packages - the Trailhead module does a pretty good job of introducing the concepts, and if you want a deep dive then check out Fabien Taillon’s blog post about how Texei moved to unlocked packages (they were called Developer Controlled Packages back then, but the principles apply even if the commands have changed slightly.

SalesforceDX Project File

To generate an unlocked package, you need to define where the code lives in your SalesforceDX project file. Here’s the key part of the file for an example package:

"packageDirectories": [
        {
            "path": "force-app",
            "default": true,
            "package": "bbexample",
            "versionName": "Version 1.0",
            "versionNumber": "1.0.0.NEXT"
        }
    ]

When I run the Salesforce CLI commands to generate a package version, everything under the

force-app

directory will be added to the package and uploaded. And this to my mind is a key difference between 1.0 and 2.0 packaging. In 1.0 I create the package from the packaging org and I get to choose which components get added. In 2.0 not so much - everything that I have under the packaging directory goes in.

Supplementary Code

When I create a package there is typically a bunch of supplementary code that helps the development and test process, but I don’t want going into the package. If the package contains Lightning Web Components for example, I might have a flexipage with a  number of instances of the component showing various aspects of the functionality, allowing me to see at a glance that everything is working correctly. If the package works against generic sobjects, I might have some sample sobjects to execute tests against that I don’t want ending up in the subscriber org. I want these items available in the scratch o rgs where I’m developiing the package, but I don’t want to release them. In 1.0 I could just avoid adding them to the package, but in 2.0 I need a way to separate them from the packaged code. I could keep it in a separate repo, but that adds complexity to the development setup, and it’s always best to keep things as simple as possible.

Multiple Package Directories

The SalesforceDX project file can define multiple package directories for a single project. When you execute force:source:push, the contents of all of these directories are pushed to the scratch org, so i can store my supplementary code in another subdirectory and have it included in the development process. 

"packageDirectories": [
        {
            "path": "force-app",
            "default": true,
            "package": "bbexample",
            "versionName": "Version 1.0",
            "versionNumber": "1.0.0.NEXT"
        },
        {
            "path": “supplementary",
            "default": false
        }
    ]

When I push to my scratch org, the contents of both the force-app and supplementary subdirectories will be deployed, but when I generate a package version, only the directory with the package attribute gets included. All of my supplementary code stays out of the package and by extension the subscriber org.

There is one wrinkle to this - if I make some changes in the scratch org, when I execute force:source:pull to retrieve them, they will go into the package directory with the default attribute set to true - in this case force-app. If I don’t want these items in the package, I have to manually move them over to the supplementary subdirectory. Not a huge amount of effort but easy to forget.

Bonus - Installations via the CLI

If you haven’t used packaging in conjunction with the CLI yet, there’s one killer feature. Anyone who has worked with packaging 1.0 knows the fun and games around uploading a package, receiving the email that it has been published, then trying and failing to install it as it turns out not to be available after all. There’s a perfectly sound explanation for this - the emails means it was successfully uploaded to wherever packages live, but after that it has to be propagated around Salesforce infrastructure world, and until it’s made it to the instance you are trying to install in, you’ll get failures.

When you install via the CLI force:package:install subcommand, you can specify two wait times. The --wait switch that, like many other commands, specifies how long to wait for the command (installation) to complete, and the —publishwait that specifies how long to wait for the package to become available to the subscriber org. I typically specify big numbers for both of these switches and then fire and forget the installation. It’s a far less stressful user experience!

Related Posts

 

Saturday, 3 August 2019

Mentz - Where We Do What We Want!

Mentz - Where We Do What We Want!

Introduction

In the TV adaptation of David Peace’sRed Riding" quartert of books, there’s a great scene where a group of corrupt West Yorkshire policemen are toasting their success in moving towards controlled vice : "Off the streets and out the shop windows, under our wing and in our pocket”. The final line of the toast is “To the North - where we do what we want!”.

An unusual introduction to a tech blog post, I’m sure you’ll agree, but there is a link, however tennis. Doing what we want is the ethos behind the Mentz community initiative (and I’ve always loved that scene).

Moar Developers

One reason I started Mentz is that we need more developers in the Salesforce ecosystem, and by developers I mean people that can write code to solve complex business problems, understanding the fine details of the Apex language and the pros and cons to specific approaches. There are plenty of initiatives around to get started learning Apex, but not too many where a more experienced developer casts an eye over your code and gives you feedback, about things both good and bad, and pointing you to resources where you can learn more.

Tapping into Top Talent

Another reason I started Mentz is that I felt we were missing out on accessing a lot of top talent by insisting on commitments to a set number of weeks, or a fixed set of hours (duration and/or fixed start/end times). People at the very top of our profession often can’t make those type of commitments - they don’t have that many hours to spare, they can’t commit to specific hours every week, or they are subject to the whims of a project or customer which means that plans have to change at the last minute. These people are often running companies so they can’t step away when the pressure is on. The upshot of all this is that they may not be participating as much as they want to, because they don’t want to let others down. This is a crying shame, as these are often extremely experienced and talented developer who have a lot to share.

Fear of Commitment

When you join Mentz, as a mentor or mentee, you commit to absolutely nothing. Nada. Zip. When you register an interest, you’ll receive a chatter login and if you never sign in, that’s fine. Mentors don’t get assigned mentees, and vice versa. Mentors and mentees aren’t assigned to each other, it’s much more relaxed than that. If a mentee feels like taking a challenge, they do so and upload their solution to the challenge chatter group. If a mentor feels like looking at someone’s work and giving feedback, they pick any unanswered solution (there’s a lightning web component that shows these, because we don’t want mentors wasting their valuable time scouring chatter groups). There are no programs, timelines, schedules, courses. Just people who take challenges and people who review the solutions, doing what they want when they want to. I’m not the boss of you, and nor is anyone else.

Dipping a Toe

If you’ve been put off signing up for Mentz because you think you might disappoint your assigned mentor or mentees, don’t be. If you want to try the challenges without signing up you can, just take a look at the Github organisation - you won’t be able to submit your solution until you sign up for Mentz, but it will give you an idea of what is involved. If you want to sign up for Mentz, head over to the home page and follow the appropriate signup link. It might take a while for your request to be approved, as I also do what I want with regard to Mentz, but it will happen eventually.

And Finally

Please join me in raising a glass to Mentz - where we do what we want!

 

Related Posts