diff --git a/packages/nodes-base/credentials/SentryIoApi.credentials.ts b/packages/nodes-base/credentials/SentryIoApi.credentials.ts new file mode 100644 index 0000000000..576f27f2f0 --- /dev/null +++ b/packages/nodes-base/credentials/SentryIoApi.credentials.ts @@ -0,0 +1,17 @@ +import { + ICredentialType, + NodePropertyTypes, +} from 'n8n-workflow'; + +export class SentryIoApi implements ICredentialType { + name = 'sentryIoApi'; + displayName = 'Sentry.io API'; + properties = [ + { + displayName: 'Token', + name: 'token', + type: 'string' as NodePropertyTypes, + default: '', + }, + ]; +} diff --git a/packages/nodes-base/credentials/SentryIoOAuth2Api.credentials.ts b/packages/nodes-base/credentials/SentryIoOAuth2Api.credentials.ts new file mode 100644 index 0000000000..3948be6c66 --- /dev/null +++ b/packages/nodes-base/credentials/SentryIoOAuth2Api.credentials.ts @@ -0,0 +1,46 @@ +import { + ICredentialType, + NodePropertyTypes, +} from 'n8n-workflow'; + +export class SentryIoOAuth2Api implements ICredentialType { + name = 'sentryIoOAuth2Api'; + extends = [ + 'oAuth2Api', + ]; + displayName = 'Sentry.io OAuth2 API'; + properties = [ + { + displayName: 'Authorization URL', + name: 'authUrl', + type: 'hidden' as NodePropertyTypes, + default: 'https://sentry.io/oauth/authorize/', + required: true, + }, + { + displayName: 'Access Token URL', + name: 'accessTokenUrl', + type: 'hidden' as NodePropertyTypes, + default: 'https://sentry.io/oauth/token/', + required: true, + }, + { + displayName: 'Scope', + name: 'scope', + type: 'hidden' as NodePropertyTypes, + default: 'event:admin event:read org:read project:read project:releases team:read event:write org:admin project:write team:write project:admin team:admin', + }, + { + displayName: 'Auth URI Query Parameters', + name: 'authQueryParameters', + type: 'hidden' as NodePropertyTypes, + default: '', + }, + { + displayName: 'Authentication', + name: 'authentication', + type: 'hidden' as NodePropertyTypes, + default: 'body', + }, + ]; +} diff --git a/packages/nodes-base/nodes/Github/Github.node.ts b/packages/nodes-base/nodes/Github/Github.node.ts index 408c0a4360..22bc7c0e70 100644 --- a/packages/nodes-base/nodes/Github/Github.node.ts +++ b/packages/nodes-base/nodes/Github/Github.node.ts @@ -14,7 +14,6 @@ import { getFileSha, } from './GenericFunctions'; - export class Github implements INodeType { description: INodeTypeDescription = { displayName: 'GitHub', diff --git a/packages/nodes-base/nodes/SentryIo/EventDescription.ts b/packages/nodes-base/nodes/SentryIo/EventDescription.ts new file mode 100644 index 0000000000..64aa81b8c6 --- /dev/null +++ b/packages/nodes-base/nodes/SentryIo/EventDescription.ts @@ -0,0 +1,204 @@ +import { + INodeProperties, +} from 'n8n-workflow'; + +export const eventOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'event', + ], + }, + }, + options: [ + { + name: 'Get', + value: 'get', + description: 'Get event by ID', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Get all events', + } + ], + default: 'get', + description: 'The operation to perform', + }, +] as INodeProperties[]; + +export const eventFields = [ + /* -------------------------------------------------------------------------- */ + /* event:getAll */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Organization Slug', + name: 'organizationSlug', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getOrganizations', + }, + default: '', + displayOptions: { + show: { + resource: [ + 'event', + ], + operation: [ + 'getAll', + ], + }, + }, + required: true, + description: 'The slug of the organization the events belong to', + }, + { + displayName: 'Project Slug', + name: 'projectSlug', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getProjects', + loadOptionsDependsOn: [ + 'organizationSlug', + ], + }, + default: '', + displayOptions: { + show: { + resource: [ + 'event', + ], + operation: [ + 'getAll', + ], + }, + }, + required: true, + description: 'The slug of the project the events belong to', + }, + { + displayName: 'Full', + name: 'full', + type: 'boolean', + default: true, + displayOptions: { + show: { + resource: [ + 'event', + ], + operation: [ + 'getAll', + ], + }, + }, + description: 'If this is set to true, then the event payload will include the full event body, including the stack trace', + }, + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'event', + ], + }, + }, + default: false, + description: 'If all results should be returned or only up to a given limit', + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'event', + ], + returnAll: [ + false, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 500, + }, + default: 100, + description: 'How many results to return', + }, + /* -------------------------------------------------------------------------- */ + /* event:get */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Organization Slug', + name: 'organizationSlug', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getOrganizations', + }, + default: '', + displayOptions: { + show: { + resource: [ + 'event', + ], + operation: [ + 'get', + ], + }, + }, + required: true, + description: 'The slug of the organization the events belong to', + }, + { + displayName: 'Project Slug', + name: 'projectSlug', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getProjects', + }, + default: '', + displayOptions: { + show: { + resource: [ + 'event', + ], + operation: [ + 'get', + ], + }, + }, + required: true, + description: 'The slug of the project the events belong to', + }, + { + displayName: 'Event ID', + name: 'eventId', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'event', + ], + operation: [ + 'get', + ], + }, + }, + required: true, + description: 'The id of the event to retrieve (either the numeric primary-key or the hexadecimal id as reported by the raven client).', + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/SentryIo/GenericFunctions.ts b/packages/nodes-base/nodes/SentryIo/GenericFunctions.ts new file mode 100644 index 0000000000..f578f35cad --- /dev/null +++ b/packages/nodes-base/nodes/SentryIo/GenericFunctions.ts @@ -0,0 +1,108 @@ +import { + OptionsWithUri + } from 'request'; + +import { + IExecuteFunctions, + IExecuteSingleFunctions, + IHookFunctions, + ILoadOptionsFunctions, + IWebhookFunctions, +} from 'n8n-core'; + +import { + IDataObject, +} from 'n8n-workflow'; + +export async function sentryIoApiRequest(this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions | IWebhookFunctions, method: string, resource: string, body: any = {}, qs: IDataObject = {}, uri?: string, option: IDataObject = {}): Promise { // tslint:disable-line:no-any + const authentication = this.getNodeParameter('authentication', 0); + + const options: OptionsWithUri = { + headers: {}, + method, + qs, + body, + uri: uri ||`https://sentry.io${resource}`, + json: true + }; + if (!Object.keys(body).length) { + delete options.body; + } + + if (Object.keys(option).length !== 0) { + Object.assign(options, option); + } + + if (options.qs.limit) { + delete options.qs.limit; + } + + try { + if (authentication === 'accessToken') { + + const credentials = this.getCredentials('sentryIoApi'); + + options.headers = { + Authorization: `Bearer ${credentials?.token}`, + }; + + console.log('options'); + console.log(options); + + + //@ts-ignore + return this.helpers.request(options); + + } else { + return await this.helpers.requestOAuth2!.call(this, 'sentryIoOAuth2Api', options); + } + + } catch (error) { + throw new Error(`Sentry.io Error: ${error}`); + } +} + +export async function sentryApiRequestAllItems(this: IHookFunctions | IExecuteFunctions| ILoadOptionsFunctions, method: string, resource: string, body: any = {}, query: IDataObject = {}): Promise { // tslint:disable-line:no-any + + const returnData: IDataObject[] = []; + + let responseData; + + let link; + + let uri: string | undefined; + + do { + responseData = await sentryIoApiRequest.call(this, method, resource, body, query, uri, { resolveWithFullResponse: true }); + link = responseData.headers.link; + uri = getNext(link); + returnData.push.apply(returnData, responseData.body); + if (query.limit && (query.limit >= returnData.length)) { + return; + } + } while ( + hasMore(link) + ); + + return returnData; +} + +function getNext(link: string) { + if (link === undefined) { + return; + } + const next = link.split(',')[1]; + if (next.includes('rel="next"')) { + return next.split(';')[0].replace('<', '').replace('>','').trim(); + } +} + +function hasMore(link: string) { + if (link === undefined) { + return; + } + const next = link.split(',')[1]; + if (next.includes('rel="next"')) { + return next.includes('results="true"'); + } +} diff --git a/packages/nodes-base/nodes/SentryIo/Interface.ts b/packages/nodes-base/nodes/SentryIo/Interface.ts new file mode 100644 index 0000000000..8e9b7f46cd --- /dev/null +++ b/packages/nodes-base/nodes/SentryIo/Interface.ts @@ -0,0 +1,20 @@ +export interface ICommit { + id: string; + repository?: string; + message?: string; + patch_set?: IPatchSet[]; + author_name?: string; + author_email?: string; + timestamp?: Date; +} + +export interface IPatchSet { + path: string; + type: string; +} + +export interface IRef { + commit: string; + repository: string; + previousCommit?: string; +} diff --git a/packages/nodes-base/nodes/SentryIo/IssueDescription.ts b/packages/nodes-base/nodes/SentryIo/IssueDescription.ts new file mode 100644 index 0000000000..cc2cb08932 --- /dev/null +++ b/packages/nodes-base/nodes/SentryIo/IssueDescription.ts @@ -0,0 +1,300 @@ +import { + INodeProperties, +} from 'n8n-workflow'; + +export const issueOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'issue', + ], + }, + }, + options: [ + { + name: 'Delete', + value: 'delete', + description: 'Delete an issue', + }, + { + name: 'Get', + value: 'get', + description: 'Get issue by ID', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Get all issues', + }, + { + name: 'Update', + value: 'update', + description: 'Update an issue', + }, + ], + default: 'get', + description: 'The operation to perform', + }, +] as INodeProperties[]; + +export const issueFields = [ + /* -------------------------------------------------------------------------- */ + /* issue:get/delete */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Issue ID', + name: 'issueId', + type: 'string', + default: '', + placeholder: '1234', + displayOptions: { + show: { + resource: [ + 'issue', + ], + operation: [ + 'get', + 'delete', + ], + }, + }, + required: true, + description: 'ID of issue to get', + }, + /* -------------------------------------------------------------------------- */ + /* issue:getAll */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Organization Slug', + name: 'organizationSlug', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getOrganizations', + }, + default: '', + displayOptions: { + show: { + resource: [ + 'issue', + ], + operation: [ + 'getAll', + ], + }, + }, + required: true, + description: 'The slug of the organization the issues belong to', + }, + { + displayName: 'Project Slug', + name: 'projectSlug', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getProjects', + loadOptionsDependsOn: [ + 'organizationSlug', + ], + }, + default: '', + displayOptions: { + show: { + resource: [ + 'issue', + ], + operation: [ + 'getAll', + ], + }, + }, + required: true, + description: 'The slug of the project the issues belong to', + }, + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'issue', + ], + }, + }, + default: false, + description: 'If all results should be returned or only up to a given limit', + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'issue', + ], + returnAll: [ + false, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 500, + }, + default: 100, + description: 'How many results to return', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'issue', + ], + operation: [ + 'getAll', + ], + }, + }, + options: [ + { + displayName: 'Stats Period', + name: 'statsPeriod', + type: 'options', + default: '', + description: 'Time period of stats', + options: [ + { + name: '14 Days', + value: '14d' + }, + { + name: '24 Hours', + value: '24h' + }, + ] + }, + { + displayName: 'Short ID lookup', + name: 'shortIdLookUp', + type: 'boolean', + default: true, + description: 'If this is set to true then short IDs are looked up by this function as well. This can cause the return value of the function to return an event issue of a different project which is why this is an opt-in', + }, + ] + }, + /* -------------------------------------------------------------------------- */ + /* issue:update */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Issue ID', + name: 'issueId', + type: 'string', + default: '', + placeholder: '1234', + displayOptions: { + show: { + resource: [ + 'issue', + ], + operation: [ + 'update', + ], + }, + }, + required: true, + description: 'ID of issue to get', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'issue', + ], + operation: [ + 'update', + ], + }, + }, + options: [ + { + displayName: 'Assigned to', + name: 'assignedTo', + type: 'string', + default: '', + description: 'The actor id (or username) of the user or team that should be assigned to this issue', + }, + { + displayName: 'Has Seen', + name: 'hasSeen', + type: 'boolean', + default: true, + description: 'In case this API call is invoked with a user context this allows changing of the flag that indicates if the user has seen the event', + }, + { + displayName: 'Is Bookmarked', + name: 'isBookmarked', + type: 'boolean', + default: true, + description: 'In case this API call is invoked with a user context this allows changing of the bookmark flag', + }, + { + displayName: 'Is Public', + name: 'isPublic', + type: 'boolean', + default: true, + description: 'Sets the issue to public or private', + }, + { + displayName: 'Is Subscribed', + name: 'isSubscribed', + type: 'boolean', + default: true, + }, + { + displayName: 'Status', + name: 'status', + type: 'options', + default: '', + description: 'The new status for the issue', + options: [ + { + name: 'Ignored', + value: 'ignored' + }, + { + name: 'Resolved', + value: 'resolved' + }, + { + name: 'Resolved Next Release', + value: 'resolvedInNextRelease' + }, + { + name: 'Unresolved', + value: 'unresolved' + }, + ] + }, + ] + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/SentryIo/OrganizationDescription.ts b/packages/nodes-base/nodes/SentryIo/OrganizationDescription.ts new file mode 100644 index 0000000000..d579479088 --- /dev/null +++ b/packages/nodes-base/nodes/SentryIo/OrganizationDescription.ts @@ -0,0 +1,205 @@ +import { + INodeProperties, +} from 'n8n-workflow'; + +export const organizationOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'organization', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create an organization', + }, + { + name: 'Get', + value: 'get', + description: 'Get organization by slug', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Get all organizations', + } + ], + default: 'get', + description: 'The operation to perform', + }, +] as INodeProperties[]; + +export const organizationFields = [ + /* -------------------------------------------------------------------------- */ + /* organization:getAll */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'organization', + ], + }, + }, + default: false, + description: 'If all results should be returned or only up to a given limit', + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'organization', + ], + returnAll: [ + false, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 500, + }, + default: 100, + description: 'How many results to return', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'organization', + ], + operation: [ + 'getAll', + ], + }, + }, + options: [ + { + displayName: 'Member', + name: 'member', + type: 'boolean', + default: true, + description: 'Restrict results to organizations which you have membership', + }, + { + displayName: 'Owner', + name: 'owner', + type: 'boolean', + default: true, + description: 'Restrict results to organizations which you are the owner', + }, + ] + }, + /* -------------------------------------------------------------------------- */ + /* organization:get */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Organization Slug', + name: 'organizationSlug', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getOrganizations', + }, + default: '', + displayOptions: { + show: { + resource: [ + 'organization', + ], + operation: [ + 'get', + ], + }, + }, + required: true, + description: 'The slug of the organization the team should be created for', + }, + /* -------------------------------------------------------------------------- */ + /* organization:create */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'organization', + ], + operation: [ + 'create', + ], + }, + }, + required: true, + description: 'The slug of the organization the team should be created for', + }, + { + displayName: 'Agree to Terms', + name: 'agreeTerms', + type: 'boolean', + default: false, + displayOptions: { + show: { + resource: [ + 'organization', + ], + operation: [ + 'create', + ], + }, + }, + description: 'Signaling you agree to the applicable terms of service and privacy policy of Sentry.io', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'organization', + ], + operation: [ + 'create', + ], + }, + }, + options: [ + { + displayName: 'Slug', + name: 'slug', + type: 'string', + default: '', + description: 'The unique URL slug for this organization. If this is not provided a slug is automatically generated based on the name', + }, + ] + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/SentryIo/ProjectDescription.ts b/packages/nodes-base/nodes/SentryIo/ProjectDescription.ts new file mode 100644 index 0000000000..44c2726935 --- /dev/null +++ b/packages/nodes-base/nodes/SentryIo/ProjectDescription.ts @@ -0,0 +1,194 @@ +import { + INodeProperties, +} from 'n8n-workflow'; + +export const projectOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'project', + ], + }, + }, + options: [ + { + name: 'Get', + value: 'get', + description: 'Get project by ID', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Get all projects', + } + ], + default: 'get', + description: 'The operation to perform', + }, +] as INodeProperties[]; + +export const projectFields = [ + /* -------------------------------------------------------------------------- */ + /* project:create/get */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Organization Slug', + name: 'organizationSlug', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getOrganizations', + }, + default: '', + displayOptions: { + show: { + resource: [ + 'project', + ], + operation: [ + 'create', + 'get', + 'update', + 'delete', + ], + }, + }, + required: true, + description: 'The slug of the organization the events belong to', + }, + { + displayName: 'Project Slug', + name: 'projectSlug', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getProjects', + loadOptionsDependsOn: [ + 'organizationSlug', + ], + }, + default: '', + displayOptions: { + show: { + resource: [ + 'project', + ], + operation: [ + 'get', + ], + }, + }, + required: true, + description: 'The slug of the project to retrieve', + }, + { + displayName: 'Team Slug', + name: 'teamSlug', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'project', + ], + operation: [ + 'create', + 'update', + 'delete', + ], + }, + }, + required: true, + description: 'The slug of the team to create a new project for', + }, + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'project', + ], + operation: [ + 'create', + ], + }, + }, + required: true, + description: 'The name for the new project', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'project', + ], + operation: [ + 'create', + ], + }, + }, + options: [ + { + displayName: 'Slug', + name: 'slug', + type: 'string', + default: '', + description: 'Optionally a slug for the new project. If it’s not provided a slug is generated from the name', + }, + ] + }, + /* -------------------------------------------------------------------------- */ + /* project:getAll */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'project', + ], + }, + }, + default: false, + description: 'If all results should be returned or only up to a given limit', + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'project', + ], + returnAll: [ + false, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 500, + }, + default: 100, + description: 'How many results to return', + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/SentryIo/ReleaseDescription.ts b/packages/nodes-base/nodes/SentryIo/ReleaseDescription.ts new file mode 100644 index 0000000000..5c5686f9af --- /dev/null +++ b/packages/nodes-base/nodes/SentryIo/ReleaseDescription.ts @@ -0,0 +1,429 @@ +import { + INodeProperties, +} from 'n8n-workflow'; + +export const releaseOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'release', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a release', + }, + { + name: 'Get', + value: 'get', + description: 'Get release by version identifier', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Get all releases', + }, + ], + default: 'get', + description: 'The operation to perform', + }, +] as INodeProperties[]; + +export const releaseFields = [ + /* -------------------------------------------------------------------------- */ + /* release:getAll */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Organization Slug', + name: 'organizationSlug', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getOrganizations', + }, + default: '', + displayOptions: { + show: { + resource: [ + 'release', + ], + operation: [ + 'getAll', + ], + }, + }, + required: true, + description: 'The slug of the organization the releases belong to', + }, + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'release', + ], + }, + }, + default: false, + description: 'If all results should be returned or only up to a given limit', + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'release', + ], + returnAll: [ + false, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 500, + }, + default: 100, + description: 'How many results to return', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'release', + ], + operation: [ + 'getAll', + ], + }, + }, + options: [ + { + displayName: 'Query', + name: 'query', + type: 'string', + default: '', + description: 'This parameter can be used to create a “starts with” filter for the version', + }, + ] + }, + /* -------------------------------------------------------------------------- */ + /* release:get */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Organization Slug', + name: 'organizationSlug', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getOrganizations', + }, + default: '', + displayOptions: { + show: { + resource: [ + 'release', + ], + operation: [ + 'get', + ], + }, + }, + required: true, + description: 'The slug of the organization the release belongs to', + }, + { + displayName: 'Version', + name: 'version', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'release', + ], + operation: [ + 'get', + ], + }, + }, + required: true, + description: 'The version identifier of the release', + }, + /* -------------------------------------------------------------------------- */ + /* release:create */ + /* -------------------------------------------------------------------------- */ + { + displayName: 'Organization Slug', + name: 'organizationSlug', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getOrganizations', + }, + default: '', + displayOptions: { + show: { + resource: [ + 'release', + ], + operation: [ + 'create', + ], + }, + }, + required: true, + description: 'The slug of the organization the release belongs to', + }, + { + displayName: 'Version', + name: 'version', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'release', + ], + operation: [ + 'create', + ], + }, + }, + required: true, + description: ' a version identifier for this release. Can be a version number, a commit hash etc', + }, + { + displayName: 'URL', + name: 'url', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'release', + ], + operation: [ + 'create', + ], + }, + }, + required: true, + description: 'A URL that points to the release. This can be the path to an online interface to the sourcecode for instance', + }, + { + displayName: 'Projects', + name: 'projects', + type: 'multiOptions', + typeOptions: { + loadOptionsMethod: 'getProjects', + }, + default: '', + displayOptions: { + show: { + resource: [ + 'release', + ], + operation: [ + 'create', + ], + }, + }, + required: true, + description: 'A list of project slugs that are involved in this release', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'release', + ], + operation: [ + 'create', + ], + }, + }, + options: [ + { + displayName: 'Date released', + name: 'dateReleased', + type: 'dateTime', + default: '', + description: 'an optional date that indicates when the release went live. If not provided the current time is assumed', + }, + { + displayName: 'Commits', + name: 'commits', + description: 'an optional list of commit data to be associated with the release', + type: 'fixedCollection', + typeOptions: { + multipleValues: true, + }, + default: {}, + options: [ + { + name: 'commitProperties', + displayName: 'Commit Properties', + values: [ + { + displayName: 'Id', + name: 'id', + type: 'string', + default: '', + description: 'the sha of the commit', + required: true + }, + { + displayName: 'Author Email', + name: 'authorEmail', + type: 'string', + default: '', + description: 'Authors email', + }, + { + displayName: 'Author Name', + name: 'authorName', + type: 'string', + default: '', + description: 'Name of author', + }, + { + displayName: 'Message', + name: 'message', + type: 'string', + default: '', + description: 'Message of commit', + }, + { + displayName: 'Patch Set', + name: 'patchSet', + description: 'A list of the files that have been changed in the commit. Specifying the patch_set is necessary to power suspect commits and suggested assignees', + type: 'fixedCollection', + typeOptions: { + multipleValues: true, + }, + default: {}, + options: [ + { + name: 'patchSetProperties', + displayName: 'Patch Set Properties', + values: [ + { + displayName: 'Path', + name: 'path', + type: 'string', + default: '', + description: 'he path to the file. Both forward and backward slashes are supported', + required: true + }, + { + displayName: 'Type', + name: 'type', + type: 'options', + default: '', + description: 'he types of changes that happend in that commit', + options: [ + { + name: 'Add', + value: 'add' + }, + { + name: 'Modify', + value: 'modify' + }, + { + name: 'Delete', + value: 'delete' + }, + ] + }, + ] + }, + ], + }, + { + displayName: 'Repository', + name: 'repository', + type: 'string', + default: '', + description: 'Repository name', + }, + { + displayName: 'Timestamp', + name: 'timestamp', + type: 'dateTime', + default: '', + description: 'Timestamp of commit', + }, + ] + }, + ], + }, + { + displayName: 'Refs', + name: 'refs', + description: 'an optional way to indicate the start and end commits for each repository included in a release', + type: 'fixedCollection', + typeOptions: { + multipleValues: true, + }, + default: {}, + options: [ + { + name: 'refProperties', + displayName: 'Ref Properties', + values: [ + { + displayName: 'Commit', + name: 'commit', + type: 'string', + default: '', + description: 'the head sha of the commit', + required: true + }, + { + displayName: 'Repository', + name: 'repository', + type: 'string', + default: '', + description: 'Repository name', + required: true + }, + { + displayName: 'Previous Commit', + name: 'previousCommit', + type: 'string', + default: '', + description: 'the sha of the HEAD of the previous release', + }, + ] + }, + ], + }, + ] + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/SentryIo/SentryIo.node.ts b/packages/nodes-base/nodes/SentryIo/SentryIo.node.ts new file mode 100644 index 0000000000..74213e5435 --- /dev/null +++ b/packages/nodes-base/nodes/SentryIo/SentryIo.node.ts @@ -0,0 +1,558 @@ +import { + IExecuteFunctions, +} from 'n8n-core'; + +import { + IDataObject, + INodeExecutionData, + INodeType, + INodeTypeDescription, + ILoadOptionsFunctions, + INodePropertyOptions, +} from 'n8n-workflow'; + +import { + eventOperations, + eventFields, +} from './EventDescription'; + +import { + issueOperations, + issueFields, +} from './IssueDescription'; + +import { + organizationFields, + organizationOperations, +} from './OrganizationDescription'; + +import { + projectOperations, + projectFields, +} from './ProjectDescription'; + +import { + releaseOperations, + releaseFields, +} from './ReleaseDescription'; + +import { + teamOperations, + teamFields, +} from './TeamDescription'; + +import { + sentryIoApiRequest, + sentryApiRequestAllItems, +} from './GenericFunctions'; +import { ICommit, IPatchSet, IRef } from './Interface'; + +export class SentryIo implements INodeType { + description: INodeTypeDescription = { + displayName: 'Sentry.io', + name: 'sentryIo', + icon: 'file:sentryio.png', + group: ['output'], + version: 1, + subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}', + description: 'Consume Sentry.io API', + defaults: { + name: 'Sentry.io', + color: '#000000', + }, + inputs: ['main'], + outputs: ['main'], + credentials: [ + { + name: 'sentryIoOAuth2Api', + required: true, + displayOptions: { + show: { + authentication: [ + 'oAuth2', + ], + }, + }, + }, + { + name: 'sentryIoApi', + required: true, + displayOptions: { + show: { + authentication: [ + 'accessToken', + ], + }, + }, + }, + ], + properties: [ + { + displayName: 'Authentication', + name: 'authentication', + type: 'options', + options: [ + { + name: 'Access Token', + value: 'accessToken', + }, + { + name: 'OAuth2', + value: 'oAuth2', + }, + ], + default: 'accessToken', + description: 'The resource to operate on.', + }, + { + displayName: 'Resource', + name: 'resource', + type: 'options', + options: [ + { + name: 'Event', + value: 'event', + }, + { + name: 'Issue', + value: 'issue', + }, + { + name: 'Project', + value: 'project', + }, + { + name: 'Release', + value: 'release', + }, + { + name: 'Organization', + value: 'organization', + }, + { + name: 'Team', + value: 'team', + }, + ], + default: 'event', + description: 'Resource to consume.', + }, + + // EVENT + ...eventOperations, + ...eventFields, + + // ISSUE + ...issueOperations, + ...issueFields, + + // ORGANIZATION + ...organizationOperations, + ...organizationFields, + + // PROJECT + ...projectOperations, + ...projectFields, + + // RELEASE + ...releaseOperations, + ...releaseFields, + + // TEAM + ...teamOperations, + ...teamFields + ], + }; + + methods = { + loadOptions: { + // Get all organizations so they can be displayed easily + async getOrganizations(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const organizations = await sentryApiRequestAllItems.call(this, 'GET', `/api/0/organizations/`, {}); + + for (const organization of organizations) { + returnData.push({ + name: organization.slug, + value: organization.slug, + }); + } + + returnData.sort((a, b) => { + if (a.name < b.name) { return -1; } + if (a.name > b.name) { return 1; } + return 0; + }); + + return returnData; + }, + // Get all projects so can be displayed easily + async getProjects(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const projects = await sentryApiRequestAllItems.call(this, 'GET', `/api/0/projects/`, {}); + + const organizationSlug = this.getNodeParameter('organizationSlug') as string; + + for (const project of projects) { + + if (organizationSlug !== project.organization.slug) { + continue; + } + + returnData.push({ + name: project.slug, + value: project.slug, + }); + } + + returnData.sort((a, b) => { + if (a.name < b.name) { return -1; } + if (a.name > b.name) { return 1; } + return 0; + }); + + return returnData; + }, + }, + }; + + + async execute(this: IExecuteFunctions): Promise { + const items = this.getInputData(); + const returnData: IDataObject[] = []; + const length = items.length as unknown as number; + let responseData; + const qs: IDataObject = {}; + 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 === 'event') { + if (operation === 'getAll') { + const organizationSlug = this.getNodeParameter('organizationSlug', i) as string; + const projectSlug = this.getNodeParameter('projectSlug', i) as string; + const full = this.getNodeParameter('full', i) as boolean; + const returnAll = this.getNodeParameter('returnAll', i) as boolean; + + const endpoint = `/api/0/projects/${organizationSlug}/${projectSlug}/events/`; + + if (returnAll === false) { + const limit = this.getNodeParameter('limit', i) as number; + qs.limit = limit; + } + + qs.full = full; + + responseData = await sentryApiRequestAllItems.call(this, 'GET', endpoint, {}, qs); + + if (returnAll === false) { + const limit = this.getNodeParameter('limit', i) as number; + responseData = responseData.splice(0, limit); + } + } + if (operation === 'get') { + const organizationSlug = this.getNodeParameter('organizationSlug', i) as string; + const projectSlug = this.getNodeParameter('projectSlug', i) as string; + const eventId = this.getNodeParameter('eventId', i) as string; + + const endpoint = `/api/0/projects/${organizationSlug}/${projectSlug}/events/${eventId}/`; + + responseData = await sentryIoApiRequest.call(this, 'GET', endpoint, qs); + } + } + if (resource === 'issue') { + if (operation === 'getAll') { + const organizationSlug = this.getNodeParameter('organizationSlug', i) as string; + const projectSlug = this.getNodeParameter('projectSlug', i) as string; + const returnAll = this.getNodeParameter('returnAll', i) as boolean; + + const endpoint = `/api/0/projects/${organizationSlug}/${projectSlug}/issues/`; + + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + + if (additionalFields.statsPeriod) { + qs.statsPeriod = additionalFields.statsPeriod as string; + } + if (additionalFields.shortIdLookup) { + qs.shortIdLookup = additionalFields.shortIdLookup as boolean; + } + if (additionalFields.query) { + qs.query = additionalFields.query as string; + } + + if (returnAll === false) { + const limit = this.getNodeParameter('limit', i) as number; + qs.limit = limit; + } + + responseData = await sentryApiRequestAllItems.call(this, 'GET', endpoint, {}, qs); + + if (returnAll === false) { + const limit = this.getNodeParameter('limit', i) as number; + responseData = responseData.splice(0, limit); + } + + } + if (operation === 'get') { + const issueId = this.getNodeParameter('issueId', i) as string; + const endpoint = `/api/0/issues/${issueId}/`; + + responseData = await sentryIoApiRequest.call(this, 'GET', endpoint, qs); + } + if (operation === 'delete') { + const issueId = this.getNodeParameter('issueId', i) as string; + const endpoint = `/api/0/issues/${issueId}/`; + + responseData = await sentryIoApiRequest.call(this, 'DELETE', endpoint, qs); + + responseData = { success: true }; + } + if (operation === 'update') { + const issueId = this.getNodeParameter('issueId', i) as string; + const endpoint = `/api/0/issues/${issueId}/`; + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + + if (additionalFields.status) { + qs.status = additionalFields.status as string; + } + if (additionalFields.assignedTo) { + qs.assignedTo = additionalFields.assignedTo as string; + } + if (additionalFields.hasSeen) { + qs.hasSeen = additionalFields.hasSeen as boolean; + } + if (additionalFields.isBookmarked) { + qs.isBookmarked = additionalFields.isBookmarked as boolean; + } + if (additionalFields.isSubscribed) { + qs.isSubscribed = additionalFields.isSubscribed as boolean; + } + if (additionalFields.isPublic) { + qs.isPublic = additionalFields.isPublic as boolean; + } + + responseData = await sentryIoApiRequest.call(this, 'PUT', endpoint, qs); + } + } + if (resource === 'organization') { + if (operation === 'get') { + const organizationSlug = this.getNodeParameter('organizationSlug', i) as string; + const endpoint = `/api/0/organizations/${organizationSlug}/`; + + responseData = await sentryIoApiRequest.call(this, 'GET', endpoint, qs); + } + if (operation === 'getAll') { + const returnAll = this.getNodeParameter('returnAll', i) as boolean; + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + const endpoint = `/api/0/organizations/`; + + if (additionalFields.member) { + qs.member = additionalFields.member as boolean; + } + if (additionalFields.owner) { + qs.owner = additionalFields.owner as boolean; + } + + if (returnAll === false) { + const limit = this.getNodeParameter('limit', i) as number; + qs.limit = limit; + } + + responseData = await sentryApiRequestAllItems.call(this, 'GET', endpoint, {}, qs); + + if (responseData === undefined) { + responseData = []; + } + + if (returnAll === false) { + const limit = this.getNodeParameter('limit', i) as number; + responseData = responseData.splice(0, limit); + } + } + if (operation === 'create') { + const name = this.getNodeParameter('name', i) as string; + const agreeTerms = this.getNodeParameter('agreeTerms', i) as boolean; + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + const endpoint = `/api/0/organizations/`; + + qs.name = name; + qs.agreeTerms = agreeTerms; + + if (additionalFields.slug) { + qs.slug = additionalFields.slug as string; + } + + responseData = await sentryIoApiRequest.call(this, 'POST', endpoint, qs); + } + } + if (resource === 'project') { + if (operation === 'get') { + const organizationSlug = this.getNodeParameter('organizationSlug', i) as string; + const projectSlug = this.getNodeParameter('projectSlug', i) as string; + const endpoint = `/api/0/projects/${organizationSlug}/${projectSlug}/`; + + responseData = await sentryIoApiRequest.call(this, 'GET', endpoint, qs); + } + if (operation === 'getAll') { + const returnAll = this.getNodeParameter('returnAll', i) as boolean; + const endpoint = `/api/0/projects/`; + + if (returnAll === false) { + const limit = this.getNodeParameter('limit', i) as number; + qs.limit = limit; + } + + responseData = await sentryApiRequestAllItems.call(this, 'GET', endpoint, {}, qs); + + if (returnAll === false) { + const limit = this.getNodeParameter('limit', i) as number; + responseData = responseData.splice(0, limit); + } + } + } + if (resource === 'release') { + if (operation === 'get') { + const organizationSlug = this.getNodeParameter('organizationSlug', i) as string; + const version = this.getNodeParameter('version', i) as string; + const endpoint = `/api/0/organizations/${organizationSlug}/releases/${version}/`; + + responseData = await sentryIoApiRequest.call(this, 'GET', endpoint, qs); + } + if (operation === 'getAll') { + const organizationSlug = this.getNodeParameter('organizationSlug', i) as string; + const endpoint = `/api/0/organizations/${organizationSlug}/releases/`; + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + const returnAll = this.getNodeParameter('returnAll', i) as boolean; + + if (additionalFields.query) { + qs.query = additionalFields.query as string; + } + + if (returnAll === false) { + const limit = this.getNodeParameter('limit', i) as number; + qs.limit = limit; + } + + responseData = await sentryApiRequestAllItems.call(this, 'GET', endpoint, {}, qs); + + if (returnAll === false) { + const limit = this.getNodeParameter('limit', i) as number; + responseData = responseData.splice(0, limit); + } + } + + if (operation === 'create') { + const organizationSlug = this.getNodeParameter('organizationSlug', i) as string; + const endpoint = `/api/0/organizations/${organizationSlug}/releases/`; + const version = this.getNodeParameter('version', i) as string; + const url = this.getNodeParameter('url', i) as string; + const projects = this.getNodeParameter('projects', i) as string[]; + + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + + if (additionalFields.dateReleased) { + qs.dateReleased = additionalFields.dateReleased as string; + } + + qs.version = version; + qs.url = url; + qs.projects = projects; + + if (additionalFields.commits) { + const commits: ICommit[] = []; + //@ts-ignore + // tslint:disable-next-line: no-any + additionalFields.commits.commitProperties.map((commit: any) => { + const commitObject: ICommit = { id: commit.id }; + + if (commit.repository) { + commitObject.repository = commit.repository; + } + if (commit.message) { + commitObject.message = commit.message; + } + if (commit.patchSet && Array.isArray(commit.patchSet)) { + commit.patchSet.patchSetProperties.map((patchSet: IPatchSet) => { + commitObject.patch_set?.push(patchSet); + }); + } + if (commit.authorName) { + commitObject.author_name = commit.authorName; + } + if (commit.authorEmail) { + commitObject.author_email = commit.authorEmail; + } + if (commit.timestamp) { + commitObject.timestamp = commit.timestamp; + } + + commits.push(commitObject); + }); + + qs.commits = commits; + } + if (additionalFields.refs) { + const refs: IRef[] = []; + //@ts-ignore + additionalFields.refs.refProperties.map((ref: IRef) => { + refs.push(ref); + }); + + qs.refs = refs; + } + + responseData = await sentryIoApiRequest.call(this, 'POST', endpoint, qs); + } + } + if (resource === 'team') { + if (operation === 'get') { + const organizationSlug = this.getNodeParameter('organizationSlug', i) as string; + const teamSlug = this.getNodeParameter('teamSlug', i) as string; + const endpoint = `/api/0/teams/${organizationSlug}/${teamSlug}/`; + + responseData = await sentryIoApiRequest.call(this, 'GET', endpoint, qs); + } + if (operation === 'getAll') { + const organizationSlug = this.getNodeParameter('organizationSlug', i) as string; + const endpoint = `/api/0/organizations/${organizationSlug}/teams/`; + const returnAll = this.getNodeParameter('returnAll', i) as boolean; + + if (returnAll === false) { + const limit = this.getNodeParameter('limit', i) as number; + qs.limit = limit; + } + + responseData = await sentryApiRequestAllItems.call(this, 'GET', endpoint, {}, qs); + + if (returnAll === false) { + const limit = this.getNodeParameter('limit', i) as number; + responseData = responseData.splice(0, limit); + } + } + + if (operation === 'create') { + const organizationSlug = this.getNodeParameter('organizationSlug', i) as string; + const name = this.getNodeParameter('name', i) as string; + const endpoint = `/api/0/organizations/${organizationSlug}/teams/`; + + const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; + + qs.name = name; + + if (additionalFields.slug) { + qs.slug = additionalFields.slug; + } + + responseData = await sentryIoApiRequest.call(this, 'POST', endpoint, qs); + } + } + + if (Array.isArray(responseData)) { + returnData.push.apply(returnData, responseData as IDataObject[]); + } else { + returnData.push(responseData as IDataObject); + } + } + return [this.helpers.returnJsonArray(returnData)]; + } +} diff --git a/packages/nodes-base/nodes/SentryIo/TeamDescription.ts b/packages/nodes-base/nodes/SentryIo/TeamDescription.ts new file mode 100644 index 0000000000..f44e6853d2 --- /dev/null +++ b/packages/nodes-base/nodes/SentryIo/TeamDescription.ts @@ -0,0 +1,290 @@ +import { + INodeProperties, +} from 'n8n-workflow'; + +export const teamOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'team', + ], + }, + }, + options: [ + { + name: 'Create', + value: 'create', + description: 'Create a new team', + }, + { + name: 'Get', + value: 'get', + description: 'Get team by slug', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Get all teams', + } + ], + default: 'get', + description: 'The operation to perform', + }, +] as INodeProperties[]; + +export const teamFields = [ +/* -------------------------------------------------------------------------- */ +/* team:getAll */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Organization Slug', + name: 'organizationSlug', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getOrganizations', + }, + default: '', + displayOptions: { + show: { + resource: [ + 'team', + ], + operation: [ + 'getAll', + ], + }, + }, + required: true, + description: 'The slug of the organization for which the teams should be listed', + }, + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'team', + ], + }, + }, + default: false, + description: 'If all results should be returned or only up to a given limit', + }, + { + displayName: 'Limit', + name: 'limit', + type: 'number', + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'team', + ], + returnAll: [ + false, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 500, + }, + default: 100, + description: 'How many results to return', + }, +/* -------------------------------------------------------------------------- */ +/* team:get */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Organization Slug', + name: 'organizationSlug', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getOrganizations', + }, + default: '', + displayOptions: { + show: { + resource: [ + 'team', + ], + operation: [ + 'get', + ], + }, + }, + required: true, + description: 'The slug of the organization the team belongs to', + }, + { + displayName: 'Team Slug', + name: 'teamSlug', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'team', + ], + operation: [ + 'get', + ], + }, + }, + required: true, + description: 'The slug of the team to get', + }, +/* -------------------------------------------------------------------------- */ +/* team:create */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Organization Slug', + name: 'organizationSlug', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getOrganizations', + }, + default: '', + displayOptions: { + show: { + resource: [ + 'team', + ], + operation: [ + 'create', + ], + }, + }, + required: true, + description: 'The slug of the organization the team belongs to', + }, + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'team', + ], + operation: [ + 'create', + ], + }, + }, + required: true, + description: 'The name of the team', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'team', + ], + operation: [ + 'create', + ], + }, + }, + options: [ + { + displayName: 'Slug', + name: 'slug', + type: 'string', + default: '', + description: 'The optional slug for this team. If not provided it will be auto generated from the name', + }, + ] + }, +/* -------------------------------------------------------------------------- */ +/* team:update */ +/* -------------------------------------------------------------------------- */ + { + displayName: 'Organization Slug', + name: 'organizationSlug', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getOrganizations', + }, + default: '', + displayOptions: { + show: { + resource: [ + 'team', + ], + operation: [ + 'update', 'delete' + ], + }, + }, + required: true, + description: 'The slug of the organization the team belongs to', + }, + { + displayName: 'Team Slug', + name: 'teamSlug', + type: 'string', + default: '', + displayOptions: { + show: { + resource: [ + 'team', + ], + operation: [ + 'update', 'delete' + ], + }, + }, + required: true, + description: 'The slug of the team to get', + }, + { + displayName: 'Additional Fields', + name: 'additionalFields', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'team', + ], + operation: [ + 'update', + ], + }, + }, + options: [ + { + displayName: 'Slug', + name: 'slug', + type: 'string', + default: '', + description: 'The new slug of the team. Must be unique and available', + }, + { + displayName: 'Name', + name: 'name', + type: 'string', + default: '', + description: 'The new name of the team', + }, + ] + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/SentryIo/sentryio.png b/packages/nodes-base/nodes/SentryIo/sentryio.png new file mode 100644 index 0000000000..31fd69c1b4 Binary files /dev/null and b/packages/nodes-base/nodes/SentryIo/sentryio.png differ diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index ea057e4591..e545e02f5b 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -139,6 +139,8 @@ "dist/credentials/RundeckApi.credentials.js", "dist/credentials/ShopifyApi.credentials.js", "dist/credentials/SalesforceOAuth2Api.credentials.js", + "dist/credentials/SentryIoApi.credentials.js", + "dist/credentials/SentryIoOAuth2Api.credentials.js", "dist/credentials/SlackApi.credentials.js", "dist/credentials/SlackOAuth2Api.credentials.js", "dist/credentials/Sms77Api.credentials.js", @@ -319,6 +321,7 @@ "dist/nodes/S3/S3.node.js", "dist/nodes/Salesforce/Salesforce.node.js", "dist/nodes/Set.node.js", + "dist/nodes/SentryIo/SentryIo.node.js", "dist/nodes/Shopify/Shopify.node.js", "dist/nodes/Shopify/ShopifyTrigger.node.js", "dist/nodes/Signl4/Signl4.node.js",