{"_id":"@bridgera-iot/salesforce","_rev":"1-f10c34bcd8f10c4442edafeb7699fd04","name":"@bridgera-iot/salesforce","dist-tags":{"latest":"1.0.0"},"versions":{"1.0.0":{"name":"@bridgera-iot/salesforce","version":"1.0.0","author":{"name":"Bikram Mondal"},"license":"ISC","_id":"@bridgera-iot/salesforce@1.0.0","maintainers":[{"name":"biplob.bose","email":"biplob.bose@bridgera.com"},{"name":"sachinatbridgera","email":"sachin.gupta@bridgera.com"},{"name":"bridgeraiot","email":"iot-automation@bridgera.com"},{"name":"avirup_bridgera","email":"avirup.das@bridgera.com"},{"name":"bridgera_manish","email":"manish.kumar@bridgera.com"},{"name":"sudbiswa","email":"sudipta.biswas@bridgera.com"},{"name":"bikram_mondal","email":"bikram.mondal@bridgera.com"}],"dist":{"shasum":"aa1b3a93578af021f2ef1a01a6b66736f8f169ef","tarball":"https://registry.npmjs.org/@bridgera-iot/salesforce/-/salesforce-1.0.0.tgz","fileCount":51,"integrity":"sha512-nAd+BznYXpcATKXTXUA48i/9oG5lJXPve0AB6HqwasWBAwBqsW7M6/4ujkI3FWr3t/cCTwXuTRNvWSW/9zmYNg==","signatures":[{"sig":"MEUCIQDLoUhnv8Lefny1f4nBcpaNvoSb3NToNQdtOJid4PfiHQIgQZTesN0uO/TEOwJwhVU9YZBfDICjowv14UyBZYBi0m0=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":397121},"main":"index.js","scripts":{"test":"echo \"Error: no test specified\" && exit 1"},"_npmUser":{"name":"bikram_mondal","email":"bikram.mondal@bridgera.com"},"_npmVersion":"10.2.4","description":"Salesforce API Connection Library for Node.js Applications","directories":{},"_nodeVersion":"20.11.1","dependencies":{"q":"0.9.x","faye":"0.8.x","async":"0.1.x","xml2js":"0.4.x","request":"2.12.x","underscore":"1.4.x","coffee-script":"1.6.x"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/salesforce_1.0.0_1717515350567_0.5829498581334172","host":"s3://npm-registry-packages"}}},"time":{"created":"2024-06-04T15:35:50.443Z","modified":"2024-09-24T10:11:52.417Z","1.0.0":"2024-06-04T15:35:50.765Z"},"author":{"name":"Bikram Mondal"},"license":"ISC","description":"Salesforce API Connection Library for Node.js Applications","maintainers":[{"email":"biplob.bose@bridgera.com","name":"biplob.bose"},{"email":"iot-automation@bridgera.com","name":"bridgeraiot"},{"email":"avirup.das@bridgera.com","name":"avirup_bridgera"},{"email":"sudipta.biswas@bridgera.com","name":"sudbiswa"},{"email":"bikram.mondal@bridgera.com","name":"bikram_mondal"}],"readme":"# node-salesforce \n\nSalesforce API Connection Library for Node.js Applications\n\n[![Build Status](https://secure.travis-ci.org/stomita/node-salesforce.png?branch=travis-ci)](http://travis-ci.org/stomita/node-salesforce)\n\n## Abstract\n\nNode-salesforce, which is designed to be a wrapper of Salesforce REST API in Node.js, enables Salesforce application development in event-driven style.\nIt capsulates the access to REST API end point in asynchronous JavaScript function call.\nYou can use both OAuth2 authorization scheme and SOAP API login for API authentication.\n\n\n## Install\n\nIf you are using node-salesforce as an API library in your Node.js project :\n\n<pre>\n  $ npm install node-salesforce\n</pre>\n\nIf you want to utilize node-salesforce REPL (interactive API console) in tty:\n\n<pre>\n  $ npm install node-salesforce -g\n</pre>\n\nIf you want to get the latest from GitHub :\n\n<pre>\n  $ git clone git://github.com/stomita/node-salesforce.git \n  $ cd node-salesforce\n  $ npm link\n</pre>\n\n\n## API Usage\n\n### Connection\n\n#### Username and Password Login\n\nWhen you have Salesforce username and password (and maybe security token if required), you can use `Connection#login(username, password)` to establish connection to Salesforce.\n\nBy default, it uses SOAP login API (so no OAuth2 client information is required).\n\n```javascript\nvar sf = require('node-salesforce');\nvar conn = new sf.Connection({\n  // you can change loginUrl to connect to sandbox or prerelease env.\n  // loginUrl : 'https://test.salesforce.com'\n});\nconn.login(username, password, function(err, userInfo) {\n  if (err) { return console.error(err); }\n  // Now you can get the access token and instance URL information.\n  // Save them to establish connection next time.\n  console.log(conn.accessToken);\n  console.log(conn.instanceUrl);\n  // logged in user property\n  console.log(\"User ID: \" + userInfo.id);\n  console.log(\"Org ID: \" + userInfo.organizationId);\n  // ...\n});\n```\n\n#### Username and Password Login (OAuth2 Resource Owner Password Credential)\n\nWhen OAuth2 client information is given, `Connection#login(username, password)` uses OAuth2 Resource Owner Password Credential flow to login to Salesforce.\n\n```javascript\nvar sf = require('node-salesforce');\nvar conn = new sf.Connection({\n  oauth2 : {\n    // you can change loginUrl to connect to sandbox or prerelease env.\n    // loginUrl : 'https://test.salesforce.com',\n    clientId : '<your Salesforce OAuth2 client ID is here>',\n    clientSecret : '<your Salesforce OAuth2 client secret is here>',\n    redirectUri : '<callback URI is here>'\n  }\n});\nconn.login(username, password, function(err, userInfo) {\n  if (err) { return console.error(err); }\n  // Now you can get the access token and instance URL information.\n  // Save them to establish connection next time.\n  console.log(conn.accessToken);\n  console.log(conn.instanceUrl);\n  // logged in user property\n  console.log(\"User ID: \" + userInfo.id);\n  console.log(\"Org ID: \" + userInfo.organizationId);\n  // ...\n});\n```\n\n#### Session ID\n\nIf Salesforce session ID and its server URL information is passed from Salesforce (from 'Custom Link' or something), you can pass it to constructor.\n\n\n```javascript\nvar sf = require('node-salesforce');\nvar conn = new sf.Connection({\n  serverUrl : '<your Salesforce server URL (e.g. https://na1.salesforce.com) is here>',\n  sessionId : '<your Salesforce session ID is here>'\n});\n```\n\n#### Access Token\n\nAfter the login API call or OAuth2 authorization, you can get Salesforce access token and its instance URL. Next time you can use them to establish connection.\n\n```javascript\nvar sf = require('node-salesforce');\nvar conn = new sf.Connection({\n  instanceUrl : '<your Salesforce server URL (e.g. https://na1.salesforce.com) is here>',\n  accessToken : '<your Salesforrce OAuth2 access token is here>'\n});\n```\n\n#### Access Token with Refresh Token\n\nIf refresh token is given in constructor, the connection will automatically refresh access token when it has expired \n\nNOTE: Refresh token is only available for OAuth2 authorization code flow.\n\n```javascript\nvar sf = require('node-salesforce');\nvar conn = new sf.Connection({\n  oauth2 : {\n    clientId : '<your Salesforce OAuth2 client ID is here>',\n    clientSecret : '<your Salesforce OAuth2 client secret is here>',\n    redirectUri : '<your Salesforce OAuth2 redirect URI is here>'\n  },\n  instanceUrl : '<your Salesforce server URL (e.g. https://na1.salesforce.com) is here>',\n  accessToken : '<your Salesforrce OAuth2 access token is here>',\n  refreshToken : '<your Salesforce OAuth2 refresh token is here>'\n});\nconn.on(\"refresh\", function(accessToken, res) {\n  // Refresh event will be fired when renewed access token\n  // to store it in your storage for next request\n});\n```\n\n\n#### Logout\n\n`Connection#logout()` to logout from server and invalidate current session. Currently this method only works for SOAP API session.\n\n```javascript\nvar sf = require('node-salesforce');\nvar conn = new sf.Connection({\n  sessionId : '<session id to logout>',\n  serverUrl : '<your Salesforce Server url to logout is here>'\n});\nconn.logout(function(err) {\n  if (err) { return console.error(err); }\n  // now the session has been expired.\n});\n```\n\n### OAuth2 Authorization\n\n(Following examples are assuming running on express.js framework.)\n\n#### Authorization Request\n\nFirst, you should redirect user to Salesforce page to get authorized. You can get Salesforce authorization page URL by `OAuth2#getAuthorizationUrl(options)`.\n\n```javascript\nvar sf = require('node-salesforce');\n//\n// OAuth2 client information can be shared with multiple connections.\n//\nvar oauth2 = new sf.OAuth2({\n  // you can change loginUrl to connect to sandbox or prerelease env.\n  // loginUrl : 'https://test.salesforce.com',\n  clientId : '<your Salesforce OAuth2 client ID is here>',\n  clientSecret : '<your Salesforce OAuth2 client secret is here>',\n  redirectUri : '<callback URI is here>'\n});\n//\n// Get authz url and redirect to it.\n//\napp.get('/oauth2/auth', function(req, res) {\n  res.redirect(oauth2.getAuthorizationUrl({ scope : 'api id web' }));\n});\n```\n\n\n#### Access Token Request\n\nAfter the acceptance of authorization request, your app is callbacked from Salesforce with authorization code in URL parameter. Pass the code to `Connection#authorize(code)` and get access token.\n\n```javascript\n//\n// Pass received authz code and get access token\n//\napp.get('/oauth2/callback', function(req, res) {\n  var conn = new sf.Connection({ oauth2 : oauth2 });\n  var code = req.param('code');\n  conn.authorize(code, function(err, userInfo) {\n    if (err) { return console.error(err); }\n    // Now you can get the access token, refresh token, and instance URL information.\n    // Save them to establish connection next time.\n    console.log(conn.accessToken);\n    console.log(conn.refreshToken);\n    console.log(conn.instanceUrl);\n    console.log(\"User ID: \" + userInfo.id);\n    console.log(\"Org ID: \" + userInfo.organizationId);\n    // ...\n  });\n});\n```\n\n\n### Query\n\n#### Using SOQL\n\nBy using `Connection#query(soql)`, you can achieve very basic SOQL query to fetch Salesforce records.\n\n```javascript\nvar records = [];\nconn.query(\"SELECT Id, Name FROM Account\", function(err, result) {\n  if (err) { return console.error(err); }\n  console.log(\"total : \" + result.totalSize);\n  console.log(\"fetched : \" + result.records.length);\n});\n```\n\n##### Callback Style\n\nThere are two ways to retrieve the result records.\n\nAs we have seen above, our package provides widely-used callback style API call for query execution. It returns one API call result in its callback.\n\n```javascript\nvar records = [];\nconn.query(\"SELECT Id, Name FROM Account\", function(err, result) {\n  if (err) { return console.error(err); }\n  console.log(\"total : \" + result.totalSize);\n  console.log(\"fetched : \" + result.records.length);\n  console.log(\"done ? : \" + result.done);\n  if (!result.done) {\n    // you can use the locator to fetch next records set.\n    // Connection#queryMore()\n    console.log(\"next records URL : \" + result.nextRecordsUrl);\n  }\n});\n```\n\n##### Event-Driven Style\n\nWhen a query is executed, it emits \"record\" event for each fetched record. By listening the event you can collect fetched records.\n\nIf you want to fetch records exceeding the limit number of returning records per one query, you can use `autoFetch` option in `Query#execute(options)` (or its synonym `Query#exec(options)`, `Query#run(options)`) method. It is recommended to use `maxFetch` option also, if you have no idea how large the query result will become.\n\nWhen query is completed, `end` event will be fired. The `error` event occurs something wrong when doing query.\n\n```javascript\nvar records = [];\nconn.query(\"SELECT Id, Name FROM Account\")\n  .on(\"record\", function(record) {\n    records.push(record);\n  })\n  .on(\"end\", function(query) {\n    console.log(\"total in database : \" + query.totalSize);\n    console.log(\"total fetched : \" + query.totalFetched);\n  })\n  .on(\"error\", function(err) {\n    console.error(err);\n  })\n  .run({ autoFetch : true, maxFetch : 4000 }); // synonym of Query#execute();\n```\n\n#### Using Query Method-Chain\n\n##### Basic Method Chaining\n\nBy using `SObject#find(conditions, fields)`, you can do query in JSON-based condition expression (like MongoDB). By chaining other query construction methods, you can create a query programatically.\n\n```javascript\n//\n// Following query is equivalent to this SOQL\n//\n// \"SELECT Id, Name, CreatedDate FROM Contact\n//  WHERE LastName LIKE 'A%' AND CreatedDate >= YESTERDAY AND Account.Name = 'Sony, Inc.'\n//  ORDER BY CreatedDate DESC, Name ASC\n//  LIMIT 5 OFFSET 10\"\n//\nconn.sobject(\"Contact\")\n  .find(\n    // conditions in JSON object\n    { LastName : { $like : 'A%' },\n      CreatedDate: { $gte : sf.Date.YESTERDAY },\n      'Account.Name' : 'Sony, Inc.' },\n    // fields in JSON object\n    { Id: 1,\n      Name: 1,\n      CreatedDate: 1 }\n  )\n  .sort({ CreatedDate: -1, Name : 1 })\n  .limit(5)\n  .skip(10)\n  .execute(function(err, records) {\n    if (err) { return console.error(err); }\n    console.log(\"fetched : \" + records.length);\n  });\n```\n\nAnother representation of the query above.\n\n```javascript\nconn.sobject(\"Contact\")\n  .find({\n    LastName : { $like : 'A%' },\n    CreatedDate: { $gte : sf.Date.YESTERDAY },\n    'Account.Name' : 'Sony, Inc.'\n  },\n    'Id, Name, CreatedDate' // fields can be string of comma-separated field names\n                            // or array of field names (e.g. [ 'Id', 'Name', 'CreatedDate' ])\n  )\n  .sort('-CreatedDate Name') // if \"-\" is prefixed to field name, considered as descending.\n  .limit(5)\n  .skip(10)\n  .execute(function(err, records) {\n    for (var i=0; i<records.length; i++) {\n      var record = records[i];\n      console.log(\"Name: \" + record.Name);\n      console.log(\"Created Date: \" + record.CreatedDate);\n    }\n  });\n```\n\n##### Wildcard Fields\n\nWhen `fields` argument is omitted in `SObject#find(conditions, fields)` call, it will implicitly describe current SObject fields before the query (lookup cached result first, if available) and then fetch all fields defined in the SObject.\n\nNOTE: In the version less than 0.6, it fetches only `Id` field if `fields` argument is omitted.\n\n```javascript\nconn.sobject(\"Contact\")\n  .find({ CreatedDate: sf.Date.TODAY }) // \"fields\" argument is omitted\n  .execute(function(err, records) {\n    // ...\n  });\n```\n\nThe above query is equivalent to:\n\n```javascript\nconn.sobject(\"Contact\")\n  .find({ CreatedDate: sf.Date.TODAY }, '*') // fields in asterisk, means wildcard.\n  .execute(function(err, records) {\n    // …\n  });\n```\n\n\nQuery can also be represented in more SQL-like verbs - `SObject#select(fields)`, `Query#where(conditions)`, `Query#orderby(sort, dir)`, and `Query#offset(num)`.\n\n```javascript\nconn.sobject(\"Contact\")\n  .select('*, Account.*') // asterisk means all fields in specified level are target.\n  .where(\"CreatedDate = TODAY\") // conditions in raw SOQL where clause.\n  .limit(10)\n  .offset(20) // synonym of \"skip\"\n  .execute(function(err, records) {\n    for (var i=0; i<records.length; i++) {\n      var record = records[i];\n      console.log(\"First Name: \" + record.FirstName);\n      console.log(\"Last Name: \" + record.LastName);\n      // fields in Account relationship are fetched\n      console.log(\"Account Name: \" + record.Account.Name); \n    }\n  });\n```\n\nYou can also include child relationship records into query result by calling `Query#include(childRelName)`. After `Query#include(childRelName)` call, it enters into the context of child query. In child query context, query construction call is applied to the child query. Use `SubQuery#end()` to recover from the child context.\n\n\n```javascript\n//\n// Following query is equivalent to this SOQL\n//\n// \"SELECT Id, FirstName, LastName, ..., \n//         Account.Id, Acount.Name, ...,\n//         (SELECT Id, Subject, … FROM Cases\n//          WHERE Status = 'New' AND OwnerId = :conn.userInfo.id\n//          ORDER BY CreatedDate DESC)\n//  FROM Contact\n//  WHERE CreatedDate = TODAY\n//  LIMIT 10 OFFSET 20\"\n//\nconn.sobject(\"Contact\")\n  .select('*, Account.*')\n  .include(\"Cases\") // include child relationship records in query result. \n     // after include() call, entering into the context of child query.\n     .select(\"*\")\n     .where({\n        Status: 'New',\n        OwnerId : conn.userInfo.id,\n     })\n     .orderby(\"CreatedDate\", \"DESC\")\n     .end() // be sure to call end() to exit child query context\n  .where(\"CreatedDate = TODAY\")\n  .limit(10)\n  .offset(20)\n  .execute(function(err, records) {\n    for (var i=0; i<records.length; i++) {\n      var record = records[i];\n      console.log(\"First Name: \" + record.FirstName);\n      console.log(\"Last Name: \" + record.LastName);\n      // fields in Account relationship are fetched\n      console.log(\"Account Name: \" + record.Account.Name); \n      // \n      if (record.Cases) {\n        console.log(\"Cases total: \" + record.Cases.totalSize);\n        console.log(\"Cases fetched: \" + record.Cases.records.length);\n      }\n    }\n  });\n```\n\n\n\n### CRUD Operation\n\nWe support basic \"CRUD\" operation for records. It also supports multiple record manipulation, but it consumes one API request per record. Be careful for the API quota.\n\n#### Retrieve\n\n`SObject#retrieve(id)` fetches a record or records specified by id(s) in first argument.\n\n```javascript\n//\n//\nconn.sobject(\"Account\").retrieve(\"0017000000hOMChAAO\", function(err, account) {\n  if (err) { return console.error(err); }\n  console.log(\"Name : \" + account.Name);\n  // ...\n});\n//\n//\nconn.sobject(\"Account\").retrieve([\n  \"0017000000hOMChAAO\",\n  \"0017000000iKOZTAA4\"\n], function(err, accounts) {\n  if (err) { return console.error(err); }\n  for (var i=0; i < accounts.length; i++) {\n    console.log(\"Name : \" + accounts[i].Name);\n  }\n  // ...\n});\n```\n\n#### Create \n\n`SObject#create(record)` (or its synonym `SObject#insert(record)`) creates a record or records given in first argument.\n\n```javascript\n//\n//\nconn.sobject(\"Account\").create({ Name : 'My Account #1' }, function(err, ret) {\n  if (err || !ret.success) { return console.error(err, ret); }\n  console.log(\"Created record id : \" + ret.id);\n  // ...\n});\n//\n//\nconn.sobject(\"Account\").create([\n  { Name : 'My Account #1' },\n  { Name : 'My Account #2' }\n],\nfunction(err, rets) {\n  if (err) { return console.error(err); }\n  for (var i=0; i < rets.length; i++) {\n    if (rets[i].success) {\n      console.log(\"Created record id : \" + rets[i].id);\n    }\n  }\n  // ...\n});\n```\n\n#### Update\n\n`SObject#update(record)` updates a record or records given in first argument.\n\n```javascript\n//\n//\nconn.sobject(\"Account\").update({ \n  Id : '0017000000hOMChAAO',\n  Name : 'Updated Account #1'\n}, function(err, ret) {\n  if (err || !ret.success) { return console.error(err, ret); }\n  console.log('Updated Successfully : ' + ret.id);\n  // ...\n});\n//\n//\nconn.sobject(\"Account\").update([\n  { Id : '0017000000hOMChAAO', Name : 'Updated Account #1' },\n  { Id : '0017000000iKOZTAA4', Name : 'Updated Account #2' }\n],\nfunction(err, rets) {\n  if (err) { return console.error(err); }\n  for (var i=0; i < rets.length; i++) {\n    if (rets[i].success) {\n      console.log(\"Updated Successfully : \" + rets[i].id);\n    }\n  }\n});\n```\n\n#### Delete\n\n`SObject#destroy(id)` (or its synonym `SObject#del(id)`, `SObject#delete(id)`) deletes a record or records given in first argument.\n\n```javascript\n//\n//\nconn.sobject(\"Account\").destroy('0017000000hOMChAAO', function(err, ret) {\n  if (err || !ret.success) { return console.error(err, ret); }\n  console.log('Deleted Successfully : ' + ret.id);\n});\n//\n//\nconn.sobject(\"Account\").del([ // synonym of \"destroy\"\n  '0017000000hOMChAAO',\n  '0017000000iKOZTAA4'\n}], \nfunction(err, rets) {\n  if (err) { return console.error(err); }\n  for (var i=0; i < rets.length; i++) {\n    if (rets[i].success) {\n      console.log(\"Deleted Successfully : \" + rets[i].id);\n    }\n  }\n});\n```\n\n\n#### Upsert\n\n`SObject#upsert(record, extIdField)` will upsert a record or records given in first argument. External ID field name must be specified in second argument.\n\n\n```javascript\n//\n//\nconn.sobject(\"UpsertTable__c\").upsert({ \n  Name : 'Record #1',\n  ExtId__c : 'ID-0000001'\n}, 'ExtId__c', function(err, ret) {\n  if (err || !ret.success) { return console.error(err, ret); }\n  console.log('Upserted Successfully');\n  // ...\n});\n//\n//\nconn.sobject(\"UpsertTable__c\").upsert([\n { Name : 'Record #1', ExtId__c : 'ID-0000001' },\n { Name : 'Record #2', ExtId__c : 'ID-0000002' }\n],\n'ExtId__c',\nfunction(err, rets) {\n  if (err) { return console.error(err); }\n  for (var i=0; i < rets.length; i++) {\n    if (rets[i].success) {\n      console.log(\"Upserted Successfully\");\n    }\n  }\n  // ...\n});\n```\n\n\n### Metadata Description\n\nMetadata description API for Salesforce object.\n\n#### Describe SObject\n\nYou can use `SObject#describe()` to fetch SObject metadata,\n\n```javascript\nconn.sobject(\"Account\").describe(function(err, meta) {\n  if (err) { return console.error(err); }\n  console.log('Label : ' + meta.label);\n  console.log('Num of Fields : ' + meta.fields.length);\n  // ...\n});\n```\n\nor can use `Connection#describe(sobjectType)` alternatively.\n\n```javascript\nconn.describe(\"Account\", function(err, meta) {\n  if (err) { return console.error(err); }\n  console.log('Label : ' + meta.label);\n  console.log('Num of Fields : ' + meta.fields.length);\n  // ...\n});\n```\n\n#### Describe Global\n\n`SObject#describeGlobal()` returns all SObject information registered in Salesforce (without detail information like fields, childRelationships).\n\n```javascript\nconn.describeGlobal(function(err, res) {\n  if (err) { return console.error(err); }\n  console.log('Num of SObjects : ' + res.sobjects.length);\n  // ...\n});\n```\n\n#### Cache\n\nEach description API has \"cached\" version with suffix of `$` (coming from similar pronounce \"cash\"), which keeps the API call result for later use.\n\n```javascript\n//\n// First lookup local cache, and then call remote API if cache doesn't exist.\n//\nconn.sobject(\"Account\").describe$(function(err, meta) {\n  if (err) { return console.error(err); }\n  console.log('Label : ' + meta.label);\n  console.log('Num of Fields : ' + meta.fields.length);\n  // ...\n});\n//\n// If you can assume it should have already cached the result,\n// you can use synchronous call to access the result;\n//\nvar meta = conn.sobject(\"Account\").describe$();\nconsole.log('Label : ' + meta.label);\nconsole.log('Num of Fields : ' + meta.fields.length);\n// ...\n```\n\nCache clearance should be done explicitly by developers.\n\n```javascript\n//\n// Delete cache of 'Account' SObject description result\n//\nconn.sobject('Account').describe$.clear();\n//\n// Delete cache of global sobject description\n//\nconn.describeGlobal$.clear();\n//\n// Delete all API caches in connection.\n//\nconn.cache.clear();\n```\n\n\n\n### Streaming API\n\nYou can subscribe topic and receive message from Salesforce Streaming API, by using `Topic#subscribe(topicName, listener)`.\n\nBefore the subscription, you should insert appropriate PushTopic record (in this example, \"InvoiceStatementUpdates\") as written in Streaming API guide.\n\n```javascript\nconn.streaming.topic(\"InvoiceStatementUpdates\").subscribe(function(message) {\n  console.log('Event Type : ' + message.event.type);\n  console.log('Event Created : ' + message.event.createdDate);\n  console.log('Object Id : ' + message.sobject.Id);\n});\n```\n\nNOTE: Before version 0.6, there are `Connection#topic(topicName)` to access streaming topic object, and `Connection#subscribe(topicName, listener)` is used to subscribe altenatively. These methods are now obsolete and use `Streaming#topic(topicName)` and `Streaming#subscribe(topicName, listener)` through `streaming` API object in connection object instead.\n\n\n### Tooling API\n\nYou can use Tooling API to execute anonymous Apex Code, by passing apex code string text to `Tooling#executeAnonymous`.\n\n```javascript\n// execute anonymous Apex Code\nvar apexBody = \"System.debug('Hello, World');\";\nconn.tooling.executeAnonymous(apexBody, function(err, res) {\n  if (err) { return console.error(err); }\n  console.log(res.compiled); // compiled successfully\n  console.log(res.success); // executed successfully\n  // ...\n});\n```\n\n### Analytics API\n\nBy using Analytics API, you can get the output result from a report registered in Salesforce.\n\n`Analytics#reports()` lists recently accessed reports.\n\n```javascript\n// get recent reports\nconn.analytics.reports(function(err, reports) {\n  if (err) { return console.error(err); }\n  for (var i=0; i < reports.length; i++) {\n    console.log(reports[i].id);\n    console.log(reports[i].name);\n  }\n  // ...\n});\n```\n\n`Analytics#report(reportId)` gives a reference to the report object specified in `reportId`.\nBy calling `Report#execute(options)`, the report is exected in Salesforce, and returns executed result synchronously.\nPlease refer to Analytics API document about the format of retruning result.\n\n```javascript\n// get report reference\nvar reportId = '00O10000000pUw2EAE';\nvar report = conn.analytics.report(reportId);\n\n// execute report synchronously\nreport.execute(function(err, result) {\n  if (err) { return console.error(err); }\n  console.log(result.reportMetadata);\n  console.log(result.factMap);\n  console.log(result.factMap[\"T!T\"]);\n  console.log(result.factMap[\"T!T\"].aggregates);\n  // ...\n});\n```\n\nSetting `details` to true in `options`, it returns execution result with detail rows.\n\n```javascript\n// execute report synchronously with details option,\n// to get detail rows in execution result.\nreport.execute({ details: true }, function(err, result) {\n  if (err) { return console.error(err); }\n  console.log(result.reportMetadata);\n  console.log(result.factMap);\n  console.log(result.factMap[\"T!T\"]);\n  console.log(result.factMap[\"T!T\"].aggregates);\n  console.log(result.factMap[\"T!T\"].rows); // <= detail rows in array\n  // ...\n});\n```\n\nYou can override report behavior by putting `metadata` object in `options`.\nFor example, following code shows how to update filtering conditions of a report on demand.\n\n```javascript\n// overriding report metadata\nvar metadata = { \n  reportMetadata : {\n    reportFilters : [{\n      column: 'COMPANY',\n      operator: 'contains',\n      value: ',Inc.'\n    }]\n  }\n};\n// execute report synchronously with overridden filters.\nreport.execute({ metadata : metadata }, function(err, result) {\n  if (err) { return console.error(err); }\n  console.log(result.reportMetadata);\n  console.log(result.reportMetadata.reportFilters.length); // <= 1\n  console.log(result.reportMetadata.reportFilters[0].column); // <= 'COMPANY' \n  console.log(result.reportMetadata.reportFilters[0].operator); // <= 'contains' \n  console.log(result.reportMetadata.reportFilters[0].value); // <= ',Inc.' \n  // ...\n});\n```\n\n`Report#executeAsync(options)` executes the report asynchronously in Salesforce, registering an instance to the report to lookup the executed result in future.\n\n```javascript\nvar instanceId;\n\n// execute report asynchronously\nreport.executeAsync({ details: true }, function(err, instance) {\n  if (err) { return console.error(err); }\n  console.log(instance.id); // <= registered report instance id\n  instanceId = instance.id;\n  // ...\n});\n\n// retrieve asynchronously executed result afterward.\nreport.instance(instanceId).retrieve(function(err, result) {\n  if (err) { return console.error(err); }\n  console.log(result.reportMetadata);\n  console.log(result.factMap);\n  console.log(result.factMap[\"T!T\"]);\n  console.log(result.factMap[\"T!T\"].aggregates);\n  console.log(result.factMap[\"T!T\"].rows);\n  // ...\n});\n```\n\n### Apex REST\n\nIf you have a static Apex class in Salesforce and are exposing it using \"Apex REST\" feature, you can call it by using `Apex#get(path)`, `Apex#post(path, body)`, `Apex#put(path, body)`, `Apex#patch(path, body)`, and `Apex#del(path, body)` (or its synonym `Apex#delete(path, body)`) through `apex` API object in connection object.\n\n```javascript\n// body payload structure is depending to the Apex REST method interface.\nvar body = { title: 'hello', num : 1 };\nconn.apex.post(\"/MyTestApexRest/\", body, function(res) {\n  // the response object structure depends on the definition of apex class\n});\n```\n\n\n### Bulk API\n\nNode-salesforce package also supports Bulk API. It is not only mapping each Bulk API endpoint in low level, but also introducing utility interface in bulk load operations.\n\n\n#### Loading From Records\n\nFirst, assume that you have record set in array object to insert into Salesforce.\n\n```javascript\n//\n// Records to insert in bulk.\n//\nvar accounts = [\n{ Name : 'Account #1', ... }, \n{ Name : 'Account #2', ... }, \n{ Name : 'Account #3', ... }, \n...\n];\n```\n\nYou can use `SObject#create(record)`, but it consumes API quota per record, so not practical for large set of records. We can use bulk API interface to load them.\n\nSimilar to Salesforce Bulk API, first create bulk job by `Bulk#createJob(sobjectType, operation)` through `bulk` API object in connection object.\n\nNext, create a new batch in the job, by calling `Job#createBatch()` through the job object created previously.\n\n```javascript\nvar job = conn.bulk.createJob(\"Account\", \"insert\");\nvar batch = job.createBatch();\n```\n\nThen bulk load the records by calling `Batch#execute(input)` of created batch object, passing the records in `input` argument.\n \nWhen the batch is queued in Salesforce, it is notified by `queue` event, and you can get job ID and batch ID.\n\n```javascript\nbatch.execute(accounts);\nbatch.on(\"queue\", function(batchInfo) { // fired when batch request is queued in server.\n  batchId = batchInfo.id);\n  jobId = batchInfo.jobId);\n  // ...\n});\n```\n\nAfter the batch is queued and job / batch ID is created, wait the batch completion by polling.\n\nWhen the batch process in Salesforce has been completed, it is notified by `response` event with batch result information.\n\n```javascript\nvar job = conn.bulk.job(jobId);\nvar batch = job.batch(batchId);\nbatch.poll(1000 /* interval(ms) */, 20000 /* timeout(ms) */); // start polling\nbatch.on(\"response\", function(rets) { // fired when batch finished and result retrieved\n  if (err) { return console.error(err); }\n  for (var i=0; i < rets.length; i++) {\n    if (rets[i].success) {\n      console.log(\"#\" + (i+1) + \" loaded successfully, id = \" + rets[i].id);\n    } else {\n      console.log(\"#\" + (i+1) + \" error occurred, message = \" + rets[i].errors.join(', '));\n    }\n  }\n  // ...\n});\n```\n\nAlternatively, you can use `Bulk#load(sobjectType, operation, input)` interface to achieve the above process in one method call.\n\n```javascript\nconn.bulk.load(\"Account\", \"insert\", accounts, function(err, rets) {\n  if (err) { return console.error(err); }\n  for (var i=0; i < rets.length; i++) {\n    if (rets[i].success) {\n      console.log(\"#\" + (i+1) + \" loaded successfully, id = \" + rets[i].id);\n    } else {\n      console.log(\"#\" + (i+1) + \" error occurred, message = \" + rets[i].errors.join(', '));\n    }\n  }\n  // ...\n});\n```\n\nFollowing are same calls but in different interfaces :\n\n```javascript\nconn.sobject(\"Account\").insertBulk(accounts, function(err, rets) {\n  // ...\n});\n```\n\n```javascript\nconn.sobject(\"Account\").bulkload(\"insert\").execute(accounts, function(err, rets) {\n  // ...\n});\n```\n\n#### Loading From CSV File\n\nIt also supports bulk loading from CSV file. Just use CSV file input stream as `input` argument in `Bulk#load(sobjectType, operation, input)`, instead of passing records in array.\n\n```javascript\n//\n// Create readable stream for CSV file to upload\n//\nvar csvFileIn = require('fs').createReadStream(\"path/to/Account.csv\");\n//\n// Call Bulk#load(sobjectType, operation, input) - use CSV file stream as \"input\" argument\n//\nconn.bulk.load(\"Account\", \"insert\", csvFileIn, function(err, rets) {\n  if (err) { return console.error(err); }\n  for (var i=0; i < rets.length; i++) {\n    if (rets[i].success) {\n      console.log(\"#\" + (i+1) + \" loaded successfully, id = \" + rets[i].id);\n    } else {\n      console.log(\"#\" + (i+1) + \" error occurred, message = \" + rets[i].errors.join(', '));\n    }\n  }\n  // ...\n});\n```\n\n`Batch#stream()` returns a Node.js standard writable stream which accepts batch input. You can pipe input stream to it afterward.\n\n\n```javascript\nvar batch = conn.bulk.load(\"Account\", \"insert\");\nbatch.on(\"response\", function(rets) { // fired when batch finished and result retrieved\n  if (err) { return console.error(err); }\n  for (var i=0; i < rets.length; i++) {\n    if (rets[i].success) {\n      console.log(\"#\" + (i+1) + \" loaded successfully, id = \" + rets[i].id);\n    } else {\n      console.log(\"#\" + (i+1) + \" error occurred, message = \" + rets[i].errors.join(', '));\n    }\n  }\n);\n//\n// When input stream becomes available, pipe it to batch stream.\n//\ncsvFileIn.pipe(batch.stream());\n```\n  \n\n#### Updating/Deleting Queried Records\n\nIf you want to update / delete records in Salesforce which match specified condition in bulk, now you don't have to write a code which download & upload records information. `Query#update(mapping)` / `Query#destroy()` will directly manipulate records.\n\n\n```javascript\n//\n// DELETE FROM Account WHERE CreatedDate < LAST_YEAR\n//\nconn.sobject('Account')\n    .find({ CreatedDate : sf.Date.TODAY })\n    .destroy(function(err, rets) {\n      // ...\n    });\n```\n\n```javascript\n//\n// UPDATE Opportunity \n// SET CloseDate = '2013-08-31'\n// WHERE Account.Name = 'Salesforce.com'\n//\nconn.sobject('Opportunity')\n    .find({ 'Account.Name' : 'Salesforce.com' })\n    .update({ CloseDate: '2013-08-31' }, function(err, rets) {\n      // ...\n    });\n```\n\nIn `Query#update(mapping)`, you can include simple templating notation in mapping record.\n\n```javascript\n//\n// UPDATE Task \n// SET Description = CONCATENATE(Subject || ' ' || Status)\n// WHERE ActivityDate = TODAY\n//\nconn.sobject('Task')\n    .find({ ActivityDate : sf.Date.TODAY })\n    .update({ Descritpion: '${Subject}  ${Status}' }, function(err, rets) {\n      //\n    });\n```\n\nTo achieve further complex mapping, `Query#update(mapping)` accepts mapping function in `mapping` argument.\n\n```javascript\nconn.sobject('Task')\n    .find({ ActivityDate : sf.Date.TODAY })\n    .update(function(rec) {\n      return {\n        Descritpion: rec.Subject + ' ' + rec.Status\n      }\n    }, function(err, rets) {\n      // ...\n    });\n```\n\nIf you are creating query object from SOQL by using `Connection#query(soql)`, the bulk delete/update operation cannot be achieved because no sobject type information available initially. You can avoid it by passing optional argument `sobjectType` in `Query#destroy(sobjectType)` or `Query#update(mapping, sobjectType)`.\n\n```javascript\nconn.query(\"SELECT Id FROM Account WHERE CreatedDate = TODAY\")\n    .destroy('Account', function(err, rets) {\n      // ...\n    });\n```\n\n```javascript\nconn.query(\"SELECT Id FROM Task WHERE ActivityDate = TODAY\")\n    .update({ Descritpion: '${Subject}  ${Status}' }, 'Task', function(err, rets) {\n      // ...\n    });\n```\n\nNOTE: Be careful when using this feature not to break/lose existing data in Salesforce. Careful testing is recommended before applying the code to your production environment.\n\n\n\n### Record Stream Pipeline\n\nRecord stream is a stream system which regards records in its stream, similar to Node.js's standard readable/writable streams.\n\nQuery object - usually returned by `Connection#query(soql)` / `SObject#find(conditions, fields)` methods - is considered as `InputRecordStream` which emits event `record` when received record from server.\n\nBatch object - usually returned by `Job#createBatch()` / `Bulk#load(sobjectType, operation, input)` / `SObject#bulkload(operation, input)` methods - is considered as `OutputRecordStream` and have `send()` and `end()` method to accept incoming record.\n\nYou can use `InputRecordStream#pipe(outputRecordStream)` to pipe record stream.\n\nRecordStream can be converted to usual Node.js's stream object by calling `RecordStream#stream()` method.\n\nBy default (and only currently) records are serialized to CSV string.\n\n\n#### Piping Query Record Stream to Batch Record Stream\n\nThe idea of record stream pipeline is the base of bulk operation for queried record. For example, the same process of `Query#destroy()` can be expressed as following:\n\n\n```javascript\n//\n// This is much more complex version of Query#destroy().\n//\nvar Account = conn.sobject('Account');\nAccount.find({ CreatedDate: { $lt: sf.Date.LAST_YEAR }})\n       .pipe(Account.deleteBulk())\n       .on('response', function(rets){\n         // ...\n       })\n       .on('error', function(err) {\n         // ...\n       });\n```\n\nAnd `Query#update(mapping)` can be expressed as following:\n\n```javascript\n//\n// This is much more complex version of Query#update().\n//\nvar Opp = conn.sobject('Opportunity');\nOpp.find({ \"Account.Id\" : accId },\n         { Id: 1, Name: 1, \"Account.Name\": 1 })\n   .pipe(sf.RecordStream.map(function(r) {\n     return { Id: r.Id,\n              Name: r.Account.Name + ' - ' + r.Name };\n   }))\n   .pipe(Opp.updateBulk())\n   .on('response', function(rets) {\n     // ...\n   })\n   .on('error', function(err) {\n     // ...\n   });\n```\n\nFollowing is an example using `Query#stream()` (inherited `RecordStream#stream()`) to convert record stream to Node.js stream, in order to export all queried records to CSV file.\n\n```javascript\nvar csvFileOut = require('fs').createWriteStream('path/to/Account.csv');\nconn.query(\"SELECT Id, Name, Type, BillingState, BillingCity, BillingStreet FROM Account\")\n    .stream() // Convert to Node.js's usual readable stream.\n    .pipe(csvFileOut);\n```\n\n#### Record Stream Filtering / Mapping\n\nYou can also filter / map queried records to output record stream. Static functions like `InputRecordStream#map(mappingFn)` and `InputRecordStream#filter(filterFn)` create a record stream which accepts records from upstream and pass to downstream, applying given filtering / mapping function.\n\n```javascript\n//\n// Write down Contact records to CSV, with header name converted.\n//\nconn.sobject('Contact')\n    .find({}, { Id: 1, Name: 1 })\n    .map(function() {\n      return { ID: r.Id, FULL_NAME: r.Name };\n    })\n    .stream().pipe(fs.createWriteStream(\"Contact.csv\"));\n//\n// Write down Lead records to CSV file, eliminating duplicated entry with same email address.\n//\nvar emails = {};\nconn.sobject('Lead')\n    .find({}, { Id: 1, Name: 1, Company: 1, Email: 1 })\n    .filter(function(r) {\n      var dup = emails[r.Email];\n      if (!dup) { emails[r.Email] = true; }\n      return !dup;\n    })\n    .stream().pipe(fs.createWriteStream(\"Lead.csv\"));\n```\n\nHere is much lower level code to achieve the same result using `InputRecordStream#pipe()`.\n\n\n```javascript\n//\n// Write down Contact records to CSV, with header name converted.\n//\nconn.sobject('Contact')\n    .find({}, { Id: 1, Name: 1 })\n    .pipe(sf.RecordStream.map(function(r) {\n      return { ID: r.Id, FULL_NAME: r.Name };\n    }))\n    .stream().pipe(fs.createWriteStream(\"Contact.csv\"));\n//\n// Write down Lead records to CSV file, eliminating duplicated entry with same email address.\n//\nvar emails = {};\nconn.sobject('Lead')\n    .find({}, { Id: 1, Name: 1, Company: 1, Email: 1 })\n    .pipe(sf.RecordStream.filter(function(r) {\n      var dup = emails[r.Email];\n      if (!dup) { emails[r.Email] = true; }\n      return !dup;\n    }))\n    .stream().pipe(fs.createWriteStream(\"Lead.csv\"));\n```\n\n#### Example: Data Migration\n\nBy using record stream pipeline, you can achieve data migration in a simple code.\n\n```javascript\n//\n// Connection for org which migrating data from\n//\nvar conn1 = new sf.Connection({\n  // ...\n});\n//\n// Connection for org which migrating data to\n//\nvar conn2 = new sf.Connection({\n  // ...\n});\n//\n// Get query record stream from Connetin #1\n// and pipe it to batch record stream from connection #2\n//\nvar query = conn1.query(\"SELECT Id, Name, Type, BillingState, BillingCity, BillingStreet FROM Account\");\nvar job = conn2.bulk.createJob(\"Account\", \"insert\");\nvar batch = job.createBatch();\nquery.pipe(batch);\nbatch.on('queue', function() {\n  jobId = job.id;\n  batchId = batch.id;\n  //...\n})\n```\n\n## API Reference\n\nSee API Reference document in https://stomita.github.io/node-salesforce/doc/ .\n\n\n## REPL (Interactive API Console) Usage\n\nNode-salesforce is not merely an API library, but gives `sfjs` and `sfcoffee` REPL interface to test and inspect node-salesforce APIs in interactive JavaScript/CoffeeScript shell.\n\nIt includes buit-in support of node-salesforce package, default connection instance. In the REPL context, package root objects and API methods of default connection are exposed.\n\nBecause the REPL automatically waits the promised object during its evaluation, no callback required for all async API calls. The `_` variable keeps evaluated result in previous statement (as usual Node.JS REPL).\n\n```\n$ sfjs\n> login(\"username@example.org\", \"mypassword123\");\n{ id: '005xxxxxxxxxxxxxxx',\n  organizationId: '00Dyyyyyyyyyyyyyyy' }\n> sobject('Account').find({}, \"Id, Name\").sort({ CreatedDate: 1}).limit(5);\n[ { attributes: \n     { type: 'Account',\n       url: '/services/data/v28.0/sobjects/Account/001i0000009PyDrAAK' },\n    Id: '001i0000009PyDrAAK',\n    Name: 'GenePoint' },\n  { attributes: \n     { type: 'Account',\n       url: '/services/data/v28.0/sobjects/Account/001i0000009PyDsAAK' },\n    Id: '001i0000009PyDsAAK',\n    Name: 'United Oil & Gas, UK' },\n  { attributes: \n     { type: 'Account',\n       url: '/services/data/v28.0/sobjects/Account/001i0000009PyDtAAK' },\n    Id: '001i0000009PyDtAAK',\n    Name: 'United Oil & Gas, Singapore' },\n  { attributes: \n     { type: 'Account',\n       url: '/services/data/v28.0/sobjects/Account/001i0000009PyDuAAK' },\n    Id: '001i0000009PyDuAAK',\n    Name: 'Edge Communications' },\n  { attributes: \n     { type: 'Account',\n       url: '/services/data/v28.0/sobjects/Account/001i0000009PyDvAAK' },\n    Id: '001i0000009PyDvAAK',\n    Name: 'Burlington Textiles Corp of America' } ]\n> _[0].Name\n'GenePoint'\n>\n```\n\n\n## Change History\n\nv0.8.0 (Jan 22, 2014):\n\n* Support Chatter API.\n\n* Support Metadata API.\n\n\nv0.7.2 (Jan 16, 2014):\n\n* Removed unneeded files in npm-published package.\n\n\nv0.7.1 (Dec 19, 2013):\n\n* Support SObject get updated/deleted.\n\n\nv0.7.0 (Dec 11, 2013):\n\n* Support Analytics API and Tooling API.\n\n* Add Connection#queryAll to include deleted/archived records in query.\n\n* Add Connection#recent to fetch recently viewed record information.\n\n* Add RecordReference#blob(fieldName) to access blob in a record field.\n\n* Fix installation issue in Windows environment.\n\n\nv0.6.4 (Dec 5, 2013):\n\n* Add Topic#unsubscribe for unsubscribing from a topic in Streaming API.\n\nv0.6.3 (Oct 31, 2013):\n\n* Fix issue in building query using $exists operator in SObject#find()\n\nv0.6.2 (Oct 15, 2013):\n\n* Change default Salesforce API ver. to 29.0 (Winter '14)\n\n* Fix issue in Connection#queryMore\n\n* Add identity URL information in the callback response of Connection#login/authorize.\n\n\nv0.6.0 (Aug 23, 2013):\n\n* Change default Salesforce API ver. to 28.0 (Summer '13)\n\n* Add REPL interface for interactive API inspection.\n\n* Return Promises/A+ interface object for all async call. The interface is also added to Query / Batch.\n\n* Accept \"*\" in fields argument in `SObject#find()` to select all fields defined in SObject.\n\n* Add `Connection#describe$()`, `Connection#describeGlobal$()`, and `SObject#describe$()`, as caching versions of correspondings.\n\n* Changed `SObject#find(conditions, fields)` behavior in fields argument omission.\n\n* Add `SObject#select()` and `Query#where()` methods to construct a query in SQL-like verb.\n\n* Add `Query#update()` and `Query#destroy()` to apply bulk operation for queried records.\n\n* Add child relationship query support in `Query#include()`\n\n* Add Apex REST support.\n\n* Move streaming API methods from connection object to separated object.\n\nv0.5.1 (Jan 11, 2013):\n\n* Move Query#stream() method to RecordStream#stream() to support stream serialization even in filtered stream.\n\nv0.5.0 (Jan 11, 2013):\n\n* Support Bulk API for insert/update/upsert/delete/hardDelete operation (except for 'query').\n\n* Refine Query#pipe to pipe to other output record stream (like bulk upload batch).\n\n* Add Query#stream() method to convert record stream to general node.js readable stream (generates CSV data).\n\n\nv0.4.0 (Nov 05, 2012):\n\n* Support JSON-style query object to query records other than SOQL, inspired by MongoDB query interface.\n\n* Change default API version to 26.0 (Winter '13).\n\n* Return logged-in user info in the callback response of Connection#login() and Connection#authorize().\n\n* Add Connection#logout() method to terminate session explicitly (Note: only useful for SOAP API login session).\n\n\nv0.3.4 (Oct 19, 2012):\n\n* Fix issue to refresh access token multiple time in concurrent requests.\n\n* Change to use \"Bearer\", not \"OAuth\" in HTTP Authorization header to attach access token.\n\n* Separate oauth2 configuration into different hash object in connection constructor option\n (old style is still supported for backward compatiblity).\n\n\nv0.3.2 (Oct 18, 2012):\n\n* Fix error handling in access token refresh flow.\n\n\nv0.3.1 (Jun 26, 2012):\n\n* Add support of Node.js 0.8.x.\n\n\nv0.3.0 (May 10, 2012):\n\n* Support Salesforce Streaming API.\n\n","readmeFilename":"README.md"}