Compare commits
11 Commits
new-find-p
...
debug/memo
Author | SHA1 | Date | |
---|---|---|---|
![]() |
7787436fd4 | ||
![]() |
8c9bf9c7ef | ||
![]() |
cc1892a5cf | ||
![]() |
c59bb85793 | ||
![]() |
a88d208cd6 | ||
![]() |
91e209e961 | ||
![]() |
7c543ff8ed | ||
![]() |
9f56fd7c3b | ||
![]() |
3a63fc376d | ||
![]() |
445ed9239d | ||
![]() |
9d5060f902 |
@@ -31,7 +31,7 @@
|
|||||||
"ajv-formats": "^2.1.1",
|
"ajv-formats": "^2.1.1",
|
||||||
"axios": "0.24.0",
|
"axios": "0.24.0",
|
||||||
"bcrypt": "^5.0.1",
|
"bcrypt": "^5.0.1",
|
||||||
"bullmq": "^1.76.1",
|
"bullmq": "^1.82.0",
|
||||||
"copyfiles": "^2.4.1",
|
"copyfiles": "^2.4.1",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"crypto-js": "^4.1.1",
|
"crypto-js": "^4.1.1",
|
||||||
|
@@ -1,13 +1,16 @@
|
|||||||
import { IJSONObject } from '@automatisch/types';
|
import { IJSONObject } from '@automatisch/types';
|
||||||
import ListRepos from './data/list-repos';
|
import ListRepos from './data/list-repos';
|
||||||
import ListBranches from './data/list-branches';
|
import ListBranches from './data/list-branches';
|
||||||
|
import ListLabels from './data/list-labels';
|
||||||
|
|
||||||
export default class Data {
|
export default class Data {
|
||||||
listRepos: ListRepos;
|
listRepos: ListRepos;
|
||||||
listBranches: ListBranches;
|
listBranches: ListBranches;
|
||||||
|
listLabels: ListLabels;
|
||||||
|
|
||||||
constructor(connectionData: IJSONObject, parameters: IJSONObject) {
|
constructor(connectionData: IJSONObject, parameters: IJSONObject) {
|
||||||
this.listRepos = new ListRepos(connectionData);
|
this.listRepos = new ListRepos(connectionData);
|
||||||
this.listBranches = new ListBranches(connectionData, parameters);
|
this.listBranches = new ListBranches(connectionData, parameters);
|
||||||
|
this.listLabels = new ListLabels(connectionData, parameters);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
36
packages/backend/src/apps/github/data/list-labels.ts
Normal file
36
packages/backend/src/apps/github/data/list-labels.ts
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import { Octokit } from 'octokit';
|
||||||
|
import type { IJSONObject } from '@automatisch/types';
|
||||||
|
|
||||||
|
import { assignOwnerAndRepo } from '../utils';
|
||||||
|
|
||||||
|
export default class ListLabels {
|
||||||
|
client?: Octokit;
|
||||||
|
repoOwner?: string;
|
||||||
|
repo?: string;
|
||||||
|
|
||||||
|
constructor(connectionData: IJSONObject, parameters?: IJSONObject) {
|
||||||
|
if (connectionData.accessToken) {
|
||||||
|
this.client = new Octokit({
|
||||||
|
auth: connectionData.accessToken as string,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
assignOwnerAndRepo(this, parameters?.repo as string);
|
||||||
|
}
|
||||||
|
|
||||||
|
get options() {
|
||||||
|
return {
|
||||||
|
owner: this.repoOwner,
|
||||||
|
repo: this.repo,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async run() {
|
||||||
|
const labels = await this.client.paginate(this.client.rest.issues.listLabelsForRepo, this.options);
|
||||||
|
|
||||||
|
return labels.map((label) => ({
|
||||||
|
value: label.name,
|
||||||
|
name: label.name,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
@@ -649,6 +649,98 @@
|
|||||||
"name": "Test trigger"
|
"name": "Test trigger"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "New issue",
|
||||||
|
"key": "newIssue",
|
||||||
|
"description": "Triggers when a new issue is created",
|
||||||
|
"substeps": [
|
||||||
|
{
|
||||||
|
"key": "chooseAccount",
|
||||||
|
"name": "Choose account"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "chooseTrigger",
|
||||||
|
"name": "Set up a trigger",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"label": "Repo",
|
||||||
|
"key": "repo",
|
||||||
|
"type": "dropdown",
|
||||||
|
"required": false,
|
||||||
|
"variables": false,
|
||||||
|
"source": {
|
||||||
|
"type": "query",
|
||||||
|
"name": "getData",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "key",
|
||||||
|
"value": "listRepos"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Which types of issues should this trigger on?",
|
||||||
|
"key": "issueType",
|
||||||
|
"type": "dropdown",
|
||||||
|
"description": "Defaults to any issue you can see.",
|
||||||
|
"required": true,
|
||||||
|
"variables": false,
|
||||||
|
"value": "all",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"label": "Any issue you can see",
|
||||||
|
"value": "all"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Only issues assigned to you",
|
||||||
|
"value": "assigned"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Only issues created by you",
|
||||||
|
"value": "created"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Only issues you're mentioned in",
|
||||||
|
"value": "mentioned"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Only issues you're subscribed to",
|
||||||
|
"value": "subscribed"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Label",
|
||||||
|
"key": "label",
|
||||||
|
"type": "dropdown",
|
||||||
|
"description": "Only trigger on issues when this label is added.",
|
||||||
|
"required": false,
|
||||||
|
"variables": false,
|
||||||
|
"dependsOn": ["parameters.repo"],
|
||||||
|
"source": {
|
||||||
|
"type": "query",
|
||||||
|
"name": "getData",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "key",
|
||||||
|
"value": "listLabels"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "parameters.repo",
|
||||||
|
"value": "{parameters.repo}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "testStep",
|
||||||
|
"name": "Test trigger"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@@ -11,6 +11,7 @@ import NewCommitComment from './triggers/new-commit-comment';
|
|||||||
import NewLabel from './triggers/new-label';
|
import NewLabel from './triggers/new-label';
|
||||||
import NewCollaborator from './triggers/new-collaborator';
|
import NewCollaborator from './triggers/new-collaborator';
|
||||||
import NewRelease from './triggers/new-release';
|
import NewRelease from './triggers/new-release';
|
||||||
|
import NewIssue from './triggers/new-issue';
|
||||||
|
|
||||||
export default class Triggers {
|
export default class Triggers {
|
||||||
newRepository: NewRepository;
|
newRepository: NewRepository;
|
||||||
@@ -25,6 +26,7 @@ export default class Triggers {
|
|||||||
newLabel: NewLabel;
|
newLabel: NewLabel;
|
||||||
newCollaborator: NewCollaborator;
|
newCollaborator: NewCollaborator;
|
||||||
newRelease: NewRelease;
|
newRelease: NewRelease;
|
||||||
|
newIssue: NewIssue;
|
||||||
|
|
||||||
constructor(connectionData: IJSONObject, parameters: IJSONObject) {
|
constructor(connectionData: IJSONObject, parameters: IJSONObject) {
|
||||||
this.newRepository = new NewRepository(connectionData);
|
this.newRepository = new NewRepository(connectionData);
|
||||||
@@ -39,5 +41,6 @@ export default class Triggers {
|
|||||||
this.newLabel = new NewLabel(connectionData, parameters);
|
this.newLabel = new NewLabel(connectionData, parameters);
|
||||||
this.newCollaborator = new NewCollaborator(connectionData, parameters);
|
this.newCollaborator = new NewCollaborator(connectionData, parameters);
|
||||||
this.newRelease = new NewRelease(connectionData, parameters);
|
this.newRelease = new NewRelease(connectionData, parameters);
|
||||||
|
this.newIssue = new NewIssue(connectionData, parameters);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
88
packages/backend/src/apps/github/triggers/new-issue.ts
Normal file
88
packages/backend/src/apps/github/triggers/new-issue.ts
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
import { Octokit } from 'octokit';
|
||||||
|
import { DateTime } from 'luxon';
|
||||||
|
import { IJSONObject } from '@automatisch/types';
|
||||||
|
|
||||||
|
import { assignOwnerAndRepo } from '../utils';
|
||||||
|
|
||||||
|
export default class NewIssue {
|
||||||
|
client?: Octokit;
|
||||||
|
connectionData?: IJSONObject;
|
||||||
|
repoOwner?: string;
|
||||||
|
repo?: string;
|
||||||
|
hasRepo?: boolean;
|
||||||
|
label?: string;
|
||||||
|
issueType?: string;
|
||||||
|
|
||||||
|
constructor(connectionData: IJSONObject, parameters: IJSONObject) {
|
||||||
|
if (connectionData.accessToken) {
|
||||||
|
this.client = new Octokit({
|
||||||
|
auth: connectionData.accessToken as string,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
assignOwnerAndRepo(this, parameters?.repo as string);
|
||||||
|
}
|
||||||
|
|
||||||
|
get options() {
|
||||||
|
return {
|
||||||
|
labels: this.label,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async listRepoIssues(options = {}, paginate = false) {
|
||||||
|
const listRepoIssues = this.client.rest.issues.listForRepo;
|
||||||
|
|
||||||
|
const extendedOptions = {
|
||||||
|
...this.options,
|
||||||
|
repo: this.repo,
|
||||||
|
owner: this.repoOwner,
|
||||||
|
filter: this.issueType,
|
||||||
|
...options,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (paginate) {
|
||||||
|
return await this.client.paginate(listRepoIssues, extendedOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (await listRepoIssues(extendedOptions)).data;
|
||||||
|
}
|
||||||
|
|
||||||
|
async listIssues(options = {}, paginate = false) {
|
||||||
|
const listIssues = this.client.rest.issues.listForAuthenticatedUser;
|
||||||
|
|
||||||
|
const extendedOptions = {
|
||||||
|
...this.options,
|
||||||
|
...options,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (paginate) {
|
||||||
|
return await this.client.paginate(listIssues, extendedOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (await listIssues(extendedOptions)).data;
|
||||||
|
}
|
||||||
|
|
||||||
|
async run(startTime: Date) {
|
||||||
|
const options = {
|
||||||
|
since: DateTime.fromJSDate(startTime).toISO(),
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this.hasRepo) {
|
||||||
|
return await this.listRepoIssues(options, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return await this.listIssues(options, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
async testRun() {
|
||||||
|
const options = {
|
||||||
|
per_page: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this.hasRepo) {
|
||||||
|
return await this.listRepoIssues(options, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return await this.listIssues(options, false);
|
||||||
|
}
|
||||||
|
}
|
@@ -9,6 +9,7 @@ export default class NewNotification {
|
|||||||
connectionData?: IJSONObject;
|
connectionData?: IJSONObject;
|
||||||
repoOwner?: string;
|
repoOwner?: string;
|
||||||
repo?: string;
|
repo?: string;
|
||||||
|
hasRepo?: boolean;
|
||||||
baseOptions = {
|
baseOptions = {
|
||||||
all: true,
|
all: true,
|
||||||
participating: false,
|
participating: false,
|
||||||
@@ -24,10 +25,6 @@ export default class NewNotification {
|
|||||||
assignOwnerAndRepo(this, parameters?.repo as string);
|
assignOwnerAndRepo(this, parameters?.repo as string);
|
||||||
}
|
}
|
||||||
|
|
||||||
get hasRepo() {
|
|
||||||
return this.repoOwner && this.repo;
|
|
||||||
}
|
|
||||||
|
|
||||||
async listRepoNotifications(options = {}, paginate = false) {
|
async listRepoNotifications(options = {}, paginate = false) {
|
||||||
const listRepoNotifications = this.client.rest.activity.listRepoNotificationsForAuthenticatedUser;
|
const listRepoNotifications = this.client.rest.activity.listRepoNotificationsForAuthenticatedUser;
|
||||||
|
|
||||||
|
@@ -1,8 +1,9 @@
|
|||||||
export function assignOwnerAndRepo<T extends { repoOwner?: string; repo?: string; }>(object: T, repoFullName: string): T {
|
export function assignOwnerAndRepo<T extends { repoOwner?: string; repo?: string; hasRepo?: boolean; }>(object: T, repoFullName: string): T {
|
||||||
if (object && repoFullName) {
|
if (object && repoFullName) {
|
||||||
const [repoOwner, repo] = repoFullName.split('/');
|
const [repoOwner, repo] = repoFullName.split('/');
|
||||||
object.repoOwner = repoOwner;
|
object.repoOwner = repoOwner;
|
||||||
object.repo = repo;
|
object.repo = repo;
|
||||||
|
object.hasRepo = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return object;
|
return object;
|
||||||
|
Before Width: | Height: | Size: 345 B After Width: | Height: | Size: 345 B |
@@ -5,7 +5,7 @@ import {
|
|||||||
IJSONObject,
|
IJSONObject,
|
||||||
} from '@automatisch/types';
|
} from '@automatisch/types';
|
||||||
|
|
||||||
export default class Schedule implements IService {
|
export default class Scheduler implements IService {
|
||||||
triggers: Triggers;
|
triggers: Triggers;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
@@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"name": "Schedule",
|
"name": "Scheduler",
|
||||||
"key": "schedule",
|
"key": "scheduler",
|
||||||
"iconUrl": "{BASE_URL}/apps/schedule/assets/favicon.svg",
|
"iconUrl": "{BASE_URL}/apps/scheduler/assets/favicon.svg",
|
||||||
"docUrl": "https://automatisch.io/docs/schedule",
|
"docUrl": "https://automatisch.io/docs/scheduler",
|
||||||
"primaryColor": "0059F7",
|
"primaryColor": "0059F7",
|
||||||
"requiresAuthentication": false,
|
"requiresAuthentication": false,
|
||||||
"triggers": [
|
"triggers": [
|
@@ -5,11 +5,13 @@ import {
|
|||||||
IJSONObject,
|
IJSONObject,
|
||||||
} from '@automatisch/types';
|
} from '@automatisch/types';
|
||||||
import Authentication from './authentication';
|
import Authentication from './authentication';
|
||||||
|
import Triggers from './triggers';
|
||||||
import Actions from './actions';
|
import Actions from './actions';
|
||||||
import Data from './data';
|
import Data from './data';
|
||||||
|
|
||||||
export default class Slack implements IService {
|
export default class Slack implements IService {
|
||||||
authenticationClient: IAuthentication;
|
authenticationClient: IAuthentication;
|
||||||
|
triggers: Triggers;
|
||||||
actions: Actions;
|
actions: Actions;
|
||||||
data: Data;
|
data: Data;
|
||||||
|
|
||||||
@@ -20,6 +22,7 @@ export default class Slack implements IService {
|
|||||||
) {
|
) {
|
||||||
this.authenticationClient = new Authentication(appData, connectionData);
|
this.authenticationClient = new Authentication(appData, connectionData);
|
||||||
this.data = new Data(connectionData);
|
this.data = new Data(connectionData);
|
||||||
|
this.triggers = new Triggers(connectionData, parameters);
|
||||||
this.actions = new Actions(connectionData, parameters);
|
this.actions = new Actions(connectionData, parameters);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -97,6 +97,65 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"triggers": [
|
||||||
|
{
|
||||||
|
"name": "New message posted to a channel",
|
||||||
|
"key": "newMessageToChannel",
|
||||||
|
"description": "Triggers when a new message is posted to a channel",
|
||||||
|
"substeps": [
|
||||||
|
{
|
||||||
|
"key": "chooseAccount",
|
||||||
|
"name": "Choose account"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "chooseTrigger",
|
||||||
|
"name": "Set up a trigger",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"label": "Channel",
|
||||||
|
"key": "channel",
|
||||||
|
"type": "dropdown",
|
||||||
|
"required": true,
|
||||||
|
"variables": false,
|
||||||
|
"source": {
|
||||||
|
"type": "query",
|
||||||
|
"name": "getData",
|
||||||
|
"arguments": [
|
||||||
|
{
|
||||||
|
"name": "key",
|
||||||
|
"value": "listChannels"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Trigger for Bot Messages?",
|
||||||
|
"key": "triggerForBotMessages",
|
||||||
|
"type": "dropdown",
|
||||||
|
"description": "Should this flow trigger for bot messages?",
|
||||||
|
"required": true,
|
||||||
|
"value": true,
|
||||||
|
"variables": false,
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"label": "Yes",
|
||||||
|
"value": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "No",
|
||||||
|
"value": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "testStep",
|
||||||
|
"name": "Test trigger"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
"actions": [
|
"actions": [
|
||||||
{
|
{
|
||||||
"name": "Send a message to channel",
|
"name": "Send a message to channel",
|
||||||
|
13
packages/backend/src/apps/slack/triggers.ts
Normal file
13
packages/backend/src/apps/slack/triggers.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { IJSONObject } from '@automatisch/types';
|
||||||
|
import NewMessageToChannel from './triggers/new-message-to-channel';
|
||||||
|
|
||||||
|
export default class Triggers {
|
||||||
|
newMessageToChannel: NewMessageToChannel;
|
||||||
|
|
||||||
|
constructor(connectionData: IJSONObject, parameters: IJSONObject) {
|
||||||
|
this.newMessageToChannel = new NewMessageToChannel(
|
||||||
|
connectionData,
|
||||||
|
parameters
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,47 @@
|
|||||||
|
import { IJSONObject } from '@automatisch/types';
|
||||||
|
import axios, { AxiosInstance } from 'axios';
|
||||||
|
|
||||||
|
export default class NewMessageToChannel {
|
||||||
|
httpClient: AxiosInstance;
|
||||||
|
parameters: IJSONObject;
|
||||||
|
connectionData: IJSONObject;
|
||||||
|
BASE_URL = 'https://slack.com/api';
|
||||||
|
|
||||||
|
constructor(connectionData: IJSONObject, parameters: IJSONObject) {
|
||||||
|
this.httpClient = axios.create({ baseURL: this.BASE_URL });
|
||||||
|
this.connectionData = connectionData;
|
||||||
|
this.parameters = parameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
async run() {
|
||||||
|
// TODO: Fix after webhook implementation.
|
||||||
|
}
|
||||||
|
|
||||||
|
async testRun() {
|
||||||
|
const headers = {
|
||||||
|
Authorization: `Bearer ${this.connectionData.accessToken}`,
|
||||||
|
};
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
channel: this.parameters.channel,
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await this.httpClient.get('/conversations.history', {
|
||||||
|
headers,
|
||||||
|
params,
|
||||||
|
});
|
||||||
|
|
||||||
|
let lastMessage;
|
||||||
|
|
||||||
|
if (this.parameters.triggerForBotMessages) {
|
||||||
|
lastMessage = response.data.messages[0];
|
||||||
|
} else {
|
||||||
|
lastMessage = response.data.messages.find(
|
||||||
|
(message: IJSONObject) =>
|
||||||
|
!Object.prototype.hasOwnProperty.call(message, 'bot_id')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [lastMessage];
|
||||||
|
}
|
||||||
|
}
|
@@ -1,15 +1,21 @@
|
|||||||
import Context from '../../types/express/context';
|
import Context from '../../types/express/context';
|
||||||
|
|
||||||
const getFlows = async (
|
type Params = {
|
||||||
_parent: unknown,
|
appKey?: string;
|
||||||
_params: unknown,
|
};
|
||||||
context: Context
|
|
||||||
) => {
|
const getFlows = async (_parent: unknown, params: Params, context: Context) => {
|
||||||
const flows = await context.currentUser
|
const flowsQuery = context.currentUser
|
||||||
.$relatedQuery('flows')
|
.$relatedQuery('flows')
|
||||||
.withGraphJoined('[steps.[connection]]')
|
.withGraphJoined('[steps.[connection]]')
|
||||||
.orderBy('created_at', 'desc');
|
.orderBy('created_at', 'desc');
|
||||||
|
|
||||||
|
if (params.appKey) {
|
||||||
|
flowsQuery.where('steps.app_key', params.appKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
const flows = await flowsQuery;
|
||||||
|
|
||||||
return flows;
|
return flows;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -5,7 +5,7 @@ type Query {
|
|||||||
getAppConnections(key: AvailableAppsEnumType!): [Connection]
|
getAppConnections(key: AvailableAppsEnumType!): [Connection]
|
||||||
testConnection(id: String!): Connection
|
testConnection(id: String!): Connection
|
||||||
getFlow(id: String!): Flow
|
getFlow(id: String!): Flow
|
||||||
getFlows: [Flow]
|
getFlows(appKey: String): [Flow]
|
||||||
getStepWithTestExecutions(stepId: String!): [Step]
|
getStepWithTestExecutions(stepId: String!): [Step]
|
||||||
getExecutions(limit: Int!, offset: Int!): ExecutionConnection
|
getExecutions(limit: Int!, offset: Int!): ExecutionConnection
|
||||||
getExecutionSteps(
|
getExecutionSteps(
|
||||||
|
@@ -1,2 +1,11 @@
|
|||||||
import './config/orm';
|
import './config/orm';
|
||||||
import './workers/processor';
|
import './workers/processor';
|
||||||
|
|
||||||
|
process
|
||||||
|
.on('unhandledRejection', (reason, p) => {
|
||||||
|
console.error(reason, 'Unhandled Rejection at Promise', p);
|
||||||
|
})
|
||||||
|
.on('uncaughtException', (err) => {
|
||||||
|
console.error(err, 'Uncaught Exception thrown');
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
|
@@ -4,8 +4,13 @@ import { GET_FLOWS } from 'graphql/queries/get-flows';
|
|||||||
import AppFlowRow from 'components/AppFlowRow';
|
import AppFlowRow from 'components/AppFlowRow';
|
||||||
import type { IFlow } from '@automatisch/types';
|
import type { IFlow } from '@automatisch/types';
|
||||||
|
|
||||||
export default function AppFlows(): React.ReactElement {
|
type AppFlowsProps = {
|
||||||
const { data } = useQuery(GET_FLOWS);
|
appKey: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function AppFlows(props: AppFlowsProps): React.ReactElement {
|
||||||
|
const { appKey } = props;
|
||||||
|
const { data } = useQuery(GET_FLOWS, { variables: { appKey }});
|
||||||
const appFlows: IFlow[] = data?.getFlows || [];
|
const appFlows: IFlow[] = data?.getFlows || [];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@@ -11,7 +11,7 @@ interface ControlledAutocompleteProps extends AutocompleteProps<IFieldDropdownOp
|
|||||||
description?: string;
|
description?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const getOption = (options: readonly IFieldDropdownOption[], value: string) => options.find(option => option.value === value);
|
const getOption = (options: readonly IFieldDropdownOption[], value: string) => options.find(option => option.value === value) || null;
|
||||||
|
|
||||||
function ControlledAutocomplete(props: ControlledAutocompleteProps): React.ReactElement {
|
function ControlledAutocomplete(props: ControlledAutocompleteProps): React.ReactElement {
|
||||||
const { control } = useFormContext();
|
const { control } = useFormContext();
|
||||||
@@ -45,7 +45,7 @@ function ControlledAutocomplete(props: ControlledAutocompleteProps): React.React
|
|||||||
value={getOption(options, field.value)}
|
value={getOption(options, field.value)}
|
||||||
onChange={(event, selectedOption, reason, details) => {
|
onChange={(event, selectedOption, reason, details) => {
|
||||||
const typedSelectedOption = selectedOption as IFieldDropdownOption;
|
const typedSelectedOption = selectedOption as IFieldDropdownOption;
|
||||||
if (Object.prototype.hasOwnProperty.call(typedSelectedOption, 'value')) {
|
if (typedSelectedOption !== null && Object.prototype.hasOwnProperty.call(typedSelectedOption, 'value')) {
|
||||||
controllerOnChange(typedSelectedOption.value);
|
controllerOnChange(typedSelectedOption.value);
|
||||||
} else {
|
} else {
|
||||||
controllerOnChange(typedSelectedOption);
|
controllerOnChange(typedSelectedOption);
|
||||||
|
@@ -69,11 +69,13 @@ function generateValidationSchema(substeps: ISubstep[]) {
|
|||||||
// if the field depends on another field, add the dependsOn required validation
|
// if the field depends on another field, add the dependsOn required validation
|
||||||
if (dependsOn?.length > 0) {
|
if (dependsOn?.length > 0) {
|
||||||
for (const dependsOnKey of dependsOn) {
|
for (const dependsOnKey of dependsOn) {
|
||||||
|
const missingDependencyValueMessage = `We're having trouble loading '${key}' data as required field '${dependsOnKey}' is missing.`;
|
||||||
|
|
||||||
// TODO: make `dependsOnKey` agnostic to the field. However, nested validation schema is not supported.
|
// TODO: make `dependsOnKey` agnostic to the field. However, nested validation schema is not supported.
|
||||||
// So the fields under the `parameters` key are subject to their siblings only and thus, `parameters.` is removed.
|
// So the fields under the `parameters` key are subject to their siblings only and thus, `parameters.` is removed.
|
||||||
substepArgumentValidations[key] = substepArgumentValidations[key].when(`${dependsOnKey.replace('parameters.', '')}`, {
|
substepArgumentValidations[key] = substepArgumentValidations[key].when(`${dependsOnKey.replace('parameters.', '')}`, {
|
||||||
is: (value: string) => Boolean(value) === false,
|
is: (value: string) => Boolean(value) === false,
|
||||||
then: (schema) => schema.required(`We're having trouble loading '${key}' data as required field '${dependsOnKey}' is missing.`),
|
then: (schema) => schema.notOneOf([''], missingDependencyValueMessage).required(missingDependencyValueMessage),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
import { gql } from '@apollo/client';
|
import { gql } from '@apollo/client';
|
||||||
|
|
||||||
export const GET_FLOWS = gql`
|
export const GET_FLOWS = gql`
|
||||||
query GetFlows {
|
query GetFlows($appKey: String) {
|
||||||
getFlows {
|
getFlows(appKey: $appKey) {
|
||||||
id
|
id
|
||||||
name
|
name
|
||||||
}
|
}
|
||||||
|
@@ -155,7 +155,7 @@ export default function Application(): React.ReactElement {
|
|||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path={`${URLS.FLOWS}/*`} element={<AppFlows />} />
|
<Route path={`${URLS.FLOWS}/*`} element={<AppFlows appKey={appKey} />} />
|
||||||
|
|
||||||
<Route path={`${URLS.CONNECTIONS}/*`} element={<AppConnections appKey={appKey} />} />
|
<Route path={`${URLS.CONNECTIONS}/*`} element={<AppConnections appKey={appKey} />} />
|
||||||
|
|
||||||
|
42
yarn.lock
42
yarn.lock
@@ -6490,15 +6490,15 @@ bull@^4.7.0:
|
|||||||
semver "^7.3.2"
|
semver "^7.3.2"
|
||||||
uuid "^8.3.0"
|
uuid "^8.3.0"
|
||||||
|
|
||||||
bullmq@^1.76.1:
|
bullmq@^1.82.0:
|
||||||
version "1.76.1"
|
version "1.82.0"
|
||||||
resolved "https://registry.yarnpkg.com/bullmq/-/bullmq-1.76.1.tgz#202cfe2a9eee0cd00f3f4995c04b6fe5f1bc1995"
|
resolved "https://registry.yarnpkg.com/bullmq/-/bullmq-1.82.0.tgz#3dbc4affbf0cb9ab88af750ad5d7ca10df5c8677"
|
||||||
integrity sha512-K+TAT2mbuDEz99IuC/rY+vwmA6jpUH0w5y61pdNx0xVdHdAWn6dd9Vs0MnCObZDHCdbNgGvFumfOjCzHjD4VVg==
|
integrity sha512-jKbmYS9+IQSubCk1FRYZRrhJxbzC5zWQjmHMBzF642YpyV360A0Lm2+oblfRX2D6cN0aDmjS8yfFcPl96b3Zcw==
|
||||||
dependencies:
|
dependencies:
|
||||||
cron-parser "^2.18.0"
|
cron-parser "^4.2.1"
|
||||||
get-port "^5.1.1"
|
get-port "^5.1.1"
|
||||||
glob "^7.2.0"
|
glob "^7.2.0"
|
||||||
ioredis "^4.28.2"
|
ioredis "^4.28.5"
|
||||||
lodash "^4.17.21"
|
lodash "^4.17.21"
|
||||||
msgpackr "^1.4.6"
|
msgpackr "^1.4.6"
|
||||||
semver "^6.3.0"
|
semver "^6.3.0"
|
||||||
@@ -7534,14 +7534,6 @@ create-require@^1.1.0:
|
|||||||
resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333"
|
resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333"
|
||||||
integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==
|
integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==
|
||||||
|
|
||||||
cron-parser@^2.18.0:
|
|
||||||
version "2.18.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/cron-parser/-/cron-parser-2.18.0.tgz#de1bb0ad528c815548371993f81a54e5a089edcf"
|
|
||||||
integrity sha512-s4odpheTyydAbTBQepsqd2rNWGa2iV3cyo8g7zbI2QQYGLVsfbhmwukayS1XHppe02Oy1fg7mg6xoaraVJeEcg==
|
|
||||||
dependencies:
|
|
||||||
is-nan "^1.3.0"
|
|
||||||
moment-timezone "^0.5.31"
|
|
||||||
|
|
||||||
cron-parser@^4.2.1:
|
cron-parser@^4.2.1:
|
||||||
version "4.3.0"
|
version "4.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/cron-parser/-/cron-parser-4.3.0.tgz#16c3932fa62d0c30708d4200f510d6ca26bf35a2"
|
resolved "https://registry.yarnpkg.com/cron-parser/-/cron-parser-4.3.0.tgz#16c3932fa62d0c30708d4200f510d6ca26bf35a2"
|
||||||
@@ -11062,7 +11054,7 @@ invariant@^2.2.4:
|
|||||||
dependencies:
|
dependencies:
|
||||||
loose-envify "^1.0.0"
|
loose-envify "^1.0.0"
|
||||||
|
|
||||||
ioredis@^4.28.2, ioredis@^4.28.5:
|
ioredis@^4.28.5:
|
||||||
version "4.28.5"
|
version "4.28.5"
|
||||||
resolved "https://registry.yarnpkg.com/ioredis/-/ioredis-4.28.5.tgz#5c149e6a8d76a7f8fa8a504ffc85b7d5b6797f9f"
|
resolved "https://registry.yarnpkg.com/ioredis/-/ioredis-4.28.5.tgz#5c149e6a8d76a7f8fa8a504ffc85b7d5b6797f9f"
|
||||||
integrity sha512-3GYo0GJtLqgNXj4YhrisLaNNvWSNwSS2wS4OELGfGxH8I69+XfNdnmV1AyN+ZqMh0i7eX+SWjrwFKDBDgfBC1A==
|
integrity sha512-3GYo0GJtLqgNXj4YhrisLaNNvWSNwSS2wS4OELGfGxH8I69+XfNdnmV1AyN+ZqMh0i7eX+SWjrwFKDBDgfBC1A==
|
||||||
@@ -11292,14 +11284,6 @@ is-module@^1.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591"
|
resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591"
|
||||||
integrity sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=
|
integrity sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=
|
||||||
|
|
||||||
is-nan@^1.3.0:
|
|
||||||
version "1.3.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/is-nan/-/is-nan-1.3.2.tgz#043a54adea31748b55b6cd4e09aadafa69bd9e1d"
|
|
||||||
integrity sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==
|
|
||||||
dependencies:
|
|
||||||
call-bind "^1.0.0"
|
|
||||||
define-properties "^1.1.3"
|
|
||||||
|
|
||||||
is-negative-zero@^2.0.1:
|
is-negative-zero@^2.0.1:
|
||||||
version "2.0.2"
|
version "2.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150"
|
resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150"
|
||||||
@@ -13391,18 +13375,6 @@ modify-values@^1.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022"
|
resolved "https://registry.yarnpkg.com/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022"
|
||||||
integrity sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==
|
integrity sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==
|
||||||
|
|
||||||
moment-timezone@^0.5.31:
|
|
||||||
version "0.5.34"
|
|
||||||
resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.34.tgz#a75938f7476b88f155d3504a9343f7519d9a405c"
|
|
||||||
integrity sha512-3zAEHh2hKUs3EXLESx/wsgw6IQdusOT8Bxm3D9UrHPQR7zlMmzwybC8zHEM1tQ4LJwP7fcxrWr8tuBg05fFCbg==
|
|
||||||
dependencies:
|
|
||||||
moment ">= 2.9.0"
|
|
||||||
|
|
||||||
"moment@>= 2.9.0":
|
|
||||||
version "2.29.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3"
|
|
||||||
integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==
|
|
||||||
|
|
||||||
morgan@^1.10.0:
|
morgan@^1.10.0:
|
||||||
version "1.10.0"
|
version "1.10.0"
|
||||||
resolved "https://registry.yarnpkg.com/morgan/-/morgan-1.10.0.tgz#091778abc1fc47cd3509824653dae1faab6b17d7"
|
resolved "https://registry.yarnpkg.com/morgan/-/morgan-1.10.0.tgz#091778abc1fc47cd3509824653dae1faab6b17d7"
|
||||||
|
Reference in New Issue
Block a user