From 651cceec14af0de32691e8068694fc853301f8ec Mon Sep 17 00:00:00 2001 From: Ali BARIN Date: Wed, 13 Apr 2022 01:22:13 +0200 Subject: [PATCH] feat: add new favorite photo trigger in flickr --- packages/backend/package.json | 4 +- .../src/apps/flickr/{types => }/index.d.ts | 0 packages/backend/src/apps/flickr/index.ts | 11 +++- packages/backend/src/apps/flickr/info.json | 18 ++++++ packages/backend/src/apps/flickr/triggers.ts | 10 +++ .../apps/flickr/triggers/favorite-photo.ts | 62 +++++++++++++++++++ packages/backend/src/models/app.ts | 5 +- packages/backend/src/services/processor.ts | 5 +- packages/backend/tsconfig.json | 5 +- .../src/components/AddAppConnection/index.tsx | 14 ++++- packages/web/src/pages/Executions/index.tsx | 6 +- yarn.lock | 7 ++- 12 files changed, 131 insertions(+), 16 deletions(-) rename packages/backend/src/apps/flickr/{types => }/index.d.ts (100%) create mode 100644 packages/backend/src/apps/flickr/triggers.ts create mode 100644 packages/backend/src/apps/flickr/triggers/favorite-photo.ts diff --git a/packages/backend/package.json b/packages/backend/package.json index 6c3afa01..19e86b5d 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -3,7 +3,7 @@ "version": "0.1.0", "description": "> TODO: description", "scripts": { - "dev": "nodemon --watch 'src/**/*.ts' --watch 'bin/**/*.ts' --exec 'ts-node' src/server.ts", + "dev": "nodemon --watch 'src/**/*.ts' --watch 'bin/**/*.ts' --exec 'ts-node' src/server.ts --ext ts,json", "worker": "nodemon --watch 'src/**/*.ts' --exec 'ts-node' src/worker.ts", "build": "tsc && yarn copy-statics", "build:watch": "nodemon --watch 'src/**/*.ts' --watch 'bin/**/*.ts' --exec yarn build --ext ts", @@ -25,6 +25,7 @@ "@graphql-tools/load": "^7.5.2", "@octokit/oauth-methods": "^1.2.6", "@slack/bolt": "3.10.0", + "@types/luxon": "^2.3.1", "ajv-formats": "^2.1.1", "axios": "0.24.0", "bcrypt": "^5.0.1", @@ -47,6 +48,7 @@ "jsonwebtoken": "^8.5.1", "knex": "^0.95.11", "lodash.get": "^4.4.2", + "luxon": "2.3.1", "morgan": "^1.10.0", "nodemailer": "6.7.0", "objection": "^3.0.0", diff --git a/packages/backend/src/apps/flickr/types/index.d.ts b/packages/backend/src/apps/flickr/index.d.ts similarity index 100% rename from packages/backend/src/apps/flickr/types/index.d.ts rename to packages/backend/src/apps/flickr/index.d.ts diff --git a/packages/backend/src/apps/flickr/index.ts b/packages/backend/src/apps/flickr/index.ts index 5829e35a..4591842a 100644 --- a/packages/backend/src/apps/flickr/index.ts +++ b/packages/backend/src/apps/flickr/index.ts @@ -1,15 +1,22 @@ -import Authentication from './authentication'; import { IService, IAuthentication, IApp, IJSONObject, } from '@automatisch/types'; +import Authentication from './authentication'; +import Triggers from './triggers'; export default class Flickr implements IService { authenticationClient: IAuthentication; + triggers: Triggers; - constructor(appData: IApp, connectionData: IJSONObject) { + constructor( + appData: IApp, + connectionData: IJSONObject, + parameters: IJSONObject + ) { this.authenticationClient = new Authentication(appData, connectionData); + this.triggers = new Triggers(connectionData, parameters); } } diff --git a/packages/backend/src/apps/flickr/info.json b/packages/backend/src/apps/flickr/info.json index 5d44e6d1..aeb1dab5 100644 --- a/packages/backend/src/apps/flickr/info.json +++ b/packages/backend/src/apps/flickr/info.json @@ -214,5 +214,23 @@ } ] } + ], + "triggers": [ + { + "name": "New favorite photo", + "key": "favoritePhoto", + "interval": "15m", + "description": "Will be triggered when you favorite a photo.", + "substeps": [ + { + "key": "chooseAccount", + "name": "Choose account" + }, + { + "key": "testStep", + "name": "Test trigger" + } + ] + } ] } diff --git a/packages/backend/src/apps/flickr/triggers.ts b/packages/backend/src/apps/flickr/triggers.ts new file mode 100644 index 00000000..27d4e0a1 --- /dev/null +++ b/packages/backend/src/apps/flickr/triggers.ts @@ -0,0 +1,10 @@ +import { IJSONObject } from '@automatisch/types'; +import FavoritePhoto from './triggers/favorite-photo'; + +export default class Triggers { + favoritePhoto: FavoritePhoto; + + constructor(connectionData: IJSONObject, parameters: IJSONObject) { + this.favoritePhoto = new FavoritePhoto(connectionData); + } +} diff --git a/packages/backend/src/apps/flickr/triggers/favorite-photo.ts b/packages/backend/src/apps/flickr/triggers/favorite-photo.ts new file mode 100644 index 00000000..f6ebaea3 --- /dev/null +++ b/packages/backend/src/apps/flickr/triggers/favorite-photo.ts @@ -0,0 +1,62 @@ +import { DateTime } from 'luxon'; +import FlickrApi from 'flickr-sdk'; +import { IJSONObject } from '@automatisch/types'; + +export default class FavoritePhoto { + client?: typeof FlickrApi; + + constructor(connectionData: IJSONObject) { + if ( + connectionData.consumerKey && + connectionData.consumerSecret && + connectionData.accessToken && + connectionData.accessSecret + ) { + this.client = new FlickrApi( + FlickrApi.OAuth.createPlugin( + connectionData.consumerKey, + connectionData.consumerSecret, + connectionData.accessToken, + connectionData.accessSecret + ) + ); + } + } + + async run(startTime: Date) { + const { photos } = (await this.client.favorites.getList({ per_page: 1, })).body; + const favPhotos = [...photos.photo]; + const newFavPhotos = []; + + let page = 1; + for (const photo of favPhotos) { + const markedFavoriteAt = DateTime.fromSeconds(parseInt(photo.date_faved, 10)); + const markedFavoriteAtInMillis = markedFavoriteAt.toMillis(); + + if (markedFavoriteAtInMillis <= startTime.getTime()) { + break; + } + + newFavPhotos.push(photo); + + const currentIndex = favPhotos.indexOf(photo); + const totalFavPhotos = favPhotos.length; + const isLastItem = currentIndex + 1 === totalFavPhotos; + + if (isLastItem && page < photos.pages) { + page = page + 1; + const { photos } = (await this.client.favorites.getList({ page, per_page: 1, })).body; + favPhotos.push(...photos.photo); + } + } + + return newFavPhotos; + } + + async testRun() { + const { photos } = (await this.client.favorites.getList({ per_page: 1, })).body; + + return photos.photo; + } +} + diff --git a/packages/backend/src/models/app.ts b/packages/backend/src/models/app.ts index 9b139aac..e8a85c9d 100644 --- a/packages/backend/src/models/app.ts +++ b/packages/backend/src/models/app.ts @@ -1,11 +1,10 @@ import fs from 'fs'; -import { dirname, join } from 'path'; +import { join } from 'path'; import { IApp } from '@automatisch/types'; import appInfoConverter from '../helpers/app-info-converter'; class App { - static backendPath = require.resolve('@automatisch/backend'); - static folderPath = join(dirname(this.backendPath), 'apps'); + static folderPath = join(__dirname, '../apps'); static list = fs.readdirSync(this.folderPath); static findAll(name?: string): IApp[] { diff --git a/packages/backend/src/services/processor.ts b/packages/backend/src/services/processor.ts index 8d50c53a..570a3cf6 100644 --- a/packages/backend/src/services/processor.ts +++ b/packages/backend/src/services/processor.ts @@ -133,13 +133,12 @@ class Processor { .orderBy('created_at', 'desc') .first(); - const lastExecutionStepCratedAt = lastExecutionStep?.dataOut - ?.created_at as string; + const lastExecutionStepCreatedAt = lastExecutionStep?.createdAt as string; const flow = (await step.$relatedQuery('flow')) as Flow; const command = appInstance.triggers[key]; - const startTime = new Date(lastExecutionStepCratedAt || flow.updatedAt); + const startTime = new Date(lastExecutionStepCreatedAt || flow.updatedAt); let fetchedData; if (this.testRun) { diff --git a/packages/backend/tsconfig.json b/packages/backend/tsconfig.json index 24c7b911..b6767148 100644 --- a/packages/backend/tsconfig.json +++ b/packages/backend/tsconfig.json @@ -11,7 +11,8 @@ "paths": { "*": [ "node_modules/*", - "src/types/*" + "src/types/*", + "src/apps/*" ] }, "skipLibCheck": true, @@ -20,7 +21,7 @@ "typeRoots": [ "node_modules/@types", "./src/types", - "./src/apps/*/types" + "./src/apps" ] }, "include": [ diff --git a/packages/web/src/components/AddAppConnection/index.tsx b/packages/web/src/components/AddAppConnection/index.tsx index b4be1361..eb401ae2 100644 --- a/packages/web/src/components/AddAppConnection/index.tsx +++ b/packages/web/src/components/AddAppConnection/index.tsx @@ -55,15 +55,23 @@ export default function AddAppConnection(props: AddAppConnectionProps): React.Re const step = steps[stepIndex]; const variables = computeAuthStepVariables(step.arguments, response); - const stepResponse = await processStep(step, variables); + try { + const stepResponse = await processStep(step, variables); - response[step.name] = stepResponse; + response[step.name] = stepResponse; + } catch (err) { + console.log(err); + break; + } stepIndex++; + + if (stepIndex === steps.length) { + onClose(); + } } setInProgress(false); - onClose?.(); }, [connectionId, key, reconnectionSteps, authenticationSteps, onClose]); return ( diff --git a/packages/web/src/pages/Executions/index.tsx b/packages/web/src/pages/Executions/index.tsx index 4c766837..46bb998c 100644 --- a/packages/web/src/pages/Executions/index.tsx +++ b/packages/web/src/pages/Executions/index.tsx @@ -25,7 +25,11 @@ export default function Executions(): React.ReactElement { const [searchParams, setSearchParams] = useSearchParams(); const page = parseInt(searchParams.get('page') || '', 10) || 1; - const { data, refetch } = useQuery(GET_EXECUTIONS, { variables: getLimitAndOffset(page), fetchPolicy: 'cache-and-network' }); + const { data, refetch } = useQuery(GET_EXECUTIONS, { + variables: getLimitAndOffset(page), + fetchPolicy: 'cache-and-network', + pollInterval: 5000, + }); const getExecutions = data?.getExecutions || {}; const { pageInfo, edges } = getExecutions; diff --git a/yarn.lock b/yarn.lock index c3b2dc0f..bccd9623 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4451,6 +4451,11 @@ resolved "https://registry.yarnpkg.com/@types/luxon/-/luxon-2.0.9.tgz#782a0edfa6d699191292c13168bd496cd66b87c6" integrity sha512-ZuzIc7aN+i2ZDMWIiSmMdubR9EMMSTdEzF6R+FckP4p6xdnOYKqknTo/k+xXQvciSXlNGIwA4OPU5X7JIFzYdA== +"@types/luxon@^2.3.1": + version "2.3.1" + resolved "https://registry.yarnpkg.com/@types/luxon/-/luxon-2.3.1.tgz#e34763178b46232e4c5f079f1706e18692415519" + integrity sha512-nAPUltOT28fal2eDZz8yyzNhBjHw1NEymFBP7Q9iCShqpflWPybxHbD7pw/46jQmT+HXOy1QN5hNTms8MOTlOQ== + "@types/mdast@^3.0.0": version "3.0.10" resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-3.0.10.tgz#4724244a82a4598884cbbe9bcfd73dff927ee8af" @@ -12451,7 +12456,7 @@ lru-cache@^7.3.1: resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.4.0.tgz#2830a779b483e9723e20f26fa5278463c50599d8" integrity sha512-YOfuyWa/Ee+PXbDm40j9WXyJrzQUynVbgn4Km643UYcWNcrSfRkKL0WaiUcxcIbkXcVTgNpDqSnPXntWXT75cw== -luxon@^2.3.1: +luxon@2.3.1, luxon@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/luxon/-/luxon-2.3.1.tgz#f276b1b53fd9a740a60e666a541a7f6dbed4155a" integrity sha512-I8vnjOmhXsMSlNMZlMkSOvgrxKJl0uOsEzdGgGNZuZPaS9KlefpE9KV95QFftlJSC+1UyCC9/I69R02cz/zcCA==