Merge pull request #453 from automatisch/refactor/flow-step-params

refactor: Pass connection, flow and step as params to apps
This commit is contained in:
Ömer Faruk Aydın
2022-08-21 22:03:28 +03:00
committed by GitHub
18 changed files with 60 additions and 85 deletions

View File

@@ -9,7 +9,7 @@ export default class CreateTweet {
async run() { async run() {
const response = await this.client.createTweet.run( const response = await this.client.createTweet.run(
this.client.parameters.tweet as string this.client.step.parameters.tweet as string
); );
return response.data.data; return response.data.data;

View File

@@ -1,4 +1,4 @@
import type { IAuthentication, IField } from '@automatisch/types'; import type { IAuthentication, IField, IApp } from '@automatisch/types';
import { URLSearchParams } from 'url'; import { URLSearchParams } from 'url';
import TwitterClient from './client'; import TwitterClient from './client';
@@ -10,7 +10,7 @@ export default class Authentication implements IAuthentication {
} }
async createAuthData() { async createAuthData() {
const appFields = this.client.appData.fields.find( const appFields = this.client.connection.appData.fields.find(
(field: IField) => field.key == 'oAuthRedirectUrl' (field: IField) => field.key == 'oAuthRedirectUrl'
); );
const callbackUrl = appFields.value; const callbackUrl = appFields.value;
@@ -30,8 +30,9 @@ export default class Authentication implements IAuthentication {
const responseData = Object.fromEntries(new URLSearchParams(response.data)); const responseData = Object.fromEntries(new URLSearchParams(response.data));
return { return {
consumerKey: this.client.connectionData.consumerKey, consumerKey: this.client.connection.formattedData.consumerKey as string,
consumerSecret: this.client.connectionData.consumerSecret, consumerSecret: this.client.connection.formattedData
.consumerSecret as string,
accessToken: responseData.oauth_token, accessToken: responseData.oauth_token,
accessSecret: responseData.oauth_token_secret, accessSecret: responseData.oauth_token_secret,
userId: responseData.user_id, userId: responseData.user_id,

View File

@@ -10,8 +10,8 @@ export default class CreateTweet {
async run(text: string) { async run(text: string) {
try { try {
const token = { const token = {
key: this.client.connectionData.accessToken as string, key: this.client.connection.formattedData.accessToken as string,
secret: this.client.connectionData.accessSecret as string, secret: this.client.connection.formattedData.accessSecret as string,
}; };
const requestData = { const requestData = {

View File

@@ -9,8 +9,8 @@ export default class GetCurrentUser {
async run() { async run() {
const token = { const token = {
key: this.client.connectionData.accessToken as string, key: this.client.connection.formattedData.accessToken as string,
secret: this.client.connectionData.accessSecret as string, secret: this.client.connection.formattedData.accessSecret as string,
}; };
const requestPath = '/2/users/me'; const requestPath = '/2/users/me';

View File

@@ -10,8 +10,8 @@ export default class GetUserByUsername {
async run(username: string) { async run(username: string) {
const token = { const token = {
key: this.client.connectionData.accessToken as string, key: this.client.connection.formattedData.accessToken as string,
secret: this.client.connectionData.accessSecret as string, secret: this.client.connection.formattedData.accessSecret as string,
}; };
const requestPath = `/2/users/by/username/${username}`; const requestPath = `/2/users/by/username/${username}`;

View File

@@ -10,8 +10,8 @@ export default class GetUserTweets {
async run(userId: string) { async run(userId: string) {
const token = { const token = {
key: this.client.connectionData.accessToken as string, key: this.client.connection.formattedData.accessToken as string,
secret: this.client.connectionData.accessSecret as string, secret: this.client.connection.formattedData.accessSecret as string,
}; };
const requestPath = `/2/users/${userId}/tweets`; const requestPath = `/2/users/${userId}/tweets`;

View File

@@ -10,7 +10,7 @@ export default class VerifyAccessToken {
async run() { async run() {
try { try {
return await this.client.httpClient.post( return await this.client.httpClient.post(
`/oauth/access_token?oauth_verifier=${this.client.connectionData.oauthVerifier}&oauth_token=${this.client.connectionData.accessToken}`, `/oauth/access_token?oauth_verifier=${this.client.connection.formattedData.oauthVerifier}&oauth_token=${this.client.connection.formattedData.accessToken}`,
null null
); );
} catch (error) { } catch (error) {

View File

@@ -1,4 +1,4 @@
import { IJSONObject, IApp } from '@automatisch/types'; import { IFlow, IStep, IConnection } from '@automatisch/types';
import OAuth from 'oauth-1.0a'; import OAuth from 'oauth-1.0a';
import crypto from 'crypto'; import crypto from 'crypto';
import HttpClient from '../../../helpers/http-client'; import HttpClient from '../../../helpers/http-client';
@@ -10,9 +10,9 @@ import GetUserTweets from './endpoints/get-user-tweets';
import CreateTweet from './endpoints/create-tweet'; import CreateTweet from './endpoints/create-tweet';
export default class TwitterClient { export default class TwitterClient {
appData: IApp; flow: IFlow;
connectionData: IJSONObject; step: IStep;
parameters: IJSONObject; connection: IConnection;
oauthClient: OAuth; oauthClient: OAuth;
httpClient: HttpClient; httpClient: HttpClient;
@@ -25,20 +25,16 @@ export default class TwitterClient {
static baseUrl = 'https://api.twitter.com'; static baseUrl = 'https://api.twitter.com';
constructor( constructor(connection: IConnection, flow?: IFlow, step?: IStep) {
appData: IApp, this.connection = connection;
connectionData: IJSONObject, this.flow = flow;
parameters: IJSONObject this.step = step;
) {
this.connectionData = connectionData;
this.appData = appData;
this.parameters = parameters;
this.httpClient = new HttpClient({ baseURL: TwitterClient.baseUrl }); this.httpClient = new HttpClient({ baseURL: TwitterClient.baseUrl });
const consumerData = { const consumerData = {
key: this.connectionData.consumerKey as string, key: this.connection.formattedData.consumerKey as string,
secret: this.connectionData.consumerSecret as string, secret: this.connection.formattedData.consumerSecret as string,
}; };
this.oauthClient = new OAuth({ this.oauthClient = new OAuth({

View File

@@ -1,8 +1,9 @@
import { import {
IService, IService,
IAuthentication, IAuthentication,
IApp, IFlow,
IJSONObject, IStep,
IConnection,
} from '@automatisch/types'; } from '@automatisch/types';
import Authentication from './authentication'; import Authentication from './authentication';
import Triggers from './triggers'; import Triggers from './triggers';
@@ -16,12 +17,8 @@ export default class Twitter implements IService {
triggers: Triggers; triggers: Triggers;
actions: Actions; actions: Actions;
constructor( constructor(connection: IConnection, flow?: IFlow, step?: IStep) {
appData: IApp, this.client = new TwitterClient(connection, flow, step);
connectionData: IJSONObject,
parameters: IJSONObject
) {
this.client = new TwitterClient(appData, connectionData, parameters);
this.authenticationClient = new Authentication(this.client); this.authenticationClient = new Authentication(this.client);
this.triggers = new Triggers(this.client); this.triggers = new Triggers(this.client);

View File

@@ -17,7 +17,7 @@ export default class UserTweet {
async getTweets() { async getTweets() {
const userResponse = await this.client.getUserByUsername.run( const userResponse = await this.client.getUserByUsername.run(
this.client.parameters.username as string this.client.step.parameters.username as string
); );
const userId = userResponse.data.data.id; const userId = userResponse.data.data.id;

View File

@@ -1,5 +1,4 @@
import Context from '../../types/express/context'; import Context from '../../types/express/context';
import App from '../../models/app';
import axios from 'axios'; import axios from 'axios';
type Params = { type Params = {
@@ -21,13 +20,12 @@ const createAuthData = async (
.throwIfNotFound(); .throwIfNotFound();
const appClass = (await import(`../../apps/${connection.key}`)).default; const appClass = (await import(`../../apps/${connection.key}`)).default;
const appData = App.findOneByKey(connection.key);
if (!connection.formattedData) { if (!connection.formattedData) {
return null; return null;
} }
const appInstance = new appClass(appData, connection.formattedData); const appInstance = new appClass(connection);
const authLink = await appInstance.authenticationClient.createAuthData(); const authLink = await appInstance.authenticationClient.createAuthData();
try { try {

View File

@@ -22,7 +22,7 @@ const verifyConnection = async (
const appClass = (await import(`../../apps/${connection.key}`)).default; const appClass = (await import(`../../apps/${connection.key}`)).default;
const app = App.findOneByKey(connection.key); const app = App.findOneByKey(connection.key);
const appInstance = new appClass(app, connection.formattedData); const appInstance = new appClass(connection);
const verifiedCredentials = const verifiedCredentials =
await appInstance.authenticationClient.verifyCredentials(); await appInstance.authenticationClient.verifyCredentials();

View File

@@ -1,5 +1,4 @@
import { IJSONObject } from '@automatisch/types'; import { IJSONObject } from '@automatisch/types';
import App from '../../models/app';
import Context from '../../types/express/context'; import Context from '../../types/express/context';
type Params = { type Params = {
@@ -11,7 +10,7 @@ type Params = {
const getData = async (_parent: unknown, params: Params, context: Context) => { const getData = async (_parent: unknown, params: Params, context: Context) => {
const step = await context.currentUser const step = await context.currentUser
.$relatedQuery('steps') .$relatedQuery('steps')
.withGraphFetched('connection') .withGraphFetched('connection, flow')
.findById(params.stepId); .findById(params.stepId);
if (!step) return null; if (!step) return null;
@@ -20,10 +19,9 @@ const getData = async (_parent: unknown, params: Params, context: Context) => {
if (!connection || !step.appKey) return null; if (!connection || !step.appKey) return null;
const appData = App.findOneByKey(step.appKey);
const AppClass = (await import(`../../apps/${step.appKey}`)).default; const AppClass = (await import(`../../apps/${step.appKey}`)).default;
const appInstance = new AppClass(connection, step.flow, step);
const appInstance = new AppClass(appData, connection.formattedData, params.parameters);
const command = appInstance.data[params.key]; const command = appInstance.data[params.key];
const fetchedData = await command.run(); const fetchedData = await command.run();

View File

@@ -1,5 +1,4 @@
import Context from '../../types/express/context'; import Context from '../../types/express/context';
import App from '../../models/app';
type Params = { type Params = {
id: string; id: string;
@@ -19,9 +18,8 @@ const testConnection = async (
.throwIfNotFound(); .throwIfNotFound();
const appClass = (await import(`../../apps/${connection.key}`)).default; const appClass = (await import(`../../apps/${connection.key}`)).default;
const appData = App.findOneByKey(connection.key); const appInstance = new appClass(connection);
const appInstance = new appClass(appData, connection.formattedData);
const isStillVerified = const isStillVerified =
await appInstance.authenticationClient.isStillVerified(); await appInstance.authenticationClient.isStillVerified();

View File

@@ -4,6 +4,7 @@ import { AES, enc } from 'crypto-js';
import Base from './base'; import Base from './base';
import User from './user'; import User from './user';
import Step from './step'; import Step from './step';
import App from './app';
import appConfig from '../config/app'; import appConfig from '../config/app';
import { IJSONObject } from '@automatisch/types'; import { IJSONObject } from '@automatisch/types';
import Telemetry from '../helpers/telemetry'; import Telemetry from '../helpers/telemetry';
@@ -55,6 +56,10 @@ class Connection extends Base {
}, },
}); });
get appData() {
return App.findOneByKey(this.key);
}
encryptData(): void { encryptData(): void {
if (!this.eligibleForEncryption()) return; if (!this.eligibleForEncryption()) return;

View File

@@ -78,6 +78,10 @@ class Step extends Base {
return `${appConfig.baseUrl}/apps/${this.appKey}/assets/favicon.svg`; return `${appConfig.baseUrl}/apps/${this.appKey}/assets/favicon.svg`;
} }
get appData() {
return App.findOneByKey(this.appKey);
}
async $afterInsert(queryContext: QueryContext) { async $afterInsert(queryContext: QueryContext) {
await super.$afterInsert(queryContext); await super.$afterInsert(queryContext);
Telemetry.stepCreated(this); Telemetry.stepCreated(this);
@@ -95,17 +99,13 @@ class Step extends Base {
async getTrigger() { async getTrigger() {
if (!this.isTrigger) return null; if (!this.isTrigger) return null;
const { appKey, key, parameters = {} } = this; const { appKey, key } = this;
const connection = await this.$relatedQuery('connection'); const connection = await this.$relatedQuery('connection');
const flow = await this.$relatedQuery('flow');
const appData = App.findOneByKey(appKey);
const AppClass = (await import(`../apps/${appKey}`)).default; const AppClass = (await import(`../apps/${appKey}`)).default;
const appInstance = new AppClass( const appInstance = new AppClass(connection, flow, this);
appData,
connection?.formattedData,
parameters
);
const command = appInstance.triggers[key]; const command = appInstance.triggers[key];
return command; return command;

View File

@@ -1,5 +1,4 @@
import get from 'lodash.get'; import get from 'lodash.get';
import App from '../models/app';
import Flow from '../models/flow'; import Flow from '../models/flow';
import Step from '../models/step'; import Step from '../models/step';
import Execution from '../models/execution'; import Execution from '../models/execution';
@@ -56,16 +55,7 @@ class Processor {
for await (const step of steps) { for await (const step of steps) {
if (!step.appKey) continue; if (!step.appKey) continue;
const appData = App.findOneByKey(step.appKey); const { appKey, key, type, parameters: rawParameters = {}, id } = step;
const {
appKey,
connection,
key,
type,
parameters: rawParameters = {},
id,
} = step;
const isTrigger = type === 'trigger'; const isTrigger = type === 'trigger';
const AppClass = (await import(`../apps/${appKey}`)).default; const AppClass = (await import(`../apps/${appKey}`)).default;
@@ -75,11 +65,7 @@ class Processor {
priorExecutionSteps priorExecutionSteps
); );
const appInstance = new AppClass( const appInstance = new AppClass(step.connection, this.flow, step);
appData,
connection?.formattedData,
computedParameters
);
if (!isTrigger && key) { if (!isTrigger && key) {
const command = appInstance.actions[key]; const command = appInstance.actions[key];
@@ -114,19 +100,10 @@ class Processor {
} }
async getInitialTriggerData(step: Step) { async getInitialTriggerData(step: Step) {
if (!step.appKey) return null; if (!step.appKey || !step.key) return null;
const appData = App.findOneByKey(step.appKey); const AppClass = (await import(`../apps/${step.appKey}`)).default;
const { appKey, connection, key, parameters: rawParameters = {} } = step; const appInstance = new AppClass(step.connection, this.flow, step);
if (!key) return null;
const AppClass = (await import(`../apps/${appKey}`)).default;
const appInstance = new AppClass(
appData,
connection?.formattedData,
rawParameters
);
const lastExecutionStep = await step const lastExecutionStep = await step
.$relatedQuery('executionSteps') .$relatedQuery('executionSteps')
@@ -136,7 +113,7 @@ class Processor {
const lastExecutionStepCreatedAt = lastExecutionStep?.createdAt as string; const lastExecutionStepCreatedAt = lastExecutionStep?.createdAt 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[step.key];
const startTime = new Date(lastExecutionStepCreatedAt || flow.updatedAt); const startTime = new Date(lastExecutionStepCreatedAt || flow.updatedAt);
let fetchedData; let fetchedData;
@@ -163,7 +140,10 @@ class Processor {
.map((part: string) => { .map((part: string) => {
const isVariable = part.match(Processor.variableRegExp); const isVariable = part.match(Processor.variableRegExp);
if (isVariable) { if (isVariable) {
const stepIdAndKeyPath = part.replace(/{{step.|}}/g, '') as string; const stepIdAndKeyPath = part.replace(
/{{step.|}}/g,
''
) as string;
const [stepId, ...keyPaths] = stepIdAndKeyPath.split('.'); const [stepId, ...keyPaths] = stepIdAndKeyPath.split('.');
const keyPath = keyPaths.join('.'); const keyPath = keyPaths.join('.');
const executionStep = executionSteps[stepId.toString() as string]; const executionStep = executionSteps[stepId.toString() as string];

View File

@@ -15,6 +15,7 @@ export interface IConnection {
verified: boolean; verified: boolean;
count: number; count: number;
flowCount: number; flowCount: number;
appData?: IApp;
createdAt: string; createdAt: string;
} }
@@ -57,6 +58,7 @@ export interface IStep {
executionSteps: IExecutionStep[]; executionSteps: IExecutionStep[];
// FIXME: remove this property once execution steps are properly exposed via queries // FIXME: remove this property once execution steps are properly exposed via queries
output: IJSONObject; output: IJSONObject;
appData?: IApp;
} }
export interface IFlow { export interface IFlow {