Using Git with Salesforce and distributed teams

by Patrick Connelly posted on July 21, 2014

Introduction

I’ve been asked several times (and have presented a couple of times) on how our team handles doing Continuous Integration (CI) and Continuous Deployment (CD) with a distributed team. However one of the points that I’m always asked is specifically how we use Software Configuration Management (SCM) with this processes. For this blog post I’m going to do a deep dive into our process.

NOTE: This process is by no means the end-all-be-all process for everyone. This is just what we have come up with (generalized for public consumption) over several years of developing on the Force.com platform


The best feature in Salesforce Summer '14

by Patrick Connelly posted on June 26, 2014

Ok, so it might not be the BEST feature ever, but it is a feature after my own heart.

If you’ve ever done anything with pricebooks, you know how painful they can be. And worst of all how you cannot create a Pricebook2 entry in a test. This means that you have to pull a Pricebook2 from your orgs data which means you have to enable the dreaded SeeAllData.

If you’ve ever seen my talk on testing, you’ll know how much I hate SeeAllData. Not only does it make your tests run slower it means that you’re not having a true test. Well, this is no more in Summer ‘14. With this release you should be able to create Pricebook2 entries without SeeAllData to your hearts content!

You can create price book entries for standard and custom price books in Apex tests.

Previously, you couldn’t create price book entries in an Apex test by default unless the test had access to organization data via the @isTest(SeeAllData=true) annotation. With this new support, you can isolate your price book test data from your organization data. Note that custom price books can be created but standard price books cannot.

Support for test price book entries is added for all tests, including tests that use the default data isolation mode (tests that can’t access organization data). With this support, you can do the following.

  • Query for the ID of the standard price book in your organization with the Test.getStandardPricebookId() method.
  • Create test price book entries with standard prices by using the standard price book ID that’s returned by Test.getStandardPricebookId().
  • Create test custom price books, which enables you to add price book entries with custom prices

If you want to read more about this and see examples, see page 257 of the summer release notes


Snapshotting objects in Salesforce with apex

by Patrick Connelly posted on May 20, 2014

A common issue that we have is a need to see information about Cases when it is created. We do this to do some analysis about how a case changes (primarily to verify how good our automated tools are working). To achieve this, we made a generic snapshot object that will store a JSON version of our data. We chose JSON for it’s portability and it’s ability to dump into other systems.

The Object

To start out we’ll need a place to put this data, so we created the object with the following fields. Download object.

  • JSON_Data_{0-9}__cRequired – These are several LongTextAreas that stores json data
  • Object_Name__cRequired – This is the name of the object that was snapshotted
  • NameRequired – An auto number, just used for identification
  • Case__cOptional – This is used for our case specific snapshot to link a snapshot back to a specific case

Board games that all board gamers should own

by Patrick Connelly posted on February 17, 2014

Now I know this is a bit out of the realm of “normalcy” for my blog, but I think there is a bit of overlap in people who would read this, and people who play board games. This list was generated by several suggestions of board gaming friends of mine. It is by no way 100% accurate or complete. We tried to get at least two suggestions per category and from there you can decide.

If we missed your favorite board game, or you think one doesn’t belong, let me know in the comments. And, feel free to buy me any board game I missed to convince me about it.


Sending pushover messages via Electric Imp

by Patrick Connelly posted on January 02, 2014

I recently got an Electric Imp april board and developer card. I’m really digging it and am planning on making a monitoring solution for my garage (including doors and freezer temps). In addition to reporting the data back via the agent, I wanted to add the ability to send a pushover notification to my phone on events. I wrote a quick method to do this, and thought it might be useful to others.

//Agent code
function send_pushover(title, message, priority) {
    local url = "https://api.pushover.net/1/messages.json";
    local token = "XXX_TOKENGOESHERE_XXX";
    local user = "XXX_USERGOESHERE_XXX";
    local headers = { "Content-Type": "application/x-www-form-urlencoded" };
    local data = {
        "token": token,
        "user": user,
        "message": message
        "title": title,
        "priority": priority
    }
    local body = http.urlencode(data);
    local response = http.post(url, headers, body).sendsync();
}

//To use, send_pushover("My title", "My message", 0)
//For more on priority see https://pushover.net/api#priority

Adding git status to the bash prompt

by Patrick Connelly posted on October 24, 2013

After talking to a friend of mine Jeff Larkin about bash prompts, I decided to modify what he did into a single line bash prompt. By adding the following to your .bashrc you’ll get the branch your on in git (if current directory is tracked), a color denoting the status of the branch as well as the return code of the last command (if non-zero)

#
# Define some colors first: Capitals denote bold
#
red='\e[0;31m'
RED='\e[1;31m'
green='\e[0;32m'
GREEN='\e[1;32m'
yellow='\e[0;33m'
YELLOW='\e[1;33m'
blue='\e[0;34m'
BLUE='\e[1;34m'
magenta='\e[0;35m'
MAGENTA='\e[1;35m'
cyan='\e[0;36m'
CYAN='\e[1;36m'
NC='\e[0m' # No Color

# Taken from http://www.opinionatedprogrammer.com/2011/01/colorful-bash-prompt-reflecting-git-status/
function _git_prompt() {
  local git_status="`git status -unormal 2>&1`"
  if ! [[ "$git_status" =~ Not\ a\ git\ repo ]]; then
    if [[ "$git_status" =~ nothing\ to\ commit ]]; then
      local ansi=$GREEN
    elif [[ "$git_status" =~ nothing\ added\ to\ commit\ but\ untracked\ files\ present ]]; then
      local ansi=$RED
    else
      local ansi=$YELLOW
    fi
    if [[ "$git_status" =~ On\ branch\ ([^[:space:]]+) ]]; then
      branch=${BASH_REMATCH[1]}
      #test "$branch" != master || branch=' '
    else
      # Detached HEAD.  (branch=HEAD is a faster alternative.)
      branch="(`git describe --all --contains --abbrev=4 HEAD 2> /dev/null ||
      echo HEAD`)"
    fi
    echo -n '[\['"$ansi"'\]'"$branch"'\[\e[0m\]]'
  fi
}

function report_status() {
  RET_CODE=$?
  if [[ $RET_CODE -ne 0 ]] ; then
    echo -ne "[\[$RED\]$RET_CODE\[$NC\]]"
  fi
}

export _PS1="\[$NC\][\u@\h \W]"
export PS2="\[$NC\]> "
export PROMPT_COMMAND='_status=$(report_status);export PS1="$(_git_prompt)${_status}${_PS1}\$ ";unset _status;'

Bash Prompt


Even better remote notifications with Irssi

by Patrick Connelly posted on July 26, 2013

Last month I wrote a Irssi plugin that pushed messages from Irssi to Beanstalkd. I was pretty happy with it, but I wanted more. So, I’ve improved it. The new version pushes in a slightly more normalized json payload to one of two beanstalk tubes. The tubes are configured for here and away. Then the python script that consumes them either displays it via a notification pop up, if sent to the here tube, or to pushover if sent to the away tube.

Setup

  1. Install beanstalkd on a system that both the irssi client (producer) and the system the notifications will be displayed on (consumer) can access via the network
  2. Install and configure the beanstalkNotify.pl script in irssi
    1. Set beanstalk_server to the address of your beanstalkd server
    2. Set beanstalk_port to the port of your beanstalkd server
    3. Set beanstalk_here_tube to the tube name you want (optional)
    4. **Set beanstalk_away_tube to the tube name you want (optional)
  3. If you want to use pushover for your away notifications, do the following. Otherwise, set your beanstalk_away_tube in step 2D to the same as your beanstalk_here_tube
    1. Purchase pushover on your preferred platform [android / ios]
    2. Create a new application
    3. Copy your application key and user token from your account settings
  4. Download and configure beanstalk-notify.py on your consumer system
    1. Run the script python beantalk-notify.py start to generate the directories and basic configuration
    2. Modify the ~/.beanstalk-notify/beanstalk-notify.conf
      1. beanstalk
        1. server: The address / hostname to your beanstalk server
        2. port: The port for your beanstalk server
        3. clear_on_start: If the tubes should be emptied on start. This keeps from flooding you with notifications if the daemon has not been run in a while
        4. away_tube: The name of the tube for pushover messages
        5. away_ignore: A comma separated list of server names (from irssi) to ignore when sending notifications
        6. here_tube: The name of the tube for noticiation messages
        7. here_ignore: A comma separated list of server names (from irssi) to ignore when sending notifications
      2. pushover
        1. app_token: Your application token
        2. user_key: Your user key
        3. notification
          1. use_native: Use the python notification library
          2. type: The type to use. Determines icon
        4. daemon
          1. log_level: The message level for the daemon loggin
    3. Run the script with the updated config python beanstalk-notify.py start
    4. Profit!

Deleting all scheduled jobs in Salesforce with CasperJs

by Patrick Connelly posted on July 26, 2013

PREFACE

In a previous post, I talked about how to log in to Salesforce with CasperJs. At the time I did not have a good example of what to do next with it. Well, this week I had a need that is something that other people can relate to.

Casper Script

If you’ve ever done any Scheduled Apex work, then you know that if there are scheduled jobs for the class and you do a deployment, the deployment fails. To help negate this with automated installs I wrote the following CasperJs script does the following:

  1. Logs into Salesforce
  2. Navigates to the scheduled jobs page
  3. Gathers all the delete links for the jobs
  4. Deletes them
/*jslint browser: true, regexp: true */
/*global casper, require, jQuery*/

var BASE_URL, LOGIN_URL, LOGIN_USERNAME, LOGIN_PASSWORD, SCHEDULED_JOBS_URI, casp;

casp = require('casper').create({
     clientScripts: [
          'jquery.min.js'
     ],
     viewportSize: {
          width: 1024,
          height: 768
     },
     verbose: true,
     logLevel: 'info'
});

if (!casp.cli.has('username') && !casp.cli.has('password')) {
     casp.echo('Usage: $ casperjs deleteScheduledJobs.casper.js --username=USERNAME --password=PASSWORD [--prod]').exit(-1);
}

if (casp.cli.has('prod')) {
     LOGIN_URL = 'https://login.salesforce.com/';
} else {
     LOGIN_URL = 'https://test.salesforce.com/';
}

LOGIN_USERNAME = casp.cli.get('username');
LOGIN_PASSWORD = casp.cli.get('password');

SCHEDULED_JOBS_URI = '08e?setupid=ScheduledJobs';

casp.start(LOGIN_URL, function () {
     'use strict';

     this.log('Logging in', 'info');
     this.fill('form', {
          'username': LOGIN_USERNAME,
          'pw': LOGIN_PASSWORD
     }, true);

     this.log('Logged in', 'info');

     BASE_URL = casp.getCurrentUrl().split('.salesforce.com')[0] + '.salesforce.com/';

     casp.thenOpen(BASE_URL + SCHEDULED_JOBS_URI, function () {
          var deleteUrls;

          this.log('Fetching jobs to delete', 'info');

          deleteUrls = this.evaluate(function () {
               var urls, url;
               urls = [];

               jQuery('table.list tr.dataRow').each(function (i, item) {
                    url = {};
                    url.href = jQuery('td.actionColumn a.actionLink', item).attr('href').substring(1);
                    url.name = jQuery('th', item).html();
                    urls.push(url);
               });

               return urls;
          });

          this.each(deleteUrls, function (self, url) {
               this.log('Deleting "' + url.name + '"', 'info');
               self.thenOpen(BASE_URL + url.href, function () {
                    this.log('"' + url.name + '" deleted', 'info');
               }, function () {
                    this.log('Timed out deleting "' + url.name + '"', 'error');
               });
          });
     });
});

casp.run();

Then we can run it like all other CasperJs scripts

casperjs deleteScheduleJobs.casper.js --username=USERNAME --password=PASSWORD [--prod]

Logging into Salesforce with CasperJs

by Patrick Connelly posted on July 09, 2013

Preface

Anyone that has ever had to deal with editing multiple Entitlement Processes in Salesforce will know the pain of having to do this in multiple environments and making sure you don’t fat finger this manual process. In the past when I’ve had to do this, I’ve either sucked it up and did it manually, or did it with Selenium. I wasn’t a big fan of either of these solutions since I’m a command-line kinda guy. That’s when I was told about CasperJs and I think I’m in love.

CasperJs

CasperJs is a framework built on top of PhantomJs, and allows you to write JavaScript to web automation. Like with all Salesforce tasks, logging in is the first thing you need to do. Let’s take a look at the sfdclogin.casper.js.

/*jslint browser: true, regexp: true */
/*global casper, require */

var LOGIN_URL, LOGIN_USERNAME, LOGIN_PASSWORD, casp;

casp = require('casper').create({
     viewportSize: {
          width: 1024,
          height: 768
     },
     verbose: true,
     logLevel: 'warning'
});

if (!casp.cli.has('username') && !casp.cli.has('password')) {
     casp.echo('Usage: $ casperjs sfdclogin.casper.js --username=USERNAME --password=PASSWORD [--prod]').exit(-1);
}

if (casp.cli.has('prod')) {
     LOGIN_URL = 'https://login.salesforce.com/';
} else {
     LOGIN_URL = 'https://test.salesforce.com/';
}

LOGIN_USERNAME = casp.cli.get('username');
LOGIN_PASSWORD = casp.cli.get('password');

casp.start(LOGIN_URL, function () {
     'use strict';

     this.log('Logging in', 'debug');
     this.fill('form', {
          'username': LOGIN_USERNAME,
          'pw': LOGIN_PASSWORD
     }, true);

     this.log('Logged in', 'debug');
});

casp.then(function () {
     'use strict';

     this.echo('We\'re logged in.  Now we can do more stuff like take a screenshot!');

     this.waitForSelector('#userNavLabel', function () {
          this.captureSelector('test.png', 'html');
          this.log('saved screenshot of ' + this.getCurrentUrl() + 'to test.png', 'warning');
     }, function () {
          this.die('Timeout reached');
          this.exit();
     }, 12000);
});

casp.run();

To run this class we simply do

casperjs sfdclogin.casper.js --username=USERNAME --password=PASSWORD [--prod]

This JavaScript is pretty simple, but it gives us a base to build on top of. We first setup our CasperJs instance with our screen size, the verbosity and the log level. Then we get our parameters from the command-line for username and password, this allows us to not store these inside the js file. We then fill in the login form and submit it. Now we’re ready to do some actual work. In the above js file, all we are doing is taking a simple screenshot of the page, but we could do any number of things from the CaseperJs documentation.


Better remote notifications with irssi

by Patrick Connelly posted on June 28, 2013

This has been deprecated in for the new script and configuration.

History

As I wrote about a long time ago I use to use a custom script with irssi to push to mumbles. Well, mumbles has gone defunct and I couldn’t really find a good growl client for linux. Plus with me traveling into the office weekly, it’s just not going to work well since growl is more push notification. So this lead me to research a true messaging system to try it.

Beanstalkd

Beanstalkd is a simple fast workqueue. Using this, we can push messages (in JSON formatting) to the queue and have the client pull from the queue whenever they want. The nice thing about designing it this way is it can be reused to push other desktop notifications from other systems.

You can find the full repo with updated documentation on github

Setup

  • Install beanstalkd on a system that both your irssi client (producer) and the system you want notifications on (consumer)

Producer

  • Install the JSON perl module as well as the Queue::Beanstalk module
  • Download the beanstalkNotify.pl file into your ~/.irssi/scripts/ directory

Consumer

  • Install pyyaml and beanstalkc. These can be installed via python-pip
  • Download the beanstalk-notify.py file onto your consumer system

Configuration

Producer

  • Install the script by running _/script load beanstalkNotify _in irssi
  • Set your server /set beanstalk_server beanstalk.example.com
  • Set your port /set beanstalk_port 12345

Consumer

  • Modify the beanstalk-notify.py file to point to your beanstalkd server
  • Run the consumer script

Future Features

In the future I plan on adding the ability to add an audible notification for these as well. Maybe I’ll add the ability to pick a specific queue instead of the default, but I doubt it.