tooling.destroy() with an array of ids always fails
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 1.5k
- Forks
- 551
- Avg merge
- 9h 2m
- Merged PRs (30d)
- 3
Description
Description
conn.tooling.destroy(type, ids) fails with NOT_FOUND: The requested resource does not exist whenever ids is an array, even an array of one element. The same call with a single id string works fine.
Reproduction
const conn = new jsforce.Connection({ instanceUrl, accessToken });
// works: DELETE /services/data/v65.0/tooling/sobjects/Flow/301S800001CkHexIAF
await conn.tooling.destroy('Flow', '301S800001CkHexIAF');
// always fails with "The requested resource does not exist":
await conn.tooling.destroy('Flow', ['301S800001CkHexIAF'], { allOrNone: false });
Root cause
Connection.destroy() routes array input to _destroyMany(), which builds a sObject Collections URL:
// lib/connection.js
async _destroyMany(type, ids, options) {
...
let url = [this._baseUrl(), 'composite', 'sobjects?ids='].join('/') + ids.join(',');
The Tooling class copies destroy / _destroyMany verbatim from Connection.prototype:
// lib/api/tooling.js
const { ..., destroy, _destroySingle, _destroyParallel, _destroyMany, ... } = Connection.prototype;
and its _baseUrl() appends /tooling, so the request becomes:
DELETE /services/data/v65.0/tooling/composite/sobjects?ids=...
The Tooling API has no sObject Collections resource (/composite/sobjects exists only in the regular REST API), so Salesforce returns 404 / The requested resource does not exist. The multi-record code paths for create() and update() are inherited the same way and should hit the same problem.
Expected behavior
Either:
Toolingoverrides_destroyMany/_createMany/_updateManyto use_destroyParallel-style unitary calls, or batches through the Tooling composite endpoint (POST /services/data/vXX.X/tooling/composite, max 25 subrequests), or- the array form throws a clear "not supported by the Tooling API" error instead of a confusing 404.
Workaround
Call destroy per id, or build a Tooling composite request manually:
await conn.requestPost(`/services/data/v${conn.version}/tooling/composite`, {
allOrNone: false,
compositeRequest: ids.map((id, i) => ({
method: 'DELETE',
url: `/services/data/v${conn.version}/tooling/sobjects/Flow/${id}`,
referenceId: `ref${i}`,
})),
});
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in lib/connection.js by tracing _destroyMany and the related multi-record create and update paths, then compare how lib/api/tooling.js inherits them and changes _baseUrl(). Determine a supported array behavior for the Tooling API, and verify that array operations no longer produce the invalid composite/sobjects 404 or instead return a clear unsupported error.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, typescript
- Domain
- api
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 55/100