NetSuite 2024.1 introduced a change to the External URL format of Suitelets marked Available Without Login. Any other scripts calling a Suitelet using a hard-coded URL with the previous format must be updated to the new format.
Try the following Suitelet and saved search combination to more quickly find scripts that contain hard-coded URLs using the &h= parameter.
Solution
Create a Suitelet and a Document Search to display the scripts that contain hard-coded URLs.
1. Create a Saved Search:
- Navigate to Reports > Saved Searches > All Saved Searches > New.
- Select Document.
- Enter a Search Title, such as Identify Scripts With External URLs.
- Enter an ID, such as _check_script_files.
- On the Criteria subtab, under Standard, select the following:
- File Type any of Javascript File.
- Folder any of SuiteScripts.
- Click the Results subtab.
- Select the Name row, then click Insert to insert a new line above the Name.
- Select Internal ID to add an Internal ID column to the far left of your search results. This is required even if you already have the Show Internal IDs preference enabled for your role.
- Click Save.
Create a Suitelet script using the following code sample:
/**
* @NApiVersion 2.x
* @NScriptType Suitelet
*/
define(['N/search', 'N/file', 'N/ui/serverWidget', 'N/log'], function(search, file, serverWidget, log) {
function onRequest(context) {
// Get Search Results
var scriptsSearch = search.load({
id: 'customsearch_check_script_files'
});
// Create the form
var form = serverWidget.createForm({
title: 'Affected Scripts'
});
// Add a sublist to display the results
var sublist = form.addSublist({
id: 'results',
type: serverWidget.SublistType.LIST,
label: 'Scripts with Hardcoded URLs'
});
sublist.addField({
id: 'scriptname',
type: serverWidget.FieldType.TEXT,
label: 'Script Name'
});
sublist.addField({
id: 'url',
type: serverWidget.FieldType.TEXT,
label: 'URL'
});
var line = 0;
// Process each search result
scriptsSearch.run().each(function(result) {
var fileId = result.getValue({
name: 'internalid'
});
var fileName = result.getValue({
name: 'name'
});
var script;
// Attempt to load the script file
try {
script = file.load({
id: fileId
});
} catch (error) {
log.error({
title: 'Error loading file',
details: 'File ID: ' + fileId + ', Error: ' + error.message
});
return true; // Skip to the next file
}
// Get content of file as a string
var content = String(script.getContents());
// Use regular expression to find URLs with &h= parameter
var urlRegex = /https?://[^s"]+&h=[^s"]+/g;
var matches = content.match(urlRegex);
if (matches) {
matches.forEach(function(url) {
sublist.setSublistValue({
id: 'scriptname',
line: line,
value: fileName
});
sublist.setSublistValue({
id: 'url',
line: line,
value: url
});
line++;
});
}
return true;
});
// Display the form
context.response.writePage(form);
}
return {
onRequest: onRequest
};
});
There is a risk of exceeding the governance limit since all the JavaScript files are being loaded. To address this, you can either:
- Use a Map/Reduce script to handle the processing in stages, or
- Modify the search to return a minimum number of rows, reducing the load and improving performance.