├── .env ├── .gitignore ├── README.md ├── lib └── findVersions.js ├── package-lock.json ├── package.json ├── results ├── restLogs.csv └── soapLogs.csv └── src └── index.js /.env: -------------------------------------------------------------------------------- 1 | token=your access token from https://happysoup.io/session 2 | url=your org url, for example https://brave-raccoon-mm7crl-dev-ed.my.salesforce.com 3 | apiVersion=current API version, for example 54.0 4 | apiThreshold=The max API version you want to search API calls for, read the docs for details -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | results/*csv -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Goodbye Old APIs 2 | ## A simple script to find API calls on a specific API version 3 | 4 | 5 | **YOU MUST HAVE EVENT MONITORING ENABLED TO USE THIS** 6 | 7 | 8 | Salesforce recently announced that are deprecating the APIs on version 7.0 through 20.0 and they have provided instructions on how to identify applications making API calls into your org and what API version they are using. 9 | 10 | [Salesforce Platform API Versions 7.0 through 20.0 Retirement](https://help.salesforce.com/articleView?id=000351312&type=1&mode=1) 11 | 12 | The instructions though are **very long and require a lot of manual steps, copying pasting, using workbench or other API tool, etc.** 13 | 14 | This script simplies the task and only requires 2 steps! 15 | 16 | ### Features 17 | 18 | * **Specify API version threshhold** Want to see only API calls using version 30 and lower? one step! In the future, when salesforce deprecates more versions, you just need to update the configuration and run the script again! 19 | * **REST and SOAP API** The logs are extracted for both the REST and SOAP APIs 20 | * **csv results** The results are provided in CSV format so that you can put them in excel/gsheets and go through them 21 | 22 | ### Download the script 23 | 24 | On your terminal, issue the following commands 25 | 26 | ```javascript 27 | git clone https://github.com/pgonzaleznetwork/sfdc-goodbye-api 28 | 29 | cd sfdc-goodbye-api 30 | 31 | npm install 32 | ``` 33 | 34 | ### Get an access token to your org 35 | 36 | Use the API to get an access token to your org. You can get one from https://happysoup.io/session 37 | 38 | ### Configure 39 | 40 | Go to the source directory and open the `.env` file. Then update the file with details of your org 41 | 42 | ``` 43 | 44 | token=your access token from https://happysoup.io/session 45 | url=your org url, for example https://brave-raccoon-mm7crl-dev-ed.my.salesforce.com 46 | apiVersion=current API version, for example 54.0 47 | apiThreshold=The max API version you want to search API calls for, read the docs for details 48 | 49 | ``` 50 | 51 | The `apiThreshold` propery is the API version that you want to search for. The results will only include API calls made on this version or **lower** 52 | 53 | So for example, to retrieve API logs using version 20 or lower (which is what Salesforce is deprecating at the time of this writing), simply specify the number 20, **NOT 20.0** 54 | 55 | ### Run the script 56 | 57 | In the terminal, go back to the `sfdc-goodbye-api` directory and run the command `npm start` 58 | 59 | ### Results 60 | 61 | The results will be in the `results` folder. 62 | -------------------------------------------------------------------------------- /lib/findVersions.js: -------------------------------------------------------------------------------- 1 | let {restAPI} = require('sfdc-happy-api')(); 2 | let fetch = require('node-fetch'); 3 | const csv = require('csvtojson'); 4 | const { convertArrayToCSV } = require('convert-array-to-csv'); 5 | let fs = require('fs'); 6 | 7 | async function findVersions(connection){ 8 | 9 | let restApi = restAPI(connection,logError); 10 | 11 | let query = `SELECT LogFile, EventType, CreatedDate FROM EventLogFile WHERE EventType IN ('API', 'RestApi', 'ApiTotalUsage') AND CreatedDate = LAST_N_DAYS:7 ORDER BY CreatedDate Desc`; 12 | let soqlQuery = {query}; 13 | 14 | let rawResults = await restApi.query(soqlQuery); 15 | 16 | let logFileUrls = rawResults.records.map(result => { 17 | return `${connection.url}${result.LogFile}`; 18 | }) 19 | 20 | let options = getFetchOptions(connection.token); 21 | 22 | let restLogs = []; 23 | let soapLogs = []; 24 | 25 | let data = await Promise.all( 26 | 27 | logFileUrls.map(async (logUrl) => { 28 | 29 | let res = await fetch(logUrl,options); 30 | let text = await res.text() 31 | 32 | const logs = await csv({ 33 | noheader:true, 34 | output: "csv" 35 | }).fromString(text); 36 | 37 | logs.forEach(log => { 38 | 39 | let logType = log[0]; 40 | 41 | if(logType == 'RestApi'){ 42 | 43 | let endpoint = log[7]; 44 | let index = endpoint.indexOf('/v'); 45 | let versionNumber = endpoint.substring(index+2,index+4); 46 | 47 | versionNumber = parseInt(versionNumber); 48 | 49 | if(versionNumber <= connection.apiThreshold){ 50 | restLogs.push(log); 51 | } 52 | } 53 | else if(logType == 'API'){ 54 | 55 | let versionNumber = parseInt(log[13]); 56 | 57 | if(versionNumber <= connection.apiThreshold){ 58 | soapLogs.push(log); 59 | } 60 | } 61 | }) 62 | }) 63 | ); 64 | 65 | 66 | const restLogsCsv = convertArrayToCSV(restLogs, { 67 | header:getRestHeader(), 68 | separator: '' 69 | }); 70 | 71 | const soapLogsCsv = convertArrayToCSV(soapLogs, { 72 | header:getSoapHeader(), 73 | separator: '' 74 | }); 75 | 76 | fs.writeFileSync('results/restLogs.csv',restLogsCsv); 77 | fs.writeFileSync('results/soapLogs.csv',soapLogsCsv); 78 | 79 | } 80 | 81 | 82 | function getFetchOptions(token){ 83 | return { 84 | headers: { 85 | 'Content-Type': 'application/json', 86 | 'Authorization': `Bearer ${token}` 87 | } 88 | } 89 | } 90 | 91 | function logError(error){ 92 | console.log(error); 93 | } 94 | 95 | function getRestHeader(){ 96 | 97 | let restHeader = [ 98 | 'EVENT_TYPE', 'TIMESTAMP', 99 | 'REQUEST_ID', 'ORGANIZATION_ID', 100 | 'USER_ID', 'RUN_TIME', 101 | 'CPU_TIME', 'URI', 102 | 'SESSION_KEY', 'LOGIN_KEY', 103 | 'REQUEST_STATUS', 'DB_TOTAL_TIME', 104 | 'METHOD', 'MEDIA_TYPE', 105 | 'STATUS_CODE', 'USER_AGENT', 106 | 'ROWS_PROCESSED', 'NUMBER_FIELDS', 107 | 'DB_BLOCKS', 'DB_CPU_TIME', 108 | 'REQUEST_SIZE', 'RESPONSE_SIZE', 109 | 'ENTITY_NAME', 'TIMESTAMP_DERIVED', 110 | 'USER_ID_DERIVED', 'CLIENT_IP', 111 | 'URI_ID_DERIVED' 112 | ]; 113 | 114 | return restHeader; 115 | } 116 | 117 | function getSoapHeader(){ 118 | 119 | let soapHeader = [ 120 | 'EVENT_TYPE', 'TIMESTAMP', 121 | 'REQUEST_ID', 'ORGANIZATION_ID', 122 | 'USER_ID', 'RUN_TIME', 123 | 'CPU_TIME', 'URI', 124 | 'SESSION_KEY', 'LOGIN_KEY', 125 | 'REQUEST_STATUS', 'DB_TOTAL_TIME', 126 | 'API_TYPE', 'API_VERSION', 127 | 'CLIENT_NAME', 'METHOD_NAME', 128 | 'ENTITY_NAME', 'ROWS_PROCESSED', 129 | 'REQUEST_SIZE', 'RESPONSE_SIZE', 130 | 'DB_BLOCKS', 'DB_CPU_TIME', 131 | 'TIMESTAMP_DERIVED', 'USER_ID_DERIVED', 132 | 'CLIENT_IP', 'URI_ID_DERIVED' 133 | ]; 134 | 135 | return soapHeader; 136 | } 137 | 138 | module.exports = findVersions; -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sfdc-goodbye-api", 3 | "version": "1.0.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "sfdc-goodbye-api", 9 | "version": "1.0.0", 10 | "license": "ISC", 11 | "dependencies": { 12 | "convert-array-to-csv": "2.0.0", 13 | "csvtojson": "2.0.10", 14 | "node-fetch": "2.6.1", 15 | "sfdc-happy-api": "2.4.0" 16 | } 17 | }, 18 | "node_modules/bluebird": { 19 | "version": "3.7.2", 20 | "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", 21 | "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" 22 | }, 23 | "node_modules/convert-array-to-csv": { 24 | "version": "2.0.0", 25 | "resolved": "https://registry.npmjs.org/convert-array-to-csv/-/convert-array-to-csv-2.0.0.tgz", 26 | "integrity": "sha512-dxUINCt28k6WbXGMoB+AaKjGY0Y6GkKwZmT+kvD4nJgVCOKsnIQ3G6n0v2II1lG4NwXQk6EWZ+pPDub9wcqqMg==" 27 | }, 28 | "node_modules/csvtojson": { 29 | "version": "2.0.10", 30 | "resolved": "https://registry.npmjs.org/csvtojson/-/csvtojson-2.0.10.tgz", 31 | "integrity": "sha512-lUWFxGKyhraKCW8Qghz6Z0f2l/PqB1W3AO0HKJzGIQ5JRSlR651ekJDiGJbBT4sRNNv5ddnSGVEnsxP9XRCVpQ==", 32 | "dependencies": { 33 | "bluebird": "^3.5.1", 34 | "lodash": "^4.17.3", 35 | "strip-bom": "^2.0.0" 36 | }, 37 | "bin": { 38 | "csvtojson": "bin/csvtojson" 39 | }, 40 | "engines": { 41 | "node": ">=4.0.0" 42 | } 43 | }, 44 | "node_modules/dotenv": { 45 | "version": "8.6.0", 46 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz", 47 | "integrity": "sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==", 48 | "engines": { 49 | "node": ">=10" 50 | } 51 | }, 52 | "node_modules/fast-xml-parser": { 53 | "version": "3.19.0", 54 | "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-3.19.0.tgz", 55 | "integrity": "sha512-4pXwmBplsCPv8FOY1WRakF970TjNGnGnfbOnLqjlYvMiF1SR3yOHyxMR/YCXpPTOspNF5gwudqktIP4VsWkvBg==", 56 | "bin": { 57 | "xml2js": "cli.js" 58 | }, 59 | "funding": { 60 | "type": "paypal", 61 | "url": "https://paypal.me/naturalintelligence" 62 | } 63 | }, 64 | "node_modules/is-utf8": { 65 | "version": "0.2.1", 66 | "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", 67 | "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=" 68 | }, 69 | "node_modules/lodash": { 70 | "version": "4.17.21", 71 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", 72 | "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" 73 | }, 74 | "node_modules/node-fetch": { 75 | "version": "2.6.1", 76 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", 77 | "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", 78 | "engines": { 79 | "node": "4.x || >=6.0.0" 80 | } 81 | }, 82 | "node_modules/sfdc-happy-api": { 83 | "version": "2.4.0", 84 | "resolved": "https://registry.npmjs.org/sfdc-happy-api/-/sfdc-happy-api-2.4.0.tgz", 85 | "integrity": "sha512-iDM63Clz4r7ffdob/APK2ld7RWEnSiGFtMc5EYRfzZZfP3ieusMdlSJtLOejcgl1iKUNGp1nf2CPXUVfZg7aDw==", 86 | "dependencies": { 87 | "dotenv": "^8.2.0", 88 | "fast-xml-parser": "^3.17.2", 89 | "node-fetch": "^2.6.0" 90 | } 91 | }, 92 | "node_modules/strip-bom": { 93 | "version": "2.0.0", 94 | "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", 95 | "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", 96 | "dependencies": { 97 | "is-utf8": "^0.2.0" 98 | }, 99 | "engines": { 100 | "node": ">=0.10.0" 101 | } 102 | } 103 | }, 104 | "dependencies": { 105 | "bluebird": { 106 | "version": "3.7.2", 107 | "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", 108 | "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" 109 | }, 110 | "convert-array-to-csv": { 111 | "version": "2.0.0", 112 | "resolved": "https://registry.npmjs.org/convert-array-to-csv/-/convert-array-to-csv-2.0.0.tgz", 113 | "integrity": "sha512-dxUINCt28k6WbXGMoB+AaKjGY0Y6GkKwZmT+kvD4nJgVCOKsnIQ3G6n0v2II1lG4NwXQk6EWZ+pPDub9wcqqMg==" 114 | }, 115 | "csvtojson": { 116 | "version": "2.0.10", 117 | "resolved": "https://registry.npmjs.org/csvtojson/-/csvtojson-2.0.10.tgz", 118 | "integrity": "sha512-lUWFxGKyhraKCW8Qghz6Z0f2l/PqB1W3AO0HKJzGIQ5JRSlR651ekJDiGJbBT4sRNNv5ddnSGVEnsxP9XRCVpQ==", 119 | "requires": { 120 | "bluebird": "^3.5.1", 121 | "lodash": "^4.17.3", 122 | "strip-bom": "^2.0.0" 123 | } 124 | }, 125 | "dotenv": { 126 | "version": "8.6.0", 127 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz", 128 | "integrity": "sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==" 129 | }, 130 | "fast-xml-parser": { 131 | "version": "3.19.0", 132 | "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-3.19.0.tgz", 133 | "integrity": "sha512-4pXwmBplsCPv8FOY1WRakF970TjNGnGnfbOnLqjlYvMiF1SR3yOHyxMR/YCXpPTOspNF5gwudqktIP4VsWkvBg==" 134 | }, 135 | "is-utf8": { 136 | "version": "0.2.1", 137 | "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", 138 | "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=" 139 | }, 140 | "lodash": { 141 | "version": "4.17.21", 142 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", 143 | "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" 144 | }, 145 | "node-fetch": { 146 | "version": "2.6.1", 147 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", 148 | "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" 149 | }, 150 | "sfdc-happy-api": { 151 | "version": "2.4.0", 152 | "resolved": "https://registry.npmjs.org/sfdc-happy-api/-/sfdc-happy-api-2.4.0.tgz", 153 | "integrity": "sha512-iDM63Clz4r7ffdob/APK2ld7RWEnSiGFtMc5EYRfzZZfP3ieusMdlSJtLOejcgl1iKUNGp1nf2CPXUVfZg7aDw==", 154 | "requires": { 155 | "dotenv": "^8.2.0", 156 | "fast-xml-parser": "^3.17.2", 157 | "node-fetch": "^2.6.0" 158 | } 159 | }, 160 | "strip-bom": { 161 | "version": "2.0.0", 162 | "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", 163 | "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", 164 | "requires": { 165 | "is-utf8": "^0.2.0" 166 | } 167 | } 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sfdc-goodbye-api", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "node src/index.js", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "dependencies": { 11 | "convert-array-to-csv": "2.0.0", 12 | "csvtojson": "2.0.10", 13 | "node-fetch": "2.6.1", 14 | "sfdc-happy-api": "2.4.0" 15 | }, 16 | "author": "Pablo Gonzalez ", 17 | "license": "ISC" 18 | } 19 | -------------------------------------------------------------------------------- /results/restLogs.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pgonzaleznetwork/sfdc-goodbye-api/3bb5129017edccaa4a6944507e62ae5c54a65994/results/restLogs.csv -------------------------------------------------------------------------------- /results/soapLogs.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pgonzaleznetwork/sfdc-goodbye-api/3bb5129017edccaa4a6944507e62ae5c54a65994/results/soapLogs.csv -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | let findVersions = require('../lib/findVersions'); 2 | 3 | let connection = { 4 | token: process.env.token, 5 | url:process.env.url, 6 | apiVersion: process.env.apiVersion, 7 | apiThreshold: process.env.apiThreshold 8 | }; 9 | 10 | console.log('Starting...'); 11 | findVersions(connection).then(()=> console.log('Done! Check the log files in the results/ folder')) 12 | --------------------------------------------------------------------------------