Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: New node - Ragic node & Ragic Trigger node #12796

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions packages/nodes-base/credentials/RagicApi.credentials.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import type { IAuthenticateGeneric, ICredentialType, INodeProperties } from 'n8n-workflow';

export class RagicApi implements ICredentialType {
name = 'ragicApi';

displayName = 'Ragic API';

documentationUrl = 'https://www.ragic.com/intl/en/doc-api'; //TODO: 之後要寫這n8n的文件,再把對外文件放到這裡

properties: INodeProperties[] = [
{
displayName: 'API Key',
name: 'apiKey',
type: 'string',
typeOptions: { password: true },
default: '',
required: true,
description:
'Please refer to <a href="https://www.ragic.com/intl/en/doc-user/20/personal-settings#4">here</a>',
},
{
displayName: 'Server Name',
name: 'serverName',
type: 'string',
default: '',
required: true,
description:
'You can find the server name in your database url, from the frist charactor after "https://" til the charactor before the next "/". It should be like "www.ragic.com" or "ap5.ragic.com".',
},
];

authenticate: IAuthenticateGeneric = {
type: 'generic',
properties: {
headers: {
Authorization: '={{"Basic " + $credentials.apiKey}}',
},
},
};
}
40 changes: 40 additions & 0 deletions packages/nodes-base/credentials/RagicTriggerApi.credentials.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import type { IAuthenticateGeneric, ICredentialType, INodeProperties } from 'n8n-workflow';

export class RagicTriggerApi implements ICredentialType {
name = 'ragicTriggerApi';

displayName = 'Ragic Trigger API';

documentationUrl = 'ragicTrigger';

properties: INodeProperties[] = [
{
displayName: 'API Key',
name: 'apiKey',
type: 'string',
typeOptions: { password: true },
default: '',
required: true,
description:
'Please refer to <a href="https://www.ragic.com/intl/en/doc-user/20/personal-settings#4">here</a>',
},
{
displayName: 'Sheet Url',
name: 'sheetUrl',
type: 'string',
default: '',
required: true,
description:
'Please copy the sheet url from "https" til the charactor before "?" and paste it.',
},
];

authenticate: IAuthenticateGeneric = {
type: 'generic',
properties: {
headers: {
Authorization: '={{"Basic " + $credentials.apiKey}}',
},
},
};
}
18 changes: 18 additions & 0 deletions packages/nodes-base/nodes/Ragic/Ragic.node.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"node": "n8n-nodes-base.Ragic",
"nodeVersion": "1.0",
"codexVersion": "1.0",
"categories": ["Miscellaneous"],
"resources": {
"credentialDocumentation": [
{
"url": ""
}
],
"primaryDocumentation": [
{
"url": ""
}
]
}
}
157 changes: 157 additions & 0 deletions packages/nodes-base/nodes/Ragic/Ragic.node.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
import type {
IDataObject,
IExecuteFunctions,
ILoadOptionsFunctions,
INodeExecutionData,
INodeType,
INodeTypeDescription,
NodeConnectionType,
NodeExecutionWithMetadata,
} from 'n8n-workflow';
import { ApplicationError, jsonParse } from 'n8n-workflow';

export class Ragic implements INodeType {
description: INodeTypeDescription = {
// Basic node details will go here
displayName: 'Ragic',
name: 'ragic',
icon: 'file:Ragic.svg',
group: ['transform'],
version: 1,
subtitle: '={{$parameter["action"]}}',
description: 'Ragic: #1 No Code database builder',
defaults: {
name: 'Ragic',
},
inputs: ['main'] as NodeConnectionType[],
outputs: ['main'] as NodeConnectionType[],
credentials: [
{
name: 'ragicApi',
required: true,
},
],
properties: [
// Resources and operations will go here
{
displayName: 'Action',
name: 'action',
type: 'options',
noDataExpression: true,
options: [
{
name: 'Create New Data',
value: 'createNewData',
},
{
name: 'Update Existed Data',
value: 'updateExistedData',
},
],
default: 'createNewData',
},
{
// eslint-disable-next-line n8n-nodes-base/node-param-display-name-wrong-for-dynamic-options
displayName: 'Form',
name: 'form',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getFormOptions',
loadOptionsDependsOn: ['credentials'],
},
default: '',
description:
'Only the forms that you are the admin user would show in this list. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
},
{
displayName: 'Record Index',
name: 'recordIndex',
type: 'string',
required: true,
displayOptions: {
show: {
action: ['updateExistedData'],
},
},
default: '',
description:
'You can find the Record Index from the URL. Record URL structure: http://{domain}/{database}/{path}/{form}/{record index}?.',
},
{
displayName: 'JSON Body',
name: 'jsonBody',
type: 'json',
default: '',
description: 'Please refer to <a href="https://www.ragic.com/intl/en/doc-api">here</a>',
},
],
};

methods = {
loadOptions: {
async getFormOptions(this: ILoadOptionsFunctions) {
const credentials = await this.getCredentials('ragicApi');
const serverName = credentials?.serverName as string;
const apiKey = credentials?.apiKey as string;
const responseString = (await this.helpers.request({
method: 'GET',
url: `https://${serverName}/api/http/integromatForms.jsp?n8n`,
headers: {
Authorization: `Basic ${apiKey}`,
},
})) as string;

const responseArray = jsonParse(responseString) as [];

return responseArray.map((form: { displayName: string; path: string }) => ({
name: form.displayName,
value: form.path,
}));
},
},
};

async execute(
this: IExecuteFunctions,
): Promise<INodeExecutionData[][] | NodeExecutionWithMetadata[][] | null> {
// 獲取憑據
const credentials = await this.getCredentials('ragicApi');

// 獲取 serverName
const serverName = credentials?.serverName as string;
const apiKey = credentials?.apiKey as string;
const path = this.getNodeParameter('form', 0);
let recordIndex;
try {
recordIndex = '/' + (this.getNodeParameter('recordIndex', 0) as string);
} catch (error) {
recordIndex = '';
}

// 構建 baseURL
const baseURL = `https://${serverName}/${path}${recordIndex}?api&n8n`;

// 執行 API 請求
const response = (await this.helpers.request({
method: 'POST',
url: `${baseURL}`,
headers: {
Authorization: `Basic ${apiKey}`,
},
body: this.getNodeParameter('jsonBody', 0),
})) as string;

// 確保返回的是 JSON 格式
let parsedResponse;
try {
parsedResponse = (
typeof response === 'string' ? JSON.parse(response) : response
) as IDataObject;
} catch (error) {
throw new ApplicationError('Failed to parse API response as JSON.');
}

// 返回結構化 JSON 數據
return [this.helpers.returnJsonArray(parsedResponse)];
}
}
38 changes: 38 additions & 0 deletions packages/nodes-base/nodes/Ragic/Ragic.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
38 changes: 38 additions & 0 deletions packages/nodes-base/nodes/RagicTrigger/Ragic.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 18 additions & 0 deletions packages/nodes-base/nodes/RagicTrigger/RagicTrigger.node.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"node": "n8n-nodes-base.RagicTrigger",
"nodeVersion": "1.0",
"codexVersion": "1.0",
"categories": ["Miscellaneous"],
"resources": {
"credentialDocumentation": [
{
"url": ""
}
],
"primaryDocumentation": [
{
"url": ""
}
]
}
}
Loading