Simon Fell > Its just code > Salesforce.com

Saturday, April 18, 2009

App Updates

I released an updated SoqlX a couple of weeks back, it has a number of tweaks, exposing relationship names to make building SOQL-R queries easier, and the filtering object/field list I discussed earlier. And I just posted an updated version of SFFS, the file extension information is now bundled into the filenames for those files that don't have it in the name directly (depending on how your documents got into salesforce to start with, they might have the extension separate to the name). Follow me on twitter to find out these things as they happen.

< 11:54 AM PDT # > tags : OSX Salesforce.com [playing "Phoenix" by The Prodigy (from Always Outnumbered, Never Outgunned [Bonus Track])]

Thursday, March 12, 2009

SoqlX Filters

Following on from the filtered object/field list discussion, seems like showing all the fields and highlighting the matching ones seems like the best bet (but filtering the list of objects based on the object name, or the object having a field matching the filter), expect to find this in the next release of SoqlX.

< 10:22 PM PDT # > tags : OSX Salesforce.com [playing "Butterfly (Tilt's Mechanism Mix)" by Tilt (from Resident: Two Years Of Oakenfold At Cream (Disc 1))]

Tuesday, March 10, 2009

SoqlX Filters

I've been thinking about adding a filter widget to the object/field list in SoqlX, right now i have so that the object list is filtered based on whether the object name or any of its fields match the filter criteria, but the actual list of fields for the object remains intact. (as shown on the left). Is that what you would find most useful? should it also filter the field list itself ? something else? (I also thought about leaving it with the full list of fields, but having it highlight the fields that match the filter). Ping me on twitter and let me know what you think.

< 10:24 PM PDT # > tags : OSX Salesforce.com [playing "Song For Life (Steppin' Razor Mix)" by Leftfield (from Renaissance: The Mix Collection [Disc 1])]

Thursday, March 05, 2009

SoqlX tips'n'tricks

Everything you wanted to know about SoqlX but were afraid to ask.

Login


Remember that SoqlX uses the API to connect to Salesforce, and so depending on how your admin configured it, you may need to use your api security token in addition to your password to login (just add the token to your password, so if your password is beerisnice and your security token is abv123, enter beerisniceabv123 as the password). getting a LOGIN_MUST_USE_SECURITY_TOKEN error means you most likely forgot to include your security token (search for security token in the salesforce.com online help for more info)


Let SoqlX store your login credentials on the keychain, you'll be much happier, next time you run soqlX it'll remember your username & password (and if you have multiple salesforce.com logins, tryout trapdoor)

Queries

To execute a SOQL query, type the SOQL query into the box at the top, click Query (or Query All), the results will appear in the table, or for count queries, in the bottom left hand corner.



SOQL-R is supported, so you can do both queries that access foreign key relationships (like accessing account from a contact), and also aggregate child queries (like select accounts, and the contacts for each account). Double click the magnifying glass to expand the 2nd query table at the bottom, showing the child rows for that particular parent row.


If you queried the Id column, then you can edit data inline, just double click the cell and type in your new value, when the cell looses focus, the reecord in salesforce will be updated. You can also check the deleted checkbox and hit the delete button to delete row(s). click the Delete column header to quickly toggle the checkbox on all rows.

Using the Window menu, you can open launch the salesforce.com web application, for the user you're currently logged in as. You can also open the recent queries window, which'll show you the last 10 queries you've executed, double click a query to copy it to the SOQL query textbox. Watch the recent queries list animate the changes as you run new queries!.


Use File -> Save Query Results to save the results of your current query as a CSV file.


Schema
The list of objects & fields appear in the list view on the left hand side, double click an object to automatically create a query to select all the fields. Use the details button at the bottom left to turn on the details window which shows all the properties for the object or field selected in the list.

Hit the schema button at the bottom right to switch to a graphical view of your schema, showing how objects are related.

Use the +/- widget inside each object to control the set of fields that appear (id + name field, all standard fields, all fields). Double click an object (or select from the list view) to make that the primary object of the view.
Right click on any object in the listview and pick 'generate schema report' to generate the schema report for that object, this contains the graphical schema view, along with a table detailing all the fields for the object, you can print or export to PDF the schema report.

Got questions, or suggestions for new features, catch me on twitter.

< 8:37 PM PST # > tags : OSX Salesforce.com [playing "Run Wild" by New Order (from Get Ready)]

Sunday, December 07, 2008

Maildrop 2.0

I just posted the final Maildrop 2.0 build. You can download it from the project download page, or if you're already running Maildrop, just restart it, or pick check for updates to have it autoupdate.

Major new features include support for email attachments, the new buttonbar UI to replace the scripts menu, and the configurable search column for the 'What' search.

Maildrop 2.0 runs on Tiger and Leopard, I have some ideas for the next version, which may mean its Leopard only, why are you still on Tiger anyway?

Thanks to everyone who tried out the beta builds and provided feedback.

< 2:43 PM PST # > tags : OSX Salesforce.com [playing "Lush 3-4 (Warrior Drift Psychick Warriors Ov Gaia)" by Orbital (from Diversions)]

Wednesday, December 03, 2008

Maildrop 2 beta 4

I did further battle with the AppleScript ninja's and won, the new beta version of Maildrop includes support for attachments, for create case, all the attachments on the email are automatically added to the case (there's a pref to turn this off). For add Email, the attachments are listed with size and name, and you can choose which to upload, and which record they should be associated with. Download beta 4 and give it whirl. Feedback welcome.

< 9:04 PM PST # > tags : OSX Salesforce.com [playing "Try All You Want" by Electronic (from Electronic)]

Saturday, November 22, 2008

Maildrop 2.0 beta

I've been working on a 2.0 version of Maildrop, one of the pain points of the current version, especially for users new to Mac is the use of the scripts menu to trigger the maildrop actions. rather than interacting with the Maildrop application directly, so I've been working on an alternative approach. I came up with this, its a small button bar window that will hover above Mail or Entourage.

In order for it to not get in the way, when neither Mail or Entourage are the active app it will go to the back of the window list (like a panel window, except its in a different application). In addition the button bar shows which set mail client is active for maildrop, and will automatically switch between Mail and Entourage based on the active app (for people unluckly enough to have to use both).

There's also some additions to the Add Email dialog, now there's an additional column in the "related to what" results, this by default will show the case subject field for case records, you can edit this mapping, and add a additional field for each object type by right clicking on the search results table and selecting configure search columns

The config sheet for this will appear, for each object type, you can select a field from the drop down list.

There are a number of other fixes and tweaks (closing the add email dialog with the window chrome doesn't leave the app in the modal state anymore), login errors are shown better, etc. One feature I'd like to get in before the final 2.0 release is to support attachments.

Finally, a few tips for Maildrop, these apply to the current 1.5 release as well,

Download the current Maildrop 2.0 beta and give it a whirl.

< 11:47 AM PST # > tags : OSX Salesforce.com [playing "Don't Leave" by Faithless (from Reverence/Irreverence [UK] Disc 1)]

Sunday, November 02, 2008

Dreamforce 08

#Dreamforce08 Is already in full swing, I'll be there most of the time, in the dev lounge, or in some of the API sessions, and of course, I'll be at both the monday night Hackathon and the meet the developers session on wednesday morning. Feel free to stop me and ask your burning API, Apex, OSX, iPhone or other force.com questions, If you can't find me in the lounge, you can track me down on twitter (@superfell). And finally, the great thing about being at Moscone, the BlueBottle cafe is only a couple of blocks away for some of the best coffee on the west coast.

< 7:05 PM PST # > tags : Salesforce.com [playing "Battle Song" by Deltron 3030 (from The Instrumentals)]
Twitter API from ApexCode

I've been trying out the Twitter API from Apex code, turns out to be reasonably straight forward, i was able to put together a little client class that can get your friends timeline, and to post new updates (the rest of the api is basically a matter of typing at this point). Using this means you can post twitter updates with just 2 lines of apex code

TwitterForce t = new TwitterForce('someUsername', 'somePassword');
t.postUpdate('@arrowpointe working on it (this from apexcode)', '985929063');
The last parameter to postUpdate is the statusId of an existing status you're replying to, or null for something that's not a reply, postUpdate returns you a TwitterForce.Status object that contains all the information about the new post. You can also fetch your current timeline, with
TwitterForce t = new TwitterForce('someUsername', 'somePassword');
List<TwiterForce.Status> statuses = t.friendsTimeline();
Using this i was able to throw together a custom visualforce controller that lets me set my twitter login info, then see my timeline, and post update.

Rebuilding the twitter UI in VF is not that interesting, but being able to programatically interact with Twitter from apex code is. Here's the code, it includes the TwitterForce client class, some tests, the demo VF controller, and demo VF page. remember you'll need to add http://twitter.com/ to your org's remote sites for the API calls to work. Enjoy, and see you at #dreamforce. apex Twitter API.zip

< 3:03 PM PST # > tags : Salesforce.com Web Services [playing "Mastermind" by Deltron 3030 (from The Instrumentals)]

Friday, October 31, 2008

Maildrop and Quicksilver videos

Michael Wilde over on the Splunk blog put together a couple of great short videos on getting upto speed with Maildrop and the Quicksilver plugin. Thanks Michael!.

< 10:53 AM PDT # > tags : OSX Salesforce.com [playing "Sultans Of Swing" by Dire Straits (from Sultans Of Swing [Disc 1] [Special Edition])]

Sunday, October 26, 2008

Metadata API and Reports

I've seen a few questions on this recently (this is new for v14.0), so i thought I put together a sample of calling retrieve in the metadata file api to fetch a package of all your reports. This is in VS.NET 2005, but should be easy to translate to other environments. The code does the following steps (this is more complex that other cases because Reports don't support Wildcards)

  1. Calls the partner api login to get a sessionId and the URL to the metadata API for your username.
  2. Creates and configures an instance of the metadata client.
  3. Calls ListMetadata to find out all the ReportFolders.
  4. Using the list of ReportFolders, calls ListMetadata again to find out all the Reports in those ReportFolders.
  5. Uses Retrieve / CheckStatus / CheckRetrieveStatus to fetch a package of the reports, and writes the package out to c:\reports.zip

using System;
using System.Collections.Generic;
using System.Text;

namespace mdRetrieve
{
    class Program
    {
        static void Main(string[] args)
        {
            // regular Enterprise/Partner Login call
	    // I added a WebReference of the partner wsdl as sf, and the Metadata WSDL as md
            sf.SforceService svc = new sf.SforceService();
            sf.LoginResult lr = svc.login(args[0], args[1]);

            // set up a MetdataService client
            md.MetadataService ms = new md.MetadataService();
            ms.SessionHeaderValue = new md.SessionHeader();
            ms.SessionHeaderValue.sessionId = lr.sessionId;
            ms.Url = lr.metadataServerUrl;

            Console.WriteLine("Logged in as {0}", lr.userInfo.userName);
            String [] reportFiles = ListReports(ms);
            RetrieveReports(ms, reportFiles);
        }


        static String [] ListReports(md.MetadataService ms)
        {
            // can't use wildcards with reports, so need to fetch the list
            // of ReportFolders first, then fetch all the reports in
            // each folder.
            md.ListMetadataQuery q = new md.ListMetadataQuery();
            q.type = "ReportFolder";
            md.FileProperties[] fp = ms.listMetadata(new md.ListMetadataQuery[] { q });
            if (fp == null)
            {
                Console.WriteLine("No report folders returned");
                return new String[0];
            }
            List reportFiles = new List();
            q.type = "Report";
            foreach (md.FileProperties p in fp)
            {
                q.folder = p.fullName;
                // listMetadata can take more than one item at a time
                // left as an exercise for the reader to batch up these calls.
                md.FileProperties[] rps = ms.listMetadata(new md.ListMetadataQuery[] { q });
                if (fp == null) continue;
                foreach (md.FileProperties rp in rps)
                {
                    Console.WriteLine("{0}", rp.fileName);
                    reportFiles.Add(rp.fullName);
                }
            }
            return reportFiles.ToArray();
        }

        static void RetrieveReports(md.MetadataService ms, String [] reportFiles) {
            // build up an unpackaged retrieve request for the list of reports.
            md.RetrieveRequest r = new md.RetrieveRequest();
            r.apiVersion = 14.0;
            r.unpackaged = new md.Package();
            md.PackageTypeMembers m = new md.PackageTypeMembers();
            m.name = "Report";
            m.members = reportFiles;
            r.unpackaged.types = new md.PackageTypeMembers[] { m };

            // start the retrieve request
            md.AsyncResult ar = ms.retrieve(r);
            // wait for it to complete, sleeping as necassary.
            while (!ar.done)
            {
                System.Threading.Thread.Sleep(ar.secondsToWait * 1000);
                ar = ms.checkStatus(new String[] { ar.id })[0];
            }

            // did it work ?
            if (ar.state == md.AsyncRequestState.Error)
                Console.WriteLine("{0} {1}", ar.statusCode, ar.message);
            else
            {
                // now actually go get the results 
                md.RetrieveResult rr = ms.checkRetrieveStatus(ar.id);
                if (rr.messages != null)
                    foreach (md.RetrieveMessage rm in rr.messages)
                        Console.WriteLine("{0} : {1}", rm.fileName, rm.problem);

                // write the zipFile out to a disk file.
                using (System.IO.FileStream fs = new System.IO.FileStream("c:\\reports.zip", System.IO.FileMode.Create))
                    fs.Write(rr.zipFile, 0, rr.zipFile.Length);
            }
        }
    }
}
< 4:47 PM PDT # > tags : .NET Salesforce.com

Monday, September 08, 2008

Apex and FKs

Just like i described yesterday for the API, you can do exactly the same foreign key resolution using external Ids in Apex code, e.g.

Case c = new Case(subject='Apex FKs');
Account a = new Account(extId__c='00001');
c.account = a;
insert c;
< 8:41 PM PDT # > tags : Salesforce.com Web Services [playing "Delerium" by Euphoria (from Brazilification Disc 1)]

Sunday, September 07, 2008

FK Resolution with external Ids

One of the tedious things about integrating data across systems is mapping keys, e.g. your account master system is pushing data into Salesforce.com, both systems have their own beliefs on what the primary key for one of those accounts is. One of the things Salesforce.com has supported for a while is the concept of external Ids, these are custom fields in Salesforce that you've said are a primary key in some other system. By setting this up, you can delegate all of the key match drudgery to the Salesforce.com infrastructure. Most people know about the upsert function, which allows the caller to create or update a row based on an external Id value, rather than the Saleforce.com primary key. But wait, order in the next 5 minutes and we'll throw in the ability to resolve foreign keys as well, this much less understood feature allows you while calling create/update/upsert, to use external Ids to resolve foreign keys as well. Keeping with our account master example, you may have a system that needs to create Cases in Salesforce, and it only know's the account Master accounts Id, and not the salesforce account Id. To use this, rather than passing the FK itself, you populate the relationship element, and populate its externalId field. Is harder to explain than to show an example, e.g. this C# code will create a new case, related to an account, where we only know one of the accounts external identifiers.

static void createCase(sf.SforceService s, String accExtId, String caseSubject) {
            sf.Case c = new sf.Case();
            c.Subject = caseSubject;
            sf.Account a = new sf.Account();
            a.extId__c = accExtId;
            c.Account = a;

            sf.SaveResult sr = s.create(new sf.sObject[] { c })[0];
            if (sr.success)
                Console.WriteLine("New case created with Id {0}", sr.id);
            else
                Console.WriteLine("Error creating case : {0} {1}", sr.errors[0].statusCode, sr.errors[0].message);
}

Note that rather than populating the AccountId field on case with the Salesforce.com Account's Id, we populate an Account with its extId__c value instead. Because you can have multiple external ids on a particular object, this nested object is used to tell Salesforce.com which particular external Id field you're using. If you're not a C# fan, then the raw soap request looks like this

	<soap:Body>
		<create xmlns="urn:enterprise.soap.sforce.com">
			<sObjects xmlns:q1="urn:sobject.enterprise.soap.sforce.com" xsi:type="q1:Case">
				<q1:Account>
					<q1:extId__c>00001</q1:extId__c>
				</q1:Account>
				<q1:Subject>test case</q1:Subject>
			</sObjects>
		</create>
	</soap:Body>
< 12:00 PM PDT # > tags : Salesforce.com Web Services [playing "Nothing Owed" by Bonobo (from Dial 'M' For Monkey)]

Sunday, July 13, 2008

Updates

I released a new version of SoqlX a few days ago, this updates it to the v13.0 API, and adds the ability to save the query results as a CSV file. And i just posted a new release of PocketHTTP, this adds a change to not send the full URL when proxying over SSL. PocketSOAP and PocketXML-RPC both now are a few revs behind pocketHTTP. I'll be updating the installers for those 2 to bring them up to date sometime in the next week.

< 9:37 PM PDT # > tags : OSX PocketHTTP Salesforce.com [playing "Searchers" by Amon Tobin (from Out From Out Where)]

Thursday, July 10, 2008

Salesforce Mobile for iPhone

Hits the streets today, even making the front page of AppStore. Finally, time for some sleep ;)

< 9:24 AM PDT # > tags : OSX Salesforce.com [playing "Hot Korean Moms" by Amon Tobin/P-Love (from Collaborations & Remixes)]