npm_update_all_global() { npm list -g --json | jq ".dependencies | keys[]" -r | while read line; do npm -g update "$line" done } brew_update_all() { brew update && brew upgrade } heroku_update_all() { heroku update } sfdx_update_all() { sfdx update } lekkim_update_all_cli() { npm_update_all_global brew_update_all heroku_update_all sfdx_update_all }
Tag: bash
jq and multi-field output to CSV
jq is some of the most underrated tools out there I think. It’s a command line JSON parser that makes it super easy to work with JSON on the command line and in turn makes developing small SalesforceDX tools a breeze. Today I needed to generate a CSV file of all fields from different objects for the integration team that doesn’t have access to Salesforce. Doing the describe is easy using the Salesforce REST API but when using jq different are usually on different lines like below (-r is a nifty switch for getting raw, unquoted strings).
$ sfdx force:schema:sobject:describe -s Account -u myorg --json | jq -r ".result.fields[] | .label, .name" Age Age__pc PO Box PO_Box__pc Postal Code Before City Postal_Code_Before_City__pc Street No Before Street Street_No_Before_Street__pc Archived State Archived_State__pc
The output is almost what I wanted but really wanted not to have to edit the file manually to build the output. Some quick googling and it appears that jq supports both CSV and tabular output from arrays. So fixing the issue was as simple as follows:
$ sfdx force:schema:sobject:describe -s Account -u myorg --json | jq -r ".result.fields[] | [.label, .name] | @csv" "Age","Age__pc" "PO Box","PO_Box__pc" "Postal Code Before City","Postal_Code_Before_City__pc" "Street No Before Street","Street_No_Before_Street__pc" "Archived State","Archived_State__pc"
This is so cool… Love it!!
Bash one-liner for Apex test coverage percentage using SalesforceDX
Update 3 May 2018: There are issues with the percentages reported by SalesforceDX plus it doesn’t report coverage on classes with 0% coverage which will shrew the results. The approach outlined above can be used as an indication but cannot as of today be used as a measure for code coverage when it comes to production deployments. As an example I’ve had the above snippet report a coverage of 88% where as a production deploy reported 63% coverage. We – Salesforce – are aware of the issue and are working to resolve it. Stay tuned!
Note to self – quick note on how to run all tests in a connected org (as identified by the -u argument) and use jq and awk to grab the overall test coverage percentage.
$ sfdx force:apex:test:run -u mheisterberg@example.com.appdev -c -w 2 -r json | jq -r ".result.coverage.coverage[].coveredPercent" | awk '{s+=$1;c++} END {print s/c}' > 88.1108
YMMV!
Salesforce username/password OAuth flow against a sandbox
We had issues today because our OAuth password flow wouldn’t work against one of our sandboxes although the code worked against production. Instead we got this error:
{"error":"invalid_grant","error_description":"authentication failure"}
After Googling and finding this thread it turned out that when using the username/password flow against a sandbox you have to either relax IP restrictions for login or authenticate against test.salesforce.com instead of login.salesforce.com (which of course makes sense).
Below is curl commands for using the username/password flow against a sandbox:
$ curl -d "grant_type=password &client_id=3MVG9X0_oZyBSzHrnzENlR...JSDz0_MiwxyieREuBhtgZJrF7Lzx8542TFpU_ &client_secret=6235860963257688256 &username=mikkel.heisterberg%40example.com.sandboxname &password=Passw0rd.SecurityToken" https://test.salesforce.com/services/oauth2/token {"access_token":"00D6E000000Cpmu!AQ0AQIj4...cGCRqmNnYc6dmgLT09VNoIFXJtHvsPGLqrBs0VlK", "instance_url":"https://someaddress.my.salesforce.com", "id":"https://test.salesforce.com/id/00D6E000000CpmuUAC/0056E000000OCcCQAW", "token_type":"Bearer","issued_at":"1485850119972","signature":"malODIaSULh1siHzdw...pHKjBpWoQcm66UQ="} $curl -H 'Authorization: Bearer 00D6E000000Cpmu!AQ0AQIj4...cGCRqmNnYc6dmgLT09VNoIFXJtHvsPGLqrBs0VlK' https://test.salesforce.com/id/00D6E000000CpmuUAC/0056E000000OCcCQAW {"id":"https://test.salesforce.com/id/00D6E000000CpmuUAC/0056E000000OCcCQAW", "asserted_user":true,"user_id":"0056E000000OCcCQAW","organization_id":"00D6E000000CpmuUAC", "username":"mikkel.heisterberg@example.com.sandboxname","nick_name":"mheis", "display_name":"Mikkel Heisterberg","email":"mheisterberg@foo.com","email_verified":true,"first_name":"Mikkel", "last_name":"Heisterberg","timezone":"Europe/Paris","photos":{"picture":"https://someaddress.content.force.com/profilephoto/005/F", "thumbnail":"https://someaddress.content.force.com/profilephoto/005/T"},"addr_street":null,"addr_city":null,"addr_state":null, "addr_country":null,"addr_zip":null,"mobile_phone":"+45 12345678","mobile_phone_verified":true,"status":{"created_date":null, "body":null},"urls":{"enterprise":"https://someaddress.my.salesforce.com/services/Soap/c/{version}/00D6E000000Cpmu", "metadata":"https://someaddress.my.salesforce.com/services/Soap/m/{version}/00D6E000000Cpmu", "partner":"https://someaddress.my.salesforce.com/services/Soap/u/{version}/00D6E000000Cpmu", "rest":"https://someaddress.my.salesforce.com/services/data/v{version}/", "sobjects":"https://someaddress.my.salesforce.com/services/data/v{version}/sobjects/", "search":"https://someaddress.my.salesforce.com/services/data/v{version}/search/", "query":"https://someaddress.my.salesforce.com/services/data/v{version}/query/", "recent":"https://someaddress.my.salesforce.com/services/data/v{version}/recent/", "profile":"https://someaddress.my.salesforce.com/0056E000000OCcCQAW", "feeds":"https://someaddress.my.salesforce.com/services/data/v{version}/chatter/feeds", "groups":"https://someaddress.my.salesforce.com/services/data/v{version}/chatter/groups", "users":"https://someaddress.my.salesforce.com/services/data/v{version}/chatter/users", "feed_items":"https://someaddress.my.salesforce.com/services/data/v{version}/chatter/feed-items", "feed_elements":"https://someaddress.my.salesforce.com/services/data/v{version}/chatter/feed-elements", "custom_domain":"https://someaddress.my.salesforce.com"},"active":true,"user_type":"STANDARD","language":"en_US","locale":"en_US", "utcOffset":3600000,"last_modified_date":"2017-01-26T13:49:33.000+0000","is_app_installed":true}
Quick CLI aliases for Salesforce instance info
Today when at a customer we were trying to figure out the release versions the various sandboxes and development environments and compare this info with the version of the production instance. Of course all this is available from status.salesforce.com but clicking around got a bit tedious so I whipped up a quick set of bash aliases for the Terminal. Since the output is also available as JSON it was pretty easy. First I grabbed the jq JSON parser for bash and then I wrote 4 aliases for bash:
- sf_instance – returns the instance ID (e.g. “na44”) from the hostname. This is useful if you are using a custom domain to access the instance e.g. lekkim-trailhead-dev-ed.my.salesforce.com
- sf_release – shows only the release the instance is on e.g. “Spring ’17 Patch 5.5”
- sf_status – shows release version, whether active or not and type of instance (production, sandbox etc)
- sf_status_raw – returns the raw JSON
Below are examples of usage and the ailases are at github.com/lekkimworld/sf_aliases.
$ sf_help Salesforce CLI actions: ----------------------- - sf_status Shows status for instance ID Syntax : sf_status Example: sf_status na44 - sf_release Shows release version of supplied instance ID Syntax: sf_release Example: sf_release na44 - sf_instance Get instance ID from hostname Syntax: sf_instance Example: sf_instance org62.lightning.force.com - sf_status_raw Shows raw JSON Syntax: sf_status_raw ------------------------ $ sf_instance na44.salesforce.com na44 $ sf_instance lekkim-trailhead-dev-ed.my.salesforce.com eu11 $ sf_release eu11 Winter '17 Patch 18.10 $ sf_release na44 Spring '17 Patch 5.5 $ sf_status eu11 Release Version : Winter '17 Patch 18.10 Active : true Status : OK Environment : production $ sf_status cs85 Release Version : Spring '17 Patch 6 Active : true Status : OK Environment : sandbox