0

I'm new to Salesforce and Soap but I find myself needing to use both for this project. I need to use the Soap Salesforce API in Node, with the Partner WSDL. I'm also using the soap library for ease of use. I have setup a developer account in Salesforce that comes with test data already there. However, I am having a little trouble understanding what comes back in the API response vs what I see in my developer console. What I want to do is call describeGlobalAsync() to see the objects available to query against. Then call describeSObjectsAsync against a specific object to see fields etc. The console shows 51+items sorted by label but the API response shows over 552 items. enter image description here

Here are the steps I am following:

  1. Create a soap client
  2. Use that client to login via the Salesforce SOAP API
  3. Use the login response to set the serverUrl and sessionId for the client
  4. Make describeGlobalAsync call
  5. Log the response for investigation

I am also adding a header via addSoapHeader and I believe the type of call needs to have the namespace set, shown in Salesforce docs.

If I call describeGlobalAsync without a parameter, I get the following error, so I'm passing in Account for now:

No operation specified in request (the Body element has no child elements)

Here is my code so far:

const soap = require('soap');
const url = './partner.wsdl';
const util = require('util');

const namespaces = {
    API: 'urn:partner.soap.sforce.com',
    OBJECT: 'urn:sobject.partner.soap.sforce.com',
    FAULT: 'urn:fault.partner.soap.sforce.com'
}

async function createClient(wsdl, namespace) {
  let client = (await soap.createClientAsync(wsdl));
  const loginResult = await client.loginAsync({username: username, password: password});
  if (loginResult) {
    client.setEndpoint(loginResult[0].result.serverUrl);
    const sheader = {
      SessionHeader : {
          sessionId: loginResult[0].result.sessionId
      }
    };
     client.addSoapHeader(sheader,"","tns", namespace);
    return client;
  } else {
    throw new Error('cant login');
  }
}

async function describeGlobal() {
  try {
    let client = await createClient(url, namespaces.OBJECT);
    const headers = client.getSoapHeaders();
    const globalObj = await client.describeGlobalAsync('Account');
    console.log(JSON.stringify(globalObj[0].result.sobjects, null, 4));
    let holder = [];
    for (let elem of globalObj[0].result.sobjects) {
      holder.push(elem.name)
    }
    console.log(holder)
  } catch(err) {
    console.log(err)
  }
}

await describeGlobal();

Where I console.log(holder) is where I get the following output:

[
  'AIApplication',
  'AIApplicationConfig',
  'AIPredictionEvent',
  'AcceptedEventRelation',
  'Account',
  'AccountChangeEvent',
  'AccountCleanInfo',
  'AccountContactRole',
  'AccountContactRoleChangeEvent',
  'AccountFeed',
  'AccountHistory',
  'AccountPartner',
  'AccountShare',
  'ActionLinkGroupTemplate',
  'ActionLinkTemplate',
  'ActiveFeatureLicenseMetric',
  'ActivePermSetLicenseMetric',
  'ActiveProfileMetric',
  'ActivityHistory',
  'AdditionalNumber',
  'AggregateResult',
  ... 552 more items
]

It looks like this may be related objects but I have yet to be able to confirm that. Also, if I try await client.describeSObjectsAsync('Account'); I get LimitInfoHeader, not any object info. What am I doing wrong?

  1. How can I get a list of all objects I can query against?
  2. How can I get key/field info about a particular object?
johnny_mac
  • 1,801
  • 3
  • 20
  • 48

1 Answers1

0

Even vanilla Salesforce instance will contain 200+ system tables, often with things that aren't real data but metadata. ApexClass, ApexTrigger for example - they're visible for completeness but to actually change server-side code you're supposed to run a deployment. You won't see them in "Object Manager". Even all validation rules, workflow rules etc click-click-click business logic configured by sysadmins on various objects sit in 1 table for completeness.

Actual number depends on your edition (Developer? Professional? Enterprise?) and how many extra features you have enabled (communities, custom fiscal calendar, knowledge base), how many plugins installed from AppExchange...

If your task is to backup everything then querying these too is not a bad idea, even if restore process would be more convoluted.

See if running a query similar to https://stackoverflow.com/a/64276053/313628 will give you a list of tables closer to what's "real data", editable/directly visible for end user. You could tweak this query a bit (see fields in EntityDefinition such as isqueryable). Or even give up, give the query to your sysadmin to tweak until they're happy and focus on building the rest of your application?

EDIT

As for actual describeglobal error - hard to say. It shouldn't require any parameters. Do you have a way to see the final SOAP message that's being sent to SF? Might be something buggy in the library...

How do you call it? First you call login, from response you read the serverUrl and sessionId, then you call describeGlobal?

Here's my raw soapui request

POST https://censored.my.salesforce.com/services/Soap/u/53.0/00Dcensored HTTP/1.1
Accept-Encoding: gzip,deflate
Content-Type: text/xml;charset=UTF-8
SOAPAction: ""
Content-Length: 457
Host: censored.my.salesforce.com
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.5.5 (Java/12.0.1)

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:partner.soap.sforce.com">
   <soapenv:Header>
      <urn:SessionHeader>
         <urn:sessionId>00D...!censored</urn:sessionId>
      </urn:SessionHeader>
   </soapenv:Header>
   <soapenv:Body>
      <urn:describeGlobal/>
   </soapenv:Body>
</soapenv:Envelope>

and response is similar to

enter image description here

eyescream
  • 18,088
  • 2
  • 34
  • 46
  • I'm using the Partner WSDL, so this is a clients data I'm trying to access so that I can pull it into our app for them, so giving up is not an option – johnny_mac Dec 20 '21 at 01:29
  • I mean "give up" as "let the client decide which tables they want to sync" ;) – eyescream Dec 20 '21 at 11:45
  • Understood. But I'm not looking to update any data, just query it. I understand that I can request the client just tells me what objects I can query against, and that may be the way to go. What I dont get it that `describeGlobal` says "Retrieves a list of available objects for your organization’s data." in the developer guide. I'm getting an error on that when I don't pass an arg, and when I do I get 551+. I'm confused https://developer.salesforce.com/docs/atlas.en-us.232.0.api.meta/api/sforce_api_calls_describeglobal.htm – johnny_mac Dec 20 '21 at 21:37
  • you'd have to post more code, do you call login with username/password first? what do you do with login results? do you see actual success in SF (setup -> find your user, scroll down to login history). – eyescream Dec 21 '21 at 21:00
  • My code already shows that I create a soap client using the Partner WSDL and make a call to login using username/password. Then, from that response, set the serverUrl and sessionId, per the docs. I don't know what other code you need – johnny_mac Dec 22 '21 at 02:31