diff --git a/nodes/WizPixelFlow/WizPixelFlow.node.ts b/nodes/WizPixelFlow/WizPixelFlow.node.ts index 8762653..3ed8c0e 100644 --- a/nodes/WizPixelFlow/WizPixelFlow.node.ts +++ b/nodes/WizPixelFlow/WizPixelFlow.node.ts @@ -1,4 +1,5 @@ import { + IDataObject, IExecuteFunctions, INodeExecutionData, INodeType, @@ -57,96 +58,30 @@ export class WizPixelFlow implements INodeType { ], default: 'render', }, - { - displayName: 'Template ID', - name: 'templateId', - type: 'string', - default: '', - required: true, - displayOptions: { show: { operation: ['render'] } }, - description: 'The ID of the Flow template to render', - }, - { - displayName: 'Output Formats', - name: 'outputFormats', - type: 'multiOptions', - options: OUTPUT_FORMATS, - default: ['leaderboard_728x90', 'medium_rectangle_300x250'], - required: true, - displayOptions: { show: { operation: ['render'] } }, - }, - { - displayName: 'Locales', - name: 'locales', - type: 'string', - default: 'en', - displayOptions: { show: { operation: ['render'] } }, - description: 'Comma-separated locale codes, e.g. en,it,fr', - }, - { - displayName: 'Variables (JSON)', - name: 'variables', - type: 'json', - default: '{}', - displayOptions: { show: { operation: ['render'] } }, - description: 'Key/value pairs to substitute into {{variable}} placeholders', - }, - { - displayName: 'Include Video (MP4)', - name: 'includeVideo', - type: 'boolean', - default: false, - displayOptions: { show: { operation: ['render'] } }, - }, - { - displayName: 'Wait for Completion', - name: 'waitForCompletion', - type: 'boolean', - default: true, - displayOptions: { show: { operation: ['render'] } }, - description: 'Poll until the job is done and return the ZIP download URL', - }, - { - displayName: 'Poll Interval (seconds)', - name: 'pollInterval', - type: 'number', - default: 5, - displayOptions: { show: { operation: ['render'], waitForCompletion: [true] } }, - }, - { - displayName: 'Max Wait (seconds)', - name: 'maxWait', - type: 'number', - default: 300, - displayOptions: { show: { operation: ['render'], waitForCompletion: [true] } }, - }, - { - displayName: 'Job ID', - name: 'jobId', - type: 'string', - default: '', - required: true, - displayOptions: { show: { operation: ['getJob'] } }, - }, - { - displayName: 'Limit', - name: 'limit', - type: 'number', - default: 50, - typeOptions: { minValue: 1, maxValue: 200 }, - displayOptions: { show: { operation: ['listTemplates'] } }, - }, + { displayName: 'Template ID', name: 'templateId', type: 'string', default: '', required: true, displayOptions: { show: { operation: ['render'] } }, description: 'The ID of the Flow template to render' }, + { displayName: 'Output Formats', name: 'outputFormats', type: 'multiOptions', options: OUTPUT_FORMATS, default: ['leaderboard_728x90', 'medium_rectangle_300x250'], required: true, displayOptions: { show: { operation: ['render'] } } }, + { displayName: 'Locales', name: 'locales', type: 'string', default: 'en', displayOptions: { show: { operation: ['render'] } }, description: 'Comma-separated locale codes, e.g. en,it,fr' }, + { displayName: 'Variables (JSON)', name: 'variables', type: 'json', default: '{}', displayOptions: { show: { operation: ['render'] } }, description: 'Key/value pairs for {{variable}} substitution' }, + { displayName: 'Include Video (MP4)', name: 'includeVideo', type: 'boolean', default: false, displayOptions: { show: { operation: ['render'] } } }, + { displayName: 'Wait for Completion', name: 'waitForCompletion', type: 'boolean', default: true, displayOptions: { show: { operation: ['render'] } }, description: 'Poll until done and return ZIP download URL' }, + { displayName: 'Poll Interval (seconds)', name: 'pollInterval', type: 'number', default: 5, displayOptions: { show: { operation: ['render'], waitForCompletion: [true] } } }, + { displayName: 'Max Wait (seconds)', name: 'maxWait', type: 'number', default: 300, displayOptions: { show: { operation: ['render'], waitForCompletion: [true] } } }, + { displayName: 'Job ID', name: 'jobId', type: 'string', default: '', required: true, displayOptions: { show: { operation: ['getJob'] } } }, + { displayName: 'Limit', name: 'limit', type: 'number', default: 50, typeOptions: { minValue: 1, maxValue: 200 }, displayOptions: { show: { operation: ['listTemplates'] } } }, ], }; async execute(this: IExecuteFunctions): Promise { const credentials = await this.getCredentials('wizPixelFlowApi'); const baseUrl = (credentials.baseUrl as string).replace(/\/$/, ''); - const apiKey = credentials.apiKey as string; + const apiKey = credentials.apiKey as string; const headers = { 'X-WPF-API-Key': apiKey, 'Content-Type': 'application/json' }; const items = this.getInputData(); const returnData: INodeExecutionData[] = []; + const parse = (r: unknown): IDataObject => + (typeof r === 'string' ? JSON.parse(r) : r) as IDataObject; + for (let i = 0; i < items.length; i++) { const operation = this.getNodeParameter('operation', i) as string; try { @@ -162,32 +97,28 @@ export class WizPixelFlow implements INodeType { const locales = localesRaw.split(',').map((l) => l.trim()).filter(Boolean); const variables = typeof variablesRaw === 'string' ? JSON.parse(variablesRaw) : variablesRaw; const body = { template_id: templateId, formats: outputFormats, locales, variables, include_video: includeVideo }; - const submitResp = await this.helpers.httpRequest({ method: 'POST', url: `${baseUrl}/render`, headers, body: JSON.stringify(body) }); - const parsed = typeof submitResp === 'string' ? JSON.parse(submitResp) : submitResp; - const jobId = parsed.job_id; - if (!waitForCompletion) { returnData.push({ json: parsed }); continue; } + const submitted = parse(await this.helpers.httpRequest({ method: 'POST', url: `${baseUrl}/render`, headers, body: JSON.stringify(body) })); + const jobId = submitted.job_id as string; + if (!waitForCompletion) { returnData.push({ json: submitted }); continue; } const deadline = Date.now() + maxWait * 1000; - let jobData: Record = {}; + let jobData: IDataObject = {}; while (Date.now() < deadline) { await new Promise((r) => setTimeout(r, pollInterval * 1000)); - const pollResp = await this.helpers.httpRequest({ method: 'GET', url: `${baseUrl}/jobs/${jobId}`, headers }); - jobData = typeof pollResp === 'string' ? JSON.parse(pollResp) : pollResp; - const job = (jobData as { job?: { status?: string } }).job; + jobData = parse(await this.helpers.httpRequest({ method: 'GET', url: `${baseUrl}/jobs/${jobId}`, headers })); + const job = jobData.job as IDataObject | undefined; if (job?.status === 'done' || job?.status === 'failed') break; } returnData.push({ json: jobData }); } else if (operation === 'getJob') { const jobId = this.getNodeParameter('jobId', i) as string; - const resp = await this.helpers.httpRequest({ method: 'GET', url: `${baseUrl}/jobs/${jobId}`, headers }); - returnData.push({ json: typeof resp === 'string' ? JSON.parse(resp) : resp }); + returnData.push({ json: parse(await this.helpers.httpRequest({ method: 'GET', url: `${baseUrl}/jobs/${jobId}`, headers })) }); } else if (operation === 'listTemplates') { const limit = this.getNodeParameter('limit', i) as number; - const resp = await this.helpers.httpRequest({ method: 'GET', url: `${baseUrl}/templates?limit=${limit}`, headers }); - returnData.push({ json: typeof resp === 'string' ? JSON.parse(resp) : resp }); + returnData.push({ json: parse(await this.helpers.httpRequest({ method: 'GET', url: `${baseUrl}/templates?limit=${limit}`, headers })) }); } } catch (error) { if (this.continueOnFail()) { - returnData.push({ json: { error: (error as Error).message }, pairedItem: i }); + returnData.push({ json: { error: (error as Error).message } as IDataObject, pairedItem: i }); } else { throw new NodeOperationError(this.getNode(), error as Error, { itemIndex: i }); }