Bitly node

This commit is contained in:
Ricardo Espinoza 2020-01-25 16:44:33 -05:00
parent 5c3ac6c22d
commit 0c92349b5d
6 changed files with 583 additions and 0 deletions

View file

@ -0,0 +1,17 @@
import {
ICredentialType,
NodePropertyTypes,
} from 'n8n-workflow';
export class BitlyApi implements ICredentialType {
name = 'bitlyApi';
displayName = 'Bitly API';
properties = [
{
displayName: 'Access Token',
name: 'accessToken',
type: 'string' as NodePropertyTypes,
default: '',
},
];
}

View file

@ -0,0 +1,188 @@
import {
IExecuteFunctions,
} from 'n8n-core';
import {
IDataObject,
INodeTypeDescription,
INodeExecutionData,
INodeType,
ILoadOptionsFunctions,
INodePropertyOptions,
} from 'n8n-workflow';
import {
linkFields,
linkOperations
} from './LinkDescription';
import {
bitlyApiRequest,
bitlyApiRequestAllItems,
} from './GenericFunctions';
export class Bitly implements INodeType {
description: INodeTypeDescription = {
displayName: 'Bitly',
name: 'bitly',
icon: 'file:bitly.png',
group: ['output'],
version: 1,
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
description: 'Consume Bitly API',
defaults: {
name: 'Bitly',
color: '#d3643b',
},
inputs: ['main'],
outputs: ['main'],
credentials: [
{
name: 'bitlyApi',
required: true,
}
],
properties: [
{
displayName: 'Resource',
name: 'resource',
type: 'options',
options: [
{
name: ' Link',
value: 'link',
},
],
default: 'link',
description: 'Resource to consume.',
},
...linkOperations,
...linkFields,
],
};
methods = {
loadOptions: {
// Get all the available groups to display them to user so that he can
// select them easily
async getGroups(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
const returnData: INodePropertyOptions[] = [];
const groups = await bitlyApiRequestAllItems.call(this, 'groups', 'GET', '/groups');
for (const group of groups) {
const groupName = group.name;
const groupId = group.guid;
returnData.push({
name: groupName,
value: groupId,
});
}
return returnData;
},
// Get all the available tags to display them to user so that he can
// select them easily
async getTags(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
const groupId = this.getCurrentNodeParameter('group') as string;
const returnData: INodePropertyOptions[] = [];
const tags = await bitlyApiRequestAllItems.call(this, 'tags', 'GET', `groups/${groupId}/tags`);
for (const tag of tags) {
const tagName = tag;
const tagId = tag;
returnData.push({
name: tagName,
value: tagId,
});
}
return returnData;
},
},
};
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
const items = this.getInputData();
const returnData: IDataObject[] = [];
const length = items.length as unknown as number;
const qs: IDataObject = {};
let responseData;
const resource = this.getNodeParameter('resource', 0) as string;
const operation = this.getNodeParameter('operation', 0) as string;
for (let i = 0; i < length; i++) {
if (resource === 'link') {
if (operation === 'create') {
const longUrl = this.getNodeParameter('longUrl', i) as string;
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
const body: IDataObject = {
long_url: longUrl,
};
if (additionalFields.title) {
body.title = additionalFields.title as string;
}
if (additionalFields.domain) {
body.domain = additionalFields.domain as string;
}
if (additionalFields.group) {
body.group = additionalFields.group as string;
}
if (additionalFields.tags) {
body.tags = additionalFields.tags as string[];
}
const deeplinks = (this.getNodeParameter('deeplink', i) as IDataObject).deeplinkUi as IDataObject[];
if (deeplinks) {
for (const deeplink of deeplinks) {
//@ts-ignore
body.deeplinks.push({
app_uri_path: deeplink.appUriPath,
install_type: deeplink.installType,
install_url: deeplink.installUrl,
app_id: deeplink.appId,
})
}
}
responseData = await bitlyApiRequest.call(this, 'POST', '/bitlinks', body);
}
if (operation === 'update') {
const linkId = this.getNodeParameter('id', i) as string;
const updateFields = this.getNodeParameter('updateFields', i) as IDataObject;
const body: IDataObject = {};
if (updateFields.longUrl) {
body.long_url = updateFields.longUrl as string;
}
if (updateFields.title) {
body.title = updateFields.title as string;
}
if (updateFields.archived !== undefined) {
body.archived = updateFields.archived as boolean;
}
if (updateFields.domain) {
body.domain = updateFields.domain as string;
}
if (updateFields.group) {
body.group = updateFields.group as string;
}
if (updateFields.tags) {
body.tags = updateFields.tags as string[];
}
const deeplinks = (this.getNodeParameter('deeplink', i) as IDataObject).deeplinkUi as IDataObject[];
if (deeplinks) {
for (const deeplink of deeplinks) {
//@ts-ignore
body.deeplinks.push({
app_uri_path: deeplink.appUriPath,
install_type: deeplink.installType,
install_url: deeplink.installUrl,
app_id: deeplink.appId,
})
}
}
responseData = await bitlyApiRequest.call(this, 'PATCH', `/bitlinks/${linkId}`, body);
}
if (operation === 'get') {
const linkId = this.getNodeParameter('id', i) as string;
responseData = await bitlyApiRequest.call(this, 'GET', `/bitlinks/${linkId}`);
}
}
if (Array.isArray(responseData)) {
returnData.push.apply(returnData, responseData as IDataObject[]);
} else {
returnData.push(responseData as IDataObject);
}
}
return [this.helpers.returnJsonArray(returnData)];
}
}

View file

@ -0,0 +1,57 @@
import { OptionsWithUri } from 'request';
import {
IExecuteFunctions,
IExecuteSingleFunctions,
IHookFunctions,
ILoadOptionsFunctions,
} from 'n8n-core';
import { IDataObject } from 'n8n-workflow';
export async function bitlyApiRequest(this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method: string, resource: string, body: any = {}, qs: IDataObject = {}, uri?: string, option: IDataObject = {}): Promise<any> { // tslint:disable-line:no-any
const credentials = this.getCredentials('bitlyApi');
if (credentials === undefined) {
throw new Error('No credentials got returned!');
}
let options: OptionsWithUri = {
headers: { Authorization: `Bearer ${credentials.accessToken}`},
method,
qs,
body,
uri: uri ||`https://api-ssl.bitly.com/v4${resource}`,
json: true
};
options = Object.assign({}, options, option);
if (Object.keys(options.body).length === 0) {
delete options.body;
}
try {
return await this.helpers.request!(options);
} catch (err) {
throw new Error(err);
}
}
/**
* Make an API request to paginated flow endpoint
* and return all results
*/
export async function bitlyApiRequestAllItems(this: IHookFunctions | IExecuteFunctions| ILoadOptionsFunctions, propertyName: string, method: string, resource: string, body: any = {}, query: IDataObject = {}): Promise<any> { // tslint:disable-line:no-any
const returnData: IDataObject[] = [];
let responseData;
let uri: string | undefined;
query.size = 50;
do {
responseData = await bitlyApiRequest.call(this, method, resource, body, query, uri);
returnData.push.apply(returnData, responseData[propertyName]);
if (responseData.pagination && responseData.pagination.next) {
uri = responseData.pagination.next;
};
} while (
responseData.pagination !== undefined &&
responseData.pagination.next !== undefined
);
return returnData;
}

View file

@ -0,0 +1,319 @@
import { INodeProperties } from 'n8n-workflow';
export const linkOperations = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
displayOptions: {
show: {
resource: [
'link',
],
},
},
options: [
{
name: 'Create',
value: 'create',
description: 'Create a link',
},
{
name: 'Update',
value: 'update',
description: 'Update a link',
},
{
name: 'Get',
value: 'get',
description: 'Get a link',
},
],
default: 'create',
description: 'The operation to perform.',
},
] as INodeProperties[];
export const linkFields = [
/* -------------------------------------------------------------------------- */
/* link:create */
/* -------------------------------------------------------------------------- */
{
displayName: 'Long URL',
name: 'longUrl',
type: 'string',
displayOptions: {
show: {
resource: [
'link',
],
operation: [
'create',
],
},
},
default: '',
required: true,
},
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: [
'link',
],
operation: [
'create',
],
},
},
options: [
{
displayName: 'Domain',
name: 'domain',
type: 'string',
default: 'bit.ly',
},
{
displayName: 'Title',
name: 'title',
type: 'string',
default: '',
},
{
displayName: 'Group',
name: 'group',
type: 'options',
default: '',
typeOptions: {
loadOptionsMethod: 'getGroups',
},
},
{
displayName: 'Tags',
name: 'tags',
type: 'multiOptions',
default: [],
typeOptions: {
loadOptionsMethod: 'getTags',
loadOptionsDependsOn: [
'group',
]
},
},
],
},
{
displayName: 'Deeplinks',
name: 'deeplink',
placeholder: 'Add Deep Link',
type: 'fixedCollection',
typeOptions: {
multipleValues: true,
},
displayOptions: {
show: {
resource: [
'link',
],
operation: [
'create',
],
},
},
default: {},
options: [
{
name: 'deeplinkUi',
displayName: 'Deep Link',
values: [
{
displayName: 'App URI Path',
name: 'appUriPath',
type: 'string',
default: '',
},
{
displayName: 'Install Type',
name: 'installType',
type: 'string',
default: '',
},
{
displayName: 'Install URL',
name: 'installUrl',
type: 'string',
default: '',
},
{
displayName: 'App ID',
name: 'appId',
type: 'string',
default: '',
},
]
},
],
},
/* -------------------------------------------------------------------------- */
/* link:update */
/* -------------------------------------------------------------------------- */
{
displayName: 'Bitlink',
name: 'id',
type: 'string',
default: '',
required: true,
displayOptions: {
show: {
resource: [
'link',
],
operation: [
'update',
],
},
},
},
{
displayName: 'Update Fields',
name: 'updateFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: [
'link',
],
operation: [
'update',
],
},
},
options: [
{
displayName: 'Long URL',
name: 'longUrl',
type: 'string',
default: '',
},
{
displayName: 'Archived',
name: 'archived',
type: 'boolean',
default: false,
},
{
displayName: 'Domain',
name: 'domain',
type: 'string',
default: 'bit.ly',
},
{
displayName: 'Title',
name: 'title',
type: 'string',
default: '',
},
{
displayName: 'Group',
name: 'group',
type: 'options',
default: '',
typeOptions: {
loadOptionsMethod: 'getGroups',
},
},
{
displayName: 'Tags',
name: 'tags',
type: 'multiOptions',
default: [],
typeOptions: {
loadOptionsMethod: 'getTags',
loadOptionsDependsOn: [
'group',
]
},
},
],
},
{
displayName: 'Deeplinks',
name: 'deeplink',
placeholder: 'Add Deep Link',
type: 'fixedCollection',
typeOptions: {
multipleValues: true,
},
displayOptions: {
show: {
resource: [
'link',
],
operation: [
'update',
],
},
},
default: {},
options: [
{
name: 'deeplinkUi',
displayName: 'Deep Link',
values: [
{
displayName: 'App URI Path',
name: 'appUriPath',
type: 'string',
default: '',
},
{
displayName: 'Install Type',
name: 'installType',
type: 'string',
default: '',
},
{
displayName: 'Install URL',
name: 'installUrl',
type: 'string',
default: '',
},
{
displayName: 'App ID',
name: 'appId',
type: 'string',
default: '',
},
]
},
],
},
/* -------------------------------------------------------------------------- */
/* link:get */
/* -------------------------------------------------------------------------- */
{
displayName: 'Bitlink',
name: 'id',
type: 'string',
default: '',
required: true,
displayOptions: {
show: {
resource: [
'link',
],
operation: [
'get',
],
},
},
},
] as INodeProperties[];

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

View file

@ -33,6 +33,7 @@
"dist/credentials/AsanaApi.credentials.js",
"dist/credentials/Aws.credentials.js",
"dist/credentials/BitbucketApi.credentials.js",
"dist/credentials/BitlyApi.credentials.js",
"dist/credentials/ChargebeeApi.credentials.js",
"dist/credentials/ClickUpApi.credentials.js",
"dist/credentials/CodaApi.credentials.js",
@ -100,6 +101,7 @@
"dist/nodes/Aws/AwsLambda.node.js",
"dist/nodes/Aws/AwsSns.node.js",
"dist/nodes/Bitbucket/BitbucketTrigger.node.js",
"dist/nodes/Bitly/Bitly.node.js",
"dist/nodes/Chargebee/Chargebee.node.js",
"dist/nodes/Chargebee/ChargebeeTrigger.node.js",
"dist/nodes/ClickUp/ClickUp.node.js",