diff --git a/packages/backend/src/apps/github/info.json b/packages/backend/src/apps/github/info.json index 6f9e9276..5eaa3887 100644 --- a/packages/backend/src/apps/github/info.json +++ b/packages/backend/src/apps/github/info.json @@ -286,6 +286,46 @@ "name": "Test trigger" } ] + }, + { + "name": "New notification", + "key": "newNotification", + "interval": "15m", + "description": "Triggers when a new notification is created", + "substeps": [ + { + "key": "chooseAccount", + "name": "Choose account" + }, + { + "key": "chooseTrigger", + "name": "Set up a trigger", + "arguments": [ + { + "label": "Repo", + "key": "repo", + "type": "dropdown", + "required": false, + "variables": false, + "description": "If blank, we will retrieve all notifications.", + "source": { + "type": "query", + "name": "getData", + "arguments": [ + { + "name": "key", + "value": "listRepos" + } + ] + } + } + ] + }, + { + "key": "testStep", + "name": "Test trigger" + } + ] } ] } diff --git a/packages/backend/src/apps/github/triggers.ts b/packages/backend/src/apps/github/triggers.ts index fe8fe22a..1e2eeb7a 100644 --- a/packages/backend/src/apps/github/triggers.ts +++ b/packages/backend/src/apps/github/triggers.ts @@ -2,15 +2,18 @@ import { IJSONObject } from '@automatisch/types'; import NewRepository from './triggers/new-repository'; import NewOrganization from './triggers/new-organization'; import NewBranch from './triggers/new-branch'; +import NewNotification from './triggers/new-notification'; export default class Triggers { newRepository: NewRepository; newOrganization: NewOrganization; newBranch: NewBranch; + newNotification: NewNotification; constructor(connectionData: IJSONObject, parameters: IJSONObject) { this.newRepository = new NewRepository(connectionData); this.newOrganization = new NewOrganization(connectionData); this.newBranch = new NewBranch(connectionData, parameters); + this.newNotification = new NewNotification(connectionData, parameters); } } diff --git a/packages/backend/src/apps/github/triggers/new-notification.ts b/packages/backend/src/apps/github/triggers/new-notification.ts new file mode 100644 index 00000000..2ffbf18a --- /dev/null +++ b/packages/backend/src/apps/github/triggers/new-notification.ts @@ -0,0 +1,89 @@ +import { Octokit } from 'octokit'; +import { DateTime } from 'luxon'; +import { IJSONObject } from '@automatisch/types'; + +export default class NewNotification { + client?: Octokit; + connectionData?: IJSONObject; + repoOwner?: string; + repo?: string; + baseOptions = { + all: true, + participating: false, + }; + + constructor(connectionData: IJSONObject, parameters: IJSONObject) { + if (connectionData.accessToken) { + this.client = new Octokit({ + auth: connectionData.accessToken as string, + }); + } + + if (parameters?.repo) { + const [owner, repo] = (parameters.repo as string).split('/'); + + this.repoOwner = owner; + this.repo = repo; + } + } + + get hasRepo() { + return this.repoOwner && this.repo; + } + + async listRepoNotifications(options = {}, paginate = false) { + const listRepoNotifications = this.client.rest.activity.listRepoNotificationsForAuthenticatedUser; + + const extendedOptions = { + ...this.baseOptions, + repo: this.repo, + owner: this.repoOwner, + ...options, + }; + + if (paginate) { + return await this.client.paginate(listRepoNotifications, extendedOptions); + } + + return (await listRepoNotifications(extendedOptions)).data; + } + + async listNotifications(options = {}, paginate = false) { + const listNotifications = this.client.rest.activity.listNotificationsForAuthenticatedUser; + + const extendedOptions = { + ...this.baseOptions, + ...options, + }; + + if (paginate) { + return await this.client.paginate(listNotifications, extendedOptions); + } + + return (await listNotifications(extendedOptions)).data; + } + + async run(startTime: Date) { + const options = { + since: DateTime.fromJSDate(startTime).toLocaleString(DateTime.TIME_24_SIMPLE), + }; + + if (this.hasRepo) { + return await this.listRepoNotifications(options, true); + } + + return await this.listNotifications(options, true); + } + + async testRun() { + const options = { + per_page: 1, + }; + + if (this.hasRepo) { + return await this.listRepoNotifications(options, false); + } + + return await this.listNotifications(options, false); + } +} diff --git a/packages/web/src/components/ControlledAutocomplete/index.tsx b/packages/web/src/components/ControlledAutocomplete/index.tsx index de6410ba..e2b965dd 100644 --- a/packages/web/src/components/ControlledAutocomplete/index.tsx +++ b/packages/web/src/components/ControlledAutocomplete/index.tsx @@ -1,4 +1,5 @@ import * as React from 'react'; +import FormHelperText from '@mui/material/FormHelperText'; import { Controller, useFormContext } from 'react-hook-form'; import Autocomplete, { AutocompleteProps } from '@mui/material/Autocomplete'; @@ -6,6 +7,7 @@ interface ControlledAutocompleteProps extends AutocompleteProps ( - { - const typedSelectedOption = selectedOption as Option; - if (typedSelectedOption?.value) { - controllerOnChange(typedSelectedOption.value); - } else { - controllerOnChange(typedSelectedOption); - } +
+ {/* encapsulated with an element such as div to vertical spacing delegated from parent */} + { + const typedSelectedOption = selectedOption as Option; + if (typedSelectedOption?.value) { + controllerOnChange(typedSelectedOption.value); + } else { + controllerOnChange(typedSelectedOption); + } - onChange?.(event, selectedOption, reason, details); - }} - onBlur={(...args) => { controllerOnBlur(); onBlur?.(...args); }} - ref={ref} - /> + onChange?.(event, selectedOption, reason, details); + }} + onBlur={(...args) => { controllerOnBlur(); onBlur?.(...args); }} + ref={ref} + /> + + + {description} + +
)} /> ); diff --git a/packages/web/src/components/InputCreator/index.tsx b/packages/web/src/components/InputCreator/index.tsx index b0f25fd2..f93e2ce2 100644 --- a/packages/web/src/components/InputCreator/index.tsx +++ b/packages/web/src/components/InputCreator/index.tsx @@ -75,16 +75,16 @@ export default function InputCreator(props: InputCreatorProps): React.ReactEleme name={computedName} fullWidth disablePortal - disableClearable={true} + disableClearable={required} options={options} renderInput={(params) => } value={getOption(options, value)} onChange={console.log} + description={description} /> ); } - if (type === 'string') { if (variables) { return ( diff --git a/packages/web/src/components/PowerInput/index.tsx b/packages/web/src/components/PowerInput/index.tsx index 6bfd7bd7..47a0b368 100644 --- a/packages/web/src/components/PowerInput/index.tsx +++ b/packages/web/src/components/PowerInput/index.tsx @@ -4,7 +4,7 @@ import Chip from '@mui/material/Chip'; import Popper from '@mui/material/Popper'; import InputLabel from '@mui/material/InputLabel'; import FormHelperText from '@mui/material/FormHelperText'; -import { Controller, Control, FieldValues, useFormContext } from 'react-hook-form'; +import { Controller, useFormContext } from 'react-hook-form'; import { Editor, Transforms, Range, createEditor } from 'slate'; import { Slate,