mirror of
https://github.com/n8n-io/n8n-nodes-starter.git
synced 2025-10-29 22:32:25 -05:00
principal node created
This commit is contained in:
parent
de3e6aa70b
commit
fc3d0df193
12 changed files with 1266 additions and 577 deletions
|
|
@ -14,7 +14,7 @@ export class S4DSApi implements ICredentialType {
|
||||||
type: 'options',
|
type: 'options',
|
||||||
options: [
|
options: [
|
||||||
{
|
{
|
||||||
name: 'Demo Test',
|
name: 'Demo TEST',
|
||||||
value: 'https://demotest.s4ds.com/demoapi-test',
|
value: 'https://demotest.s4ds.com/demoapi-test',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -22,32 +22,20 @@ export class S4DSApi implements ICredentialType {
|
||||||
value: 'https://demouat.s4ds.com/demoapi-uat',
|
value: 'https://demouat.s4ds.com/demoapi-uat',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Demo Production',
|
name: 'Demo CORE',
|
||||||
value: 'https://demoprod.s4ds.com/demoapi-prod',
|
value: 'https://demo.s4ds.com/demoapi-core',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Cliente 1 Test',
|
name: 'Aquasource TEST',
|
||||||
value: 'https://cliente1test.s4ds.com/cliente1api-test',
|
value: 'https://aquasourcetest.s4ds.com/aquasourceapi-test',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Cliente 1 UAT',
|
name: 'Aquasource UAT',
|
||||||
value: 'https://cliente1uat.s4ds.com/cliente1api-uat',
|
value: 'https://aquasourceuat.s4ds.com/aquasourceapi-uat',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Cliente 1 Production',
|
name: 'Aquasource CORE',
|
||||||
value: 'https://cliente1prod.s4ds.com/cliente1api-prod',
|
value: 'https://aquasource.s4ds.com/aquasourceapi-core',
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Cliente 2 Test',
|
|
||||||
value: 'https://cliente2test.s4ds.com/cliente2api-test',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Cliente 2 UAT',
|
|
||||||
value: 'https://cliente2uat.s4ds.com/cliente2api-uat',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Cliente 2 Production',
|
|
||||||
value: 'https://cliente2prod.s4ds.com/cliente2api-prod',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Custom URL',
|
name: 'Custom URL',
|
||||||
|
|
|
||||||
5
index.js
5
index.js
|
|
@ -1,9 +1,8 @@
|
||||||
const { S4DSAuth } = require('./dist/nodes/S4DSAuth/S4DSAuth.node.js');
|
const { S4DSMain } = require('./dist/nodes/S4DSMain/S4DSMain.node.js');
|
||||||
const { S4DSExample } = require('./dist/nodes/S4DSExample/S4DSExample.node.js');
|
|
||||||
|
|
||||||
const { S4DSApi } = require('./dist/credentials/S4DSApi.credentials.js');
|
const { S4DSApi } = require('./dist/credentials/S4DSApi.credentials.js');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
nodes: [S4DSAuth, S4DSExample],
|
nodes: [S4DSMain],
|
||||||
credentials: [S4DSApi],
|
credentials: [S4DSApi],
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,109 +0,0 @@
|
||||||
import { INodeProperties } from 'n8n-workflow';
|
|
||||||
|
|
||||||
// Operaciones disponibles para el recurso de autenticación
|
|
||||||
export const authOperations: INodeProperties[] = [
|
|
||||||
{
|
|
||||||
displayName: 'Operation',
|
|
||||||
name: 'operation',
|
|
||||||
type: 'options',
|
|
||||||
noDataExpression: true,
|
|
||||||
displayOptions: {
|
|
||||||
show: {
|
|
||||||
resource: ['auth'],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
name: 'Generate Token',
|
|
||||||
value: 'generateToken',
|
|
||||||
description: 'Generate authentication token using credentials',
|
|
||||||
action: 'Generate authentication token',
|
|
||||||
routing: {
|
|
||||||
request: {
|
|
||||||
method: 'POST',
|
|
||||||
url: '/login/generateToken',
|
|
||||||
body: {
|
|
||||||
username: '={{$credentials.s4dsApi.username}}',
|
|
||||||
password: '={{$credentials.s4dsApi.password}}',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Validate Token',
|
|
||||||
value: 'validateToken',
|
|
||||||
description: 'Validate existing token',
|
|
||||||
action: 'Validate existing token',
|
|
||||||
routing: {
|
|
||||||
request: {
|
|
||||||
method: 'GET',
|
|
||||||
url: '/auth/validate',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
default: 'generateToken',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
// Campos para la operación de generación de token
|
|
||||||
const generateTokenOperation: INodeProperties[] = [
|
|
||||||
{
|
|
||||||
displayName: 'Additional Fields',
|
|
||||||
name: 'additionalFields',
|
|
||||||
type: 'collection',
|
|
||||||
placeholder: 'Add Field',
|
|
||||||
default: {},
|
|
||||||
displayOptions: {
|
|
||||||
show: {
|
|
||||||
resource: ['auth'],
|
|
||||||
operation: ['generateToken'],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
displayName: 'Store Token in Context',
|
|
||||||
name: 'storeInContext',
|
|
||||||
type: 'boolean',
|
|
||||||
default: true,
|
|
||||||
description: 'Whether to store the token in the workflow context for other nodes to use',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: 'Token Context Key',
|
|
||||||
name: 'tokenContextKey',
|
|
||||||
type: 'string',
|
|
||||||
default: 's4ds_token',
|
|
||||||
description: 'Key to store the token in the workflow context',
|
|
||||||
displayOptions: {
|
|
||||||
show: {
|
|
||||||
storeInContext: [true],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
// Campos para la operación de validación de token
|
|
||||||
const validateTokenOperation: INodeProperties[] = [
|
|
||||||
{
|
|
||||||
displayName: 'Token',
|
|
||||||
name: 'token',
|
|
||||||
type: 'string',
|
|
||||||
required: true,
|
|
||||||
default: '',
|
|
||||||
displayOptions: {
|
|
||||||
show: {
|
|
||||||
resource: ['auth'],
|
|
||||||
operation: ['validateToken'],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
description: 'Token to validate',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
// Exportar todos los campos
|
|
||||||
export const authFields: INodeProperties[] = [
|
|
||||||
...generateTokenOperation,
|
|
||||||
...validateTokenOperation,
|
|
||||||
];
|
|
||||||
|
|
@ -1,168 +0,0 @@
|
||||||
import { INodeType, INodeTypeDescription, NodeConnectionType, IExecuteFunctions, INodeExecutionData } from 'n8n-workflow';
|
|
||||||
import { authOperations, authFields } from './AuthDescription';
|
|
||||||
|
|
||||||
export class S4DSAuth implements INodeType {
|
|
||||||
description: INodeTypeDescription = {
|
|
||||||
displayName: 'S4DS Authentication',
|
|
||||||
name: 's4dsAuth',
|
|
||||||
icon: { light: 'file:s4ds.svg', dark: 'file:s4ds.svg' },
|
|
||||||
group: ['transform'],
|
|
||||||
version: 1,
|
|
||||||
subtitle: '={{$parameter["operation"]}}',
|
|
||||||
description: 'Authenticate with S4DS API and manage tokens',
|
|
||||||
defaults: {
|
|
||||||
name: 'S4DS Auth',
|
|
||||||
},
|
|
||||||
inputs: [NodeConnectionType.Main],
|
|
||||||
outputs: [NodeConnectionType.Main],
|
|
||||||
usableAsTool: true,
|
|
||||||
credentials: [
|
|
||||||
{
|
|
||||||
name: 's4dsApi',
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
requestDefaults: {
|
|
||||||
baseURL: '={{$credentials.s4dsApi.baseUrl === "custom" ? $credentials.s4dsApi.customBaseUrl : $credentials.s4dsApi.baseUrl}}',
|
|
||||||
url: '',
|
|
||||||
headers: {
|
|
||||||
Accept: 'application/json',
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
properties: [
|
|
||||||
{
|
|
||||||
displayName: 'Resource',
|
|
||||||
name: 'resource',
|
|
||||||
type: 'options',
|
|
||||||
noDataExpression: true,
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
name: 'Authentication',
|
|
||||||
value: 'auth',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
default: 'auth',
|
|
||||||
},
|
|
||||||
...authOperations,
|
|
||||||
...authFields,
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
|
||||||
const items = this.getInputData();
|
|
||||||
const returnData: INodeExecutionData[] = [];
|
|
||||||
|
|
||||||
for (let i = 0; i < items.length; i++) {
|
|
||||||
try {
|
|
||||||
const resource = this.getNodeParameter('resource', i) as string;
|
|
||||||
const operation = this.getNodeParameter('operation', i) as string;
|
|
||||||
|
|
||||||
if (resource === 'auth') {
|
|
||||||
if (operation === 'generateToken') {
|
|
||||||
// Obtener credenciales
|
|
||||||
const credentials = await this.getCredentials('s4dsApi');
|
|
||||||
|
|
||||||
// Determinar la URL base
|
|
||||||
let baseUrl = credentials.baseUrl;
|
|
||||||
if (baseUrl === 'custom') {
|
|
||||||
baseUrl = credentials.customBaseUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!baseUrl) {
|
|
||||||
throw new Error('Base URL is required. Please configure the S4DS API credentials.');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ejecutar la petición HTTP para generar token
|
|
||||||
const response = await this.helpers.httpRequest({
|
|
||||||
method: 'POST',
|
|
||||||
url: `${baseUrl}/login/generateToken`,
|
|
||||||
headers: {
|
|
||||||
'Accept': 'application/json',
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
body: {
|
|
||||||
username: credentials.username,
|
|
||||||
password: credentials.password,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// Almacenar token en contexto
|
|
||||||
const additionalFields = this.getNodeParameter('additionalFields', i) as {
|
|
||||||
storeInContext?: boolean;
|
|
||||||
tokenContextKey?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
const contextKey = additionalFields.tokenContextKey || 's4ds_token';
|
|
||||||
const tokenData = {
|
|
||||||
token: response.token,
|
|
||||||
token_type: response.token_type,
|
|
||||||
expires_in: response.expires_in,
|
|
||||||
expires_at: new Date(Date.now() + response.expires_in * 1000).toISOString(),
|
|
||||||
authorization_header: `${response.token_type} ${response.token}`,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Almacenar en contexto del workflow
|
|
||||||
this.getWorkflowStaticData('global')[contextKey] = tokenData;
|
|
||||||
|
|
||||||
// Retornar respuesta sin el token por seguridad
|
|
||||||
const secureResponse = {
|
|
||||||
success: true,
|
|
||||||
token_type: response.token_type,
|
|
||||||
expires_in: response.expires_in,
|
|
||||||
expires_at: tokenData.expires_at,
|
|
||||||
message: 'Token generated successfully and stored in workflow context',
|
|
||||||
context_key: contextKey,
|
|
||||||
};
|
|
||||||
|
|
||||||
returnData.push({
|
|
||||||
json: secureResponse,
|
|
||||||
});
|
|
||||||
} else if (operation === 'validateToken') {
|
|
||||||
const token = this.getNodeParameter('token', i) as string;
|
|
||||||
|
|
||||||
// Obtener credenciales
|
|
||||||
const credentials = await this.getCredentials('s4dsApi');
|
|
||||||
|
|
||||||
// Determinar la URL base
|
|
||||||
let baseUrl = credentials.baseUrl;
|
|
||||||
if (baseUrl === 'custom') {
|
|
||||||
baseUrl = credentials.customBaseUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!baseUrl) {
|
|
||||||
throw new Error('Base URL is required. Please configure the S4DS API credentials.');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ejecutar validación de token
|
|
||||||
const response = await this.helpers.httpRequest({
|
|
||||||
method: 'GET',
|
|
||||||
url: `${baseUrl}/auth/validate`,
|
|
||||||
headers: {
|
|
||||||
'Accept': 'application/json',
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
'Authorization': `Bearer ${token}`,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
returnData.push({
|
|
||||||
json: response,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
if (this.continueOnFail()) {
|
|
||||||
returnData.push({
|
|
||||||
json: {
|
|
||||||
error: error.message,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return [returnData];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,80 +0,0 @@
|
||||||
import { IExecuteFunctions } from 'n8n-workflow';
|
|
||||||
|
|
||||||
export interface S4DSTokenData {
|
|
||||||
token: string;
|
|
||||||
token_type: string;
|
|
||||||
expires_in: number;
|
|
||||||
expires_at: string;
|
|
||||||
authorization_header: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class S4DSHelper {
|
|
||||||
/**
|
|
||||||
* Obtiene el token de autorización del contexto del workflow
|
|
||||||
* @param this - Contexto de ejecución del nodo
|
|
||||||
* @param contextKey - Clave del contexto donde está almacenado el token (por defecto: 's4ds_token')
|
|
||||||
* @returns Objeto con datos del token incluyendo el header de autorización
|
|
||||||
*/
|
|
||||||
static getTokenFromContext(this: IExecuteFunctions, contextKey: string = 's4ds_token'): S4DSTokenData {
|
|
||||||
const tokenData = this.getWorkflowStaticData('global')[contextKey] as S4DSTokenData;
|
|
||||||
|
|
||||||
if (!tokenData || !tokenData.token) {
|
|
||||||
throw new Error(`No S4DS token found in context with key: ${contextKey}. Please run S4DS Auth node first.`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verificar si el token ha expirado
|
|
||||||
if (tokenData.expires_at && new Date(tokenData.expires_at) <= new Date()) {
|
|
||||||
throw new Error('S4DS token has expired. Please regenerate token using S4DS Auth node.');
|
|
||||||
}
|
|
||||||
|
|
||||||
return tokenData;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Obtiene el header de autorización listo para usar
|
|
||||||
* @param this - Contexto de ejecución del nodo
|
|
||||||
* @param contextKey - Clave del contexto donde está almacenado el token (por defecto: 's4ds_token')
|
|
||||||
* @returns Header de autorización completo (ej: "Bearer 8a6c71b3-fa62-434d-8b38-907de24c3176")
|
|
||||||
*/
|
|
||||||
static getAuthorizationHeader(this: IExecuteFunctions, contextKey: string = 's4ds_token'): string {
|
|
||||||
const tokenData = S4DSHelper.getTokenFromContext.call(this, contextKey);
|
|
||||||
return tokenData.authorization_header;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Obtiene headers HTTP completos con autorización
|
|
||||||
* @param this - Contexto de ejecución del nodo
|
|
||||||
* @param contextKey - Clave del contexto donde está almacenado el token (por defecto: 's4ds_token')
|
|
||||||
* @param additionalHeaders - Headers adicionales opcionales
|
|
||||||
* @returns Objeto con headers HTTP completos
|
|
||||||
*/
|
|
||||||
static getHeadersWithAuth(
|
|
||||||
this: IExecuteFunctions,
|
|
||||||
contextKey: string = 's4ds_token',
|
|
||||||
additionalHeaders: Record<string, string> = {}
|
|
||||||
): Record<string, string> {
|
|
||||||
const authHeader = S4DSHelper.getAuthorizationHeader.call(this, contextKey);
|
|
||||||
|
|
||||||
return {
|
|
||||||
Authorization: authHeader,
|
|
||||||
Accept: 'application/json',
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
...additionalHeaders,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Verifica si existe un token válido en el contexto
|
|
||||||
* @param this - Contexto de ejecución del nodo
|
|
||||||
* @param contextKey - Clave del contexto donde está almacenado el token (por defecto: 's4ds_token')
|
|
||||||
* @returns true si existe un token válido, false en caso contrario
|
|
||||||
*/
|
|
||||||
static hasValidToken(this: IExecuteFunctions, contextKey: string = 's4ds_token'): boolean {
|
|
||||||
try {
|
|
||||||
S4DSHelper.getTokenFromContext.call(this, contextKey);
|
|
||||||
return true;
|
|
||||||
} catch {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,194 +0,0 @@
|
||||||
import { INodeType, INodeTypeDescription, NodeConnectionType, IExecuteFunctions, INodeExecutionData } from 'n8n-workflow';
|
|
||||||
import { S4DSHelper } from '../S4DSAuth/S4DSHelper';
|
|
||||||
|
|
||||||
export class S4DSExample implements INodeType {
|
|
||||||
description: INodeTypeDescription = {
|
|
||||||
displayName: 'S4DS Example',
|
|
||||||
name: 's4dsExample',
|
|
||||||
icon: { light: 'file:s4ds.svg', dark: 'file:s4ds.svg' },
|
|
||||||
group: ['transform'],
|
|
||||||
version: 1,
|
|
||||||
subtitle: '={{$parameter["operation"]}}',
|
|
||||||
description: 'Example node that uses S4DS authentication token',
|
|
||||||
defaults: {
|
|
||||||
name: 'S4DS Example',
|
|
||||||
},
|
|
||||||
inputs: [NodeConnectionType.Main],
|
|
||||||
outputs: [NodeConnectionType.Main],
|
|
||||||
credentials: [
|
|
||||||
{
|
|
||||||
name: 's4dsApi',
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
requestDefaults: {
|
|
||||||
baseURL: '={{$credentials.s4dsApi.baseUrl}}',
|
|
||||||
url: '',
|
|
||||||
headers: {
|
|
||||||
Accept: 'application/json',
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
properties: [
|
|
||||||
{
|
|
||||||
displayName: 'Operation',
|
|
||||||
name: 'operation',
|
|
||||||
type: 'options',
|
|
||||||
noDataExpression: true,
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
name: 'Get Product Count',
|
|
||||||
value: 'getProductCount',
|
|
||||||
description: 'Get the total count of products',
|
|
||||||
action: 'Get product count',
|
|
||||||
routing: {
|
|
||||||
request: {
|
|
||||||
method: 'GET',
|
|
||||||
url: '/product/count',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
default: 'getProductCount',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: 'Token Source',
|
|
||||||
name: 'tokenSource',
|
|
||||||
type: 'options',
|
|
||||||
required: true,
|
|
||||||
default: 'context',
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
name: 'From Context',
|
|
||||||
value: 'context',
|
|
||||||
description: 'Use token stored in workflow context',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Custom Token',
|
|
||||||
value: 'custom',
|
|
||||||
description: 'Provide custom token',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: 'Context Token Key',
|
|
||||||
name: 'contextTokenKey',
|
|
||||||
type: 'string',
|
|
||||||
default: 's4ds_token',
|
|
||||||
required: true,
|
|
||||||
displayOptions: {
|
|
||||||
show: {
|
|
||||||
tokenSource: ['context'],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
description: 'Key used to store token in workflow context',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: 'Custom Token',
|
|
||||||
name: 'customToken',
|
|
||||||
type: 'string',
|
|
||||||
default: '',
|
|
||||||
required: true,
|
|
||||||
displayOptions: {
|
|
||||||
show: {
|
|
||||||
tokenSource: ['custom'],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
description: 'Custom authentication token',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
|
||||||
const items = this.getInputData();
|
|
||||||
const returnData: INodeExecutionData[] = [];
|
|
||||||
|
|
||||||
for (let i = 0; i < items.length; i++) {
|
|
||||||
try {
|
|
||||||
const operation = this.getNodeParameter('operation', i) as string;
|
|
||||||
const tokenSource = this.getNodeParameter('tokenSource', i) as string;
|
|
||||||
|
|
||||||
// Obtener headers según la fuente
|
|
||||||
let headers: Record<string, string>;
|
|
||||||
|
|
||||||
if (tokenSource === 'context') {
|
|
||||||
const contextKey = this.getNodeParameter('contextTokenKey', i) as string;
|
|
||||||
|
|
||||||
// Debug: Verificar qué hay en el contexto
|
|
||||||
const contextData = this.getWorkflowStaticData('global');
|
|
||||||
const availableKeys = Object.keys(contextData);
|
|
||||||
|
|
||||||
// Usar el helper para obtener headers con autorización
|
|
||||||
try {
|
|
||||||
headers = S4DSHelper.getHeadersWithAuth.call(this, contextKey);
|
|
||||||
} catch (error) {
|
|
||||||
// Si falla, retornar información de debug
|
|
||||||
returnData.push({
|
|
||||||
json: {
|
|
||||||
error: error.message,
|
|
||||||
debug_info: {
|
|
||||||
context_key_requested: contextKey,
|
|
||||||
available_context_keys: availableKeys,
|
|
||||||
context_data: contextData,
|
|
||||||
message: 'Please ensure S4DS Auth node runs before this node and generates a token successfully.'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const customToken = this.getNodeParameter('customToken', i) as string;
|
|
||||||
headers = {
|
|
||||||
Authorization: customToken,
|
|
||||||
Accept: 'application/json',
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ejecutar la operación
|
|
||||||
let response;
|
|
||||||
if (operation === 'getProductCount') {
|
|
||||||
// Obtener credenciales para construir la URL base
|
|
||||||
const credentials = await this.getCredentials('s4dsApi');
|
|
||||||
|
|
||||||
// Determinar la URL base
|
|
||||||
let baseUrl = credentials.baseUrl;
|
|
||||||
if (baseUrl === 'custom') {
|
|
||||||
baseUrl = credentials.customBaseUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!baseUrl) {
|
|
||||||
throw new Error('Base URL is required. Please configure the S4DS API credentials.');
|
|
||||||
}
|
|
||||||
|
|
||||||
response = await this.helpers.httpRequest({
|
|
||||||
method: 'GET',
|
|
||||||
url: `${baseUrl}/product/count`,
|
|
||||||
headers,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
returnData.push({
|
|
||||||
json: {
|
|
||||||
...response,
|
|
||||||
_token_info: {
|
|
||||||
source: tokenSource,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
if (this.continueOnFail()) {
|
|
||||||
returnData.push({
|
|
||||||
json: {
|
|
||||||
error: error.message,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return [returnData];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
369
nodes/S4DSMain/ApiHelper.ts
Normal file
369
nodes/S4DSMain/ApiHelper.ts
Normal file
|
|
@ -0,0 +1,369 @@
|
||||||
|
import { INodeProperties, NodePropertyTypes } from 'n8n-workflow';
|
||||||
|
import * as apiDefinitions from './api-definitions.json';
|
||||||
|
import * as dtoDefinitions from './dto-definitions.json';
|
||||||
|
|
||||||
|
export interface ApiDefinition {
|
||||||
|
method: string;
|
||||||
|
endpoint: string;
|
||||||
|
description: string;
|
||||||
|
parameters: ApiParameter[];
|
||||||
|
requiresAuth: boolean;
|
||||||
|
requestBody?: {
|
||||||
|
schema: string;
|
||||||
|
required: boolean;
|
||||||
|
};
|
||||||
|
response: {
|
||||||
|
type: string;
|
||||||
|
properties: Record<string, string>;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ApiParameter {
|
||||||
|
name: string;
|
||||||
|
type: string;
|
||||||
|
required: boolean;
|
||||||
|
description: string;
|
||||||
|
in: 'query' | 'body' | 'path';
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DtoProperty {
|
||||||
|
type: string;
|
||||||
|
description: string;
|
||||||
|
required: boolean;
|
||||||
|
schema?: string;
|
||||||
|
items?: {
|
||||||
|
type: string;
|
||||||
|
schema?: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DtoDefinition {
|
||||||
|
type: string;
|
||||||
|
properties: Record<string, DtoProperty>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ApiHelper {
|
||||||
|
static getApiDefinitions(): Record<string, Record<string, ApiDefinition>> {
|
||||||
|
return apiDefinitions as any;
|
||||||
|
}
|
||||||
|
|
||||||
|
static getResources(): INodeProperties[] {
|
||||||
|
const definitions = this.getApiDefinitions();
|
||||||
|
const resources = Object.keys(definitions).map(resource => ({
|
||||||
|
name: this.capitalizeFirst(resource),
|
||||||
|
value: resource,
|
||||||
|
description: `${resource} operations`,
|
||||||
|
}));
|
||||||
|
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
displayName: 'Resource',
|
||||||
|
name: 'resource',
|
||||||
|
type: 'options',
|
||||||
|
noDataExpression: true,
|
||||||
|
options: resources,
|
||||||
|
default: resources[0]?.value || 'authentication',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
static getOperations(): INodeProperties[] {
|
||||||
|
const definitions = this.getApiDefinitions();
|
||||||
|
const operations: INodeProperties[] = [];
|
||||||
|
|
||||||
|
// Crear operaciones para cada recurso
|
||||||
|
Object.keys(definitions).forEach(resource => {
|
||||||
|
const resourceOperations = Object.keys(definitions[resource]).map(operation => ({
|
||||||
|
name: this.formatOperationName(operation),
|
||||||
|
value: operation,
|
||||||
|
description: definitions[resource][operation].description,
|
||||||
|
action: this.formatOperationName(operation),
|
||||||
|
}));
|
||||||
|
|
||||||
|
operations.push({
|
||||||
|
displayName: 'Operation',
|
||||||
|
name: 'operation',
|
||||||
|
type: 'options',
|
||||||
|
noDataExpression: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [resource],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: resourceOperations,
|
||||||
|
default: resourceOperations[0]?.value || '',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return operations;
|
||||||
|
}
|
||||||
|
|
||||||
|
static getFields(): INodeProperties[] {
|
||||||
|
const definitions = this.getApiDefinitions();
|
||||||
|
const fields: INodeProperties[] = [];
|
||||||
|
|
||||||
|
// Ya no agregamos campos de autenticación
|
||||||
|
|
||||||
|
Object.keys(definitions).forEach(resource => {
|
||||||
|
Object.keys(definitions[resource]).forEach(operation => {
|
||||||
|
const apiDef = definitions[resource][operation];
|
||||||
|
|
||||||
|
// Campos específicos de la operación
|
||||||
|
if (apiDef.parameters && apiDef.parameters.length > 0) {
|
||||||
|
fields.push(...this.getOperationFields(resource, operation, apiDef.parameters));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Campos para request body con DTOs
|
||||||
|
if (apiDef.requestBody && apiDef.requestBody.schema) {
|
||||||
|
fields.push(...this.getRequestBodyFields(resource, operation, apiDef.requestBody.schema));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static getOperationFields(resource: string, operation: string, parameters: ApiParameter[]): INodeProperties[] {
|
||||||
|
const fields: INodeProperties[] = [];
|
||||||
|
|
||||||
|
// Agrupar parámetros por tipo
|
||||||
|
const queryParams = parameters.filter(p => p.in === 'query');
|
||||||
|
const bodyParams = parameters.filter(p => p.in === 'body');
|
||||||
|
|
||||||
|
// Campos para parámetros de query
|
||||||
|
if (queryParams.length > 0) {
|
||||||
|
fields.push({
|
||||||
|
displayName: 'Query Parameters',
|
||||||
|
name: 'queryParameters',
|
||||||
|
type: 'collection',
|
||||||
|
placeholder: 'Add Query Parameter',
|
||||||
|
default: {},
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [resource],
|
||||||
|
operation: [operation],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: queryParams.map(param => ({
|
||||||
|
displayName: this.capitalizeFirst(param.name),
|
||||||
|
name: param.name,
|
||||||
|
type: this.mapParameterType(param.type),
|
||||||
|
default: '',
|
||||||
|
required: param.required,
|
||||||
|
description: param.description,
|
||||||
|
})),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Campos para parámetros de body
|
||||||
|
if (bodyParams.length > 0) {
|
||||||
|
fields.push({
|
||||||
|
displayName: 'Body Parameters',
|
||||||
|
name: 'bodyParameters',
|
||||||
|
type: 'collection',
|
||||||
|
placeholder: 'Add Body Parameter',
|
||||||
|
default: {},
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [resource],
|
||||||
|
operation: [operation],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: bodyParams.map(param => ({
|
||||||
|
displayName: this.capitalizeFirst(param.name),
|
||||||
|
name: param.name,
|
||||||
|
type: this.mapParameterType(param.type),
|
||||||
|
default: '',
|
||||||
|
required: param.required,
|
||||||
|
description: param.description,
|
||||||
|
})),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static getRequestBodyFields(resource: string, operation: string, dtoSchema: string): INodeProperties[] {
|
||||||
|
const example = this.generateDtoExample(dtoSchema);
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
displayName: 'Request Body (JSON)',
|
||||||
|
name: 'requestBody',
|
||||||
|
type: 'json',
|
||||||
|
default: JSON.stringify(example, null, 2),
|
||||||
|
required: false,
|
||||||
|
description: 'Pega aquí el JSON completo según el esquema del DTO.',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [resource],
|
||||||
|
operation: [operation],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper para generar un ejemplo de JSON para un DTO
|
||||||
|
private static generateDtoExample(dtoName: string): any {
|
||||||
|
const dtoDef = this.getDtoDefinition(dtoName);
|
||||||
|
if (!dtoDef) return {};
|
||||||
|
const result: any = {};
|
||||||
|
Object.keys(dtoDef.properties).forEach(propName => {
|
||||||
|
const prop = dtoDef.properties[propName];
|
||||||
|
if (prop.type === 'object' && prop.schema) {
|
||||||
|
result[propName] = this.generateDtoExample(prop.schema);
|
||||||
|
} else if (prop.type === 'object') {
|
||||||
|
result[propName] = {};
|
||||||
|
} else if (prop.type === 'array' && prop.items?.schema) {
|
||||||
|
result[propName] = [this.generateDtoExample(prop.items.schema)];
|
||||||
|
} else if (prop.type === 'array') {
|
||||||
|
result[propName] = [];
|
||||||
|
} else if (prop.type === 'boolean') {
|
||||||
|
result[propName] = false;
|
||||||
|
} else if (prop.type === 'number' || prop.type === 'integer') {
|
||||||
|
result[propName] = 0;
|
||||||
|
} else {
|
||||||
|
result[propName] = '';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static mapParameterType(apiType: string): NodePropertyTypes {
|
||||||
|
switch (apiType.toLowerCase()) {
|
||||||
|
case 'string':
|
||||||
|
return 'string';
|
||||||
|
case 'number':
|
||||||
|
case 'integer':
|
||||||
|
return 'number';
|
||||||
|
case 'boolean':
|
||||||
|
return 'boolean';
|
||||||
|
case 'array':
|
||||||
|
return 'fixedCollection';
|
||||||
|
case 'object':
|
||||||
|
return 'json';
|
||||||
|
default:
|
||||||
|
return 'string';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static capitalizeFirst(str: string): string {
|
||||||
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static formatOperationName(operation: string): string {
|
||||||
|
// Convertir camelCase a palabras separadas
|
||||||
|
return operation
|
||||||
|
.replace(/([A-Z])/g, ' $1')
|
||||||
|
.replace(/^./, str => str.toUpperCase())
|
||||||
|
.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
static getApiDefinition(resource: string, operation: string): ApiDefinition | null {
|
||||||
|
const definitions = this.getApiDefinitions();
|
||||||
|
return definitions[resource]?.[operation] || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static getDtoDefinitions(): Record<string, DtoDefinition> {
|
||||||
|
return dtoDefinitions as any;
|
||||||
|
}
|
||||||
|
|
||||||
|
static getDtoDefinition(dtoName: string): DtoDefinition | null {
|
||||||
|
const definitions = this.getDtoDefinitions();
|
||||||
|
return definitions[dtoName] || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static generateDtoFields(dtoName: string, prefix: string = ''): INodeProperties[] {
|
||||||
|
const dtoDef = this.getDtoDefinition(dtoName);
|
||||||
|
if (!dtoDef) return [];
|
||||||
|
|
||||||
|
const fields: INodeProperties[] = [];
|
||||||
|
|
||||||
|
Object.keys(dtoDef.properties).forEach(propName => {
|
||||||
|
const prop = dtoDef.properties[propName];
|
||||||
|
const fieldName = prefix ? `${prefix}.${propName}` : propName;
|
||||||
|
const displayName = this.capitalizeFirst(propName);
|
||||||
|
|
||||||
|
if (prop.type === 'object' && prop.schema) {
|
||||||
|
// Campo de objeto anidado
|
||||||
|
fields.push({
|
||||||
|
displayName: `${displayName} (${prop.schema})`,
|
||||||
|
name: fieldName,
|
||||||
|
type: 'collection',
|
||||||
|
placeholder: `Add ${displayName}`,
|
||||||
|
default: {},
|
||||||
|
required: prop.required,
|
||||||
|
description: prop.description,
|
||||||
|
options: this.generateDtoFields(prop.schema, fieldName),
|
||||||
|
});
|
||||||
|
} else if (prop.type === 'array' && prop.items?.schema) {
|
||||||
|
// Campo de array de objetos
|
||||||
|
fields.push({
|
||||||
|
displayName: `${displayName} (Array of ${prop.items.schema})`,
|
||||||
|
name: fieldName,
|
||||||
|
type: 'fixedCollection',
|
||||||
|
typeOptions: {
|
||||||
|
multipleValues: true,
|
||||||
|
},
|
||||||
|
placeholder: `Add ${displayName} Item`,
|
||||||
|
default: {},
|
||||||
|
required: prop.required,
|
||||||
|
description: prop.description,
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
displayName: prop.items.schema,
|
||||||
|
name: 'item',
|
||||||
|
values: this.generateDtoFields(prop.items.schema, `${fieldName}.item`),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Campo simple
|
||||||
|
fields.push({
|
||||||
|
displayName,
|
||||||
|
name: fieldName,
|
||||||
|
type: this.mapParameterType(prop.type),
|
||||||
|
default: '',
|
||||||
|
required: prop.required,
|
||||||
|
description: prop.description,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
static buildDtoObject(data: Record<string, any>, dtoSchema: string): any {
|
||||||
|
const dtoDef = this.getDtoDefinition(dtoSchema);
|
||||||
|
if (!dtoDef) return data;
|
||||||
|
|
||||||
|
const result: any = {};
|
||||||
|
|
||||||
|
Object.keys(dtoDef.properties).forEach(propName => {
|
||||||
|
const prop = dtoDef.properties[propName];
|
||||||
|
const value = data[propName];
|
||||||
|
|
||||||
|
if (value !== undefined && value !== '') {
|
||||||
|
if (prop.type === 'object' && prop.schema) {
|
||||||
|
// Objeto anidado
|
||||||
|
result[propName] = this.buildDtoObject(value, prop.schema);
|
||||||
|
} else if (prop.type === 'array' && prop.items?.schema) {
|
||||||
|
// Array de objetos
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
result[propName] = value.map(item => this.buildDtoObject(item, prop.items!.schema!));
|
||||||
|
} else if (value && typeof value === 'object') {
|
||||||
|
// Para fixedCollection de n8n
|
||||||
|
result[propName] = Object.values(value).map((item: any) =>
|
||||||
|
this.buildDtoObject(item, prop.items!.schema!)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Campo simple
|
||||||
|
result[propName] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
196
nodes/S4DSMain/S4DSMain.node.ts
Normal file
196
nodes/S4DSMain/S4DSMain.node.ts
Normal file
|
|
@ -0,0 +1,196 @@
|
||||||
|
import { INodeType, INodeTypeDescription, NodeConnectionType, IExecuteFunctions, INodeExecutionData, IHttpRequestMethods } from 'n8n-workflow';
|
||||||
|
import { ApiHelper } from './ApiHelper';
|
||||||
|
|
||||||
|
export class S4DSMain implements INodeType {
|
||||||
|
description: INodeTypeDescription = {
|
||||||
|
displayName: 'S4DS',
|
||||||
|
name: 's4ds',
|
||||||
|
icon: { light: 'file:logo_generic.png', dark: 'file:logo_generic.png' },
|
||||||
|
group: ['transform'],
|
||||||
|
version: 1,
|
||||||
|
subtitle: '={{$parameter["resource"]}} - {{$parameter["operation"]}}',
|
||||||
|
description: 'S4DS API operations including authentication and product management',
|
||||||
|
defaults: {
|
||||||
|
name: 'S4DS',
|
||||||
|
},
|
||||||
|
inputs: [NodeConnectionType.Main],
|
||||||
|
outputs: [NodeConnectionType.Main],
|
||||||
|
credentials: [
|
||||||
|
{
|
||||||
|
name: 's4dsApi',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
requestDefaults: {
|
||||||
|
baseURL: '={{$credentials.s4dsApi.baseUrl === "custom" ? $credentials.s4dsApi.customBaseUrl : $credentials.s4dsApi.baseUrl}}',
|
||||||
|
url: '',
|
||||||
|
headers: {
|
||||||
|
Accept: 'application/json',
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
properties: [
|
||||||
|
...ApiHelper.getResources(),
|
||||||
|
...ApiHelper.getOperations(),
|
||||||
|
...ApiHelper.getFields(),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
||||||
|
const items = this.getInputData();
|
||||||
|
const returnData: INodeExecutionData[] = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < items.length; i++) {
|
||||||
|
try {
|
||||||
|
const resource = this.getNodeParameter('resource', i) as string;
|
||||||
|
const operation = this.getNodeParameter('operation', i) as string;
|
||||||
|
|
||||||
|
// Obtener definición de la API
|
||||||
|
const apiDefinition = ApiHelper.getApiDefinition(resource, operation);
|
||||||
|
if (!apiDefinition) {
|
||||||
|
throw new Error(`API definition not found for resource: ${resource}, operation: ${operation}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Obtener credenciales
|
||||||
|
const credentials = await this.getCredentials('s4dsApi');
|
||||||
|
|
||||||
|
// Determinar la URL base
|
||||||
|
let baseUrl = credentials.baseUrl;
|
||||||
|
if (baseUrl === 'custom') {
|
||||||
|
baseUrl = credentials.customBaseUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!baseUrl) {
|
||||||
|
throw new Error('Base URL is required. Please configure the S4DS API credentials.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Preparar headers
|
||||||
|
let headers: Record<string, string> = {
|
||||||
|
Accept: 'application/json',
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
};
|
||||||
|
|
||||||
|
// Agregar autenticación si es requerida
|
||||||
|
if (apiDefinition.requiresAuth) {
|
||||||
|
// Siempre usar la key por defecto 's4ds_token'
|
||||||
|
const contextKey = 's4ds_token';
|
||||||
|
const contextData = this.getWorkflowStaticData('global');
|
||||||
|
const tokenData = contextData[contextKey] as any;
|
||||||
|
if (!tokenData || !tokenData.authorization_header) {
|
||||||
|
throw new Error(`Token not found in context with key 's4ds_token'. Please run an authentication operation first.`);
|
||||||
|
}
|
||||||
|
headers.Authorization = tokenData.authorization_header;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Preparar parámetros de query
|
||||||
|
const queryParams: Record<string, any> = {};
|
||||||
|
if (apiDefinition.parameters) {
|
||||||
|
const queryParameters = this.getNodeParameter('queryParameters', i, {}) as Record<string, any>;
|
||||||
|
apiDefinition.parameters
|
||||||
|
.filter(p => p.in === 'query')
|
||||||
|
.forEach(param => {
|
||||||
|
if (queryParameters[param.name] !== undefined && queryParameters[param.name] !== '') {
|
||||||
|
queryParams[param.name] = queryParameters[param.name];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Preparar body si es necesario
|
||||||
|
let body: any = undefined;
|
||||||
|
if (apiDefinition.method === 'POST' && resource === 'authentication') {
|
||||||
|
body = {
|
||||||
|
username: credentials.username,
|
||||||
|
password: credentials.password,
|
||||||
|
};
|
||||||
|
} else if (apiDefinition.requestBody && apiDefinition.requestBody.schema) {
|
||||||
|
// Usar DTO para request body
|
||||||
|
let requestBodyData = this.getNodeParameter('requestBody', i, {});
|
||||||
|
if (typeof requestBodyData === 'string') {
|
||||||
|
try {
|
||||||
|
requestBodyData = JSON.parse(requestBodyData);
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error('El JSON del body no es válido: ' + e.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
body = requestBodyData;
|
||||||
|
} else if (apiDefinition.parameters) {
|
||||||
|
const bodyParameters = this.getNodeParameter('bodyParameters', i, {}) as Record<string, any>;
|
||||||
|
const bodyParams = apiDefinition.parameters.filter(p => p.in === 'body');
|
||||||
|
if (bodyParams.length > 0) {
|
||||||
|
body = {};
|
||||||
|
bodyParams.forEach(param => {
|
||||||
|
if (bodyParameters[param.name] !== undefined && bodyParameters[param.name] !== '') {
|
||||||
|
body[param.name] = bodyParameters[param.name];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construir URL con parámetros de query
|
||||||
|
let url = `${baseUrl}${apiDefinition.endpoint}`;
|
||||||
|
if (Object.keys(queryParams).length > 0) {
|
||||||
|
const queryString = Object.keys(queryParams)
|
||||||
|
.map(key => `${encodeURIComponent(key)}=${encodeURIComponent(queryParams[key])}`)
|
||||||
|
.join('&');
|
||||||
|
url += `?${queryString}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ejecutar la petición HTTP
|
||||||
|
const response = await this.helpers.httpRequest({
|
||||||
|
method: apiDefinition.method as IHttpRequestMethods,
|
||||||
|
url,
|
||||||
|
headers,
|
||||||
|
body,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Procesar respuesta según el tipo de operación
|
||||||
|
if (resource === 'authentication' && operation === 'generateToken') {
|
||||||
|
// Almacenar token en contexto
|
||||||
|
const contextKey = 's4ds_token';
|
||||||
|
const tokenData = {
|
||||||
|
token: response.token,
|
||||||
|
token_type: response.token_type,
|
||||||
|
expires_in: response.expires_in,
|
||||||
|
expires_at: new Date(Date.now() + response.expires_in * 1000).toISOString(),
|
||||||
|
authorization_header: `${response.token_type} ${response.token}`,
|
||||||
|
};
|
||||||
|
// Almacenar en contexto del workflow
|
||||||
|
this.getWorkflowStaticData('global')[contextKey] = tokenData;
|
||||||
|
|
||||||
|
// Retornar respuesta sin el token por seguridad
|
||||||
|
const secureResponse = {
|
||||||
|
success: true,
|
||||||
|
token_type: response.token_type,
|
||||||
|
expires_in: response.expires_in,
|
||||||
|
expires_at: tokenData.expires_at,
|
||||||
|
message: 'Token generated successfully and stored in workflow context',
|
||||||
|
context_key: contextKey,
|
||||||
|
};
|
||||||
|
|
||||||
|
returnData.push({
|
||||||
|
json: secureResponse,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Retornar respuesta normal
|
||||||
|
returnData.push({
|
||||||
|
json: response,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
if (this.continueOnFail()) {
|
||||||
|
returnData.push({
|
||||||
|
json: {
|
||||||
|
error: error.message,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [returnData];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
81
nodes/S4DSMain/api-definitions.json
Normal file
81
nodes/S4DSMain/api-definitions.json
Normal file
|
|
@ -0,0 +1,81 @@
|
||||||
|
{
|
||||||
|
"authentication": {
|
||||||
|
"generateToken": {
|
||||||
|
"method": "POST",
|
||||||
|
"endpoint": "/login/generateToken",
|
||||||
|
"description": "Generate authentication token using credentials",
|
||||||
|
"parameters": [],
|
||||||
|
"requiresAuth": false,
|
||||||
|
"response": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"token": "string",
|
||||||
|
"token_type": "string",
|
||||||
|
"expires_in": "number"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"products": {
|
||||||
|
"getProductCount": {
|
||||||
|
"method": "GET",
|
||||||
|
"endpoint": "/product/count",
|
||||||
|
"description": "Get the total count of products",
|
||||||
|
"parameters": [],
|
||||||
|
"requiresAuth": true,
|
||||||
|
"response": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"count": "number"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"customers": {
|
||||||
|
"getCustomerByDocument": {
|
||||||
|
"method": "GET",
|
||||||
|
"endpoint": "/customer/specificCustomer",
|
||||||
|
"description": "Get customer information by document ID and type",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "documentId",
|
||||||
|
"type": "string",
|
||||||
|
"required": false,
|
||||||
|
"description": "Document ID of the customer",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "documentType",
|
||||||
|
"type": "string",
|
||||||
|
"required": false,
|
||||||
|
"description": "Type of document (e.g., CC, CE, NIT)",
|
||||||
|
"in": "query"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"requiresAuth": true,
|
||||||
|
"response": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"customer": "object"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"createCustomer": {
|
||||||
|
"method": "POST",
|
||||||
|
"endpoint": "/customer",
|
||||||
|
"description": "Create a new customer",
|
||||||
|
"parameters": [],
|
||||||
|
"requiresAuth": true,
|
||||||
|
"requestBody": {
|
||||||
|
"schema": "SetNewCustomerDTO",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"customer": "object"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
608
nodes/S4DSMain/dto-definitions.json
Normal file
608
nodes/S4DSMain/dto-definitions.json
Normal file
|
|
@ -0,0 +1,608 @@
|
||||||
|
{
|
||||||
|
"SetNewCustomerDTO": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"documentType": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Type of document",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"docType": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Document type code",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"docTypeDesc": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Document type description",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"documentId": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Document ID",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"newDocumentId": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "New document ID",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"newDocumentType": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "New document type",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"verificationDigit": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Verification digit",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"internalCode": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Internal code",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"customerType": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Customer type",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Status",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"customerStatus": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Customer status",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "First name",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"secondName": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Second name",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"lastName": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Last name",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"lastName1": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "First last name",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"lastName2": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Second last name",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"email": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Email address",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"gender": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Gender",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"genderDesc": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Gender description",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"language": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Language",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"languageCode": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Language code",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"username": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Username",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"password": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Password",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"loginType": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Login type",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"roles": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "User roles",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"metadata": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "Metadata del cliente",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"priceList": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Price list",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"shippingAddress": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "Shipping address",
|
||||||
|
"schema": "AddressDTO",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"homeAddress": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "Home address",
|
||||||
|
"schema": "AddressDTO",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"phone": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "Phone numbers",
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"schema": "PhoneDTO"
|
||||||
|
},
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"birthdate": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Birth date",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"sellerId": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Seller ID",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"balanceDate": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Balance date",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"balance": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Balance",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"balanceMoneyFormat": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Balance money format",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"paymentTerms": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Payment terms",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"creditDays": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Credit days",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"creditQuota": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Credit quota",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"creditSegment": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Credit segment",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"creditStatus": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Credit status",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"territorialDivision": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Territorial division",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"territoryDescription": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Territory description",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"zoneDivisionId": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Zone division ID",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"zoneDivisionDesc": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Zone division description",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"zoneDivisionCode": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Zone division code",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"tdDivisionId": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "TD division ID",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"tdDivisionCode": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "TD division code",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"regionDivisionId": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Region division ID",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"regionDivisionDescription": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Region division description",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"regionDivisionCode": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Region division code",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"countryDivisionId": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Country division ID",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"countryDivisionCode": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Country division code",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"countryDivisionDesc": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Country division description",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"bankCode": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Bank code",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"bankDescription": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Bank description",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"accountType": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Account type",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"taxRegime": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Tax regime",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"taxRegimeCode": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Tax regime code",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"taxRegimeDescription": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Tax regime description",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"accountNumber": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Account number",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"segment": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Segment",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"registration": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Registration",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"reactivationDate": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Reactivation date",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"referent": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "Referent information",
|
||||||
|
"schema": "ReferentDTO",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"sponsor": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "Sponsor information",
|
||||||
|
"schema": "SponsorDTO",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"sync": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Sync status",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"currencyId": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Currency ID",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"continuity": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Continuity",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"accumulatedPoints": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Accumulated points",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"rankCode": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Rank code",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"rankDescription": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Rank description",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"honorificRankCode": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Honorific rank code",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"honorificRankDescription": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Honorific rank description",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"honorificDate": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Honorific date",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"compensationStructure": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Compensation structure",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"inactivityCampaigns": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Inactivity campaigns",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"commercialStatus": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Commercial status",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"desertionDate": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Desertion date",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"outstandingCreditBalanceSync": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Outstanding credit balance sync",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"outstandingCreditBalanceSyncFormatted": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Outstanding credit balance sync formatted",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"notificationType": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Notification type",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"needsAuditing": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Needs auditing",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"rut": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "RUT",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"georeference": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Georeference",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"warehouseId": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Warehouse ID",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"warehouseCode": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Warehouse code",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"currency": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Currency",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"postalCode": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Postal code",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"shippingPostalCode": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Shipping postal code",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"tdivisionDescription": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "T division description",
|
||||||
|
"required": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"AddressDTO": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"address": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Address line",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"address2": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Address line 2",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"address3": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Address line 3",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"postalCode": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Postal code",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"neighborhood": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Neighborhood",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"city": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "City",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"cityCode": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "City code",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"state": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "State",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"stateCode": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "State code",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"country": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Country",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"countryCode": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Country code",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"addressName": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Address name",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"addressDescription": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Address description",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"addressMetadata": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "Address metadata",
|
||||||
|
"required": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"PhoneDTO": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Phone type",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"countryCode": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Country code",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"number": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Phone number",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"modifiedAt": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Modified at timestamp",
|
||||||
|
"required": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ReferentDTO": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"documentId": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Document ID",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"docType": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Document type",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"docTypeDesc": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Document type description",
|
||||||
|
"required": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"SponsorDTO": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"documentId": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Document ID",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"docType": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Document type",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
"docTypeDesc": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Document type description",
|
||||||
|
"required": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
nodes/S4DSMain/logo_generic.png
Normal file
BIN
nodes/S4DSMain/logo_generic.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.8 KiB |
|
|
@ -36,8 +36,7 @@
|
||||||
"dist/credentials/S4DSApi.credentials.js"
|
"dist/credentials/S4DSApi.credentials.js"
|
||||||
],
|
],
|
||||||
"nodes": [
|
"nodes": [
|
||||||
"dist/nodes/S4DSAuth/S4DSAuth.node.js",
|
"dist/nodes/S4DSMain/S4DSMain.node.js"
|
||||||
"dist/nodes/S4DSExample/S4DSExample.node.js"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue