chore: introduce @automatisch/types
This commit is contained in:

committed by
Ömer Faruk Aydın

parent
bbb6f0b0ff
commit
3391578655
@@ -15,7 +15,8 @@
|
||||
"nohoist": [
|
||||
"**/babel-loader",
|
||||
"**/webpack",
|
||||
"**/@automatisch/web"
|
||||
"**/@automatisch/web",
|
||||
"**/@automatisch/types"
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@@ -18,7 +18,6 @@
|
||||
"dependencies": {
|
||||
"@automatisch/web": "0.1.0",
|
||||
"@octokit/oauth-methods": "^1.2.6",
|
||||
"@types/lodash.get": "^4.4.6",
|
||||
"axios": "0.24.0",
|
||||
"bcrypt": "^5.0.1",
|
||||
"cors": "^2.8.5",
|
||||
@@ -66,11 +65,13 @@
|
||||
"url": "https://github.com/automatisch/automatisch/issues"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@automatisch/types": "0.1.0",
|
||||
"@types/bcrypt": "^5.0.0",
|
||||
"@types/cors": "^2.8.12",
|
||||
"@types/crypto-js": "^4.0.2",
|
||||
"@types/express": "^4.17.13",
|
||||
"@types/http-errors": "^1.8.1",
|
||||
"@types/lodash.get": "^4.4.6",
|
||||
"@types/morgan": "^1.9.3",
|
||||
"@types/node": "^16.10.2",
|
||||
"@types/nodemailer": "^6.4.4",
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import type { IField } from '@automatisch/types';
|
||||
import { URLSearchParams } from 'url';
|
||||
import axios, { AxiosInstance } from 'axios';
|
||||
import Field from '../../types/field';
|
||||
|
||||
export default class Authentication {
|
||||
appData: any;
|
||||
@@ -18,7 +18,7 @@ export default class Authentication {
|
||||
|
||||
get oauthRedirectUrl() {
|
||||
return this.appData.fields.find(
|
||||
(field: Field) => field.key == 'oAuthRedirectUrl'
|
||||
(field: IField) => field.key == 'oAuthRedirectUrl'
|
||||
).value;
|
||||
}
|
||||
|
||||
|
@@ -1,13 +1,15 @@
|
||||
import AuthenticationInterface from '../../types/interfaces/authentication-interface';
|
||||
import type {
|
||||
IAuthentication,
|
||||
IApp,
|
||||
IField,
|
||||
IJSONObject,
|
||||
} from '@automatisch/types';
|
||||
import { google as GoogleApi } from 'googleapis';
|
||||
import { OAuth2Client } from 'google-auth-library';
|
||||
import Field from '../../types/field';
|
||||
import AppInfo from '../../types/app-info';
|
||||
import JSONObject from '../../types/interfaces/json-object';
|
||||
|
||||
export default class Authentication implements AuthenticationInterface {
|
||||
appData: AppInfo;
|
||||
connectionData: JSONObject;
|
||||
export default class Authentication implements IAuthentication {
|
||||
appData: IApp;
|
||||
connectionData: IJSONObject;
|
||||
client: OAuth2Client;
|
||||
|
||||
scopes: string[] = [
|
||||
@@ -17,7 +19,7 @@ export default class Authentication implements AuthenticationInterface {
|
||||
'profile',
|
||||
];
|
||||
|
||||
constructor(appData: AppInfo, connectionData: JSONObject) {
|
||||
constructor(appData: IApp, connectionData: IJSONObject) {
|
||||
this.appData = appData;
|
||||
this.connectionData = connectionData;
|
||||
|
||||
@@ -32,7 +34,7 @@ export default class Authentication implements AuthenticationInterface {
|
||||
|
||||
get oauthRedirectUrl() {
|
||||
return this.appData.fields.find(
|
||||
(field: Field) => field.key == 'oAuthRedirectUrl'
|
||||
(field: IField) => field.key == 'oAuthRedirectUrl'
|
||||
).value;
|
||||
}
|
||||
|
||||
|
@@ -1,16 +1,18 @@
|
||||
import AuthenticationInterface from '../../types/interfaces/authentication-interface';
|
||||
import type {
|
||||
IAuthentication,
|
||||
IApp,
|
||||
IField,
|
||||
IJSONObject,
|
||||
} from '@automatisch/types';
|
||||
import FlickrApi from 'flickr-sdk';
|
||||
import AppInfo from '../../types/app-info';
|
||||
import Field from '../../types/field';
|
||||
import JSONObject from '../../types/interfaces/json-object';
|
||||
|
||||
export default class Authentication implements AuthenticationInterface {
|
||||
appData: AppInfo;
|
||||
connectionData: JSONObject;
|
||||
export default class Authentication implements IAuthentication {
|
||||
appData: IApp;
|
||||
connectionData: IJSONObject;
|
||||
client: typeof FlickrApi;
|
||||
oauthClient: typeof FlickrApi;
|
||||
|
||||
constructor(appData: AppInfo, connectionData: JSONObject) {
|
||||
constructor(appData: IApp, connectionData: IJSONObject) {
|
||||
this.oauthClient = new FlickrApi.OAuth(
|
||||
connectionData.consumerKey,
|
||||
connectionData.consumerSecret
|
||||
@@ -33,7 +35,7 @@ export default class Authentication implements AuthenticationInterface {
|
||||
|
||||
async createAuthData() {
|
||||
const appFields = this.appData.fields.find(
|
||||
(field: Field) => field.key == 'oAuthRedirectUrl'
|
||||
(field: IField) => field.key == 'oAuthRedirectUrl'
|
||||
);
|
||||
const callbackUrl = appFields.value;
|
||||
|
||||
|
@@ -1,16 +1,18 @@
|
||||
import AuthenticationInterface from '../../types/interfaces/authentication-interface';
|
||||
import type {
|
||||
IAuthentication,
|
||||
IApp,
|
||||
IField,
|
||||
IJSONObject,
|
||||
} from '@automatisch/types';
|
||||
import {
|
||||
getWebFlowAuthorizationUrl,
|
||||
exchangeWebFlowCode,
|
||||
checkToken,
|
||||
} from '@octokit/oauth-methods';
|
||||
import AppInfo from '../../types/app-info';
|
||||
import Field from '../../types/field';
|
||||
import JSONObject from '../../types/interfaces/json-object';
|
||||
|
||||
export default class Authentication implements AuthenticationInterface {
|
||||
appData: AppInfo;
|
||||
connectionData: JSONObject;
|
||||
export default class Authentication implements IAuthentication {
|
||||
appData: IApp;
|
||||
connectionData: IJSONObject;
|
||||
scopes: string[] = ['repo'];
|
||||
client: {
|
||||
getWebFlowAuthorizationUrl: typeof getWebFlowAuthorizationUrl;
|
||||
@@ -18,7 +20,7 @@ export default class Authentication implements AuthenticationInterface {
|
||||
checkToken: typeof checkToken;
|
||||
};
|
||||
|
||||
constructor(appData: AppInfo, connectionData: JSONObject) {
|
||||
constructor(appData: IApp, connectionData: IJSONObject) {
|
||||
this.connectionData = connectionData;
|
||||
this.appData = appData;
|
||||
|
||||
@@ -31,7 +33,7 @@ export default class Authentication implements AuthenticationInterface {
|
||||
|
||||
get oauthRedirectUrl(): string {
|
||||
return this.appData.fields.find(
|
||||
(field: Field) => field.key == 'oAuthRedirectUrl'
|
||||
(field: IField) => field.key == 'oAuthRedirectUrl'
|
||||
).value;
|
||||
}
|
||||
|
||||
|
@@ -1,14 +1,17 @@
|
||||
import AuthenticationInterface from '../../types/interfaces/authentication-interface';
|
||||
import type {
|
||||
IAuthentication,
|
||||
IApp,
|
||||
IField,
|
||||
IJSONObject,
|
||||
} from '@automatisch/types';
|
||||
import { Client } from 'pg';
|
||||
import AppInfo from '../../types/app-info';
|
||||
import JSONObject from '../../types/interfaces/json-object';
|
||||
|
||||
export default class Authentication implements AuthenticationInterface {
|
||||
appData: AppInfo;
|
||||
connectionData: JSONObject;
|
||||
export default class Authentication implements IAuthentication {
|
||||
appData: IApp;
|
||||
connectionData: IJSONObject;
|
||||
client: Client;
|
||||
|
||||
constructor(appData: AppInfo, connectionData: JSONObject) {
|
||||
constructor(appData: IApp, connectionData: IJSONObject) {
|
||||
this.client = new Client({
|
||||
host: connectionData.host as string,
|
||||
port: connectionData.port as number,
|
||||
|
@@ -1,13 +1,16 @@
|
||||
import nodemailer, { Transporter, TransportOptions } from 'nodemailer';
|
||||
import AppInfo from '../../types/app-info';
|
||||
import JSONObject from '../../types/interfaces/json-object';
|
||||
import type {
|
||||
IAuthentication,
|
||||
IApp,
|
||||
IJSONObject,
|
||||
} from '@automatisch/types';
|
||||
|
||||
export default class Authentication {
|
||||
appData: AppInfo;
|
||||
connectionData: JSONObject;
|
||||
export default class Authentication implements IAuthentication {
|
||||
appData: IApp;
|
||||
connectionData: IJSONObject;
|
||||
client: Transporter;
|
||||
|
||||
constructor(appData: AppInfo, connectionData: JSONObject) {
|
||||
constructor(appData: IApp, connectionData: IJSONObject) {
|
||||
this.client = nodemailer.createTransport({
|
||||
host: connectionData.host,
|
||||
port: connectionData.port,
|
||||
|
@@ -1,14 +1,16 @@
|
||||
import AuthenticationInterface from '../../types/interfaces/authentication-interface';
|
||||
import type {
|
||||
IAuthentication,
|
||||
IApp,
|
||||
IJSONObject,
|
||||
} from '@automatisch/types';
|
||||
import TwilioApi from 'twilio';
|
||||
import AppInfo from '../../types/app-info';
|
||||
import JSONObject from '../../types/interfaces/json-object';
|
||||
|
||||
export default class Authentication implements AuthenticationInterface {
|
||||
appData: AppInfo;
|
||||
connectionData: JSONObject;
|
||||
export default class Authentication implements IAuthentication {
|
||||
appData: IApp;
|
||||
connectionData: IJSONObject;
|
||||
client: TwilioApi.Twilio;
|
||||
|
||||
constructor(appData: AppInfo, connectionData: JSONObject) {
|
||||
constructor(appData: IApp, connectionData: IJSONObject) {
|
||||
this.client = TwilioApi(
|
||||
connectionData.accountSid as string,
|
||||
connectionData.authToken as string
|
||||
|
@@ -1,9 +1,11 @@
|
||||
import AuthenticationInterface from '../../types/interfaces/authentication-interface';
|
||||
import type {
|
||||
IAuthentication,
|
||||
IApp,
|
||||
IField,
|
||||
IJSONObject,
|
||||
} from '@automatisch/types';
|
||||
import TwitchApi, { TwitchJsOptions } from 'twitch-js';
|
||||
import fetchUtil from 'twitch-js/lib/utils/fetch';
|
||||
import AppInfo from '../../types/app-info';
|
||||
import Field from '../../types/field';
|
||||
import JSONObject from '../../types/interfaces/json-object';
|
||||
|
||||
type TwitchTokenResponse = {
|
||||
accessToken: string;
|
||||
@@ -12,12 +14,12 @@ type TwitchTokenResponse = {
|
||||
tokenType: string;
|
||||
};
|
||||
|
||||
export default class Authentication implements AuthenticationInterface {
|
||||
appData: AppInfo;
|
||||
connectionData: JSONObject;
|
||||
export default class Authentication implements IAuthentication {
|
||||
appData: IApp;
|
||||
connectionData: IJSONObject;
|
||||
client: TwitchApi;
|
||||
|
||||
constructor(appData: AppInfo, connectionData: JSONObject) {
|
||||
constructor(appData: IApp, connectionData: IJSONObject) {
|
||||
this.connectionData = connectionData;
|
||||
this.appData = appData;
|
||||
|
||||
@@ -36,7 +38,7 @@ export default class Authentication implements AuthenticationInterface {
|
||||
|
||||
get oauthRedirectUrl() {
|
||||
return this.appData.fields.find(
|
||||
(field: Field) => field.key == 'oAuthRedirectUrl'
|
||||
(field: IField) => field.key == 'oAuthRedirectUrl'
|
||||
).value;
|
||||
}
|
||||
|
||||
|
@@ -1,15 +1,17 @@
|
||||
import AuthenticationInterface from '../../types/interfaces/authentication-interface';
|
||||
import type {
|
||||
IAuthentication,
|
||||
IApp,
|
||||
IField,
|
||||
IJSONObject,
|
||||
} from '@automatisch/types';
|
||||
import TwitterApi, { TwitterApiTokens } from 'twitter-api-v2';
|
||||
import AppInfo from '../../types/app-info';
|
||||
import Field from '../../types/field';
|
||||
import JSONObject from '../../types/interfaces/json-object';
|
||||
|
||||
export default class Authentication implements AuthenticationInterface {
|
||||
appData: AppInfo;
|
||||
connectionData: JSONObject;
|
||||
export default class Authentication implements IAuthentication {
|
||||
appData: IApp;
|
||||
connectionData: IJSONObject;
|
||||
client: TwitterApi;
|
||||
|
||||
constructor(appData: AppInfo, connectionData: JSONObject) {
|
||||
constructor(appData: IApp, connectionData: IJSONObject) {
|
||||
this.appData = appData;
|
||||
this.connectionData = connectionData;
|
||||
|
||||
@@ -25,7 +27,7 @@ export default class Authentication implements AuthenticationInterface {
|
||||
|
||||
async createAuthData() {
|
||||
const appFields = this.appData.fields.find(
|
||||
(field: Field) => field.key == 'oAuthRedirectUrl'
|
||||
(field: IField) => field.key == 'oAuthRedirectUrl'
|
||||
);
|
||||
const callbackUrl = appFields.value;
|
||||
|
||||
|
@@ -1,13 +1,15 @@
|
||||
import AuthenticationInterface from '../../types/interfaces/authentication-interface';
|
||||
import type {
|
||||
IAuthentication,
|
||||
IApp,
|
||||
IField,
|
||||
IJSONObject,
|
||||
} from '@automatisch/types';
|
||||
import { URLSearchParams } from 'url';
|
||||
import axios, { AxiosInstance } from 'axios';
|
||||
import AppInfo from '../../types/app-info';
|
||||
import Field from '../../types/field';
|
||||
import JSONObject from '../../types/interfaces/json-object';
|
||||
|
||||
export default class Authentication implements AuthenticationInterface {
|
||||
appData: AppInfo;
|
||||
connectionData: JSONObject;
|
||||
export default class Authentication implements IAuthentication {
|
||||
appData: IApp;
|
||||
connectionData: IJSONObject;
|
||||
client: AxiosInstance = axios.create({
|
||||
baseURL: 'https://api.typeform.com',
|
||||
});
|
||||
@@ -22,14 +24,14 @@ export default class Authentication implements AuthenticationInterface {
|
||||
'workspaces:read',
|
||||
];
|
||||
|
||||
constructor(appData: AppInfo, connectionData: JSONObject) {
|
||||
constructor(appData: IApp, connectionData: IJSONObject) {
|
||||
this.connectionData = connectionData;
|
||||
this.appData = appData;
|
||||
}
|
||||
|
||||
get oauthRedirectUrl() {
|
||||
return this.appData.fields.find(
|
||||
(field: Field) => field.key == 'oAuthRedirectUrl'
|
||||
(field: IField) => field.key == 'oAuthRedirectUrl'
|
||||
).value;
|
||||
}
|
||||
|
||||
|
@@ -1,7 +1,6 @@
|
||||
import Step from '../../models/step';
|
||||
import flowType, { flowInputType } from '../types/flow';
|
||||
import RequestWithCurrentUser from '../../types/express/request-with-current-user';
|
||||
import { StepType } from '../../types/step';
|
||||
|
||||
type Params = {
|
||||
input: {
|
||||
@@ -21,7 +20,7 @@ const createFlowResolver = async (
|
||||
|
||||
await Step.query().insert({
|
||||
flowId: flow.id,
|
||||
type: StepType.Trigger,
|
||||
type: 'trigger',
|
||||
position: 1,
|
||||
appKey,
|
||||
});
|
||||
|
@@ -1,7 +1,6 @@
|
||||
import { GraphQLNonNull } from 'graphql';
|
||||
import stepType, { stepInputType } from '../types/step';
|
||||
import RequestWithCurrentUser from '../../types/express/request-with-current-user';
|
||||
import { StepType } from '../../types/step';
|
||||
|
||||
type Params = {
|
||||
input: {
|
||||
@@ -42,7 +41,7 @@ const createStepResolver = async (
|
||||
const step = await flow.$relatedQuery('steps').insertAndFetch({
|
||||
key: input.key,
|
||||
appKey: input.appKey,
|
||||
type: StepType.Action,
|
||||
type: 'action',
|
||||
position: previousStep.position + 1,
|
||||
parameters: {},
|
||||
});
|
||||
|
@@ -1,11 +1,11 @@
|
||||
import type { IApp } from '@automatisch/types';
|
||||
import { GraphQLEnumType } from 'graphql';
|
||||
import App from '../../models/app';
|
||||
import appInfoType from '../../types/app-info'
|
||||
|
||||
const apps = App.findAll();
|
||||
const availableAppEnumValues: any = {}
|
||||
|
||||
apps.forEach((app: appInfoType) => {
|
||||
apps.forEach((app: IApp) => {
|
||||
availableAppEnumValues[app.key] = { value: app.key }
|
||||
})
|
||||
|
||||
|
@@ -1,11 +1,11 @@
|
||||
import AppInfoType from '../types/app-info';
|
||||
import type { IApp } from '@automatisch/types';
|
||||
import appConfig from '../config/app';
|
||||
|
||||
const appInfoConverter = (rawAppData: string) => {
|
||||
let computedRawData = rawAppData.replace('{BASE_URL}', appConfig.baseUrl);
|
||||
computedRawData = computedRawData.replace('{WEB_APP_URL}', appConfig.webAppUrl);
|
||||
|
||||
const computedJSONData: AppInfoType = JSON.parse(computedRawData)
|
||||
const computedJSONData: IApp = JSON.parse(computedRawData)
|
||||
return computedJSONData;
|
||||
}
|
||||
|
||||
|
@@ -6,10 +6,10 @@ import User from './user';
|
||||
import appConfig from '../config/app';
|
||||
|
||||
class Connection extends Base {
|
||||
id!: number;
|
||||
id!: string;
|
||||
key!: string;
|
||||
data!: any;
|
||||
userId!: number;
|
||||
userId!: string;
|
||||
verified: boolean;
|
||||
count: number;
|
||||
|
||||
|
@@ -4,8 +4,8 @@ import Step from './step';
|
||||
|
||||
class ExecutionStep extends Base {
|
||||
id!: string;
|
||||
executionId!: number;
|
||||
stepId!: number;
|
||||
executionId!: string;
|
||||
stepId!: string;
|
||||
dataIn!: any;
|
||||
dataOut!: any;
|
||||
status: string;
|
||||
@@ -17,8 +17,8 @@ class ExecutionStep extends Base {
|
||||
|
||||
properties: {
|
||||
id: { type: 'string' },
|
||||
executionId: { type: 'integer' },
|
||||
stepId: { type: 'integer' },
|
||||
executionId: { type: 'string' },
|
||||
stepId: { type: 'string' },
|
||||
dataIn: { type: 'object' },
|
||||
dataOut: { type: 'object' },
|
||||
status: { type: 'string', enum: ['success', 'failure'] },
|
||||
|
@@ -1,11 +1,11 @@
|
||||
import { ModelOptions, QueryContext, ValidationError } from 'objection';
|
||||
import { ValidationError } from 'objection';
|
||||
import Base from './base';
|
||||
import Step from './step';
|
||||
|
||||
class Flow extends Base {
|
||||
id!: string;
|
||||
name: string;
|
||||
userId!: number;
|
||||
userId!: string;
|
||||
active: boolean;
|
||||
steps?: [Step];
|
||||
|
||||
@@ -17,7 +17,7 @@ class Flow extends Base {
|
||||
properties: {
|
||||
id: { type: 'string' },
|
||||
name: { type: 'string' },
|
||||
userId: { type: 'integer' },
|
||||
userId: { type: 'string' },
|
||||
active: { type: 'boolean' },
|
||||
},
|
||||
};
|
||||
|
@@ -2,14 +2,14 @@ import Base from './base';
|
||||
import Flow from './flow';
|
||||
import Connection from './connection';
|
||||
import ExecutionStep from './execution-step';
|
||||
import { StepType } from '../types/step';
|
||||
import type { IStep } from '@automatisch/types';
|
||||
|
||||
class Step extends Base {
|
||||
id!: number;
|
||||
id!: string;
|
||||
flowId!: string;
|
||||
key: string;
|
||||
appKey: string;
|
||||
type!: StepType;
|
||||
type!: IStep["type"];
|
||||
connectionId?: string;
|
||||
status: string;
|
||||
position: number;
|
||||
@@ -25,7 +25,7 @@ class Step extends Base {
|
||||
required: ['type'],
|
||||
|
||||
properties: {
|
||||
id: { type: 'integer' },
|
||||
id: { type: 'string' },
|
||||
flowId: { type: 'string' },
|
||||
key: { type: ['string', 'null'] },
|
||||
appKey: { type: ['string', 'null'], minLength: 1, maxLength: 255 },
|
||||
|
@@ -6,7 +6,7 @@ import Step from './step';
|
||||
import bcrypt from 'bcrypt';
|
||||
|
||||
class User extends Base {
|
||||
id!: number;
|
||||
id!: string;
|
||||
email!: string;
|
||||
password!: string;
|
||||
connections?: [Connection];
|
||||
@@ -20,7 +20,7 @@ class User extends Base {
|
||||
required: ['email', 'password'],
|
||||
|
||||
properties: {
|
||||
id: { type: 'integer' },
|
||||
id: { type: 'string' },
|
||||
email: { type: 'string', format: 'email', minLength: 1, maxLength: 255 },
|
||||
password: { type: 'string', minLength: 1, maxLength: 255 },
|
||||
},
|
||||
|
@@ -4,7 +4,6 @@ import Flow from '../models/flow';
|
||||
import Step from '../models/step';
|
||||
import Execution from '../models/execution';
|
||||
import ExecutionStep from '../models/execution-step';
|
||||
import { StepType } from '../types/step';
|
||||
|
||||
type ExecutionSteps = Record<string, ExecutionStep>;
|
||||
|
||||
@@ -44,7 +43,7 @@ class Processor {
|
||||
parameters: rawParameters = {},
|
||||
id
|
||||
} = step;
|
||||
const isTrigger = type === StepType.Trigger;
|
||||
const isTrigger = type === 'trigger';
|
||||
const AppClass = (await import(`../apps/${appKey}`)).default;
|
||||
const computedParameters = Processor.computeParameters(rawParameters, priorExecutionSteps);
|
||||
const appInstance = new AppClass(appData, connection.data, computedParameters);
|
||||
|
@@ -1,10 +1,10 @@
|
||||
import AuthenticationStepField from '../types/authentication-step-field';
|
||||
import type { IAuthenticationStepField } from '@automatisch/types';
|
||||
|
||||
type AuthenticationStep = {
|
||||
step: number,
|
||||
type: string,
|
||||
name: string,
|
||||
fields: AuthenticationStepField[];
|
||||
fields: IAuthenticationStepField[];
|
||||
}
|
||||
|
||||
export default AuthenticationStep;
|
||||
|
@@ -1,9 +1,9 @@
|
||||
import appInfoType from '../../types/app-info';
|
||||
import type { IApp } from '@automatisch/types';
|
||||
import JSONObject from './json-object';
|
||||
|
||||
export default interface AuthenticationInterface {
|
||||
appData: appInfoType;
|
||||
connectionData: JSONObject;
|
||||
appData: IApp;
|
||||
connectionData: IJSONObject;
|
||||
client: unknown;
|
||||
verifyCredentials(): Promise<JSONObject>;
|
||||
isStillVerified(): Promise<boolean>;
|
||||
|
@@ -9,9 +9,18 @@
|
||||
"outDir": "dist",
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"*": ["node_modules/*", "src/types/*"]
|
||||
"*": [
|
||||
"node_modules/*",
|
||||
"src/types/*"
|
||||
]
|
||||
},
|
||||
"typeRoots": ["node_modules/@types", "./src/types", "./src/apps/*/types"]
|
||||
"typeRoots": [
|
||||
"node_modules/@types",
|
||||
"./src/types",
|
||||
"./src/apps/*/types"
|
||||
]
|
||||
},
|
||||
"include": ["src/**/*"]
|
||||
"include": [
|
||||
"src/**/*"
|
||||
]
|
||||
}
|
||||
|
11
packages/types/README.md
Normal file
11
packages/types/README.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# `@automatisch/types`
|
||||
|
||||
> TODO: description
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
const types = require('@automatisch/types');
|
||||
|
||||
// TODO: DEMONSTRATE API
|
||||
```
|
127
packages/types/index.d.ts
vendored
Normal file
127
packages/types/index.d.ts
vendored
Normal file
@@ -0,0 +1,127 @@
|
||||
// Type definitions for automatisch
|
||||
|
||||
export type IJSONValue = string | number | boolean | JSONObject | JSONArray;
|
||||
export type IJSONArray = Array<JSONValue>;
|
||||
export interface IJSONObject {
|
||||
[x: string]: JSONValue;
|
||||
}
|
||||
|
||||
export interface IConnection<D extends IJSONObject | string> {
|
||||
id: string;
|
||||
key: string;
|
||||
data: D;
|
||||
userId: string;
|
||||
verified: boolean;
|
||||
count: number;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
export interface IExecutionStep {
|
||||
id: string;
|
||||
executionId: string;
|
||||
stepId: string;
|
||||
dataIn: IJSONObject;
|
||||
dataOut: IJSONObject;
|
||||
status: string;
|
||||
}
|
||||
|
||||
export interface IExecution {
|
||||
id: string;
|
||||
flowId: string;
|
||||
testRun: boolean;
|
||||
executionSteps: IExecutionStep[];
|
||||
}
|
||||
|
||||
export interface IStep {
|
||||
id: string;
|
||||
name: string;
|
||||
flowId: string;
|
||||
key: string;
|
||||
appKey: string;
|
||||
type: 'action' | 'trigger';
|
||||
connectionId: string;
|
||||
status: string;
|
||||
position: number;
|
||||
parameters: Record<string, unknown>;
|
||||
connection: IConnection;
|
||||
flow: IFlow;
|
||||
executionSteps: IExecutionStep[];
|
||||
// FIXME: remove this property once execution steps are properly exposed via queries
|
||||
output: IJSONObject;
|
||||
}
|
||||
|
||||
export interface IFlow {
|
||||
id: string;
|
||||
name: string;
|
||||
userId: string;
|
||||
active: boolean;
|
||||
steps: IStep[];
|
||||
}
|
||||
|
||||
export interface IUser {
|
||||
id: string;
|
||||
email: string;
|
||||
password: string;
|
||||
connections: IConnection[];
|
||||
flows: IFlow[];
|
||||
steps: IStep[];
|
||||
}
|
||||
|
||||
export interface IField {
|
||||
key: string;
|
||||
label: string;
|
||||
type: string;
|
||||
required: boolean;
|
||||
readOnly: boolean;
|
||||
value: string;
|
||||
placeholder: string | null;
|
||||
description: string;
|
||||
docUrl: string;
|
||||
clickToCopy: boolean;
|
||||
name: string;
|
||||
variables: boolean;
|
||||
}
|
||||
|
||||
export interface IAuthenticationStepField {
|
||||
name: string;
|
||||
value: string | null;
|
||||
properties: {
|
||||
name: string;
|
||||
value: string;
|
||||
}[];
|
||||
}
|
||||
|
||||
export interface IAuthenticationStep {
|
||||
step: number;
|
||||
type: 'mutation' | 'openWithPopup';
|
||||
name: string;
|
||||
arguments: IAuthenticationStepField[];
|
||||
}
|
||||
|
||||
export interface IApp {
|
||||
name: string;
|
||||
key: string;
|
||||
iconUrl: string;
|
||||
docUrl: string;
|
||||
primaryColor: string;
|
||||
fields: IField[];
|
||||
authenticationSteps: IAuthenticationStep[];
|
||||
reconnectionSteps: IAuthenticationStep[];
|
||||
connectionCount: number;
|
||||
triggers: any[];
|
||||
actions: any[];
|
||||
connections: IConnection[];
|
||||
}
|
||||
|
||||
export interface IAuthentication {
|
||||
appData: IApp;
|
||||
connectionData: IJSONObject;
|
||||
client: unknown;
|
||||
verifyCredentials(): Promise<JSONObject>;
|
||||
isStillVerified(): Promise<boolean>;
|
||||
}
|
||||
|
||||
export interface ISubstep {
|
||||
name: string;
|
||||
arguments: IField[];
|
||||
}
|
17
packages/types/package.json
Normal file
17
packages/types/package.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "@automatisch/types",
|
||||
"version": "0.1.0",
|
||||
"description": "Type definitions for automatisch",
|
||||
"homepage": "https://github.com/automatisch/automatisch",
|
||||
"types": "./index.d.ts",
|
||||
"scripts": {},
|
||||
"dependencies": {},
|
||||
"typeScriptVersion": "4.1",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/automatisch/automatisch.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/automatisch/automatisch/issues"
|
||||
}
|
||||
}
|
@@ -4,6 +4,7 @@
|
||||
"description": "> TODO: description",
|
||||
"dependencies": {
|
||||
"@apollo/client": "^3.4.15",
|
||||
"@automatisch/types": "0.1.0",
|
||||
"@emotion/react": "^11.4.1",
|
||||
"@emotion/styled": "^11.3.0",
|
||||
"@mui/icons-material": "^5.0.1",
|
||||
|
@@ -10,12 +10,12 @@ import useFormatMessage from 'hooks/useFormatMessage';
|
||||
import computeAuthStepVariables from 'helpers/computeAuthStepVariables';
|
||||
import { processStep } from 'helpers/authenticationSteps';
|
||||
import InputCreator from 'components/InputCreator';
|
||||
import type { App } from 'types/app';
|
||||
import type { IApp, IField } from '@automatisch/types';
|
||||
import { Form } from './style';
|
||||
|
||||
type AddAppConnectionProps = {
|
||||
onClose: () => void;
|
||||
application: App;
|
||||
application: IApp;
|
||||
connectionId?: string;
|
||||
};
|
||||
|
||||
@@ -73,7 +73,7 @@ export default function AddAppConnection(props: AddAppConnectionProps): React.Re
|
||||
<DialogContent>
|
||||
<DialogContentText tabIndex={-1} component="div">
|
||||
<Form onSubmit={submitHandler}>
|
||||
{fields?.map(field => (<InputCreator key={field.key} schema={field} />))}
|
||||
{fields?.map((field: IField) => (<InputCreator key={field.key} schema={field} />))}
|
||||
|
||||
<LoadingButton
|
||||
type="submit"
|
||||
|
@@ -16,10 +16,10 @@ import ListItemText from '@mui/material/ListItemText';
|
||||
import InputLabel from '@mui/material/InputLabel';
|
||||
import OutlinedInput from '@mui/material/OutlinedInput';
|
||||
import FormControl from '@mui/material/FormControl';
|
||||
import type { IApp } from '@automatisch/types';
|
||||
|
||||
import * as URLS from 'config/urls';
|
||||
import AppIcon from 'components/AppIcon';
|
||||
import type { App } from 'types/app';
|
||||
import { GET_APPS } from 'graphql/queries/get-apps';
|
||||
import useFormatMessage from 'hooks/useFormatMessage';
|
||||
|
||||
@@ -68,7 +68,7 @@ export default function AddNewAppConnection(props: AddNewAppConnectionProps): Re
|
||||
</FormControl>
|
||||
|
||||
<List sx={{ pt: 2 }}>
|
||||
{data?.getApps?.map((app: App) => (
|
||||
{data?.getApps?.map((app: IApp) => (
|
||||
<ListItem disablePadding key={app.name}>
|
||||
<ListItemButton component={Link} to={URLS.APP_ADD_CONNECTION(app.name.toLowerCase())}>
|
||||
<ListItemIcon sx={{ minWidth: 74 }}>
|
||||
|
@@ -2,15 +2,15 @@ import { useQuery } from '@apollo/client';
|
||||
import { GET_FLOWS } from 'graphql/queries/get-flows';
|
||||
|
||||
import AppFlowRow from 'components/AppFlowRow';
|
||||
import type { Flow } from 'types/flow';
|
||||
import type { IFlow } from '@automatisch/types';
|
||||
|
||||
export default function AppFlows(): React.ReactElement {
|
||||
const { data } = useQuery(GET_FLOWS);
|
||||
const appFlows: Flow[] = data?.getFlows || [];
|
||||
const appFlows: IFlow[] = data?.getFlows || [];
|
||||
|
||||
return (
|
||||
<>
|
||||
{appFlows.map((appFlow: Flow) => (
|
||||
{appFlows.map((appFlow: IFlow) => (
|
||||
<AppFlowRow key={appFlow.id} flow={appFlow} />
|
||||
))}
|
||||
</>
|
||||
|
@@ -8,11 +8,12 @@ import ArrowForwardIosIcon from '@mui/icons-material/ArrowForwardIos';
|
||||
import useFormatMessage from 'hooks/useFormatMessage';
|
||||
import AppIcon from 'components/AppIcon';
|
||||
import * as URLS from 'config/urls';
|
||||
import type { App } from 'types/app';
|
||||
import type { IApp } from '@automatisch/types';
|
||||
|
||||
import { CardContent, Typography } from './style';
|
||||
|
||||
type AppRowProps = {
|
||||
application: App;
|
||||
application: IApp;
|
||||
}
|
||||
|
||||
const countTranslation = (value: React.ReactNode) => (
|
||||
|
@@ -7,22 +7,21 @@ import ListItem from '@mui/material/ListItem';
|
||||
import Autocomplete from '@mui/material/Autocomplete';
|
||||
|
||||
import FlowSubstepTitle from 'components/FlowSubstepTitle';
|
||||
import type { App, AppConnection } from 'types/app';
|
||||
import type { Step, Substep } from 'types/step';
|
||||
import type { IApp, IConnection, IStep, ISubstep, IJSONObject } from '@automatisch/types';
|
||||
import { GET_APP_CONNECTIONS } from 'graphql/queries/get-app-connections';
|
||||
import { TEST_CONNECTION } from 'graphql/queries/test-connection';
|
||||
|
||||
type ChooseAccountSubstepProps = {
|
||||
substep: Substep,
|
||||
substep: ISubstep,
|
||||
expanded?: boolean;
|
||||
onExpand: () => void;
|
||||
onCollapse: () => void;
|
||||
onChange: ({ step }: { step: Step}) => void;
|
||||
onChange: ({ step }: { step: IStep }) => void;
|
||||
onSubmit: () => void;
|
||||
step: Step;
|
||||
step: IStep;
|
||||
};
|
||||
|
||||
const optionGenerator = (connection: AppConnection): { label: string; value: string; } => ({
|
||||
const optionGenerator = (connection: IConnection<IJSONObject>): { label: string; value: string; } => ({
|
||||
label: connection?.data?.screenName as string ?? 'Unnamed',
|
||||
value: connection?.id as string,
|
||||
});
|
||||
@@ -62,7 +61,7 @@ function ChooseAccountSubstep(props: ChooseAccountSubstepProps): React.ReactElem
|
||||
// intentionally no dependencies for initial test
|
||||
}, []);
|
||||
|
||||
const connectionOptions = React.useMemo(() => (data?.getApp as App)?.connections?.map((connection) => optionGenerator(connection)) || [], [data]);
|
||||
const connectionOptions = React.useMemo(() => (data?.getApp as IApp)?.connections?.map((connection) => optionGenerator(connection)) || [], [data]);
|
||||
|
||||
const { name } = substep;
|
||||
|
||||
|
@@ -10,26 +10,24 @@ import Autocomplete from '@mui/material/Autocomplete';
|
||||
|
||||
import { GET_APPS } from 'graphql/queries/get-apps';
|
||||
import FlowSubstepTitle from 'components/FlowSubstepTitle';
|
||||
import type { App } from 'types/app';
|
||||
import type { Step, Substep } from 'types/step';
|
||||
import { StepType } from 'types/step';
|
||||
import type { IApp, IStep, ISubstep } from '@automatisch/types';
|
||||
|
||||
type ChooseAppAndEventSubstepProps = {
|
||||
substep: Substep,
|
||||
substep: ISubstep,
|
||||
expanded?: boolean;
|
||||
onExpand: () => void;
|
||||
onCollapse: () => void;
|
||||
onChange: ({ step }: { step: Step}) => void;
|
||||
onChange: ({ step }: { step: IStep }) => void;
|
||||
onSubmit: () => void;
|
||||
step: Step;
|
||||
step: IStep;
|
||||
};
|
||||
|
||||
const optionGenerator = (app: Record<string, unknown>): { label: string; value: string; } => ({
|
||||
const optionGenerator = (app: IApp): { label: string; value: string; } => ({
|
||||
label: app.name as string,
|
||||
value: app.key as string,
|
||||
});
|
||||
|
||||
const getOption = (options: Record<string, unknown>[], appKey: unknown) => options.find(app => app.value === appKey as string) || null;
|
||||
const getOption = (options: Record<string, unknown>[], appKey: IApp["key"]) => options.find(option => option.value === appKey as string) || null;
|
||||
|
||||
function ChooseAppAndEventSubstep(props: ChooseAppAndEventSubstepProps): React.ReactElement {
|
||||
const {
|
||||
@@ -42,11 +40,11 @@ function ChooseAppAndEventSubstep(props: ChooseAppAndEventSubstepProps): React.R
|
||||
onChange,
|
||||
} = props;
|
||||
|
||||
const isTrigger = step.type === StepType.Trigger;
|
||||
const isTrigger = step.type === 'trigger';
|
||||
|
||||
const { data } = useQuery(GET_APPS, { variables: { onlyWithTriggers: isTrigger }});
|
||||
const apps: App[] = data?.getApps;
|
||||
const app = apps?.find((currentApp: App) => currentApp.key === step.appKey);
|
||||
const apps: IApp[] = data?.getApps;
|
||||
const app = apps?.find((currentApp: IApp) => currentApp.key === step.appKey);
|
||||
|
||||
const appOptions = React.useMemo(() => apps?.map((app) => optionGenerator(app)), [apps]);
|
||||
const actionsOrTriggers = isTrigger ? app?.triggers : app?.actions;
|
||||
@@ -88,7 +86,7 @@ function ChooseAppAndEventSubstep(props: ChooseAppAndEventSubstepProps): React.R
|
||||
onChange({
|
||||
step: {
|
||||
...step,
|
||||
key: null,
|
||||
key: '',
|
||||
appKey,
|
||||
},
|
||||
});
|
||||
|
@@ -8,10 +8,10 @@ import { GET_FLOW } from 'graphql/queries/get-flow';
|
||||
import { CREATE_STEP } from 'graphql/mutations/create-step';
|
||||
import { UPDATE_STEP } from 'graphql/mutations/update-step';
|
||||
import FlowStep from 'components/FlowStep';
|
||||
import type { Flow } from 'types/flow';
|
||||
import type { IFlow } from '@automatisch/types';
|
||||
|
||||
type EditorProps = {
|
||||
flow: Flow;
|
||||
flow: IFlow;
|
||||
};
|
||||
|
||||
function updateHandlerFactory(flowId: string, previousStepId: string) {
|
||||
|
@@ -14,7 +14,7 @@ import Editor from 'components/Editor';
|
||||
import useFormatMessage from 'hooks/useFormatMessage';
|
||||
import { UPDATE_FLOW } from 'graphql/mutations/update-flow';
|
||||
import { GET_FLOW } from 'graphql/queries/get-flow';
|
||||
import type { Flow } from 'types/flow';
|
||||
import type { IFlow } from '@automatisch/types';
|
||||
import * as URLS from 'config/urls';
|
||||
|
||||
export default function EditorLayout(): React.ReactElement {
|
||||
@@ -22,7 +22,7 @@ export default function EditorLayout(): React.ReactElement {
|
||||
const formatMessage = useFormatMessage();
|
||||
const [updateFlow] = useMutation(UPDATE_FLOW);
|
||||
const { data, loading } = useQuery(GET_FLOW, { variables: { id: flowId }});
|
||||
const flow: Flow = data?.getFlow;
|
||||
const flow: IFlow = data?.getFlow;
|
||||
|
||||
const onFlowNameUpdate = React.useCallback(async (name: string) => {
|
||||
await updateFlow({
|
||||
|
@@ -5,12 +5,12 @@ import Box from '@mui/material/Box';
|
||||
import CardActionArea from '@mui/material/CardActionArea';
|
||||
import ArrowForwardIosIcon from '@mui/icons-material/ArrowForwardIos';
|
||||
|
||||
import type { Flow } from 'types/flow';
|
||||
import type { IFlow } from '@automatisch/types';
|
||||
import * as URLS from 'config/urls';
|
||||
import { CardContent, Typography } from './style';
|
||||
|
||||
type FlowRowProps = {
|
||||
flow: Flow;
|
||||
flow: IFlow;
|
||||
}
|
||||
|
||||
export default function FlowRow(props: FlowRowProps): React.ReactElement {
|
||||
|
@@ -10,6 +10,7 @@ import MoreHorizIcon from '@mui/icons-material/MoreHoriz';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
import ErrorIcon from '@mui/icons-material/Error';
|
||||
import CheckCircleIcon from '@mui/icons-material/CheckCircle';
|
||||
import type { IApp, IField, IStep } from '@automatisch/types';
|
||||
|
||||
import { StepExecutionsProvider } from 'contexts/StepExecutions';
|
||||
import TestSubstep from 'components/TestSubstep';
|
||||
@@ -22,18 +23,15 @@ import AppIcon from 'components/AppIcon';
|
||||
import { GET_APPS } from 'graphql/queries/get-apps';
|
||||
import { GET_STEP_WITH_TEST_EXECUTIONS } from 'graphql/queries/get-step-with-test-executions';
|
||||
import useFormatMessage from 'hooks/useFormatMessage';
|
||||
import type { App, AppFields } from 'types/app';
|
||||
import type { Step } from 'types/step';
|
||||
import { StepType } from 'types/step';
|
||||
import { AppIconWrapper, AppIconStatusIconWrapper, Content, Header, Wrapper } from './style';
|
||||
|
||||
type FlowStepProps = {
|
||||
collapsed?: boolean;
|
||||
step: Step;
|
||||
step: IStep;
|
||||
index?: number;
|
||||
onOpen?: () => void;
|
||||
onClose?: () => void;
|
||||
onChange: (step: Step) => void;
|
||||
onChange: (step: IStep) => void;
|
||||
}
|
||||
|
||||
const validIcon = <CheckCircleIcon color="success" />;
|
||||
@@ -42,9 +40,9 @@ const errorIcon = <ErrorIcon color="error" />;
|
||||
export default function FlowStep(props: FlowStepProps): React.ReactElement | null {
|
||||
const { collapsed, index, onChange } = props;
|
||||
const contextButtonRef = React.useRef<HTMLButtonElement | null>(null);
|
||||
const step: Step = props.step;
|
||||
const step: IStep = props.step;
|
||||
const [anchorEl, setAnchorEl] = React.useState<HTMLButtonElement | null>(null);
|
||||
const isTrigger = step.type === StepType.Trigger;
|
||||
const isTrigger = step.type === 'trigger';
|
||||
const formatMessage = useFormatMessage();
|
||||
const [currentSubstep, setCurrentSubstep] = React.useState<number | null>(2);
|
||||
const { data } = useQuery(GET_APPS, { variables: { onlyWithTriggers: isTrigger }});
|
||||
@@ -70,13 +68,13 @@ export default function FlowStep(props: FlowStepProps): React.ReactElement | nul
|
||||
}
|
||||
}, [collapsed, stepWithTestExecutionsCalled, getStepWithTestExecutions, step.id, isTrigger]);
|
||||
|
||||
const apps: App[] = data?.getApps;
|
||||
const app = apps?.find((currentApp: App) => currentApp.key === step.appKey);
|
||||
const apps: IApp[] = data?.getApps;
|
||||
const app = apps?.find((currentApp: IApp) => currentApp.key === step.appKey);
|
||||
|
||||
const actionsOrTriggers = isTrigger ? app?.triggers : app?.actions;
|
||||
const substeps = React.useMemo(() => actionsOrTriggers?.find(({ key }) => key === step.key)?.subSteps || [], [actionsOrTriggers, step?.key]);
|
||||
|
||||
const handleChange = React.useCallback(({ step }: { step: Step }) => {
|
||||
const handleChange = React.useCallback(({ step }: { step: IStep }) => {
|
||||
onChange(step);
|
||||
}, [])
|
||||
|
||||
@@ -85,7 +83,7 @@ export default function FlowStep(props: FlowStepProps): React.ReactElement | nul
|
||||
}, []);
|
||||
|
||||
const handleSubmit = (val: any) => {
|
||||
handleChange({ step: val as Step });
|
||||
handleChange({ step: val as IStep });
|
||||
}
|
||||
|
||||
if (!apps) return null;
|
||||
@@ -143,7 +141,7 @@ export default function FlowStep(props: FlowStepProps): React.ReactElement | nul
|
||||
<Collapse in={!collapsed} unmountOnExit>
|
||||
<Content>
|
||||
<List>
|
||||
<StepExecutionsProvider value={stepWithTestExecutionsData?.getStepWithTestExecutions as Step[]}>
|
||||
<StepExecutionsProvider value={stepWithTestExecutionsData?.getStepWithTestExecutions as IStep[]}>
|
||||
<Form defaultValues={step} onSubmit={handleSubmit}>
|
||||
<ChooseAppAndEventSubstep
|
||||
expanded={currentSubstep === 0}
|
||||
@@ -155,7 +153,7 @@ export default function FlowStep(props: FlowStepProps): React.ReactElement | nul
|
||||
step={step}
|
||||
/>
|
||||
|
||||
{substeps?.length > 0 && substeps.map((substep: { name: string, key: string, arguments: AppFields[] }, index: number) => (
|
||||
{substeps?.length > 0 && substeps.map((substep: { name: string, key: string, arguments: IField[] }, index: number) => (
|
||||
<React.Fragment key={`${substep?.name}-${index}`}>
|
||||
{substep.key === 'chooseAccount' && (
|
||||
<ChooseAccountSubstep
|
||||
|
@@ -6,23 +6,22 @@ import Button from '@mui/material/Button';
|
||||
|
||||
import FlowSubstepTitle from 'components/FlowSubstepTitle';
|
||||
import InputCreator from 'components/InputCreator';
|
||||
import type { Step, Substep } from 'types/step';
|
||||
import type { AppFields } from 'types/app';
|
||||
import type { IField, IStep, ISubstep } from '@automatisch/types';
|
||||
|
||||
type FlowSubstepProps = {
|
||||
substep: Substep,
|
||||
substep: ISubstep,
|
||||
expanded?: boolean;
|
||||
onExpand: () => void;
|
||||
onCollapse: () => void;
|
||||
onChange: ({ step }: { step: Step }) => void;
|
||||
onChange: ({ step }: { step: IStep }) => void;
|
||||
onSubmit: () => void;
|
||||
step: Step;
|
||||
step: IStep;
|
||||
};
|
||||
|
||||
const validateSubstep = (substep: Substep, step: Step) => {
|
||||
const validateSubstep = (substep: ISubstep, step: IStep) => {
|
||||
if (!substep) return true;
|
||||
|
||||
const args: AppFields[] = substep.arguments || [];
|
||||
const args: IField[] = substep.arguments || [];
|
||||
|
||||
return args.every(arg => {
|
||||
if (arg.required === false) { return true; }
|
||||
@@ -50,7 +49,7 @@ function FlowSubstep(props: FlowSubstepProps): React.ReactElement {
|
||||
} = substep;
|
||||
|
||||
const formContext = useFormContext();
|
||||
const [validationStatus, setValidationStatus] = React.useState<boolean | null>(validateSubstep(substep, formContext.getValues() as Step));
|
||||
const [validationStatus, setValidationStatus] = React.useState<boolean | null>(validateSubstep(substep, formContext.getValues() as IStep));
|
||||
|
||||
|
||||
const handleChangeOnBlur = React.useCallback((key: string) => {
|
||||
@@ -73,7 +72,7 @@ function FlowSubstep(props: FlowSubstepProps): React.ReactElement {
|
||||
|
||||
React.useEffect(() => {
|
||||
function validate (step: unknown) {
|
||||
const validationResult = validateSubstep(substep, step as Step);
|
||||
const validationResult = validateSubstep(substep, step as IStep);
|
||||
setValidationStatus(validationResult);
|
||||
};
|
||||
const subscription = formContext.watch(validate);
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import * as React from 'react';
|
||||
import { useFormContext } from 'react-hook-form';
|
||||
import type { AppFields } from 'types/app';
|
||||
import type { IField } from '@automatisch/types';
|
||||
|
||||
import PowerInput from 'components/PowerInput';
|
||||
import TextField from 'components/TextField';
|
||||
@@ -8,7 +8,7 @@ import TextField from 'components/TextField';
|
||||
type InputCreatorProps = {
|
||||
onChange?: React.ChangeEventHandler;
|
||||
onBlur?: React.FocusEventHandler;
|
||||
schema: AppFields;
|
||||
schema: IField;
|
||||
namePrefix?: string;
|
||||
};
|
||||
|
||||
|
@@ -10,7 +10,7 @@ import Collapse from '@mui/material/Collapse';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import ExpandLess from '@mui/icons-material/ExpandLess';
|
||||
import ExpandMore from '@mui/icons-material/ExpandMore';
|
||||
import { Step } from 'types/step';
|
||||
import type { IStep } from '@automatisch/types';
|
||||
|
||||
const ListItemText = styled(MuiListItemText)``;
|
||||
|
||||
@@ -61,7 +61,7 @@ const Suggestions = (props: SuggestionsProps) => {
|
||||
<List
|
||||
disablePadding
|
||||
>
|
||||
{data.map((option: Step, index: number) => (
|
||||
{data.map((option: IStep, index: number) => (
|
||||
<>
|
||||
<ListItemButton
|
||||
divider
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { Step } from 'types/step';
|
||||
import type { IStep } from '@automatisch/types';
|
||||
|
||||
const joinBy = (delimiter = '.', ...args: string[]) => args.filter(Boolean).join(delimiter);
|
||||
|
||||
@@ -32,10 +32,10 @@ const process = (data: any, parentKey?: any, index?: number): any[] => {
|
||||
});
|
||||
};
|
||||
|
||||
export const processStepWithExecutions = (steps: Step[]): any[] => {
|
||||
export const processStepWithExecutions = (steps: IStep[]): any[] => {
|
||||
if (!steps) return [];
|
||||
|
||||
return steps.map((step: Step, index: number) => ({
|
||||
return steps.map((step: IStep, index: number) => ({
|
||||
id: step.id,
|
||||
// TODO: replace with step.name once introduced
|
||||
name: `${index + 1}. ${step.appKey}`,
|
||||
|
@@ -6,17 +6,16 @@ import Button from '@mui/material/Button';
|
||||
|
||||
import { EXECUTE_FLOW } from 'graphql/mutations/execute-flow';
|
||||
import FlowSubstepTitle from 'components/FlowSubstepTitle';
|
||||
import type { Step, Substep } from 'types/step';
|
||||
import type { AppFields } from 'types/app';
|
||||
import type { IStep, ISubstep } from '@automatisch/types';
|
||||
|
||||
type TestSubstepProps = {
|
||||
substep: Substep,
|
||||
substep: ISubstep,
|
||||
expanded?: boolean;
|
||||
onExpand: () => void;
|
||||
onCollapse: () => void;
|
||||
onChange?: ({ step }: { step: Step }) => void;
|
||||
onChange?: ({ step }: { step: IStep }) => void;
|
||||
onSubmit?: () => void;
|
||||
step: Step;
|
||||
step: IStep;
|
||||
};
|
||||
|
||||
function TestSubstep(props: TestSubstepProps): React.ReactElement {
|
||||
|
@@ -1,11 +1,11 @@
|
||||
import * as React from 'react';
|
||||
import { Step } from 'types/step';
|
||||
import type { IStep } from '@automatisch/types';
|
||||
|
||||
export const StepExecutionsContext = React.createContext<Step[]>([]);
|
||||
export const StepExecutionsContext = React.createContext<IStep[]>([]);
|
||||
|
||||
type StepExecutionsProviderProps = {
|
||||
children: React.ReactNode;
|
||||
value: Step[];
|
||||
value: IStep[];
|
||||
}
|
||||
|
||||
export const StepExecutionsProvider = (props: StepExecutionsProviderProps): React.ReactElement => {
|
||||
|
@@ -8,6 +8,7 @@ export const CREATE_STEP = gql`
|
||||
key
|
||||
appKey
|
||||
parameters
|
||||
status
|
||||
connection {
|
||||
id
|
||||
}
|
||||
|
@@ -1,3 +1,4 @@
|
||||
import type { IAuthenticationStep } from '@automatisch/types';
|
||||
import apolloClient from 'graphql/client';
|
||||
import MUTATIONS from 'graphql/mutations';
|
||||
import appConfig from 'config/app';
|
||||
@@ -7,14 +8,7 @@ enum AuthenticationSteps {
|
||||
OpenWithPopup = 'openWithPopup',
|
||||
}
|
||||
|
||||
type Step = {
|
||||
name: string;
|
||||
variables: Record<string, unknown>;
|
||||
process: (step: any, variables: Record<string, unknown>) => Promise<any>;
|
||||
type: AuthenticationSteps.Mutation | AuthenticationSteps.OpenWithPopup;
|
||||
};
|
||||
|
||||
const processMutation = async (step: Step, variables: Record<string, unknown>) => {
|
||||
const processMutation = async (step: IAuthenticationStep, variables: Record<string, unknown>) => {
|
||||
const mutation = MUTATIONS[step.name];
|
||||
const mutationResponse = await apolloClient.mutate({ mutation, variables });
|
||||
const responseData = mutationResponse.data[step.name];
|
||||
@@ -38,7 +32,7 @@ function getObjectOfEntries(iterator: any) {
|
||||
return result;
|
||||
}
|
||||
|
||||
const processOpenWithPopup = (step: Step, variables: Record<string, string>) => {
|
||||
const processOpenWithPopup = (step: IAuthenticationStep, variables: Record<string, string>) => {
|
||||
return new Promise((resolve) => {
|
||||
const windowFeatures = 'toolbar=no, titlebar=no, menubar=no, width=500, height=700, top=100, left=100';
|
||||
const url = variables.url;
|
||||
@@ -62,7 +56,7 @@ const processOpenWithPopup = (step: Step, variables: Record<string, string>) =>
|
||||
});
|
||||
};
|
||||
|
||||
export const processStep = async (step: Step, variables: Record<string, string>): Promise<any> => {
|
||||
export const processStep = async (step: IAuthenticationStep, variables: Record<string, string>): Promise<any> => {
|
||||
if (step.type === AuthenticationSteps.Mutation) {
|
||||
return processMutation(step, variables);
|
||||
} else if (step.type === AuthenticationSteps.OpenWithPopup) {
|
||||
|
@@ -1,4 +1,5 @@
|
||||
import template from 'lodash.template';
|
||||
import type { IAuthenticationStepField, IJSONObject } from '@automatisch/types';
|
||||
|
||||
const interpolate = /{([\s\S]+?)}/g;
|
||||
|
||||
@@ -6,18 +7,9 @@ type Variables = {
|
||||
[key: string]: any
|
||||
}
|
||||
|
||||
type VariableSchema = {
|
||||
properties: VariableSchema[];
|
||||
name: string;
|
||||
type: 'string' | 'integer';
|
||||
value: string;
|
||||
}
|
||||
type IVariable = Omit<IAuthenticationStepField, "properties"> & Partial<Pick<IAuthenticationStepField, "properties">>;
|
||||
|
||||
type AggregatedData = {
|
||||
[key: string]: Record<string, unknown> | string;
|
||||
}
|
||||
|
||||
const computeAuthStepVariables = (variableSchema: VariableSchema[], aggregatedData: AggregatedData): Variables => {
|
||||
const computeAuthStepVariables = (variableSchema: IVariable[], aggregatedData: IJSONObject): IJSONObject => {
|
||||
const variables: Variables = {};
|
||||
|
||||
for (const variable of variableSchema) {
|
||||
@@ -27,11 +19,9 @@ const computeAuthStepVariables = (variableSchema: VariableSchema[], aggregatedDa
|
||||
continue;
|
||||
}
|
||||
|
||||
if (variable.value) {
|
||||
const computedVariable = template(variable.value, { interpolate })(aggregatedData);
|
||||
|
||||
if (variable.type === 'integer') {
|
||||
variables[variable.name] = parseInt(computedVariable, 10);
|
||||
} else {
|
||||
variables[variable.name] = computedVariable;
|
||||
}
|
||||
}
|
||||
|
@@ -5,6 +5,7 @@ import { useQuery } from '@apollo/client';
|
||||
import Box from '@mui/material/Box';
|
||||
import Grid from '@mui/material/Grid';
|
||||
import AddIcon from '@mui/icons-material/Add';
|
||||
import type { IApp } from '@automatisch/types';
|
||||
|
||||
import ConditionalIconButton from 'components/ConditionalIconButton';
|
||||
import Container from 'components/Container';
|
||||
@@ -15,7 +16,6 @@ import SearchInput from 'components/SearchInput';
|
||||
import useFormatMessage from 'hooks/useFormatMessage'
|
||||
import { GET_CONNECTED_APPS } from 'graphql/queries/get-connected-apps';
|
||||
import * as URLS from 'config/urls';
|
||||
import type { App } from 'types/app';
|
||||
|
||||
export default function Applications(): React.ReactElement {
|
||||
const navigate = useNavigate();
|
||||
@@ -69,7 +69,7 @@ export default function Applications(): React.ReactElement {
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
{data?.getConnectedApps?.map((app: App) => (
|
||||
{data?.getConnectedApps?.map((app: IApp) => (
|
||||
<AppRow key={app.name} application={app} />
|
||||
))}
|
||||
|
||||
|
@@ -5,6 +5,7 @@ import { useQuery } from '@apollo/client';
|
||||
import Box from '@mui/material/Box';
|
||||
import Grid from '@mui/material/Grid';
|
||||
import AddIcon from '@mui/icons-material/Add';
|
||||
import type { IFlow } from '@automatisch/types';
|
||||
|
||||
import FlowRow from 'components/FlowRow';
|
||||
import ConditionalIconButton from 'components/ConditionalIconButton';
|
||||
@@ -14,14 +15,13 @@ import SearchInput from 'components/SearchInput';
|
||||
import useFormatMessage from 'hooks/useFormatMessage'
|
||||
import { GET_FLOWS } from 'graphql/queries/get-flows';
|
||||
import * as URLS from 'config/urls';
|
||||
import type { Flow } from 'types/flow';
|
||||
|
||||
export default function Flows(): React.ReactElement {
|
||||
const formatMessage = useFormatMessage();
|
||||
const [flowName, setFlowName] = React.useState('');
|
||||
const { data } = useQuery(GET_FLOWS);
|
||||
|
||||
const flows: Flow[] = data?.getFlows?.filter((flow: Flow) => flow.name?.toLowerCase().includes(flowName.toLowerCase()));
|
||||
const flows: IFlow[] = data?.getFlows?.filter((flow: IFlow) => flow.name?.toLowerCase().includes(flowName.toLowerCase()));
|
||||
|
||||
const onSearchChange = React.useCallback((event) => {
|
||||
setFlowName(event.target.value);
|
||||
|
@@ -1,40 +0,0 @@
|
||||
type AppFields = {
|
||||
key: string;
|
||||
name: string;
|
||||
label: string;
|
||||
type: string;
|
||||
required: boolean;
|
||||
readOnly: boolean;
|
||||
value: string;
|
||||
description: string;
|
||||
docUrl: string;
|
||||
clickToCopy: boolean;
|
||||
variables?: boolean;
|
||||
};
|
||||
|
||||
type AppConnection = {
|
||||
id: string;
|
||||
key: string;
|
||||
verified: boolean;
|
||||
createdAt: string;
|
||||
data: {
|
||||
[key: string]: any;
|
||||
};
|
||||
};
|
||||
|
||||
type App = {
|
||||
key: string;
|
||||
name: string;
|
||||
connectionCount: number;
|
||||
iconUrl: string;
|
||||
docUrl: string;
|
||||
primaryColor: string;
|
||||
fields: AppFields[];
|
||||
authenticationSteps: any[];
|
||||
reconnectionSteps: any[];
|
||||
triggers: any[];
|
||||
actions: any[];
|
||||
connections: AppConnection[];
|
||||
};
|
||||
|
||||
export type { App, AppFields, AppConnection };
|
@@ -1,13 +1,3 @@
|
||||
type ConnectionData = {
|
||||
screenName: string;
|
||||
}
|
||||
import type { IConnection, IJSONObject } from '@automatisch/types';
|
||||
|
||||
type Connection = {
|
||||
id: string;
|
||||
key: string;
|
||||
data: ConnectionData;
|
||||
verified?: boolean;
|
||||
createdAt: string;
|
||||
};
|
||||
|
||||
export type { Connection, ConnectionData };
|
||||
export type Connection = IConnection<IJSONObject>;
|
||||
|
@@ -1,8 +0,0 @@
|
||||
import type { Step } from './step';
|
||||
|
||||
export type Flow = {
|
||||
id: string;
|
||||
name: string;
|
||||
steps: Step[];
|
||||
active: boolean;
|
||||
};
|
@@ -1,25 +0,0 @@
|
||||
import type { AppFields } from './app';
|
||||
import type { Connection } from './connection';
|
||||
|
||||
export enum StepType {
|
||||
Trigger = 'trigger',
|
||||
Action = 'action',
|
||||
}
|
||||
|
||||
export type Step = {
|
||||
id: string;
|
||||
key: string | null;
|
||||
name: string;
|
||||
appKey: string | null;
|
||||
type: StepType;
|
||||
previousStepId: string | null;
|
||||
parameters: Record<string, unknown>;
|
||||
connection: Pick<Connection, 'id' | 'verified'>;
|
||||
status: 'completed' | 'incomplete';
|
||||
output: Record<string, unknown>;
|
||||
};
|
||||
|
||||
export type Substep = {
|
||||
name: string;
|
||||
arguments: AppFields[];
|
||||
};
|
Reference in New Issue
Block a user