Add Activepieces integration for workflow automation

- Add Activepieces fork with SmoothSchedule custom piece
- Create integrations app with Activepieces service layer
- Add embed token endpoint for iframe integration
- Create Automations page with embedded workflow builder
- Add sidebar visibility fix for embed mode
- Add list inactive customers endpoint to Public API
- Include SmoothSchedule triggers: event created/updated/cancelled
- Include SmoothSchedule actions: create/update/cancel events, list resources/services/customers

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
poduck
2025-12-18 22:59:37 -05:00
parent 9848268d34
commit 3aa7199503
16292 changed files with 1284892 additions and 4708 deletions

View File

@@ -0,0 +1,33 @@
{
"extends": [
"../../../../.eslintrc.base.json"
],
"ignorePatterns": [
"!**/*"
],
"overrides": [
{
"files": [
"*.ts",
"*.tsx",
"*.js",
"*.jsx"
],
"rules": {}
},
{
"files": [
"*.ts",
"*.tsx"
],
"rules": {}
},
{
"files": [
"*.js",
"*.jsx"
],
"rules": {}
}
]
}

View File

@@ -0,0 +1,7 @@
# pieces-netsuite
This library was generated with [Nx](https://nx.dev).
## Building
Run `nx build pieces-netsuite` to build the library.

View File

@@ -0,0 +1,7 @@
{
"name": "@activepieces/piece-netsuite",
"version": "0.0.13",
"dependencies": {
"sql-formatter": "15.6.10"
}
}

View File

@@ -0,0 +1,65 @@
{
"name": "pieces-netsuite",
"$schema": "../../../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "packages/pieces/community/netsuite/src",
"projectType": "library",
"release": {
"version": {
"currentVersionResolver": "git-tag",
"preserveLocalDependencyProtocols": false,
"manifestRootsToUpdate": [
"dist/{projectRoot}"
]
}
},
"tags": [],
"targets": {
"build": {
"executor": "@nx/js:tsc",
"outputs": [
"{options.outputPath}"
],
"options": {
"outputPath": "dist/packages/pieces/community/netsuite",
"tsConfig": "packages/pieces/community/netsuite/tsconfig.lib.json",
"packageJson": "packages/pieces/community/netsuite/package.json",
"main": "packages/pieces/community/netsuite/src/index.ts",
"assets": [
"packages/pieces/community/netsuite/*.md",
{
"input": "packages/pieces/community/netsuite/src/i18n",
"output": "./src/i18n",
"glob": "**/!(i18n.json)"
}
],
"buildableProjectDepsInPackageJsonType": "dependencies",
"updateBuildableProjectDepsInPackageJson": true
},
"dependsOn": [
"^build",
"prebuild"
]
},
"nx-release-publish": {
"options": {
"packageRoot": "dist/{projectRoot}"
}
},
"lint": {
"executor": "@nx/eslint:lint",
"outputs": [
"{options.outputFile}"
]
},
"prebuild": {
"executor": "nx:run-commands",
"options": {
"cwd": "packages/pieces/community/netsuite",
"command": "bun install --no-save --silent"
},
"dependsOn": [
"^build"
]
}
}
}

View File

@@ -0,0 +1,45 @@
{
"Account ID": "Konto-ID",
"Consumer Key": "Konsumschlüssel",
"Consumer Secret": "Verbrauchergeheimnis",
"Token ID": "Token ID",
"Token Secret": "Token-Geheimnis",
"Your NetSuite account ID": "Ihre NetSuite-Konto-ID",
"Your NetSuite consumer key": "Ihr NetSuite-Verbrauchsschlüssel",
"Your NetSuite consumer secret": "Ihr NetSuite-Kundengeheimnis",
"Your NetSuite token ID": "Ihre NetSuite Token ID",
"Your NetSuite token secret": "Ihr NetSuite Token Geheimnis",
"Get Vendor": "Get Vendor",
"Get Customer": "Kunde erhalten",
"Run SuiteQL Query": "SuiteQL-Abfrage ausführen",
"Custom API Call": "Eigener API-Aufruf",
"Gets vendor details from NetSuite.": "Ruft Händlerdetails von NetSuite ab.",
"Gets customer details from NetSuite.": "Ruft Kundendaten von NetSuite ab.",
"Run SuiteQL Query on NetSuite.": "SuiteQL Query auf NetSuite ausführen.",
"Make a custom API call to a specific endpoint": "Einen benutzerdefinierten API-Aufruf an einen bestimmten Endpunkt machen",
"Vendor ID": "Lieferant ID",
"Customer ID": "Kunden-ID",
"Markdown": "Markdown",
"Query": "Abfrage",
"Arguments": "Argumente",
"Method": "Methode",
"Headers": "Kopfzeilen",
"Query Parameters": "Abfrageparameter",
"Body": "Körper",
"Response is Binary ?": "Antwort ist binär?",
"No Error on Failure": "Kein Fehler bei Fehler",
"Timeout (in seconds)": "Timeout (in Sekunden)",
"The ID of the vendor to retrieve.": "Die ID des zu holenden Verkäufers.",
"The ID of the customer to retrieve.": "Die ID des Kunden zu ermitteln.",
"\n- **DO NOT** insert dynamic input directly into the query string. Instead, use :1, :2, :3 and add them in args for parameterized queries\n- Arguments are treated as string and inserted as a [Text Literal](https://docs.oracle.com/en/database/oracle/oracle-database/26/sqlrf/Literals.html)\n": "\n- **DO NOT** fügen Sie dynamische Eingaben direkt in den Abfrage-String ein. Stattdessen :1, :2, verwenden :3 und fügen Sie sie in Args für parametrierte Abfragen\n- Argumente werden als String behandelt und als [Text Literal](https://docs.oracle.com/en/database/oracle/oracle-database/26/sqlrf/Literals.html)\n eingefügt",
"Please use :1, :2, :3 etc. for parameterized queries": "Bitte verwenden Sie :1, :2, :3 etc. für parametrierte Abfragen",
"Arguments to be used in the query": "Argumente, die in der Abfrage verwendet werden sollen",
"Authorization headers are injected automatically from your connection.": "Autorisierungs-Header werden automatisch von Ihrer Verbindung injiziert.",
"Enable for files like PDFs, images, etc..": "Aktivieren für Dateien wie PDFs, Bilder, etc..",
"GET": "ERHALTEN",
"POST": "POST",
"PATCH": "PATCH",
"PUT": "PUT",
"DELETE": "LÖSCHEN",
"HEAD": "HEAD"
}

View File

@@ -0,0 +1,45 @@
{
"Account ID": "ID de cuenta",
"Consumer Key": "Clave del consumidor",
"Consumer Secret": "Secreto de consumidor",
"Token ID": "Token ID",
"Token Secret": "Clave de Token",
"Your NetSuite account ID": "Su ID de cuenta de NetSuite",
"Your NetSuite consumer key": "Tu clave de consumidor de NetSuite",
"Your NetSuite consumer secret": "Tu secreto de consumidor de NetSuite",
"Your NetSuite token ID": "Su ID de token de NetSuite",
"Your NetSuite token secret": "Tu secreto de NetSuite",
"Get Vendor": "Get Vendor",
"Get Customer": "Obtener cliente",
"Run SuiteQL Query": "Ejecutar consulta SuiteQL",
"Custom API Call": "Llamada API personalizada",
"Gets vendor details from NetSuite.": "Obtiene detalles del proveedor de NetSuite.",
"Gets customer details from NetSuite.": "Obtiene los detalles del cliente de NetSuite.",
"Run SuiteQL Query on NetSuite.": "Ejecute SuiteQL Query en NetSuite.",
"Make a custom API call to a specific endpoint": "Hacer una llamada API personalizada a un extremo específico",
"Vendor ID": "ID del proveedor",
"Customer ID": "ID del cliente",
"Markdown": "Markdown",
"Query": "Consulta",
"Arguments": "Argumentos",
"Method": "Método",
"Headers": "Encabezados",
"Query Parameters": "Parámetros de consulta",
"Body": "Cuerpo",
"Response is Binary ?": "¿Respuesta es binaria?",
"No Error on Failure": "No hay ningún error en fallo",
"Timeout (in seconds)": "Tiempo de espera (en segundos)",
"The ID of the vendor to retrieve.": "El ID del proveedor a recuperar.",
"The ID of the customer to retrieve.": "El ID del cliente a recuperar.",
"\n- **DO NOT** insert dynamic input directly into the query string. Instead, use :1, :2, :3 and add them in args for parameterized queries\n- Arguments are treated as string and inserted as a [Text Literal](https://docs.oracle.com/en/database/oracle/oracle-database/26/sqlrf/Literals.html)\n": "\n- **DO NOT** insert dynamic input directly into the query string. Instead, use :1, :2, :3 and add them in args for parameterized queries\n- Arguments are treated as string and inserted as a [Text Literal](https://docs.oracle.com/en/database/oracle/oracle-database/26/sqlrf/Literals.html)\n",
"Please use :1, :2, :3 etc. for parameterized queries": "Por favor usa :1, :2, :3 etc. para consultas parametrizadas",
"Arguments to be used in the query": "Argumentos a usar en la consulta",
"Authorization headers are injected automatically from your connection.": "Las cabeceras de autorización se inyectan automáticamente desde tu conexión.",
"Enable for files like PDFs, images, etc..": "Activar para archivos como PDFs, imágenes, etc.",
"GET": "RECOGER",
"POST": "POST",
"PATCH": "PATCH",
"PUT": "PUT",
"DELETE": "BORRAR",
"HEAD": "LIMPIO"
}

View File

@@ -0,0 +1,45 @@
{
"Account ID": "ID du compte client",
"Consumer Key": "Clé de consommateur",
"Consumer Secret": "Secret du consommateur",
"Token ID": "Token ID",
"Token Secret": "Secret du jeton",
"Your NetSuite account ID": "ID de votre compte NetSuite",
"Your NetSuite consumer key": "Votre clé utilisateur NetSuite",
"Your NetSuite consumer secret": "Secret de votre consommateur NetSuite",
"Your NetSuite token ID": "Votre identifiant de jeton NetSuite",
"Your NetSuite token secret": "Votre jeton NetSuite secret",
"Get Vendor": "Get Vendor",
"Get Customer": "Obtenir le client",
"Run SuiteQL Query": "Exécuter la requête SuiteQL",
"Custom API Call": "Appel d'API personnalisé",
"Gets vendor details from NetSuite.": "Obtient les détails du vendeur depuis NetSuite.",
"Gets customer details from NetSuite.": "Obtient les détails du client depuis NetSuite.",
"Run SuiteQL Query on NetSuite.": "Exécutez SuiteQL Query sur NetSuite.",
"Make a custom API call to a specific endpoint": "Passer un appel API personnalisé à un endpoint spécifique",
"Vendor ID": "ID Fournisseur",
"Customer ID": "ID du client",
"Markdown": "Markdown",
"Query": "Requête",
"Arguments": "Arguments",
"Method": "Méthode",
"Headers": "Headers",
"Query Parameters": "Paramètres de requête",
"Body": "Body",
"Response is Binary ?": "La réponse est Binaire ?",
"No Error on Failure": "Aucune erreur en cas d'échec",
"Timeout (in seconds)": "Délai d'expiration (en secondes)",
"The ID of the vendor to retrieve.": "L'ID du vendeur à récupérer.",
"The ID of the customer to retrieve.": "L'ID du client à récupérer.",
"\n- **DO NOT** insert dynamic input directly into the query string. Instead, use :1, :2, :3 and add them in args for parameterized queries\n- Arguments are treated as string and inserted as a [Text Literal](https://docs.oracle.com/en/database/oracle/oracle-database/26/sqlrf/Literals.html)\n": "\n- **NE PAS** insérer une entrée dynamique directement dans la chaîne de requête. Utilisez plutôt :1, :2, :3 et ajoutez-les dans args pour les requêtes paramétrées\n- Les arguments sont traités comme une chaîne et insérés comme un [texte littéral](https://docs.oracle.com/en/database/oracle/oracle-database/26/sqlrf/Literals.html)\n",
"Please use :1, :2, :3 etc. for parameterized queries": "Veuillez utiliser :1, :2, :3 etc. pour les requêtes paramétrées",
"Arguments to be used in the query": "Arguments à utiliser dans la requête",
"Authorization headers are injected automatically from your connection.": "Les Headers d'autorisation sont injectés automatiquement à partir de votre connexion.",
"Enable for files like PDFs, images, etc..": "Activer pour les fichiers comme les PDFs, les images, etc.",
"GET": "GET",
"POST": "POST",
"PATCH": "PATCH",
"PUT": "PUT",
"DELETE": "DELETE",
"HEAD": "HEAD"
}

View File

@@ -0,0 +1,45 @@
{
"Account ID": "アカウントID",
"Consumer Key": "コンシューマー キー",
"Consumer Secret": "コンシューマーシークレット",
"Token ID": "Token ID",
"Token Secret": "トークンシークレット",
"Your NetSuite account ID": "NetSuiteアカウントID",
"Your NetSuite consumer key": "NetSuiteコンシューマーキー",
"Your NetSuite consumer secret": "NetSuiteのコンシューマーシークレット",
"Your NetSuite token ID": "NetSuiteトークンID",
"Your NetSuite token secret": "NetSuiteトークンシークレット",
"Get Vendor": "Get Vendor",
"Get Customer": "顧客を取得",
"Run SuiteQL Query": "SuiteQL クエリを実行",
"Custom API Call": "カスタムAPI通話",
"Gets vendor details from NetSuite.": "NetSuite からベンダーの詳細を取得します。",
"Gets customer details from NetSuite.": "NetSuiteから顧客の詳細を取得します。",
"Run SuiteQL Query on NetSuite.": "NetSuite 上で SuiteQL クエリを実行します。",
"Make a custom API call to a specific endpoint": "特定のエンドポイントへのカスタム API コールを実行します。",
"Vendor ID": "仕入先ID",
"Customer ID": "顧客 ID",
"Markdown": "Markdown",
"Query": "クエリ",
"Arguments": "引数",
"Method": "方法",
"Headers": "ヘッダー",
"Query Parameters": "クエリパラメータ",
"Body": "本文",
"Response is Binary ?": "応答はバイナリですか?",
"No Error on Failure": "失敗時にエラーはありません",
"Timeout (in seconds)": "タイムアウト(秒)",
"The ID of the vendor to retrieve.": "取得するベンダID。",
"The ID of the customer to retrieve.": "取得する顧客ID。",
"\n- **DO NOT** insert dynamic input directly into the query string. Instead, use :1, :2, :3 and add them in args for parameterized queries\n- Arguments are treated as string and inserted as a [Text Literal](https://docs.oracle.com/en/database/oracle/oracle-database/26/sqlrf/Literals.html)\n": "\n- **DO NOT** insert dynamic input directly into the query string. Instead, use :1, :2, :3 and add them in args for parameterized queries\n- Arguments are treated as string and inserted as a [Text Literal](https://docs.oracle.com/en/database/oracle/oracle-database/26/sqlrf/Literals.html)\n",
"Please use :1, :2, :3 etc. for parameterized queries": "パラメータ化されたクエリには、:1、:2、:3などを使用してください",
"Arguments to be used in the query": "クエリで使用する引数",
"Authorization headers are injected automatically from your connection.": "認証ヘッダは接続から自動的に注入されます。",
"Enable for files like PDFs, images, etc..": "PDF、画像などのファイルを有効にします。",
"GET": "取得",
"POST": "POST",
"PATCH": "PATCH",
"PUT": "PUT",
"DELETE": "削除",
"HEAD": "頭"
}

View File

@@ -0,0 +1,45 @@
{
"Account ID": "ID klant",
"Consumer Key": "Gebruiker Sleutel",
"Consumer Secret": "Gebruiker geheim",
"Token ID": "Token ID",
"Token Secret": "Token Geheim",
"Your NetSuite account ID": "Uw NetSuite account-ID",
"Your NetSuite consumer key": "Uw NetSuite consumentensleutel",
"Your NetSuite consumer secret": "Uw NetSuite consumentengeheim",
"Your NetSuite token ID": "Uw NetSuite token ID",
"Your NetSuite token secret": "Uw NetSuite token geheim",
"Get Vendor": "Get Vendor",
"Get Customer": "Klant ophalen",
"Run SuiteQL Query": "SuiteQL query uitvoeren",
"Custom API Call": "Custom API Call",
"Gets vendor details from NetSuite.": "Verkoper gegevens ophalen uit NetSuite.",
"Gets customer details from NetSuite.": "Haal klantgegevens op uit NetSuite.",
"Run SuiteQL Query on NetSuite.": "Start SuiteQL Query op NetSuite.",
"Make a custom API call to a specific endpoint": "Maak een aangepaste API call naar een specifiek eindpunt",
"Vendor ID": "Verkoper ID",
"Customer ID": "Klant ID",
"Markdown": "Markdown",
"Query": "Zoekopdracht",
"Arguments": "Argumenten",
"Method": "Methode",
"Headers": "Kopteksten",
"Query Parameters": "Query parameters",
"Body": "Lichaam",
"Response is Binary ?": "Antwoord is binair?",
"No Error on Failure": "Geen fout bij fout",
"Timeout (in seconds)": "Time-out (in seconden)",
"The ID of the vendor to retrieve.": "De ID van de verkoper die opgehaald moet worden.",
"The ID of the customer to retrieve.": "Het ID van de op te halen klant.",
"\n- **DO NOT** insert dynamic input directly into the query string. Instead, use :1, :2, :3 and add them in args for parameterized queries\n- Arguments are treated as string and inserted as a [Text Literal](https://docs.oracle.com/en/database/oracle/oracle-database/26/sqlrf/Literals.html)\n": "\n- **NIET** voeg dynamische invoer rechtstreeks in de querystring in. Gebruik in plaats daarvan :1, :2, :3 en voeg ze toe in args voor geparameteriseerde queries\n- Argumenten worden behandeld als een tekst en ingevoegd als een [Tekst Literal](https://docs.oracle.com/en/database/oracle/oracle-database/26/sqlrf/Literals.html)\n",
"Please use :1, :2, :3 etc. for parameterized queries": "Gebruik :1, :2, :3 etc. voor geparameteriseerde zoekopdrachten",
"Arguments to be used in the query": "Argumenten te gebruiken in de query",
"Authorization headers are injected automatically from your connection.": "Autorisatie headers worden automatisch geïnjecteerd vanuit uw verbinding.",
"Enable for files like PDFs, images, etc..": "Inschakelen voor bestanden zoals PDF's, afbeeldingen etc..",
"GET": "KRIJG",
"POST": "POSTE",
"PATCH": "BEKIJK",
"PUT": "PUT",
"DELETE": "VERWIJDEREN",
"HEAD": "HOOFD"
}

View File

@@ -0,0 +1,45 @@
{
"Account ID": "ID da Conta",
"Consumer Key": "Chave do Consumidor",
"Consumer Secret": "Segredo de Consumidor",
"Token ID": "Token ID",
"Token Secret": "Token Secreto",
"Your NetSuite account ID": "ID da sua conta NetSuite",
"Your NetSuite consumer key": "Sua chave de consumidor NetSuite",
"Your NetSuite consumer secret": "Seu segredo de consumidor NetSuite",
"Your NetSuite token ID": "Seu ID do token NetSuite",
"Your NetSuite token secret": "Seu token NetSuite",
"Get Vendor": "Get Vendor",
"Get Customer": "Obter Cliente",
"Run SuiteQL Query": "Executar consulta SuiteQL",
"Custom API Call": "Chamada de API personalizada",
"Gets vendor details from NetSuite.": "Obtém detalhes do fornecedor da NetSuite.",
"Gets customer details from NetSuite.": "Obtém os detalhes do cliente no NetSuite.",
"Run SuiteQL Query on NetSuite.": "Execute a consulta SuiteCRM no NetSuite.",
"Make a custom API call to a specific endpoint": "Faça uma chamada de API personalizada para um ponto de extremidade específico",
"Vendor ID": "ID do fornecedor",
"Customer ID": "ID do cliente",
"Markdown": "Markdown",
"Query": "Requisição",
"Arguments": "Parâmetros",
"Method": "Método",
"Headers": "Cabeçalhos",
"Query Parameters": "Parâmetros da consulta",
"Body": "Conteúdo",
"Response is Binary ?": "A resposta é binária ?",
"No Error on Failure": "Nenhum erro no Failure",
"Timeout (in seconds)": "Tempo limite (em segundos)",
"The ID of the vendor to retrieve.": "O ID do fornecedor a recuperar.",
"The ID of the customer to retrieve.": "O ID do cliente a recuperar.",
"\n- **DO NOT** insert dynamic input directly into the query string. Instead, use :1, :2, :3 and add them in args for parameterized queries\n- Arguments are treated as string and inserted as a [Text Literal](https://docs.oracle.com/en/database/oracle/oracle-database/26/sqlrf/Literals.html)\n": "\n- **NÃO** insira a entrada dinâmica diretamente na string de consulta. Ao invés disso, use :1, :2, :3 e adicione-os em argumentos para consultas parametrizadas\n- Os argumentos são tratados como string e inseridos como um [Text Literal](https://docs.oracle.com/en/database/oracle/oracle-database/26/sqlrf/Literals.html)\n",
"Please use :1, :2, :3 etc. for parameterized queries": "Por favor, use :1, :2, :3, etc. para consultas parametrizadas",
"Arguments to be used in the query": "Argumentos a serem usados na consulta",
"Authorization headers are injected automatically from your connection.": "Os cabeçalhos de autorização são inseridos automaticamente a partir da sua conexão.",
"Enable for files like PDFs, images, etc..": "Habilitar para arquivos como PDFs, imagens, etc..",
"GET": "OBTER",
"POST": "POSTAR",
"PATCH": "COMPRAR",
"PUT": "COLOCAR",
"DELETE": "EXCLUIR",
"HEAD": "CABEÇA"
}

View File

@@ -0,0 +1,21 @@
{
"NetSuite": "NetSuite",
"Account ID": "ID клиента",
"Consumer Key": "Ключ потребителя",
"Consumer Secret": "Секрет потребителя",
"Token ID": "Token ID",
"Token Secret": "Секрет токена",
"Your NetSuite account ID": "ID вашей учетной записи NetSuite",
"Your NetSuite consumer key": "Ваш пользовательский ключ NetSuite",
"Your NetSuite consumer secret": "Пароль клиента NetSuite",
"Your NetSuite token ID": "Идентификатор вашего токена NetSuite",
"Your NetSuite token secret": "Ваш секретный ключ NetSuite",
"Get Vendor": "Get Vendor",
"Get Customer": "Получить клиента",
"Gets vendor details from NetSuite.": "Получает информацию о поставщике от NetSuite.",
"Gets customer details from NetSuite.": "Получает данные о клиенте из NetSuite.",
"Vendor ID": "ID поставщика",
"Customer ID": "ID клиента",
"The ID of the vendor to retrieve.": "Идентификатор поставщика для извлечения.",
"The ID of the customer to retrieve.": "Идентификатор клиента для извлечения."
}

View File

@@ -0,0 +1,45 @@
{
"Account ID": "Account ID",
"Consumer Key": "Consumer Key",
"Consumer Secret": "Consumer Secret",
"Token ID": "Token ID",
"Token Secret": "Token Secret",
"Your NetSuite account ID": "Your NetSuite account ID",
"Your NetSuite consumer key": "Your NetSuite consumer key",
"Your NetSuite consumer secret": "Your NetSuite consumer secret",
"Your NetSuite token ID": "Your NetSuite token ID",
"Your NetSuite token secret": "Your NetSuite token secret",
"Get Vendor": "Get Vendor",
"Get Customer": "Get Customer",
"Run SuiteQL Query": "Run SuiteQL Query",
"Custom API Call": "Custom API Call",
"Gets vendor details from NetSuite.": "Gets vendor details from NetSuite.",
"Gets customer details from NetSuite.": "Gets customer details from NetSuite.",
"Run SuiteQL Query on NetSuite.": "Run SuiteQL Query on NetSuite.",
"Make a custom API call to a specific endpoint": "Make a custom API call to a specific endpoint",
"Vendor ID": "Vendor ID",
"Customer ID": "Customer ID",
"Markdown": "Markdown",
"Query": "Query",
"Arguments": "Arguments",
"Method": "Method",
"Headers": "Headers",
"Query Parameters": "Query Parameters",
"Body": "Body",
"Response is Binary ?": "Response is Binary ?",
"No Error on Failure": "No Error on Failure",
"Timeout (in seconds)": "Timeout (in seconds)",
"The ID of the vendor to retrieve.": "The ID of the vendor to retrieve.",
"The ID of the customer to retrieve.": "The ID of the customer to retrieve.",
"\n- **DO NOT** insert dynamic input directly into the query string. Instead, use :1, :2, :3 and add them in args for parameterized queries\n- Arguments are treated as string and inserted as a [Text Literal](https://docs.oracle.com/en/database/oracle/oracle-database/26/sqlrf/Literals.html)\n": "\n- **DO NOT** insert dynamic input directly into the query string. Instead, use :1, :2, :3 and add them in args for parameterized queries\n- Arguments are treated as string and inserted as a [Text Literal](https://docs.oracle.com/en/database/oracle/oracle-database/26/sqlrf/Literals.html)\n",
"Please use :1, :2, :3 etc. for parameterized queries": "Please use :1, :2, :3 etc. for parameterized queries",
"Arguments to be used in the query": "Arguments to be used in the query",
"Authorization headers are injected automatically from your connection.": "Authorization headers are injected automatically from your connection.",
"Enable for files like PDFs, images, etc..": "Enable for files like PDFs, images, etc..",
"GET": "GET",
"POST": "POST",
"PATCH": "PATCH",
"PUT": "PUT",
"DELETE": "DELETE",
"HEAD": "HEAD"
}

View File

@@ -0,0 +1,21 @@
{
"NetSuite": "NetSuite",
"Account ID": "Account ID",
"Consumer Key": "Consumer Key",
"Consumer Secret": "Consumer Secret",
"Token ID": "Token ID",
"Token Secret": "Token Secret",
"Your NetSuite account ID": "Your NetSuite account ID",
"Your NetSuite consumer key": "Your NetSuite consumer key",
"Your NetSuite consumer secret": "Your NetSuite consumer secret",
"Your NetSuite token ID": "Your NetSuite token ID",
"Your NetSuite token secret": "Your NetSuite token secret",
"Get Vendor": "Get Vendor",
"Get Customer": "Get Customer",
"Gets vendor details from NetSuite.": "Gets vendor details from NetSuite.",
"Gets customer details from NetSuite.": "Gets customer details from NetSuite.",
"Vendor ID": "Vendor ID",
"Customer ID": "Customer ID",
"The ID of the vendor to retrieve.": "The ID of the vendor to retrieve.",
"The ID of the customer to retrieve.": "The ID of the customer to retrieve."
}

View File

@@ -0,0 +1,45 @@
{
"Account ID": "Account ID",
"Consumer Key": "Consumer Key",
"Consumer Secret": "Consumer Secret",
"Token ID": "Token ID",
"Token Secret": "Token Secret",
"Your NetSuite account ID": "Your NetSuite account ID",
"Your NetSuite consumer key": "Your NetSuite consumer key",
"Your NetSuite consumer secret": "Your NetSuite consumer secret",
"Your NetSuite token ID": "Your NetSuite token ID",
"Your NetSuite token secret": "Your NetSuite token secret",
"Get Vendor": "Get Vendor",
"Get Customer": "Get Customer",
"Run SuiteQL Query": "Run SuiteQL Query",
"Custom API Call": "自定义 API 呼叫",
"Gets vendor details from NetSuite.": "Gets vendor details from NetSuite.",
"Gets customer details from NetSuite.": "Gets customer details from NetSuite.",
"Run SuiteQL Query on NetSuite.": "Run SuiteQL Query on NetSuite.",
"Make a custom API call to a specific endpoint": "将一个自定义 API 调用到一个特定的终点",
"Vendor ID": "Vendor ID",
"Customer ID": "Customer ID",
"Markdown": "Markdown",
"Query": "Query",
"Arguments": "Arguments",
"Method": "方法",
"Headers": "信头",
"Query Parameters": "查询参数",
"Body": "正文内容",
"Response is Binary ?": "Response is Binary ?",
"No Error on Failure": "失败时没有错误",
"Timeout (in seconds)": "超时(秒)",
"The ID of the vendor to retrieve.": "The ID of the vendor to retrieve.",
"The ID of the customer to retrieve.": "The ID of the customer to retrieve.",
"\n- **DO NOT** insert dynamic input directly into the query string. Instead, use :1, :2, :3 and add them in args for parameterized queries\n- Arguments are treated as string and inserted as a [Text Literal](https://docs.oracle.com/en/database/oracle/oracle-database/26/sqlrf/Literals.html)\n": "\n- **DO NOT** insert dynamic input directly into the query string. Instead, use :1, :2, :3 and add them in args for parameterized queries\n- Arguments are treated as string and inserted as a [Text Literal](https://docs.oracle.com/en/database/oracle/oracle-database/26/sqlrf/Literals.html)\n",
"Please use :1, :2, :3 etc. for parameterized queries": "Please use :1, :2, :3 etc. for parameterized queries",
"Arguments to be used in the query": "Arguments to be used in the query",
"Authorization headers are injected automatically from your connection.": "授权头自动从您的连接中注入。",
"Enable for files like PDFs, images, etc..": "Enable for files like PDFs, images, etc..",
"GET": "获取",
"POST": "帖子",
"PATCH": "PATCH",
"PUT": "弹出",
"DELETE": "删除",
"HEAD": "黑色"
}

View File

@@ -0,0 +1,87 @@
import {
PieceAuth,
createPiece,
Property,
PiecePropValueSchema,
} from '@activepieces/pieces-framework';
import { getVendor } from './lib/actions/get-vendor';
import { getCustomer } from './lib/actions/get-customer';
import { runSuiteQL } from './lib/actions/run-suiteql';
import { PieceCategory } from '@activepieces/shared';
import { createCustomApiCallAction } from '@activepieces/pieces-common';
import { createOAuthHeader } from './lib/oauth';
export const netsuiteAuth = PieceAuth.CustomAuth({
required: true,
props: {
accountId: Property.ShortText({
displayName: 'Account ID',
required: true,
description: 'Your NetSuite account ID',
}),
consumerKey: Property.ShortText({
displayName: 'Consumer Key',
required: true,
description: 'Your NetSuite consumer key',
}),
consumerSecret: PieceAuth.SecretText({
displayName: 'Consumer Secret',
required: true,
description: 'Your NetSuite consumer secret',
}),
tokenId: Property.ShortText({
displayName: 'Token ID',
required: true,
description: 'Your NetSuite token ID',
}),
tokenSecret: PieceAuth.SecretText({
displayName: 'Token Secret',
required: true,
description: 'Your NetSuite token secret',
}),
},
});
export const netsuite = createPiece({
displayName: 'NetSuite',
logoUrl: 'https://cdn.activepieces.com/pieces/netsuite.png',
categories: [PieceCategory.ACCOUNTING],
auth: netsuiteAuth,
authors: ['geekyme', 'danielpoonwj'],
actions: [
getVendor,
getCustomer,
runSuiteQL,
createCustomApiCallAction({
baseUrl: (auth) => {
if (!auth) {
return '';
}
const authValue = auth.props;
return `https://${authValue.accountId}.suitetalk.api.netsuite.com`;
},
auth: netsuiteAuth,
authMapping: async (auth, propsValue) => {
const authValue = auth.props;
const authHeader = createOAuthHeader(
authValue.accountId,
authValue.consumerKey,
authValue.consumerSecret,
authValue.tokenId,
authValue.tokenSecret,
propsValue['url']['url'],
propsValue['method'],
propsValue['queryParams']
);
return {
Authorization: authHeader,
prefer: 'transient',
Cookie: 'NS_ROUTING_VERSION=LAGGING',
};
},
}),
],
triggers: [],
});

View File

@@ -0,0 +1,48 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import { httpClient, HttpMethod } from '@activepieces/pieces-common';
import { netsuiteAuth } from '../..';
import { createOAuthHeader } from '../oauth';
export const getCustomer = createAction({
name: 'getCustomer',
auth: netsuiteAuth,
displayName: 'Get Customer',
description: 'Gets customer details from NetSuite.',
props: {
customerId: Property.ShortText({
displayName: 'Customer ID',
required: true,
description: 'The ID of the customer to retrieve.',
}),
},
async run(context) {
const { accountId, consumerKey, consumerSecret, tokenId, tokenSecret } =
context.auth.props;
const { customerId } = context.propsValue;
const requestUrl = `https://${accountId}.suitetalk.api.netsuite.com/services/rest/record/v1/customer/${customerId}`;
const httpMethod = HttpMethod.GET;
const authHeader = createOAuthHeader(
accountId,
consumerKey,
consumerSecret,
tokenId,
tokenSecret,
requestUrl,
httpMethod
);
const response = await httpClient.sendRequest({
method: httpMethod,
url: requestUrl,
headers: {
Authorization: authHeader,
prefer: 'transient',
Cookie: 'NS_ROUTING_VERSION=LAGGING',
},
});
return response.body;
},
});

View File

@@ -0,0 +1,48 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import { httpClient, HttpMethod } from '@activepieces/pieces-common';
import { netsuiteAuth } from '../..';
import { createOAuthHeader } from '../oauth';
export const getVendor = createAction({
name: 'getVendor',
auth: netsuiteAuth,
displayName: 'Get Vendor',
description: 'Gets vendor details from NetSuite.',
props: {
vendorId: Property.ShortText({
displayName: 'Vendor ID',
required: true,
description: 'The ID of the vendor to retrieve.',
}),
},
async run(context) {
const { accountId, consumerKey, consumerSecret, tokenId, tokenSecret } =
context.auth.props;
const { vendorId } = context.propsValue;
const requestUrl = `https://${accountId}.suitetalk.api.netsuite.com/services/rest/record/v1/vendor/${vendorId}`;
const httpMethod = HttpMethod.GET;
const authHeader = createOAuthHeader(
accountId,
consumerKey,
consumerSecret,
tokenId,
tokenSecret,
requestUrl,
httpMethod
);
const response = await httpClient.sendRequest({
method: httpMethod,
url: requestUrl,
headers: {
Authorization: authHeader,
prefer: 'transient',
Cookie: 'NS_ROUTING_VERSION=LAGGING',
},
});
return response.body;
},
});

View File

@@ -0,0 +1,105 @@
import { createAction, Property } from '@activepieces/pieces-framework';
import { httpClient, HttpMethod } from '@activepieces/pieces-common';
import { netsuiteAuth } from '../..';
import { createOAuthHeader } from '../oauth';
import { format as formatSQL, ParamItems } from 'sql-formatter';
const PAGE_SIZE = 1000;
const mkdown = `
- **DO NOT** insert dynamic input directly into the query string. Instead, use :1, :2, :3 and add them in args for parameterized queries
- Arguments are treated as string and inserted as a [Text Literal](https://docs.oracle.com/en/database/oracle/oracle-database/26/sqlrf/Literals.html)
`;
export const runSuiteQL = createAction({
name: 'runSuiteQL',
auth: netsuiteAuth,
displayName: 'Run SuiteQL Query',
description: 'Run SuiteQL Query on NetSuite.',
props: {
markdown: Property.MarkDown({
value: mkdown,
}),
query: Property.ShortText({
displayName: 'Query',
description: 'Please use :1, :2, :3 etc. for parameterized queries',
required: true,
}),
args: Property.Array({
displayName: 'Arguments',
description: 'Arguments to be used in the query',
required: false,
}),
},
async run(context) {
const { accountId, consumerKey, consumerSecret, tokenId, tokenSecret } =
context.auth.props;
const query = context.propsValue.query;
const args: string[] = (context.propsValue.args as string[]) || [];
// numbered placeholders: https://github.com/sql-formatter-org/sql-formatter/blob/master/docs/params.md#numbered-placeholders
const formattedArgs = args.reduce((acc, arg, idx) => {
// numbered placeholders are 1 indexed
const argNum = idx + 1;
// use q notation to escape quotes
acc[argNum] = `q'{${arg}}'`;
return acc;
}, {} as ParamItems);
// netsuite uses oracle sql: https://docs.oracle.com/en/cloud/saas/netsuite/ns-online-help/section_156257790831.html
const formattedSQL = formatSQL(query, {
language: 'plsql',
params: formattedArgs,
});
const results = [];
let pageOffset = 0;
let hasMore = true;
const requestUrl = `https://${accountId}.suitetalk.api.netsuite.com/services/rest/query/v1/suiteql`;
const httpMethod = HttpMethod.POST;
// paginate results: https://docs.oracle.com/en/cloud/saas/netsuite/ns-online-help/section_156414087576.html
while (hasMore) {
const queryParams = {
limit: String(PAGE_SIZE),
offset: String(pageOffset),
};
const authHeader = createOAuthHeader(
accountId,
consumerKey,
consumerSecret,
tokenId,
tokenSecret,
requestUrl,
httpMethod,
queryParams
);
const response = await httpClient.sendRequest({
method: httpMethod,
url: requestUrl,
headers: {
Authorization: authHeader,
prefer: 'transient',
Cookie: 'NS_ROUTING_VERSION=LAGGING',
},
body: {
q: formattedSQL,
},
queryParams: queryParams,
});
results.push(...(response.body?.items || []));
hasMore = response.body?.hasMore || false;
pageOffset += PAGE_SIZE;
}
return results;
},
});

View File

@@ -0,0 +1,114 @@
import { createHmac } from 'crypto';
const SIGN_METHOD = 'HMAC-SHA256';
const OAUTH_VERSION = '1.0';
function generateNonce(): string {
const length = 11;
const possible =
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
return Array.from(
{ length },
() => possible[Math.floor(Math.random() * possible.length)]
).join('');
}
function generateSignature(
requestUrl: string,
httpMethod: string,
oauthNonce: string,
timestamp: number,
consumerKey: string,
consumerSecret: string,
tokenId: string,
tokenSecret: string,
queryParams?: Record<string, string | number | boolean>
): string {
// Parse URL to get base URL and existing query params
const parsedUrl = new URL(requestUrl);
const baseUrl = `${parsedUrl.origin}${parsedUrl.pathname}`;
// Collect OAuth parameters
const allParams: Record<string, string> = {
oauth_consumer_key: consumerKey,
oauth_nonce: oauthNonce,
oauth_signature_method: SIGN_METHOD,
oauth_timestamp: timestamp.toString(),
oauth_token: tokenId,
oauth_version: OAUTH_VERSION,
};
// Include query parameters from URL
parsedUrl.searchParams.forEach((value, key) => {
allParams[key] = value;
});
// Include additional query parameters (will override URL params if same key)
if (queryParams) {
Object.entries(queryParams).forEach(([key, value]) => {
allParams[key] = String(value);
});
}
// Sort parameters by key, then by value (OAuth 1.0 spec)
const sortedKeys = Object.keys(allParams).sort();
const paramPairs = sortedKeys.map(
(key) => `${encodeURIComponent(key)}=${encodeURIComponent(allParams[key])}`
);
const paramString = paramPairs.join('&');
const signatureBaseString = [
httpMethod.toUpperCase(),
encodeURIComponent(baseUrl),
encodeURIComponent(paramString),
].join('&');
const signingKey = [
encodeURIComponent(consumerSecret),
encodeURIComponent(tokenSecret),
].join('&');
const hmac = createHmac('sha256', signingKey);
hmac.update(signatureBaseString);
const signature = hmac.digest('base64');
return encodeURIComponent(signature);
}
export function createOAuthHeader(
accountId: string,
consumerKey: string,
consumerSecret: string,
tokenId: string,
tokenSecret: string,
requestUrl: string,
httpMethod: string,
queryParams?: Record<string, string | number | boolean>
): string {
const oauthNonce = generateNonce();
const timestamp = Math.floor(Date.now() / 1000);
const signature = generateSignature(
requestUrl,
httpMethod,
oauthNonce,
timestamp,
consumerKey,
consumerSecret,
tokenId,
tokenSecret,
queryParams
);
const headerParams = [
`realm="${accountId}"`,
`oauth_token="${tokenId}"`,
`oauth_consumer_key="${consumerKey}"`,
`oauth_nonce="${oauthNonce}"`,
`oauth_timestamp="${timestamp}"`,
`oauth_signature_method="${SIGN_METHOD}"`,
`oauth_version="${OAUTH_VERSION}"`,
`oauth_signature="${signature}"`,
].join(',');
return `OAuth ${headerParams}`;
}

View File

@@ -0,0 +1,19 @@
{
"extends": "../../../../tsconfig.base.json",
"compilerOptions": {
"module": "commonjs",
"forceConsistentCasingInFileNames": true,
"strict": true,
"noImplicitOverride": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"noPropertyAccessFromIndexSignature": true
},
"files": [],
"include": [],
"references": [
{
"path": "./tsconfig.lib.json"
}
]
}

View File

@@ -0,0 +1,11 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"module": "commonjs",
"outDir": "../../../../dist/out-tsc",
"declaration": true,
"types": ["node"]
},
"exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"],
"include": ["src/**/*.ts"]
}