Skip to content

Commit

Permalink
feat(PostBin Node): Add PostBin node (#3236)
Browse files Browse the repository at this point in the history
* 🚧 Initial progress on PostBin node.

* ✨ Implemented Bin and Request operations for PostBin node.

* 🚧 Reworked the node in the declarative way.

* 🚧 PosBin node refactoring after reworking it.

* ✨ Implemented Bin id parsing in PostBin node. Done some final refactoring and documentation.

* ⚑ Improvements

* ⚑ Add comments

* πŸ‘ŒUpdating the PostBin node based on the product review

* πŸ’„Updating PostBin node Bin ID validation logic

* ⚑ Small improvements

* ⚑ Transform the bin requests and add additional properties

Co-authored-by: ricardo <ricardoespinoza105@gmail.com>
Co-authored-by: Jan Oberhauser <jan.oberhauser@gmail.com>
  • Loading branch information
3 people authored May 27, 2022
1 parent 5f3bed3 commit 06c407d
Show file tree
Hide file tree
Showing 8 changed files with 455 additions and 1 deletion.
5 changes: 4 additions & 1 deletion packages/core/src/NodeExecuteFunctions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -708,7 +708,10 @@ function convertN8nRequestToAxios(n8nRequest: IHttpRequestOptions): AxiosRequest
};
}

if (n8nRequest.body) {
// if there is a body and it's empty (does not have properties),
// make sure not to send anything in it as some services fail when
// sending GET request with empty body.
if (n8nRequest.body && Object.keys(n8nRequest.body).length) {
axiosRequest.data = n8nRequest.body;
// Let's add some useful header standards here.
const existingContentTypeHeaderKey = searchForHeader(axiosRequest.headers, 'content-type');
Expand Down
104 changes: 104 additions & 0 deletions packages/nodes-base/nodes/PostBin/BinDescription.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import {
INodeProperties
} from 'n8n-workflow';

import {
buildBinAPIURL,
transformBinReponse,
} from './GenericFunctions';


// Operations for the `Bin` resource:
export const binOperations: INodeProperties[] = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
displayOptions: {
show: {
resource: [
'bin',
],
},
},
options: [
{
name: 'Create',
value: 'create',
description: 'Create bin',
routing: {
request: {
method: 'POST',
url: '/developers/postbin/api/bin',
},
output: {
postReceive: [
transformBinReponse,
],
},
},
},
{
name: 'Get',
value: 'get',
description: 'Get a bin',
routing: {
request: {
method: 'GET',
},
output: {
postReceive: [
transformBinReponse,
],
},
send: {
preSend: [
// Parse binId before sending to make sure it's in the right format
buildBinAPIURL,
],
},
},
},
{
name: 'Delete',
value: 'delete',
description: 'Delete a bin',
routing: {
request: {
method: 'DELETE',
},
send: {
preSend: [
// Parse binId before sending to make sure it's in the right format
buildBinAPIURL,
],
},
},
},
],
default: 'create',
},
];

// Properties of the `Bin` resource
export const binFields: INodeProperties[] = [
{
name: 'binId',
displayName: 'Bin ID',
type: 'string',
default: '',
required: true,
displayOptions: {
show: {
resource: [
'bin',
],
operation: [
'get',
'delete',
],
},
},
description: 'Unique identifier for each bin',
},
];
113 changes: 113 additions & 0 deletions packages/nodes-base/nodes/PostBin/GenericFunctions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import {
IExecuteSingleFunctions,
IHttpRequestOptions,
IN8nHttpFullResponse,
INodeExecutionData,
NodeApiError,
} from 'n8n-workflow';

// Regular expressions used to extract binId from parameter value
const BIN_ID_REGEX = /\b\d{13}-\d{13}\b/g;

/**
* Creates correctly-formatted PostBin API URL based on the entered binId.
* This function makes sure binId is in the expected format by parsing it
* from current node parameter value.
*
* @export
* @param {IExecuteSingleFunctions} this
* @param {IHttpRequestOptions} requestOptions
* @returns {Promise<IHttpRequestOptions>} requestOptions
*/
export async function buildBinAPIURL(this: IExecuteSingleFunctions, requestOptions: IHttpRequestOptions): Promise<IHttpRequestOptions> {
const binId = parseBinId(this);
// Assemble the PostBin API URL and put it back to requestOptions
requestOptions.url = `/developers/postbin/api/bin/${binId}`;

return requestOptions;
}

/**
* Creates correctly-formatted PostBin Bin test URL based on the entered binId.
* This function makes sure binId is in the expected format by parsing it
* from current node parameter value.
*
* @export
* @param {IExecuteSingleFunctions} this
* @param {IHttpRequestOptions} requestOptions
* @returns {Promise<IHttpRequestOptions>} requestOptions
*/
export async function buildBinTestURL(this: IExecuteSingleFunctions, requestOptions: IHttpRequestOptions): Promise<IHttpRequestOptions> {
const binId = parseBinId(this);

// Assemble the PostBin API URL and put it back to requestOptions
requestOptions.url = `/developers/postbin/${binId}`;
return requestOptions;
}

/**
* Creates correctly-formatted PostBin API URL based on the entered binId and reqId.
* This function makes sure binId is in the expected format by parsing it
* from current node parameter value.
*
* @export
* @param {IExecuteSingleFunctions} this
* @param {IHttpRequestOptions} requestOptions
* @returns {Promise<IHttpRequestOptions>} requestOptions
*/
export async function buildRequestURL(this: IExecuteSingleFunctions, requestOptions: IHttpRequestOptions): Promise<IHttpRequestOptions> {
const reqId = this.getNodeParameter('requestId', 'shift') as string;
const binId = parseBinId(this);

requestOptions.url = `/developers/postbin/api/bin/${binId}/req/${reqId}`;
return requestOptions;
}

/**
* Extracts the PostBin Bin Id from the specified string.
* This method should be able to extract bin Id from the
* PostBin URL or from the string in the following format:
* `Bin '<binId>'.`
*
* @param {IExecuteSingleFunctions} this
* @param {IHttpRequestOptions} requestOptions
* @returns {Promise<IHttpRequestOptions>} requestOptions
*/
function parseBinId(context: IExecuteSingleFunctions) {
const binId = context.getNodeParameter('binId') as string;
// Test if the Bin id is in the expected format
const idMatch = BIN_ID_REGEX.exec(binId);

// Return what is matched
if(idMatch) {
return idMatch[0];
}

// If it's not recognized, error out
throw new NodeApiError(context.getNode(), {}, {
message: 'Bin ID format is not valid',
description: 'Please check the provided Bin ID and try again.',
parseXml: false,
});
}

/**
* Converts the bin response data and adds additional properties
*
* @param {IExecuteSingleFunctions} this
* @param {INodeExecutionData} items[]
* @param {IN8nHttpFullResponse} response
* @returns {Promise<INodeExecutionData[]>}
*/
export async function transformBinReponse(this: IExecuteSingleFunctions, items: INodeExecutionData[], response: IN8nHttpFullResponse): Promise<INodeExecutionData[]> {
items.forEach(item => item.json = {
'binId': item.json.binId,
'nowTimestamp': item.json.now,
'nowIso': new Date(item.json.now as string).toISOString(),
'expiresTimestamp': item.json.expires,
'expiresIso': new Date(item.json.expires as string).toISOString(),
'requestUrl': 'https://www.toptal.com/developers/postbin/' + item.json.binId,
'viewUrl': 'https://www.toptal.com/developers/postbin/b/' + item.json.binId,
});
return items;
}
16 changes: 16 additions & 0 deletions packages/nodes-base/nodes/PostBin/PostBin.node.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"node": "n8n-nodes-base.postbin",
"nodeVersion": "1.0",
"codexVersion": "1.0",
"categories": [
"Development",
"Data & Storage"
],
"resources": {
"primaryDocumentation": [
{
"url": "https://docs.n8n.io/nodes/n8n-nodes-base.postbin/"
}
]
}
}
59 changes: 59 additions & 0 deletions packages/nodes-base/nodes/PostBin/PostBin.node.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import {
INodeType,
INodeTypeDescription
} from 'n8n-workflow';

import {
binFields,
binOperations,
} from './BinDescription';

import {
requestFields,
requestOperations,
} from './RequestDescription';

export class PostBin implements INodeType {
description: INodeTypeDescription = {
displayName: 'PostBin',
name: 'postBin',
icon: 'file:postbin.svg',
group: ['transform'],
version: 1,
subtitle: '={{ $parameter["operation"] + ": " + $parameter["resource"] }}',
description: 'Consume PostBin API',
defaults: {
name: 'PostBin',
color: '#4dc0b5',
},
inputs: ['main'],
outputs: ['main'],
credentials: [],
requestDefaults: {
baseURL: 'https://www.toptal.com',
},
properties: [
{
displayName: 'Resource',
name: 'resource',
type: 'options',
options: [
{
name: 'Bin',
value: 'bin',
},
{
name: 'Request',
value: 'request',
},
],
default: 'bin',
required: true,
},
...binOperations,
...binFields,
...requestOperations,
...requestFields,
],
};
}
Loading

0 comments on commit 06c407d

Please sign in to comment.