UNPKG

n8n-nodes-netsuite-markival

Version:

NetSuite integration node for n8n with SuiteQL, RESTlet, and Raw REST support

480 lines 22.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.MrkNetsuite = void 0; const n8n_workflow_1 = require("n8n-workflow"); const NetsuiteAuth_1 = require("./NetsuiteAuth"); const supabase_1 = require("../../credentials/config/supabase"); const LicenseCache_1 = require("./LicenseCache"); class MrkNetsuite { constructor() { this.description = { displayName: 'MRK NetSuite', name: 'mrkNetsuite', icon: 'file:NetSuite.svg', group: ['transform'], defaults: { name: 'NetSuite', }, version: 1, subtitle: '={{$parameter["operation"]}}', description: 'Make NetSuite REST requests', inputs: [{ type: "main" }], outputs: [{ type: "main" }], credentials: [{ name: 'netSuiteOAuth1Api', required: true }], properties: [ { displayName: 'Operation', noDataExpression: true, name: 'operation', type: 'options', options: [ { name: 'SuiteQL', value: 'suiteql', description: 'Execute SuiteQL queries against NetSuite data', action: 'Execute suiteql queries against netsuite data', noDataExpression: true, }, { name: 'RESTlet', value: 'restlet', description: 'Make requests to NetSuite RESTlets', action: 'Make requests to netsuite restlets', noDataExpression: true, }, { name: 'Raw Request', value: 'raw', description: 'Make raw REST API requests to NetSuite endpoints', action: 'Make raw rest api requests to netsuite endpoints', noDataExpression: true, }, ], default: 'suiteql', }, { displayName: 'Endpoint', name: 'commonEndpoint', type: 'options', options: [ { name: 'Custom', value: 'custom' }, { name: 'Customer', value: '/services/rest/record/v1/customer' }, { name: 'Employee', value: '/services/rest/record/v1/employee' }, { name: 'Invoice', value: '/services/rest/record/v1/invoice' }, { name: 'Item', value: '/services/rest/record/v1/item' }, { name: 'Journal Entry', value: '/services/rest/record/v1/journalEntry' }, { name: 'Sales Order', value: '/services/rest/record/v1/salesOrder' }, { name: 'Vendor', value: '/services/rest/record/v1/vendor' }, ], displayOptions: { show: { operation: ['raw'] } }, default: 'custom', description: 'Common NetSuite REST endpoints or choose custom to enter your own', }, { displayName: 'SQL Query', name: 'sql', type: 'string', typeOptions: { rows: 4 }, default: "SELECT id, companyName, email FROM customer WHERE isInactive = 'F'", displayOptions: { show: { operation: ['suiteql'] } }, description: 'SuiteQL query to execute. Use SQL-like syntax to query NetSuite records.', }, { displayName: 'Result Limit', name: 'limit', type: 'number', typeOptions: { minValue: 1 }, default: 50, description: 'Max number of results to return', displayOptions: { show: { operation: ['suiteql'] } }, }, { displayName: 'Result Offset', name: 'offset', type: 'number', typeOptions: { minValue: 0 }, default: 0, description: 'Number of results to skip (for pagination)', displayOptions: { show: { operation: ['suiteql'] } }, }, { displayName: 'HTTP Method', name: 'method', type: 'options', options: [ { name: 'DELETE', value: 'DELETE', description: 'Delete a record' }, { name: 'GET', value: 'GET', description: 'Retrieve data' }, { name: 'POST', value: 'POST', description: 'Create or update data' }, { name: 'PUT', value: 'PUT', description: 'Update data' }, ], default: 'GET', displayOptions: { show: { operation: ['restlet'] } }, description: 'HTTP method to use for the RESTlet request', }, { displayName: 'Script ID', name: 'scriptId', type: 'string', default: '', required: true, displayOptions: { show: { operation: ['restlet'] } }, description: 'The Script ID of the NetSuite RESTlet (e.g., customscript_my_restlet)', }, { displayName: 'Deploy ID', name: 'deployId', type: 'string', default: '', required: true, displayOptions: { show: { operation: ['restlet'] } }, description: 'The Deployment ID of the NetSuite RESTlet (e.g., customdeploy_my_restlet)', }, { displayName: 'JSON Body', name: 'body', type: 'json', default: '', displayOptions: { show: { operation: ['restlet'], method: ['POST', 'PUT'], }, }, description: 'The data to send to the RESTlet', }, { displayName: 'HTTP Method', name: 'method', type: 'options', options: [ { name: 'DELETE', value: 'DELETE', description: 'Delete a record' }, { name: 'GET', value: 'GET', description: 'Retrieve records' }, { name: 'PATCH', value: 'PATCH', description: 'Update specific fields' }, { name: 'POST', value: 'POST', description: 'Create a new record' }, { name: 'PUT', value: 'PUT', description: 'Update entire record' }, ], default: 'GET', displayOptions: { show: { operation: ['raw'] } }, description: 'HTTP method to use for the request', }, { displayName: 'Custom Endpoint', name: 'endpoint', type: 'string', default: '/services/rest/record/v1/customer', required: true, displayOptions: { show: { operation: ['raw'], commonEndpoint: ['custom'], }, }, description: 'Enter a custom REST API endpoint path (e.g. /services/rest/record/v1/customer)', }, { displayName: 'Resource Path', name: 'resourcePath', type: 'string', default: '', placeholder: 'e.g. 123 or eid:CID001/subscriptions/eid:SUID042', displayOptions: { show: { operation: ['raw'], }, }, description: 'Additional resource path to append to the endpoint (e.g. record ID, subresources)', noDataExpression: false, }, { displayName: 'Query Parameters', name: 'queryParametersUi', type: 'fixedCollection', typeOptions: { multipleValues: true }, placeholder: 'Add Parameter', default: {}, options: [ { name: 'parameter', displayName: 'Parameter', values: [ { displayName: 'Name', name: 'name', type: 'string', default: '' }, { displayName: 'Value', name: 'value', type: 'string', default: '', noDataExpression: false, }, ], }, ], description: 'Add custom query parameters to the request', }, { displayName: 'JSON Body', name: 'body', type: 'json', default: '', displayOptions: { show: { operation: ['raw'], method: ['POST', 'PUT', 'PATCH'], }, }, }, { displayName: 'Response Format', name: 'responseFormat', type: 'options', options: [ { name: 'Simple', value: 'simple', description: 'Returns only the response body', }, { name: 'Full Response', value: 'fullResponse', description: 'Returns the full response data including headers, status code, and body', }, ], default: 'simple', description: 'The format of the response data', }, { displayName: 'Additional Headers', name: 'headerParametersUi', type: 'fixedCollection', typeOptions: { multipleValues: true }, placeholder: 'Add Header', default: {}, options: [ { name: 'header', displayName: 'Header', values: [ { displayName: 'Name', name: 'name', type: 'string', default: '' }, { displayName: 'Value', name: 'value', type: 'string', default: '' }, ], }, ], description: 'Add custom headers to the request', }, ], }; } async execute() { var _a, _b, _c, _d, _e; const credentials = await this.getCredentials('netSuiteOAuth1Api'); const accountId = credentials.accountId; const licenseKey = credentials.licenseKey; try { const isValid = await (0, LicenseCache_1.checkAndUpdateLicense)(accountId, licenseKey, async () => { const response = await supabase_1.supabase.schema('api').rpc('validate_netsuite_license', { p_account_id: accountId, p_license_key: licenseKey, }); this.logger.debug(`licenseResponse:${JSON.stringify(response)}`); if (response.error) { if (response.error.code === 'P0001') { return false; } throw new n8n_workflow_1.NodeOperationError(this.getNode(), `License validation failed: ${response.error.message}`); } return true; }, this.logger); if (!isValid) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Invalid or expired license key', { description: 'Please contact support to renew your license.', itemIndex: 0, }); } } catch (error) { if (error instanceof n8n_workflow_1.NodeOperationError) { throw error; } throw new n8n_workflow_1.NodeOperationError(this.getNode(), `License validation error: ${error.message}`, { description: 'An unexpected error occurred during license validation.', itemIndex: 0, }); } const items = this.getInputData(); const returnData = []; const credName = 'netSuiteOAuth1Api'; let creds; try { creds = await this.getCredentials(credName); if (!(creds === null || creds === void 0 ? void 0 : creds.realm)) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'No valid credentials provided! Please check your NetSuite OAuth configuration.'); } } catch (error) { if (this.continueOnFail()) { return [returnData]; } throw error; } const operation = this.getNodeParameter('operation', 0); const host = operation === 'restlet' ? `https://${creds.accountId}.restlets.api.netsuite.com` : `https://${creds.accountId}.suitetalk.api.netsuite.com`; for (let i = 0; i < items.length; i++) { const operation = this.getNodeParameter('operation', i); const responseFormat = this.getNodeParameter('responseFormat', i); try { let method; let endpoint; let body; const qs = {}; const headers = { 'Content-Type': 'application/json', Accept: 'application/json', Cookie: 'NS_ROUTING_VERSION=LAGGING', Prefer: 'transient', }; this.getNodeParameter('headerParametersUi.header', i, []).forEach((h) => (headers[h.name] = h.value)); this.getNodeParameter('queryParametersUi.parameter', i, []).forEach((p) => (qs[p.name] = p.value)); if (operation === 'suiteql') { method = 'POST'; endpoint = '/services/rest/query/v1/suiteql'; const sql = this.getNodeParameter('sql', i); const limit = this.getNodeParameter('limit', i, 50); const offset = this.getNodeParameter('offset', i); qs.limit = limit.toString(); qs.offset = offset.toString(); body = { q: sql }; } else if (operation === 'restlet') { const methodStr = this.getNodeParameter('method', i); method = methodStr; const scriptId = this.getNodeParameter('scriptId', i); const deployId = this.getNodeParameter('deployId', i); endpoint = '/app/site/hosting/restlet.nl'; qs.script = scriptId; qs.deploy = deployId; if (['POST', 'PUT'].includes(method)) { const bodyJson = this.getNodeParameter('body', i, ''); if (bodyJson) { try { body = JSON.parse(bodyJson); } catch (e) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Invalid JSON in request body', { itemIndex: i, }); } } } } else { const methodStr = this.getNodeParameter('method', i); method = methodStr; const commonEndpoint = this.getNodeParameter('commonEndpoint', i); let path = commonEndpoint === 'custom' ? this.getNodeParameter('endpoint', i) : commonEndpoint; const resourcePath = this.getNodeParameter('resourcePath', i, ''); if (resourcePath && typeof resourcePath === 'string') { path = path.replace(/\/+$/, ''); path = `${path}/${resourcePath.replace(/^\/+/, '')}`; } endpoint = path; if (['POST', 'PUT', 'PATCH'].includes(method)) { const bodyJson = this.getNodeParameter('body', i, ''); if (bodyJson) { try { body = JSON.parse(bodyJson); } catch (e) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Invalid JSON in request body', { itemIndex: i, }); } } } } const url = `${host}${endpoint}`; const { consumerKey, consumerSecret, accessToken, tokenSecret, realm } = creds; if (!consumerKey || !consumerSecret || !accessToken || !tokenSecret || !realm) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Missing required OAuth credentials', { itemIndex: i, }); } const authHeaders = (0, NetsuiteAuth_1.generateNetSuiteAuth)(method, url, consumerKey, consumerSecret, accessToken, tokenSecret, realm, qs); const options = { method, url, headers: { ...headers, ...authHeaders, }, body, qs, json: true, resolveWithFullResponse: true, }; let response; try { response = await this.helpers.request(options); const responseData = { statusCode: response.statusCode, headers: response.headers, body: response.body, }; if (response.statusCode === 204 && ((_a = response.headers) === null || _a === void 0 ? void 0 : _a.location)) { responseData.body = { success: true, location: response.headers.location, }; } if (responseFormat === 'simple') { returnData.push({ json: responseData.body || { success: true }, pairedItem: { item: i }, }); } else { returnData.push({ json: responseData, pairedItem: { item: i }, }); } } catch (error) { if (this.continueOnFail()) { returnData.push({ json: { statusCode: error.statusCode || 500, headers: ((_b = error.response) === null || _b === void 0 ? void 0 : _b.headers) || {}, body: ((_c = error.response) === null || _c === void 0 ? void 0 : _c.body) || null, error: error.message, }, pairedItem: { item: i }, }); continue; } throw error; } } catch (error) { if (this.continueOnFail()) { returnData.push({ json: { error: error.message, statusCode: error.statusCode || 500, headers: ((_d = error.response) === null || _d === void 0 ? void 0 : _d.headers) || {}, body: ((_e = error.response) === null || _e === void 0 ? void 0 : _e.body) || null, }, pairedItem: { item: i }, }); continue; } throw error; } } return [returnData]; } } exports.MrkNetsuite = MrkNetsuite; //# sourceMappingURL=MrkNetsuite.node.js.map