diff --git a/packages/backend/src/apps/github/triggers/new-stargazers/index.ts b/packages/backend/src/apps/github/triggers/new-stargazers/index.ts new file mode 100644 index 00000000..5916b305 --- /dev/null +++ b/packages/backend/src/apps/github/triggers/new-stargazers/index.ts @@ -0,0 +1,46 @@ +import defineTrigger from '../../../../helpers/define-trigger'; +import newStargazers from './new-stargazers'; + +export default defineTrigger({ + name: 'New stargazers', + key: 'newStargazers', + pollInterval: 15, + description: 'Triggers when a user stars a repository', + substeps: [ + { + key: 'chooseConnection', + name: 'Choose connection' + }, + { + key: 'chooseTrigger', + name: 'Set up a trigger', + arguments: [ + { + label: 'Repo', + key: 'repo', + type: 'dropdown', + required: true, + variables: false, + source: { + type: 'query', + name: 'getData', + arguments: [ + { + name: 'key', + value: 'listRepos' + } + ] + } + }, + ] + }, + { + key: 'testStep', + name: 'Test trigger' + } + ], + + async run($) { + return await newStargazers($); + }, +}); diff --git a/packages/backend/src/apps/github/triggers/new-stargazers/new-stargazers.ts b/packages/backend/src/apps/github/triggers/new-stargazers/new-stargazers.ts new file mode 100644 index 00000000..a22ee083 --- /dev/null +++ b/packages/backend/src/apps/github/triggers/new-stargazers/new-stargazers.ts @@ -0,0 +1,80 @@ +import { DateTime } from 'luxon'; +import { + IGlobalVariable, + IJSONObject, + ITriggerOutput, +} from '@automatisch/types'; +import getRepoOwnerAndRepo from '../../common/get-repo-owner-and-repo'; +import parseLinkHeader from '../../../../helpers/parse-header-link'; + +type TResponseDataItem = { + starred_at: string; + user: IJSONObject; +} + +const fetchStargazers = async ($: IGlobalVariable) => { + const { repoOwner, repo } = getRepoOwnerAndRepo($.step.parameters.repo as string); + const firstPagePathname = `/repos/${repoOwner}/${repo}/stargazers`; + const requestConfig = { + params: { + per_page: 100, + }, + headers: { + // needed to get `starred_at` time + Accept: 'application/vnd.github.star+json', + } + } + + const firstPageResponse = await $.http.get(firstPagePathname, requestConfig); + const firstPageLinks = parseLinkHeader(firstPageResponse.headers.link); + + // in case there is only single page to fetch + let pathname = firstPageLinks.last?.uri || firstPagePathname; + + const stargazers: ITriggerOutput = { + data: [], + }; + + do { + const response = await $.http.get(pathname, requestConfig); + const links = parseLinkHeader(response.headers.link); + pathname = links.prev?.uri; + + if (response.integrationError) { + stargazers.error = response.integrationError; + return stargazers; + } + + if (response.data.length) { + for (const starEntry of response.data) { + const { starred_at, user } = starEntry; + const timestamp = DateTime.fromISO(starred_at).toMillis(); + + if (timestamp <= Number($.flow.lastInternalId) && !$.execution.testRun) return stargazers; + + const dataItem = { + raw: user, + meta: { + internalId: timestamp.toString(), + }, + }; + + stargazers.data.push(dataItem); + } + } + } while (pathname && !$.execution.testRun); + + return stargazers; +} + +const newStargazers = async ($: IGlobalVariable) => { + const stargazers = await fetchStargazers($); + + stargazers.data.sort((stargazerA, stargazerB) => { + return Number(stargazerA.meta.internalId) - Number(stargazerB.meta.internalId); + }); + + return stargazers; +}; + +export default newStargazers;