🔀 Merge branch 'RicardoE105-feature/slack-node' into oauth-support

This commit is contained in:
Jan Oberhauser 2020-03-15 15:52:36 +01:00
commit db89e9eabb
13 changed files with 2885 additions and 613 deletions

View file

@ -2,6 +2,34 @@
This list shows all the versions which include breaking changes and how to upgrade
## ???
### What changed?
To make it easier to use the data which the Slack-Node outputs we no longer return the whole
object the Slack-API returns if the only other property is `"ok": true`. In this case it returns
now directly the data under "channel".
### When is action necessary?
When you currently use the Slack-Node with Operations Channel -> Create and you use
any of the data the node outputs.
### How to upgrade:
All values that get referenced which were before under the property "channel" are now on the main level.
This means that these expressions have to get adjusted.
Meaning if the expression used before was:
```
{{ $node["Slack"].data["channel"]["id"] }}
```
it has to get changed to:
```
{{ $node["Slack"].data["id"] }}
```
## 0.37.0
### What changed?

View file

@ -203,7 +203,7 @@ class App {
});
}
jwt.verify(token, getKey, {}, (err: Error, decoded: string) => {
jwt.verify(token, getKey, {}, (err: Error, decoded: object) => {
if (err) return ResponseHelper.jwtAuthAuthorizationError(res, "Invalid token");
next();
@ -550,7 +550,7 @@ class App {
const nodeTypes = NodeTypes();
const loadDataInstance = new LoadNodeParameterOptions(nodeType, nodeTypes, credentials);
const loadDataInstance = new LoadNodeParameterOptions(nodeType, nodeTypes, JSON.parse('' + req.query.currentNodeParameters), credentials);
const workflowData = loadDataInstance.getWorkflowData() as IWorkflowBase;
const workflowCredentials = await WorkflowCredentials(workflowData.nodes);

View file

@ -1,6 +1,7 @@
import {
INode,
INodeCredentials,
INodeParameters,
INodePropertyOptions,
INodeTypes,
IWorkflowExecuteAdditionalData,
@ -20,7 +21,7 @@ export class LoadNodeParameterOptions {
workflow: Workflow;
constructor(nodeTypeName: string, nodeTypes: INodeTypes, credentials?: INodeCredentials) {
constructor(nodeTypeName: string, nodeTypes: INodeTypes, currentNodeParameters: INodeParameters, credentials?: INodeCredentials) {
const nodeType = nodeTypes.getByName(nodeTypeName);
if (nodeType === undefined) {
@ -28,8 +29,7 @@ export class LoadNodeParameterOptions {
}
const nodeData: INode = {
parameters: {
},
parameters: currentNodeParameters,
name: TEMP_NODE_NAME,
type: nodeTypeName,
typeVersion: 1,

View file

@ -115,7 +115,7 @@ export async function prepareBinaryData(binaryData: Buffer, filePath?: string, m
* @param {IWorkflowExecuteAdditionalData} additionalData
* @returns
*/
export function requestOAuth(this: IAllExecuteFunctions, credentialsType: string, requestOptions: OptionsWithUri | requestPromise.RequestPromiseOptions, node: INode, additionalData: IWorkflowExecuteAdditionalData) {
export function requestOAuth(this: IAllExecuteFunctions, credentialsType: string, requestOptions: OptionsWithUri | requestPromise.RequestPromiseOptions, node: INode, additionalData: IWorkflowExecuteAdditionalData, tokenType?: string, property?: string) {
const credentials = this.getCredentials(credentialsType) as ICredentialDataDecryptedObject;
if (credentials === undefined) {
@ -133,8 +133,8 @@ export function requestOAuth(this: IAllExecuteFunctions, credentialsType: string
});
const oauthTokenData = credentials.oauthTokenData as clientOAuth2.Data;
const token = oAuthClient.createToken(oauthTokenData);
const token = oAuthClient.createToken(get(oauthTokenData, property as string) || oauthTokenData.accessToken, oauthTokenData.refreshToken, tokenType || oauthTokenData.tokenType, oauthTokenData);
// Signs the request by adding authorization headers or query parameters depending
// on the token-type used.
const newRequestOptions = token.sign(requestOptions as clientOAuth2.RequestObject);
@ -412,8 +412,8 @@ export function getExecutePollFunctions(workflow: Workflow, node: INode, additio
helpers: {
prepareBinaryData,
request: requestPromise,
requestOAuth(this: IAllExecuteFunctions, credentialsType: string, requestOptions: OptionsWithUri | requestPromise.RequestPromiseOptions): Promise<any> { // tslint:disable-line:no-any
return requestOAuth.call(this, credentialsType, requestOptions, node, additionalData);
requestOAuth(this: IAllExecuteFunctions, credentialsType: string, requestOptions: OptionsWithUri | requestPromise.RequestPromiseOptions, tokenType?: string, property?: string): Promise<any> { // tslint:disable-line:no-any
return requestOAuth.call(this, credentialsType, requestOptions, node, additionalData, tokenType, property);
},
returnJsonArray,
},
@ -466,8 +466,8 @@ export function getExecuteTriggerFunctions(workflow: Workflow, node: INode, addi
helpers: {
prepareBinaryData,
request: requestPromise,
requestOAuth(this: IAllExecuteFunctions, credentialsType: string, requestOptions: OptionsWithUri | requestPromise.RequestPromiseOptions): Promise<any> { // tslint:disable-line:no-any
return requestOAuth.call(this, credentialsType, requestOptions, node, additionalData);
requestOAuth(this: IAllExecuteFunctions, credentialsType: string, requestOptions: OptionsWithUri | requestPromise.RequestPromiseOptions, tokenType?: string, property?: string): Promise<any> { // tslint:disable-line:no-any
return requestOAuth.call(this, credentialsType, requestOptions, node, additionalData, tokenType, property);
},
returnJsonArray,
},
@ -547,8 +547,8 @@ export function getExecuteFunctions(workflow: Workflow, runExecutionData: IRunEx
helpers: {
prepareBinaryData,
request: requestPromise,
requestOAuth(this: IAllExecuteFunctions, credentialsType: string, requestOptions: OptionsWithUri | requestPromise.RequestPromiseOptions): Promise<any> { // tslint:disable-line:no-any
return requestOAuth.call(this, credentialsType, requestOptions, node, additionalData);
requestOAuth(this: IAllExecuteFunctions, credentialsType: string, requestOptions: OptionsWithUri | requestPromise.RequestPromiseOptions, tokenType?: string, property?: string): Promise<any> { // tslint:disable-line:no-any
return requestOAuth.call(this, credentialsType, requestOptions, node, additionalData, tokenType, property);
},
returnJsonArray,
},
@ -629,8 +629,8 @@ export function getExecuteSingleFunctions(workflow: Workflow, runExecutionData:
helpers: {
prepareBinaryData,
request: requestPromise,
requestOAuth(this: IAllExecuteFunctions, credentialsType: string, requestOptions: OptionsWithUri | requestPromise.RequestPromiseOptions): Promise<any> { // tslint:disable-line:no-any
return requestOAuth.call(this, credentialsType, requestOptions, node, additionalData);
requestOAuth(this: IAllExecuteFunctions, credentialsType: string, requestOptions: OptionsWithUri | requestPromise.RequestPromiseOptions, tokenType?: string, property?: string): Promise<any> { // tslint:disable-line:no-any
return requestOAuth.call(this, credentialsType, requestOptions, node, additionalData, tokenType, property);
},
},
};
@ -679,8 +679,8 @@ export function getLoadOptionsFunctions(workflow: Workflow, node: INode, additio
},
helpers: {
request: requestPromise,
requestOAuth(this: IAllExecuteFunctions, credentialsType: string, requestOptions: OptionsWithUri | requestPromise.RequestPromiseOptions): Promise<any> { // tslint:disable-line:no-any
return requestOAuth.call(this, credentialsType, requestOptions, node, additionalData);
requestOAuth(this: IAllExecuteFunctions, credentialsType: string, requestOptions: OptionsWithUri | requestPromise.RequestPromiseOptions, tokenType?: string, property?: string): Promise<any> { // tslint:disable-line:no-any
return requestOAuth.call(this, credentialsType, requestOptions, node, additionalData, tokenType, property);
},
},
};
@ -737,8 +737,8 @@ export function getExecuteHookFunctions(workflow: Workflow, node: INode, additio
},
helpers: {
request: requestPromise,
requestOAuth(this: IAllExecuteFunctions, credentialsType: string, requestOptions: OptionsWithUri | requestPromise.RequestPromiseOptions): Promise<any> { // tslint:disable-line:no-any
return requestOAuth.call(this, credentialsType, requestOptions, node, additionalData);
requestOAuth(this: IAllExecuteFunctions, credentialsType: string, requestOptions: OptionsWithUri | requestPromise.RequestPromiseOptions, tokenType?: string, property?: string): Promise<any> { // tslint:disable-line:no-any
return requestOAuth.call(this, credentialsType, requestOptions, node, additionalData, tokenType, property);
},
},
};
@ -822,8 +822,8 @@ export function getExecuteWebhookFunctions(workflow: Workflow, node: INode, addi
helpers: {
prepareBinaryData,
request: requestPromise,
requestOAuth(this: IAllExecuteFunctions, credentialsType: string, requestOptions: OptionsWithUri | requestPromise.RequestPromiseOptions): Promise<any> { // tslint:disable-line:no-any
return requestOAuth.call(this, credentialsType, requestOptions, node, additionalData);
requestOAuth(this: IAllExecuteFunctions, credentialsType: string, requestOptions: OptionsWithUri | requestPromise.RequestPromiseOptions, tokenType?: string, property?: string): Promise<any> { // tslint:disable-line:no-any
return requestOAuth.call(this, credentialsType, requestOptions, node, additionalData, tokenType, property);
},
returnJsonArray,
},

View file

@ -0,0 +1,57 @@
import {
ICredentialType,
NodePropertyTypes,
} from 'n8n-workflow';
//https://api.slack.com/authentication/oauth-v2
const userScopes = [
'chat:write',
'conversations:history',
'conversations:read',
'files:read',
'files:write',
'stars:read',
'stars:write',
];
export class SlackOAuth2Api implements ICredentialType {
name = 'slackOAuth2Api';
extends = [
'oAuth2Api',
];
displayName = 'Slack OAuth2 API';
properties = [
{
displayName: 'Authorization URL',
name: 'authUrl',
type: 'hidden' as NodePropertyTypes,
default: 'https://slack.com/oauth/v2/authorize',
},
{
displayName: 'Access Token URL',
name: 'accessTokenUrl',
type: 'hidden' as NodePropertyTypes,
default: 'https://slack.com/api/oauth.v2.access',
},
//https://api.slack.com/scopes
{
displayName: 'Scope',
name: 'scope',
type: 'hidden' as NodePropertyTypes,
default: 'chat:write',
},
{
displayName: 'Auth URI Query Parameters',
name: 'authQueryParameters',
type: 'hidden' as NodePropertyTypes,
default: `user_scope=${userScopes.join(' ')}`,
},
{
displayName: 'Authentication',
name: 'authentication',
type: 'hidden' as NodePropertyTypes,
default: 'body',
},
];
}

View file

@ -0,0 +1,914 @@
import { INodeProperties } from 'n8n-workflow';
export const channelOperations = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
displayOptions: {
show: {
resource: [
'channel',
],
},
},
options: [
{
name: 'Archive',
value: 'archive',
description: 'Archives a conversation.',
},
{
name: 'Close',
value: 'close',
description: 'Closes a direct message or multi-person direct message.',
},
{
name: 'Create',
value: 'create',
description: 'Initiates a public or private channel-based conversation',
},
{
name: 'Get',
value: 'get',
description: 'Get information about a channel.',
},
{
name: 'Get All',
value: 'getAll',
description: 'Get all channels in a Slack team.',
},
{
name: 'History',
value: 'history',
description: `Get a conversation's history of messages and events.`,
},
{
name: 'Invite',
value: 'invite',
description: 'Invite a user to a channel',
},
{
name: 'Join',
value: 'join',
description: 'Joins an existing conversation.',
},
{
name: 'Kick',
value: 'kick',
description: 'Removes a user from a channel.',
},
{
name: 'Leave',
value: 'leave',
description: 'Leaves a conversation.',
},
{
name: 'Open',
value: 'open',
description: 'Opens or resumes a direct message or multi-person direct message.',
},
{
name: 'Rename',
value: 'rename',
description: 'Renames a conversation.',
},
{
name: 'Replies',
value: 'replies',
description: 'Get a thread of messages posted to a channel',
},
{
name: 'Set Purpose',
value: 'setPurpose',
description: 'Sets the purpose for a conversation.',
},
{
name: 'Set Topic',
value: 'setTopic',
description: 'Sets the topic for a conversation.',
},
{
name: 'Unarchive',
value: 'unarchive',
description: 'Unarchives a conversation.',
},
],
default: 'create',
description: 'The operation to perform.',
},
] as INodeProperties[];
export const channelFields = [
/* -------------------------------------------------------------------------- */
/* channel:archive */
/* -------------------------------------------------------------------------- */
{
displayName: 'Channel',
name: 'channelId',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getChannels',
},
displayOptions: {
show: {
operation: [
'archive'
],
resource: [
'channel',
],
},
},
default: '',
required: true,
description: 'The name of the channel to archive.',
},
/* -------------------------------------------------------------------------- */
/* channel:close */
/* -------------------------------------------------------------------------- */
{
displayName: 'Channel',
name: 'channelId',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getChannels',
},
displayOptions: {
show: {
operation: [
'close'
],
resource: [
'channel',
],
},
},
default: '',
required: true,
description: 'The name of the channel to close.',
},
/* -------------------------------------------------------------------------- */
/* channel:create */
/* -------------------------------------------------------------------------- */
{
displayName: 'Channel',
name: 'channelId',
type: 'string',
default: '',
placeholder: 'Channel name',
displayOptions: {
show: {
operation: [
'create'
],
resource: [
'channel',
],
},
},
required: true,
description: 'The name of the channel to create.',
},
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: [
'channel',
],
operation: [
'create',
],
},
},
options: [
{
displayName: 'Is Private',
name: 'isPrivate',
type: 'boolean',
default: false,
description: 'Create a private channel instead of a public one',
},
{
displayName: 'Users',
name: 'users',
type: 'multiOptions',
typeOptions: {
loadOptionsMethod: 'getUsers',
},
default: [],
description: `Required for workspace apps. A list of between 1 and 30 human users that will be added to the newly-created conversation`,
},
]
},
/* -------------------------------------------------------------------------- */
/* channel:invite */
/* -------------------------------------------------------------------------- */
{
displayName: 'Channel',
name: 'channelId',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getChannels',
},
default: '',
displayOptions: {
show: {
operation: [
'invite'
],
resource: [
'channel',
],
},
},
required: true,
description: 'The ID of the channel to invite user to.',
},
{
displayName: 'User ID',
name: 'userId',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getUsers',
},
default: '',
displayOptions: {
show: {
operation: [
'invite'
],
resource: [
'channel',
],
},
},
required: true,
description: 'The ID of the user to invite into channel.',
},
/* -------------------------------------------------------------------------- */
/* channel:get */
/* -------------------------------------------------------------------------- */
{
displayName: 'Channel',
name: 'channelId',
type: 'string',
default: '',
displayOptions: {
show: {
operation: [
'get'
],
resource: [
'channel',
],
},
},
required: true,
description: 'Channel ID to learn more about',
},
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: [
'channel',
],
operation: [
'get',
],
},
},
options: [
{
displayName: 'Include Num of Members',
name: 'includeNumMembers',
type: 'boolean',
default: false,
},
]
},
/* -------------------------------------------------------------------------- */
/* channel:kick */
/* -------------------------------------------------------------------------- */
{
displayName: 'Channel',
name: 'channelId',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getChannels',
},
default: '',
placeholder: 'Channel name',
displayOptions: {
show: {
operation: [
'kick'
],
resource: [
'channel',
],
},
},
required: true,
description: 'The name of the channel to create.',
},
{
displayName: 'User',
name: 'userId',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getUsers',
},
displayOptions: {
show: {
operation: [
'kick'
],
resource: [
'channel',
],
},
},
default: '',
},
/* -------------------------------------------------------------------------- */
/* channel:join */
/* -------------------------------------------------------------------------- */
{
displayName: 'Channel',
name: 'channelId',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getChannels',
},
default: '',
placeholder: 'Channel name',
displayOptions: {
show: {
operation: [
'join'
],
resource: [
'channel',
],
},
},
required: true,
},
/* -------------------------------------------------------------------------- */
/* channel:getAll */
/* -------------------------------------------------------------------------- */
{
displayName: 'Return All',
name: 'returnAll',
type: 'boolean',
displayOptions: {
show: {
resource: [
'channel',
],
operation: [
'getAll',
],
},
},
default: false,
description: 'If all results should be returned or only up to a given limit.',
},
{
displayName: 'Limit',
name: 'limit',
type: 'number',
displayOptions: {
show: {
resource: [
'channel',
],
operation: [
'getAll',
],
returnAll: [
false,
],
},
},
typeOptions: {
minValue: 1,
maxValue: 100,
},
default: 50,
description: 'How many results to return.',
},
{
displayName: 'Filters',
name: 'filters',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: [
'channel',
],
operation: [
'getAll',
],
},
},
options: [
{
displayName: 'Exclude Archived',
name: 'excludeArchived',
type: 'boolean',
default: false,
description: 'Set to true to exclude archived channels from the list',
},
{
displayName: 'Types',
name: 'types',
type: 'multiOptions',
options: [
{
name: 'Public Channel',
value: 'public_channel'
},
{
name: 'Private Channel',
value: 'private_channel'
},
{
name: 'mpim',
value: 'mpim'
},
{
name: 'im',
value: 'im'
},
],
default: ['public_channel'],
description: 'Mix and match channel types',
},
]
},
/* -------------------------------------------------------------------------- */
/* channel:history */
/* -------------------------------------------------------------------------- */
{
displayName: 'Channel',
name: 'channelId',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getChannels',
},
default: '',
placeholder: 'Channel name',
displayOptions: {
show: {
operation: [
'history'
],
resource: [
'channel',
],
},
},
required: true,
description: 'The name of the channel to create.',
},
{
displayName: 'Return All',
name: 'returnAll',
type: 'boolean',
displayOptions: {
show: {
resource: [
'channel',
],
operation: [
'history',
],
},
},
default: false,
description: 'If all results should be returned or only up to a given limit.',
},
{
displayName: 'Limit',
name: 'limit',
type: 'number',
displayOptions: {
show: {
resource: [
'channel',
],
operation: [
'history',
],
returnAll: [
false,
],
},
},
typeOptions: {
minValue: 1,
maxValue: 100,
},
default: 50,
description: 'How many results to return.',
},
{
displayName: 'Filters',
name: 'filters',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: [
'channel',
],
operation: [
'history',
],
},
},
options: [
{
displayName: 'Inclusive',
name: 'inclusive',
type: 'boolean',
default: false,
description: 'Include messages with latest or oldest timestamp in results only when either timestamp is specified.',
},
{
displayName: 'Latest',
name: 'latest',
type: 'dateTime',
default: '',
description: 'End of time range of messages to include in results.',
},
{
displayName: 'Oldest',
name: 'oldest',
type: 'dateTime',
default: '',
description: 'Start of time range of messages to include in results.',
},
]
},
/* -------------------------------------------------------------------------- */
/* channel:leave */
/* -------------------------------------------------------------------------- */
{
displayName: 'Channel',
name: 'channelId',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getChannels',
},
displayOptions: {
show: {
operation: [
'leave'
],
resource: [
'channel',
],
},
},
default: '',
required: true,
description: 'The name of the channel to leave.',
},
/* -------------------------------------------------------------------------- */
/* channel:open */
/* -------------------------------------------------------------------------- */
{
displayName: 'Options',
name: 'options',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: [
'channel',
],
operation: [
'open',
],
},
},
options: [
{
displayName: 'Channel ID',
name: 'channelId',
type: 'string',
default: '',
description: `Resume a conversation by supplying an im or mpim's ID. Or provide the users field instead`,
},
{
displayName: 'Return IM',
name: 'returnIm',
type: 'boolean',
default: false,
description: 'Boolean, indicates you want the full IM channel definition in the response.',
},
{
displayName: 'Users',
name: 'users',
type: 'multiOptions',
typeOptions: {
loadOptionsMethod: 'getUsers',
},
default: [],
description: `If only one user is included, this creates a 1:1 DM. The ordering of the users is preserved whenever a multi-person direct message is returned. Supply a channel when not supplying users.`,
},
]
},
/* -------------------------------------------------------------------------- */
/* channel:rename */
/* -------------------------------------------------------------------------- */
{
displayName: 'Channel',
name: 'channelId',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getChannels',
},
displayOptions: {
show: {
operation: [
'rename'
],
resource: [
'channel',
],
},
},
default: '',
required: true,
description: 'The name of the channel to rename.',
},
{
displayName: 'Name',
name: 'name',
type: 'string',
displayOptions: {
show: {
operation: [
'rename'
],
resource: [
'channel',
],
},
},
default: '',
required: true,
description: 'New name for conversation.',
},
/* -------------------------------------------------------------------------- */
/* channel:replies */
/* -------------------------------------------------------------------------- */
{
displayName: 'Channel',
name: 'channelId',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getChannels',
},
default: '',
placeholder: 'Channel name',
displayOptions: {
show: {
operation: [
'replies'
],
resource: [
'channel',
],
},
},
required: true,
description: 'The name of the channel to create.',
},
{
displayName: 'TS',
name: 'ts',
type: 'string',
default: '',
displayOptions: {
show: {
operation: [
'replies'
],
resource: [
'channel',
],
},
},
required: true,
description: `Unique identifier of a thread's parent message.`,
},
{
displayName: 'Return All',
name: 'returnAll',
type: 'boolean',
displayOptions: {
show: {
resource: [
'channel',
],
operation: [
'replies',
],
},
},
default: false,
description: 'If all results should be returned or only up to a given limit.',
},
{
displayName: 'Limit',
name: 'limit',
type: 'number',
displayOptions: {
show: {
resource: [
'channel',
],
operation: [
'replies',
],
returnAll: [
false,
],
},
},
typeOptions: {
minValue: 1,
maxValue: 100,
},
default: 50,
description: 'How many results to return.',
},
{
displayName: 'Filters',
name: 'filters',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: [
'channel',
],
operation: [
'replies',
],
},
},
options: [
{
displayName: 'Inclusive',
name: 'inclusive',
type: 'boolean',
default: false,
description: 'Include messages with latest or oldest timestamp in results only when either timestamp is specified.',
},
{
displayName: 'Latest',
name: 'latest',
type: 'string',
default: '',
description: 'End of time range of messages to include in results.',
},
{
displayName: 'Oldest',
name: 'oldest',
type: 'string',
default: '',
description: 'Start of time range of messages to include in results.',
},
]
},
/* -------------------------------------------------------------------------- */
/* channel:setPurpose */
/* -------------------------------------------------------------------------- */
{
displayName: 'Channel',
name: 'channelId',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getChannels',
},
displayOptions: {
show: {
operation: [
'setPurpose'
],
resource: [
'channel',
],
},
},
default: '',
required: true,
description: 'Conversation to set the purpose of',
},
{
displayName: 'Purpose',
name: 'purpose',
type: 'string',
displayOptions: {
show: {
operation: [
'setPurpose'
],
resource: [
'channel',
],
},
},
default: '',
required: true,
description: 'A new, specialer purpose',
},
/* -------------------------------------------------------------------------- */
/* channel:setTopic */
/* -------------------------------------------------------------------------- */
{
displayName: 'Channel',
name: 'channelId',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getChannels',
},
displayOptions: {
show: {
operation: [
'setTopic'
],
resource: [
'channel',
],
},
},
default: '',
required: true,
description: 'Conversation to set the topic of',
},
{
displayName: 'Topic',
name: 'topic',
type: 'string',
displayOptions: {
show: {
operation: [
'setTopic'
],
resource: [
'channel',
],
},
},
default: '',
required: true,
description: 'The new topic string. Does not support formatting or linkification.',
},
/* -------------------------------------------------------------------------- */
/* channel:unarchive */
/* -------------------------------------------------------------------------- */
{
displayName: 'Channel',
name: 'channelId',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getChannels',
},
displayOptions: {
show: {
operation: [
'unarchive'
],
resource: [
'channel',
],
},
},
default: '',
required: true,
description: 'The ID of the channel to unarchive.',
},
] as INodeProperties[];

View file

@ -0,0 +1,322 @@
import { INodeProperties } from 'n8n-workflow';
export const fileOperations = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
displayOptions: {
show: {
resource: [
'file',
],
},
},
options: [
{
name: 'Get',
value: 'get',
description: 'Get a file info',
},
{
name: 'Get All',
value: 'getAll',
description: 'Get & filters team files.',
},
{
name: 'Upload',
value: 'upload',
description: 'Create or upload an existing file.',
},
],
default: 'upload',
description: 'The operation to perform.',
},
] as INodeProperties[];
export const fileFields = [
/* -------------------------------------------------------------------------- */
/* file:upload */
/* -------------------------------------------------------------------------- */
{
displayName: 'Binary Data',
name: 'binaryData',
type: 'boolean',
default: false,
displayOptions: {
show: {
operation: [
'upload'
],
resource: [
'file',
],
},
},
description: 'If the data to upload should be taken from binary field.',
},
{
displayName: 'File Content',
name: 'fileContent',
type: 'string',
default: '',
displayOptions: {
show: {
operation: [
'upload'
],
resource: [
'file',
],
binaryData: [
false
],
},
},
placeholder: '',
description: 'The text content of the file to upload.',
},
{
displayName: 'Binary Property',
name: 'binaryPropertyName',
type: 'string',
default: 'data',
required: true,
displayOptions: {
show: {
operation: [
'upload'
],
resource: [
'file',
],
binaryData: [
true
],
},
},
placeholder: '',
description: 'Name of the binary property which contains<br />the data for the file to be uploaded.',
},
{
displayName: 'Options',
name: 'options',
type: 'collection',
displayOptions: {
show: {
operation: [
'upload'
],
resource: [
'file',
],
},
},
default: {},
description: 'Other options to set',
placeholder: 'Add options',
options: [
{
displayName: 'Channels',
name: 'channelIds',
type: 'multiOptions',
typeOptions: {
loadOptionsMethod: 'getChannels',
},
default: [],
description: 'The channels to send the file to.',
},
{
displayName: 'File Name',
name: 'fileName',
type: 'string',
default: '',
description: 'Filename of file.',
},
{
displayName: 'Initial Comment',
name: 'initialComment',
type: 'string',
default: '',
description: 'The message text introducing the file in specified channels.',
},
{
displayName: 'Thread TS',
name: 'threadTs',
type: 'string',
default: '',
description: `Provide another message's ts value to upload this file as a reply. Never use a reply's ts value; use its parent instead.`,
},
{
displayName: 'Title',
name: 'title',
type: 'string',
default: '',
description: 'Title of file.',
},
],
},
/* ----------------------------------------------------------------------- */
/* file:getAll */
/* ----------------------------------------------------------------------- */
{
displayName: 'Return All',
name: 'returnAll',
type: 'boolean',
displayOptions: {
show: {
resource: [
'file',
],
operation: [
'getAll',
],
},
},
default: false,
description: 'If all results should be returned or only up to a given limit.',
},
{
displayName: 'Limit',
name: 'limit',
type: 'number',
displayOptions: {
show: {
resource: [
'file',
],
operation: [
'getAll',
],
returnAll: [
false,
],
},
},
typeOptions: {
minValue: 1,
maxValue: 100,
},
default: 50,
description: 'How many results to return.',
},
{
displayName: 'Filters',
name: 'filters',
type: 'collection',
displayOptions: {
show: {
operation: [
'getAll'
],
resource: [
'file',
],
},
},
default: {},
placeholder: 'Add Field',
options: [
{
displayName: 'Channel',
name: 'channelId',
type: 'options',
default: '',
typeOptions: {
loadOptionsMethod: 'getChannels',
},
description: 'Channel containing the file to be listed.',
},
{
displayName: 'Show Files Hidden By Limit',
name: 'showFilesHidden',
type: 'boolean',
default: false,
description: 'Show truncated file info for files hidden due to being too old, and the team who owns the file being over the file limit.',
},
{
displayName: 'TS From',
name: 'tsFrom',
type: 'string',
default: '',
description: 'Filter files created after this timestamp (inclusive).',
},
{
displayName: 'TS To',
name: 'tsTo',
type: 'string',
default: '',
description: 'Filter files created before this timestamp (inclusive).',
},
{
displayName: 'Types',
name: 'types',
type: 'multiOptions',
options: [
{
name: 'All',
value: 'all',
},
{
name: 'Spaces',
value: 'spaces',
},
{
name: 'Snippets',
value: 'snippets',
},
{
name: 'Images',
value: 'images',
},
{
name: 'Google Docs',
value: 'gdocs',
},
{
name: 'Zips',
value: 'zips',
},
{
name: 'pdfs',
value: 'pdfs',
},
],
default: ['all'],
description: 'Filter files by type',
},
{
displayName: 'User',
name: 'userId',
type: 'options',
default: '',
typeOptions: {
loadOptionsMethod: 'getUsers',
},
description: 'Filter files created by a single user.',
},
],
},
/* ----------------------------------------------------------------------- */
/* file:get */
/* ----------------------------------------------------------------------- */
{
displayName: 'File ID',
name: 'fileId',
type: 'string',
displayOptions: {
show: {
resource: [
'file',
],
operation: [
'get',
],
},
},
default: '',
},
] as INodeProperties[];

View file

@ -0,0 +1,86 @@
import {
OptionsWithUri,
} from 'request';
import {
IExecuteFunctions,
IExecuteSingleFunctions,
ILoadOptionsFunctions,
} from 'n8n-core';
import {
IDataObject
} from 'n8n-workflow';
import * as _ from 'lodash';
export async function slackApiRequest(this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method: string, resource: string, body: object = {}, query: object = {}, headers: {} | undefined = undefined, option: {} = {}): Promise<any> { // tslint:disable-line:no-any
const authenticationMethod = this.getNodeParameter('authentication', 0, 'accessToken') as string;
let options: OptionsWithUri = {
method,
headers: headers || {
'Content-Type': 'application/json; charset=utf-8'
},
body,
qs: query,
uri: `https://slack.com/api${resource}`,
json: true
};
options = Object.assign({}, options, option);
if (Object.keys(body).length === 0) {
delete options.body;
}
if (Object.keys(query).length === 0) {
delete options.qs;
}
try {
if (authenticationMethod === 'accessToken') {
const credentials = this.getCredentials('slackApi');
if (credentials === undefined) {
throw new Error('No credentials got returned!');
}
options.headers!.Authorization = `Bearer ${credentials.accessToken}`;
//@ts-ignore
return await this.helpers.request(options);
} else {
//@ts-ignore
return await this.helpers.requestOAuth.call(this, 'slackOAuth2Api', options, 'bearer', 'authed_user.access_token');
}
} catch (error) {
if (error.statusCode === 401) {
// Return a clear error
throw new Error('The Slack credentials are not valid!');
}
if (error.response && error.response.body && error.response.body.message) {
// Try to return the error prettier
throw new Error(`Slack error response [${error.statusCode}]: ${error.response.body.message}`);
}
// If that data does not exist for some reason return the actual error
throw error;
}
}
export async function slackApiRequestAllItems(this: IExecuteFunctions | ILoadOptionsFunctions, propertyName: string ,method: string, endpoint: string, body: any = {}, query: IDataObject = {}): Promise<any> { // tslint:disable-line:no-any
const returnData: IDataObject[] = [];
let responseData;
query.page = 1;
query.count = 100;
do {
responseData = await slackApiRequest.call(this, method, endpoint, body, query);
query.cursor = encodeURIComponent(_.get(responseData, 'response_metadata.next_cursor'));
query.page++;
returnData.push.apply(returnData, responseData[propertyName]);
} while (
(responseData.response_metadata !== undefined &&
responseData.response_metadata.mext_cursor !== undefined &&
responseData.response_metadata.next_cursor !== '' &&
responseData.response_metadata.next_cursor !== null) ||
(responseData.paging !== undefined &&
responseData.paging.pages !== undefined &&
responseData.paging.page !== undefined &&
responseData.paging.page < responseData.paging.pages
)
);
return returnData;
}

View file

@ -0,0 +1,735 @@
import { INodeProperties } from 'n8n-workflow';
export const messageOperations = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
displayOptions: {
show: {
resource: [
'message',
],
},
},
options: [
{
name: 'Post',
value: 'post',
description: 'Post a message into a channel',
},
{
name: 'Update',
value: 'update',
description: 'Updates a message.',
},
],
default: 'post',
description: 'The operation to perform.',
},
] as INodeProperties[];
export const messageFields = [
/* -------------------------------------------------------------------------- */
/* message:post */
/* -------------------------------------------------------------------------- */
{
displayName: 'Channel',
name: 'channel',
type: 'string',
default: '',
placeholder: 'Channel name',
displayOptions: {
show: {
operation: [
'post',
],
resource: [
'message',
],
},
},
required: true,
description: 'The channel to send the message to.',
},
{
displayName: 'Text',
name: 'text',
type: 'string',
typeOptions: {
alwaysOpenEditWindow: true,
},
default: '',
displayOptions: {
show: {
operation: [
'post',
],
resource: [
'message',
],
},
},
description: 'The text to send.',
},
{
displayName: 'As User',
name: 'as_user',
type: 'boolean',
default: false,
displayOptions: {
show: {
authentication: [
'accessToken',
],
operation: [
'post'
],
resource: [
'message',
],
},
},
description: 'Post the message as authenticated user instead of bot.',
},
{
displayName: 'User Name',
name: 'username',
type: 'string',
default: '',
displayOptions: {
show: {
as_user: [
false,
],
operation: [
'post',
],
resource: [
'message',
],
},
},
description: 'Set the bot\'s user name.',
},
{
displayName: 'Attachments',
name: 'attachments',
type: 'collection',
typeOptions: {
multipleValues: true,
multipleValueButtonText: 'Add attachment',
},
displayOptions: {
show: {
operation: [
'post',
],
resource: [
'message',
],
},
},
default: {}, // TODO: Remove comment: has to make default array for the main property, check where that happens in UI
description: 'The attachment to add',
placeholder: 'Add attachment item',
options: [
{
displayName: 'Fallback Text',
name: 'fallback',
type: 'string',
typeOptions: {
alwaysOpenEditWindow: true,
},
default: '',
description: 'Required plain-text summary of the attachment.',
},
{
displayName: 'Text',
name: 'text',
type: 'string',
typeOptions: {
alwaysOpenEditWindow: true,
},
default: '',
description: 'Text to send.',
},
{
displayName: 'Title',
name: 'title',
type: 'string',
typeOptions: {
alwaysOpenEditWindow: true,
},
default: '',
description: 'Title of the message.',
},
{
displayName: 'Title Link',
name: 'title_link',
type: 'string',
typeOptions: {
alwaysOpenEditWindow: true,
},
default: '',
description: 'Link of the title.',
},
{
displayName: 'Color',
name: 'color',
type: 'color',
default: '#ff0000',
description: 'Color of the line left of text.',
},
{
displayName: 'Pretext',
name: 'pretext',
type: 'string',
typeOptions: {
alwaysOpenEditWindow: true,
},
default: '',
description: 'Text which appears before the message block.',
},
{
displayName: 'Author Name',
name: 'author_name',
type: 'string',
default: '',
description: 'Name that should appear.',
},
{
displayName: 'Author Link',
name: 'author_link',
type: 'string',
typeOptions: {
alwaysOpenEditWindow: true,
},
default: '',
description: 'Link for the author.',
},
{
displayName: 'Author Icon',
name: 'author_icon',
type: 'string',
typeOptions: {
alwaysOpenEditWindow: true,
},
default: '',
description: 'Icon which should appear for the user.',
},
{
displayName: 'Image URL',
name: 'image_url',
type: 'string',
typeOptions: {
alwaysOpenEditWindow: true,
},
default: '',
description: 'URL of image.',
},
{
displayName: 'Thumbnail URL',
name: 'thumb_url',
type: 'string',
typeOptions: {
alwaysOpenEditWindow: true,
},
default: '',
description: 'URL of thumbnail.',
},
{
displayName: 'Footer',
name: 'footer',
type: 'string',
typeOptions: {
alwaysOpenEditWindow: true,
},
default: '',
description: 'Text of footer to add.',
},
{
displayName: 'Footer Icon',
name: 'footer_icon',
type: 'string',
typeOptions: {
alwaysOpenEditWindow: true,
},
default: '',
description: 'Icon which should appear next to footer.',
},
{
displayName: 'Timestamp',
name: 'ts',
type: 'dateTime',
default: '',
description: 'Time message relates to.',
},
{
displayName: 'Fields',
name: 'fields',
placeholder: 'Add Fields',
description: 'Fields to add to message.',
type: 'fixedCollection',
typeOptions: {
multipleValues: true,
},
default: {},
options: [
{
name: 'item',
displayName: 'Item',
values: [
{
displayName: 'Title',
name: 'title',
type: 'string',
default: '',
description: 'Title of the item.',
},
{
displayName: 'Value',
name: 'value',
type: 'string',
default: '',
description: 'Value of the item.',
},
{
displayName: 'Short',
name: 'short',
type: 'boolean',
default: true,
description: 'If items can be displayed next to each other.',
},
]
},
],
}
],
},
{
displayName: 'Other Options',
name: 'otherOptions',
type: 'collection',
displayOptions: {
show: {
operation: [
'post'
],
resource: [
'message',
],
},
},
default: {},
description: 'Other options to set',
placeholder: 'Add options',
options: [
{
displayName: 'Icon Emoji',
name: 'icon_emoji',
type: 'string',
displayOptions: {
show: {
'/as_user': [
false
],
'/operation': [
'post'
],
'/resource': [
'message',
],
},
},
default: '',
description: 'Emoji to use as the icon for this message. Overrides icon_url.',
},
{
displayName: 'Icon URL',
name: 'icon_url',
type: 'string',
displayOptions: {
show: {
'/as_user': [
false
],
'/operation': [
'post'
],
'/resource': [
'message',
],
},
},
default: '',
description: 'URL to an image to use as the icon for this message.',
},
{
displayName: 'Make Reply',
name: 'thread_ts',
type: 'string',
default: '',
description: 'Provide another message\'s ts value to make this message a reply.',
},
{
displayName: 'Unfurl Links',
name: 'unfurl_links',
type: 'boolean',
default: false,
description: 'Pass true to enable unfurling of primarily text-based content.',
},
{
displayName: 'Unfurl Media',
name: 'unfurl_media',
type: 'boolean',
default: true,
description: 'Pass false to disable unfurling of media content.',
},
{
displayName: 'Markdown',
name: 'mrkdwn',
type: 'boolean',
default: true,
description: 'Use Slack Markdown parsing.',
},
{
displayName: 'Reply Broadcast',
name: 'reply_broadcast',
type: 'boolean',
default: false,
description: 'Used in conjunction with thread_ts and indicates whether reply should be made visible to everyone in the channel or conversation.',
},
{
displayName: 'Link Names',
name: 'link_names',
type: 'boolean',
default: false,
description: 'Find and link channel names and usernames.',
},
],
},
/* ----------------------------------------------------------------------- */
/* message:update */
/* ----------------------------------------------------------------------- */
{
displayName: 'Channel',
name: 'channelId',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getChannels',
},
required: true,
default: '',
displayOptions: {
show: {
resource: [
'message',
],
operation: [
'update',
]
},
},
description: 'Channel containing the message to be updated.',
},
{
displayName: 'Text',
name: 'text',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: [
'message',
],
operation: [
'update',
]
},
},
description: `New text for the message, using the default formatting rules. It's not required when presenting attachments.`,
},
{
displayName: 'TS',
name: 'ts',
type: 'string',
required: true,
default: '',
displayOptions: {
show: {
resource: [
'message',
],
operation: [
'update',
]
},
},
description: `Timestamp of the message to be updated.`,
},
{
displayName: 'As User',
name: 'as_user',
type: 'boolean',
default: false,
displayOptions: {
show: {
authentication: [
'accessToken',
],
operation: [
'update'
],
resource: [
'message',
],
},
},
description: 'Pass true to update the message as the authed user. Bot users in this context are considered authed users.',
},
{
displayName: 'Update Fields',
name: 'updateFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: [
'message',
],
operation: [
'update',
],
},
},
options: [
{
displayName: 'Link Names',
name: 'link_names',
type: 'boolean',
default: false,
description: 'Find and link channel names and usernames.',
},
{
displayName: 'Parse',
name: 'parse',
type: 'options',
options: [
{
name: 'Client',
value: 'client',
},
{
name: 'Full',
value: 'full',
},
{
name: 'None',
value: 'none',
},
],
default: 'client',
description: 'Change how messages are treated',
},
],
},
{
displayName: 'Attachments',
name: 'attachments',
type: 'collection',
typeOptions: {
multipleValues: true,
multipleValueButtonText: 'Add attachment',
},
displayOptions: {
show: {
operation: [
'update'
],
resource: [
'message',
],
},
},
default: {}, // TODO: Remove comment: has to make default array for the main property, check where that happens in UI
description: 'The attachment to add',
placeholder: 'Add attachment item',
options: [
{
displayName: 'Fallback Text',
name: 'fallback',
type: 'string',
typeOptions: {
alwaysOpenEditWindow: true,
},
default: '',
description: 'Required plain-text summary of the attachment.',
},
{
displayName: 'Text',
name: 'text',
type: 'string',
typeOptions: {
alwaysOpenEditWindow: true,
},
default: '',
description: 'Text to send.',
},
{
displayName: 'Title',
name: 'title',
type: 'string',
typeOptions: {
alwaysOpenEditWindow: true,
},
default: '',
description: 'Title of the message.',
},
{
displayName: 'Title Link',
name: 'title_link',
type: 'string',
typeOptions: {
alwaysOpenEditWindow: true,
},
default: '',
description: 'Link of the title.',
},
{
displayName: 'Color',
name: 'color',
type: 'color',
default: '#ff0000',
description: 'Color of the line left of text.',
},
{
displayName: 'Pretext',
name: 'pretext',
type: 'string',
typeOptions: {
alwaysOpenEditWindow: true,
},
default: '',
description: 'Text which appears before the message block.',
},
{
displayName: 'Author Name',
name: 'author_name',
type: 'string',
default: '',
description: 'Name that should appear.',
},
{
displayName: 'Author Link',
name: 'author_link',
type: 'string',
typeOptions: {
alwaysOpenEditWindow: true,
},
default: '',
description: 'Link for the author.',
},
{
displayName: 'Author Icon',
name: 'author_icon',
type: 'string',
typeOptions: {
alwaysOpenEditWindow: true,
},
default: '',
description: 'Icon which should appear for the user.',
},
{
displayName: 'Image URL',
name: 'image_url',
type: 'string',
typeOptions: {
alwaysOpenEditWindow: true,
},
default: '',
description: 'URL of image.',
},
{
displayName: 'Thumbnail URL',
name: 'thumb_url',
type: 'string',
typeOptions: {
alwaysOpenEditWindow: true,
},
default: '',
description: 'URL of thumbnail.',
},
{
displayName: 'Footer',
name: 'footer',
type: 'string',
typeOptions: {
alwaysOpenEditWindow: true,
},
default: '',
description: 'Text of footer to add.',
},
{
displayName: 'Footer Icon',
name: 'footer_icon',
type: 'string',
typeOptions: {
alwaysOpenEditWindow: true,
},
default: '',
description: 'Icon which should appear next to footer.',
},
{
displayName: 'Timestamp',
name: 'ts',
type: 'dateTime',
default: '',
description: 'Time message relates to.',
},
{
displayName: 'Fields',
name: 'fields',
placeholder: 'Add Fields',
description: 'Fields to add to message.',
type: 'fixedCollection',
typeOptions: {
multipleValues: true,
},
default: {},
options: [
{
name: 'item',
displayName: 'Item',
values: [
{
displayName: 'Title',
name: 'title',
type: 'string',
default: '',
description: 'Title of the item.',
},
{
displayName: 'Value',
name: 'value',
type: 'string',
default: '',
description: 'Value of the item.',
},
{
displayName: 'Short',
name: 'short',
type: 'boolean',
default: true,
description: 'If items can be displayed next to each other.',
},
]
},
],
}
],
},
] as INodeProperties[];

View file

@ -0,0 +1,7 @@
export interface IAttachment {
fields: {
item?: object[];
};
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,191 @@
import { INodeProperties } from 'n8n-workflow';
export const starOperations = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
displayOptions: {
show: {
resource: [
'star',
],
},
},
options: [
{
name: 'Add',
value: 'add',
description: 'Add a star to an item.',
},
{
name: 'Delete',
value: 'delete',
description: 'Delete a star from an item.',
},
{
name: 'Get All',
value: 'getAll',
description: 'Get all stars of autenticated user.',
},
],
default: 'add',
description: 'The operation to perform.',
},
] as INodeProperties[];
export const starFields = [
/* -------------------------------------------------------------------------- */
/* star:add */
/* -------------------------------------------------------------------------- */
{
displayName: 'Options',
name: 'options',
type: 'collection',
displayOptions: {
show: {
operation: [
'add'
],
resource: [
'star',
],
},
},
default: {},
description: 'Options to set',
placeholder: 'Add options',
options: [
{
displayName: 'Channel ID',
name: 'channelId',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getChannels',
},
default: '',
description: 'Channel to add star to, or channel where the message to add star to was posted (used with timestamp).',
},
{
displayName: 'File ID',
name: 'fileId',
type: 'string',
default: '',
description: 'File to add star to.',
},
{
displayName: 'File Comment',
name: 'fileComment',
type: 'string',
default: '',
description: 'File comment to add star to.',
},
{
displayName: 'Timestamp',
name: 'timestamp',
type: 'string',
default: '',
description: 'Timestamp of the message to add star to.',
},
],
},
/* ----------------------------------------------------------------------- */
/* star:delete */
/* ----------------------------------------------------------------------- */
{
displayName: 'Options',
name: 'options',
type: 'collection',
displayOptions: {
show: {
operation: [
'delete'
],
resource: [
'star',
],
},
},
default: {},
description: 'Options to set',
placeholder: 'Add options',
options: [
{
displayName: 'Channel ID',
name: 'channelId',
type: 'options',
typeOptions: {
loadOptionsMethod: 'getChannels',
},
default: '',
description: 'Channel to add star to, or channel where the message to add star to was posted (used with timestamp).',
},
{
displayName: 'File ID',
name: 'fileId',
type: 'string',
default: '',
description: 'File to add star to.',
},
{
displayName: 'File Comment',
name: 'fileComment',
type: 'string',
default: '',
description: 'File comment to add star to.',
},
{
displayName: 'Timestamp',
name: 'timestamp',
type: 'string',
default: '',
description: 'Timestamp of the message to add star to.',
},
],
},
/* -------------------------------------------------------------------------- */
/* star:getAll */
/* -------------------------------------------------------------------------- */
{
displayName: 'Return All',
name: 'returnAll',
type: 'boolean',
displayOptions: {
show: {
resource: [
'star',
],
operation: [
'getAll',
],
},
},
default: false,
description: 'If all results should be returned or only up to a given limit.',
},
{
displayName: 'Limit',
name: 'limit',
type: 'number',
displayOptions: {
show: {
resource: [
'star',
],
operation: [
'getAll',
],
returnAll: [
false,
],
},
},
typeOptions: {
minValue: 1,
maxValue: 100,
},
default: 50,
description: 'How many results to return.',
},
] as INodeProperties[];

View file

@ -68,6 +68,7 @@
"dist/credentials/ShopifyApi.credentials.js",
"dist/credentials/SalesforceOAuth2Api.credentials.js",
"dist/credentials/SlackApi.credentials.js",
"dist/credentials/SlackOAuth2Api.credentials.js",
"dist/credentials/Smtp.credentials.js",
"dist/credentials/StripeApi.credentials.js",
"dist/credentials/TelegramApi.credentials.js",