feat: add new favorite photo trigger in flickr
This commit is contained in:

committed by
Ömer Faruk Aydın

parent
d4ad8645c9
commit
651cceec14
@@ -3,7 +3,7 @@
|
|||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"description": "> TODO: description",
|
"description": "> TODO: description",
|
||||||
"scripts": {
|
"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",
|
"worker": "nodemon --watch 'src/**/*.ts' --exec 'ts-node' src/worker.ts",
|
||||||
"build": "tsc && yarn copy-statics",
|
"build": "tsc && yarn copy-statics",
|
||||||
"build:watch": "nodemon --watch 'src/**/*.ts' --watch 'bin/**/*.ts' --exec yarn build --ext ts",
|
"build:watch": "nodemon --watch 'src/**/*.ts' --watch 'bin/**/*.ts' --exec yarn build --ext ts",
|
||||||
@@ -25,6 +25,7 @@
|
|||||||
"@graphql-tools/load": "^7.5.2",
|
"@graphql-tools/load": "^7.5.2",
|
||||||
"@octokit/oauth-methods": "^1.2.6",
|
"@octokit/oauth-methods": "^1.2.6",
|
||||||
"@slack/bolt": "3.10.0",
|
"@slack/bolt": "3.10.0",
|
||||||
|
"@types/luxon": "^2.3.1",
|
||||||
"ajv-formats": "^2.1.1",
|
"ajv-formats": "^2.1.1",
|
||||||
"axios": "0.24.0",
|
"axios": "0.24.0",
|
||||||
"bcrypt": "^5.0.1",
|
"bcrypt": "^5.0.1",
|
||||||
@@ -47,6 +48,7 @@
|
|||||||
"jsonwebtoken": "^8.5.1",
|
"jsonwebtoken": "^8.5.1",
|
||||||
"knex": "^0.95.11",
|
"knex": "^0.95.11",
|
||||||
"lodash.get": "^4.4.2",
|
"lodash.get": "^4.4.2",
|
||||||
|
"luxon": "2.3.1",
|
||||||
"morgan": "^1.10.0",
|
"morgan": "^1.10.0",
|
||||||
"nodemailer": "6.7.0",
|
"nodemailer": "6.7.0",
|
||||||
"objection": "^3.0.0",
|
"objection": "^3.0.0",
|
||||||
|
@@ -1,15 +1,22 @@
|
|||||||
import Authentication from './authentication';
|
|
||||||
import {
|
import {
|
||||||
IService,
|
IService,
|
||||||
IAuthentication,
|
IAuthentication,
|
||||||
IApp,
|
IApp,
|
||||||
IJSONObject,
|
IJSONObject,
|
||||||
} from '@automatisch/types';
|
} from '@automatisch/types';
|
||||||
|
import Authentication from './authentication';
|
||||||
|
import Triggers from './triggers';
|
||||||
|
|
||||||
export default class Flickr implements IService {
|
export default class Flickr implements IService {
|
||||||
authenticationClient: IAuthentication;
|
authenticationClient: IAuthentication;
|
||||||
|
triggers: Triggers;
|
||||||
|
|
||||||
constructor(appData: IApp, connectionData: IJSONObject) {
|
constructor(
|
||||||
|
appData: IApp,
|
||||||
|
connectionData: IJSONObject,
|
||||||
|
parameters: IJSONObject
|
||||||
|
) {
|
||||||
this.authenticationClient = new Authentication(appData, connectionData);
|
this.authenticationClient = new Authentication(appData, connectionData);
|
||||||
|
this.triggers = new Triggers(connectionData, parameters);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
10
packages/backend/src/apps/flickr/triggers.ts
Normal file
10
packages/backend/src/apps/flickr/triggers.ts
Normal file
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
62
packages/backend/src/apps/flickr/triggers/favorite-photo.ts
Normal file
62
packages/backend/src/apps/flickr/triggers/favorite-photo.ts
Normal file
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@@ -1,11 +1,10 @@
|
|||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import { dirname, join } from 'path';
|
import { join } from 'path';
|
||||||
import { IApp } from '@automatisch/types';
|
import { IApp } from '@automatisch/types';
|
||||||
import appInfoConverter from '../helpers/app-info-converter';
|
import appInfoConverter from '../helpers/app-info-converter';
|
||||||
|
|
||||||
class App {
|
class App {
|
||||||
static backendPath = require.resolve('@automatisch/backend');
|
static folderPath = join(__dirname, '../apps');
|
||||||
static folderPath = join(dirname(this.backendPath), 'apps');
|
|
||||||
static list = fs.readdirSync(this.folderPath);
|
static list = fs.readdirSync(this.folderPath);
|
||||||
|
|
||||||
static findAll(name?: string): IApp[] {
|
static findAll(name?: string): IApp[] {
|
||||||
|
@@ -133,13 +133,12 @@ class Processor {
|
|||||||
.orderBy('created_at', 'desc')
|
.orderBy('created_at', 'desc')
|
||||||
.first();
|
.first();
|
||||||
|
|
||||||
const lastExecutionStepCratedAt = lastExecutionStep?.dataOut
|
const lastExecutionStepCreatedAt = lastExecutionStep?.createdAt as string;
|
||||||
?.created_at as string;
|
|
||||||
const flow = (await step.$relatedQuery('flow')) as Flow;
|
const flow = (await step.$relatedQuery('flow')) as Flow;
|
||||||
|
|
||||||
const command = appInstance.triggers[key];
|
const command = appInstance.triggers[key];
|
||||||
|
|
||||||
const startTime = new Date(lastExecutionStepCratedAt || flow.updatedAt);
|
const startTime = new Date(lastExecutionStepCreatedAt || flow.updatedAt);
|
||||||
let fetchedData;
|
let fetchedData;
|
||||||
|
|
||||||
if (this.testRun) {
|
if (this.testRun) {
|
||||||
|
@@ -11,7 +11,8 @@
|
|||||||
"paths": {
|
"paths": {
|
||||||
"*": [
|
"*": [
|
||||||
"node_modules/*",
|
"node_modules/*",
|
||||||
"src/types/*"
|
"src/types/*",
|
||||||
|
"src/apps/*"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
@@ -20,7 +21,7 @@
|
|||||||
"typeRoots": [
|
"typeRoots": [
|
||||||
"node_modules/@types",
|
"node_modules/@types",
|
||||||
"./src/types",
|
"./src/types",
|
||||||
"./src/apps/*/types"
|
"./src/apps"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
|
@@ -55,15 +55,23 @@ export default function AddAppConnection(props: AddAppConnectionProps): React.Re
|
|||||||
const step = steps[stepIndex];
|
const step = steps[stepIndex];
|
||||||
const variables = computeAuthStepVariables(step.arguments, response);
|
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++;
|
stepIndex++;
|
||||||
|
|
||||||
|
if (stepIndex === steps.length) {
|
||||||
|
onClose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setInProgress(false);
|
setInProgress(false);
|
||||||
onClose?.();
|
|
||||||
}, [connectionId, key, reconnectionSteps, authenticationSteps, onClose]);
|
}, [connectionId, key, reconnectionSteps, authenticationSteps, onClose]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@@ -25,7 +25,11 @@ export default function Executions(): React.ReactElement {
|
|||||||
const [searchParams, setSearchParams] = useSearchParams();
|
const [searchParams, setSearchParams] = useSearchParams();
|
||||||
const page = parseInt(searchParams.get('page') || '', 10) || 1;
|
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 getExecutions = data?.getExecutions || {};
|
||||||
const { pageInfo, edges } = getExecutions;
|
const { pageInfo, edges } = getExecutions;
|
||||||
|
|
||||||
|
@@ -4451,6 +4451,11 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/luxon/-/luxon-2.0.9.tgz#782a0edfa6d699191292c13168bd496cd66b87c6"
|
resolved "https://registry.yarnpkg.com/@types/luxon/-/luxon-2.0.9.tgz#782a0edfa6d699191292c13168bd496cd66b87c6"
|
||||||
integrity sha512-ZuzIc7aN+i2ZDMWIiSmMdubR9EMMSTdEzF6R+FckP4p6xdnOYKqknTo/k+xXQvciSXlNGIwA4OPU5X7JIFzYdA==
|
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":
|
"@types/mdast@^3.0.0":
|
||||||
version "3.0.10"
|
version "3.0.10"
|
||||||
resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-3.0.10.tgz#4724244a82a4598884cbbe9bcfd73dff927ee8af"
|
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"
|
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.4.0.tgz#2830a779b483e9723e20f26fa5278463c50599d8"
|
||||||
integrity sha512-YOfuyWa/Ee+PXbDm40j9WXyJrzQUynVbgn4Km643UYcWNcrSfRkKL0WaiUcxcIbkXcVTgNpDqSnPXntWXT75cw==
|
integrity sha512-YOfuyWa/Ee+PXbDm40j9WXyJrzQUynVbgn4Km643UYcWNcrSfRkKL0WaiUcxcIbkXcVTgNpDqSnPXntWXT75cw==
|
||||||
|
|
||||||
luxon@^2.3.1:
|
luxon@2.3.1, luxon@^2.3.1:
|
||||||
version "2.3.1"
|
version "2.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/luxon/-/luxon-2.3.1.tgz#f276b1b53fd9a740a60e666a541a7f6dbed4155a"
|
resolved "https://registry.yarnpkg.com/luxon/-/luxon-2.3.1.tgz#f276b1b53fd9a740a60e666a541a7f6dbed4155a"
|
||||||
integrity sha512-I8vnjOmhXsMSlNMZlMkSOvgrxKJl0uOsEzdGgGNZuZPaS9KlefpE9KV95QFftlJSC+1UyCC9/I69R02cz/zcCA==
|
integrity sha512-I8vnjOmhXsMSlNMZlMkSOvgrxKJl0uOsEzdGgGNZuZPaS9KlefpE9KV95QFftlJSC+1UyCC9/I69R02cz/zcCA==
|
||||||
|
Reference in New Issue
Block a user