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:
@@ -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;
|
||||||
|
@@ -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,
|
||||||
|
@@ -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 = {
|
||||||
|
@@ -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';
|
||||||
|
@@ -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}`;
|
||||||
|
@@ -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`;
|
||||||
|
@@ -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) {
|
||||||
|
@@ -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({
|
||||||
|
@@ -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);
|
||||||
|
@@ -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;
|
||||||
|
@@ -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 {
|
||||||
|
@@ -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();
|
||||||
|
|
||||||
|
@@ -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();
|
||||||
|
|
||||||
|
@@ -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();
|
||||||
|
|
||||||
|
@@ -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;
|
||||||
|
|
||||||
|
@@ -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;
|
||||||
|
@@ -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];
|
||||||
|
2
packages/types/index.d.ts
vendored
2
packages/types/index.d.ts
vendored
@@ -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 {
|
||||||
|
Reference in New Issue
Block a user