Merge pull request #545 from automatisch/errors
Store and expose integration errors
This commit is contained in:
@@ -19,7 +19,13 @@ export default class SearchTweets {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let response;
|
let response;
|
||||||
const tweets: IJSONObject[] = [];
|
const tweets: {
|
||||||
|
data: IJSONObject[];
|
||||||
|
error: IJSONObject | null;
|
||||||
|
} = {
|
||||||
|
data: [],
|
||||||
|
error: null,
|
||||||
|
};
|
||||||
|
|
||||||
do {
|
do {
|
||||||
const params: IJSONObject = {
|
const params: IJSONObject = {
|
||||||
@@ -47,10 +53,15 @@ export default class SearchTweets {
|
|||||||
headers: { ...authHeader },
|
headers: { ...authHeader },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (response.integrationError) {
|
||||||
|
tweets.error = response.integrationError;
|
||||||
|
return tweets;
|
||||||
|
}
|
||||||
|
|
||||||
if (response.data.meta.result_count > 0) {
|
if (response.data.meta.result_count > 0) {
|
||||||
response.data.data.forEach((tweet: IJSONObject) => {
|
response.data.data.forEach((tweet: IJSONObject) => {
|
||||||
if (!lastInternalId || Number(tweet.id) > Number(lastInternalId)) {
|
if (!lastInternalId || Number(tweet.id) > Number(lastInternalId)) {
|
||||||
tweets.push(tweet);
|
tweets.data.push(tweet);
|
||||||
} else {
|
} else {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -58,16 +69,6 @@ export default class SearchTweets {
|
|||||||
}
|
}
|
||||||
} while (response.data.meta.next_token && lastInternalId);
|
} while (response.data.meta.next_token && lastInternalId);
|
||||||
|
|
||||||
if (response.data?.errors) {
|
|
||||||
const errorMessages = response.data.errors
|
|
||||||
.map((error: IJSONObject) => error.detail)
|
|
||||||
.join(' ');
|
|
||||||
|
|
||||||
throw new Error(
|
|
||||||
`Error occured while fetching user data: ${errorMessages}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return tweets;
|
return tweets;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -188,6 +188,7 @@ type ExecutionStep {
|
|||||||
status: String
|
status: String
|
||||||
dataIn: JSONObject
|
dataIn: JSONObject
|
||||||
dataOut: JSONObject
|
dataOut: JSONObject
|
||||||
|
errorDetails: JSONObject
|
||||||
createdAt: String
|
createdAt: String
|
||||||
updatedAt: String
|
updatedAt: String
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,8 @@
|
|||||||
import axios, { AxiosInstance } from 'axios';
|
import axios, { AxiosInstance, AxiosResponse } from 'axios';
|
||||||
import { IJSONObject, IHttpClientParams } from '@automatisch/types';
|
import { IJSONObject, IHttpClientParams } from '@automatisch/types';
|
||||||
|
|
||||||
|
type ExtendedAxiosResponse = AxiosResponse & { integrationError: IJSONObject };
|
||||||
|
|
||||||
export default class HttpClient {
|
export default class HttpClient {
|
||||||
instance: AxiosInstance;
|
instance: AxiosInstance;
|
||||||
|
|
||||||
@@ -8,13 +10,25 @@ export default class HttpClient {
|
|||||||
this.instance = axios.create({
|
this.instance = axios.create({
|
||||||
baseURL: params.baseURL,
|
baseURL: params.baseURL,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.instance.interceptors.response.use(
|
||||||
|
(response) => response,
|
||||||
|
(error) => {
|
||||||
|
error.response.integrationError = error.response.data;
|
||||||
|
return error.response;
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async get(path: string, options?: IJSONObject) {
|
async get(path: string, options?: IJSONObject) {
|
||||||
return await this.instance.get(path, options);
|
return (await this.instance.get(path, options)) as ExtendedAxiosResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
async post(path: string, body: IJSONObject | string, options?: IJSONObject) {
|
async post(path: string, body: IJSONObject | string, options?: IJSONObject) {
|
||||||
return await this.instance.post(path, body, options);
|
return (await this.instance.post(
|
||||||
|
path,
|
||||||
|
body,
|
||||||
|
options
|
||||||
|
)) as ExtendedAxiosResponse;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -33,9 +33,9 @@ class Processor {
|
|||||||
|
|
||||||
const triggerStep = steps.find((step) => step.type === 'trigger');
|
const triggerStep = steps.find((step) => step.type === 'trigger');
|
||||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||||
let initialTriggerData = await this.getInitialTriggerData(triggerStep!);
|
const initialTriggerData = await this.getInitialTriggerData(triggerStep!);
|
||||||
|
|
||||||
if (initialTriggerData.length === 0) {
|
if (!initialTriggerData.error && initialTriggerData.data.length === 0) {
|
||||||
const lastInternalId = await this.flow.lastInternalId();
|
const lastInternalId = await this.flow.lastInternalId();
|
||||||
|
|
||||||
const executionData: Partial<Execution> = {
|
const executionData: Partial<Execution> = {
|
||||||
@@ -52,12 +52,12 @@ class Processor {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.testRun) {
|
if (this.testRun && initialTriggerData.data.length > 0) {
|
||||||
initialTriggerData = [initialTriggerData[0]];
|
initialTriggerData.data = [initialTriggerData.data[0]];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (initialTriggerData.length > 1) {
|
if (initialTriggerData.data.length > 1) {
|
||||||
initialTriggerData = initialTriggerData.sort(
|
initialTriggerData.data = initialTriggerData.data.sort(
|
||||||
(item: IJSONObject, nextItem: IJSONObject) => {
|
(item: IJSONObject, nextItem: IJSONObject) => {
|
||||||
return (item.id as number) - (nextItem.id as number);
|
return (item.id as number) - (nextItem.id as number);
|
||||||
}
|
}
|
||||||
@@ -66,7 +66,7 @@ class Processor {
|
|||||||
|
|
||||||
const executions: Execution[] = [];
|
const executions: Execution[] = [];
|
||||||
|
|
||||||
for await (const data of initialTriggerData) {
|
for await (const data of initialTriggerData.data) {
|
||||||
const execution = await Execution.query().insert({
|
const execution = await Execution.query().insert({
|
||||||
flowId: this.flow.id,
|
flowId: this.flow.id,
|
||||||
testRun: this.testRun,
|
testRun: this.testRun,
|
||||||
@@ -118,6 +118,22 @@ class Processor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (initialTriggerData.error) {
|
||||||
|
const executionWithError = await Execution.query().insert({
|
||||||
|
flowId: this.flow.id,
|
||||||
|
testRun: this.testRun,
|
||||||
|
});
|
||||||
|
|
||||||
|
executions.push(executionWithError);
|
||||||
|
|
||||||
|
await executionWithError.$relatedQuery('executionSteps').insertAndFetch({
|
||||||
|
stepId: triggerStep.id,
|
||||||
|
status: 'failure',
|
||||||
|
dataIn: triggerStep.parameters,
|
||||||
|
errorDetails: initialTriggerData.error,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (!this.testRun) return;
|
if (!this.testRun) return;
|
||||||
|
|
||||||
const lastExecutionStepFromFirstExecution = await executions[0]
|
const lastExecutionStepFromFirstExecution = await executions[0]
|
||||||
@@ -125,7 +141,11 @@ class Processor {
|
|||||||
.orderBy('created_at', 'desc')
|
.orderBy('created_at', 'desc')
|
||||||
.first();
|
.first();
|
||||||
|
|
||||||
return lastExecutionStepFromFirstExecution?.dataOut;
|
if (lastExecutionStepFromFirstExecution.errorDetails) {
|
||||||
|
return lastExecutionStepFromFirstExecution.errorDetails;
|
||||||
|
} else {
|
||||||
|
return lastExecutionStepFromFirstExecution?.dataOut;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async getInitialTriggerData(step: Step) {
|
async getInitialTriggerData(step: Step) {
|
||||||
|
1
packages/types/index.d.ts
vendored
1
packages/types/index.d.ts
vendored
@@ -26,6 +26,7 @@ export interface IExecutionStep {
|
|||||||
step: IStep;
|
step: IStep;
|
||||||
dataIn: IJSONObject;
|
dataIn: IJSONObject;
|
||||||
dataOut: IJSONObject;
|
dataOut: IJSONObject;
|
||||||
|
errorDetails: IJSONObject;
|
||||||
status: string;
|
status: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
|
@@ -73,6 +73,7 @@ export default function ExecutionStep(props: ExecutionStepProps): React.ReactEle
|
|||||||
<Tabs value={activeTabIndex} onChange={(event, tabIndex) => setActiveTabIndex(tabIndex)}>
|
<Tabs value={activeTabIndex} onChange={(event, tabIndex) => setActiveTabIndex(tabIndex)}>
|
||||||
<Tab label="Data in" />
|
<Tab label="Data in" />
|
||||||
<Tab label="Data out" />
|
<Tab label="Data out" />
|
||||||
|
<Tab label="Error" />
|
||||||
<Tab label="Execution step" />
|
<Tab label="Execution step" />
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</Box>
|
</Box>
|
||||||
@@ -86,6 +87,10 @@ export default function ExecutionStep(props: ExecutionStepProps): React.ReactEle
|
|||||||
</TabPanel>
|
</TabPanel>
|
||||||
|
|
||||||
<TabPanel value={activeTabIndex} index={2}>
|
<TabPanel value={activeTabIndex} index={2}>
|
||||||
|
<JSONViewer data={executionStep.errorDetails} />
|
||||||
|
</TabPanel>
|
||||||
|
|
||||||
|
<TabPanel value={activeTabIndex} index={3}>
|
||||||
<JSONViewer data={(executionStep as unknown) as IJSONObject} />
|
<JSONViewer data={(executionStep as unknown) as IJSONObject} />
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
</Content>
|
</Content>
|
||||||
|
@@ -14,6 +14,7 @@ export const GET_EXECUTION_STEPS = gql`
|
|||||||
status
|
status
|
||||||
dataIn
|
dataIn
|
||||||
dataOut
|
dataOut
|
||||||
|
errorDetails
|
||||||
createdAt
|
createdAt
|
||||||
updatedAt
|
updatedAt
|
||||||
step {
|
step {
|
||||||
|
Reference in New Issue
Block a user