Merge pull request #1714 from automatisch/AUT-693
refactor: rewrite get executions using useExecutions with RQ
This commit is contained in:
@@ -1,70 +0,0 @@
|
|||||||
import { raw } from 'objection';
|
|
||||||
import { DateTime } from 'luxon';
|
|
||||||
import Execution from '../../models/execution.js';
|
|
||||||
import paginate from '../../helpers/pagination.js';
|
|
||||||
|
|
||||||
const getExecutions = async (_parent, params, context) => {
|
|
||||||
const conditions = context.currentUser.can('read', 'Execution');
|
|
||||||
|
|
||||||
const filters = params.filters;
|
|
||||||
|
|
||||||
const userExecutions = context.currentUser.$relatedQuery('executions');
|
|
||||||
const allExecutions = Execution.query();
|
|
||||||
const executionBaseQuery = conditions.isCreator
|
|
||||||
? userExecutions
|
|
||||||
: allExecutions;
|
|
||||||
|
|
||||||
const selectStatusStatement = `
|
|
||||||
case
|
|
||||||
when count(*) filter (where execution_steps.status = 'failure') > 0
|
|
||||||
then 'failure'
|
|
||||||
else 'success'
|
|
||||||
end
|
|
||||||
as status
|
|
||||||
`;
|
|
||||||
|
|
||||||
const executions = executionBaseQuery
|
|
||||||
.clone()
|
|
||||||
.joinRelated('executionSteps as execution_steps')
|
|
||||||
.select('executions.*', raw(selectStatusStatement))
|
|
||||||
.groupBy('executions.id')
|
|
||||||
.orderBy('created_at', 'desc');
|
|
||||||
|
|
||||||
const computedExecutions = Execution.query()
|
|
||||||
.with('executions', executions)
|
|
||||||
.withSoftDeleted()
|
|
||||||
.withGraphFetched({
|
|
||||||
flow: {
|
|
||||||
steps: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (filters?.flowId) {
|
|
||||||
computedExecutions.where('executions.flow_id', filters.flowId);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (filters?.status) {
|
|
||||||
computedExecutions.where('executions.status', filters.status);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (filters?.createdAt) {
|
|
||||||
const createdAtFilter = filters.createdAt;
|
|
||||||
if (createdAtFilter.from) {
|
|
||||||
const isoFromDateTime = DateTime.fromMillis(
|
|
||||||
parseInt(createdAtFilter.from, 10)
|
|
||||||
).toISO();
|
|
||||||
computedExecutions.where('executions.created_at', '>=', isoFromDateTime);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (createdAtFilter.to) {
|
|
||||||
const isoToDateTime = DateTime.fromMillis(
|
|
||||||
parseInt(createdAtFilter.to, 10)
|
|
||||||
).toISO();
|
|
||||||
computedExecutions.where('executions.created_at', '<=', isoToDateTime);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return paginate(computedExecutions, params.limit, params.offset);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default getExecutions;
|
|
@@ -1,472 +0,0 @@
|
|||||||
import { describe, it, expect, beforeEach } from 'vitest';
|
|
||||||
import request from 'supertest';
|
|
||||||
import app from '../../app';
|
|
||||||
import appConfig from '../../config/app';
|
|
||||||
import createAuthTokenByUserId from '../../helpers/create-auth-token-by-user-id';
|
|
||||||
import { createRole } from '../../../test/factories/role';
|
|
||||||
import { createPermission } from '../../../test/factories/permission';
|
|
||||||
import { createUser } from '../../../test/factories/user';
|
|
||||||
import { createFlow } from '../../../test/factories/flow';
|
|
||||||
import { createStep } from '../../../test/factories/step';
|
|
||||||
import { createExecution } from '../../../test/factories/execution';
|
|
||||||
import { createExecutionStep } from '../../../test/factories/execution-step';
|
|
||||||
|
|
||||||
describe('graphQL getExecutions query', () => {
|
|
||||||
const query = `
|
|
||||||
query {
|
|
||||||
getExecutions(limit: 10, offset: 0) {
|
|
||||||
pageInfo {
|
|
||||||
currentPage
|
|
||||||
totalPages
|
|
||||||
}
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
id
|
|
||||||
testRun
|
|
||||||
createdAt
|
|
||||||
updatedAt
|
|
||||||
status
|
|
||||||
flow {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
active
|
|
||||||
steps {
|
|
||||||
iconUrl
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
describe('and without correct permissions', () => {
|
|
||||||
it('should throw not authorized error', async () => {
|
|
||||||
const userWithoutPermissions = await createUser();
|
|
||||||
const token = createAuthTokenByUserId(userWithoutPermissions.id);
|
|
||||||
|
|
||||||
const response = await request(app)
|
|
||||||
.post('/graphql')
|
|
||||||
.set('Authorization', token)
|
|
||||||
.send({ query })
|
|
||||||
.expect(200);
|
|
||||||
|
|
||||||
expect(response.body.errors).toBeDefined();
|
|
||||||
expect(response.body.errors[0].message).toEqual('Not authorized!');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('and with correct permission', () => {
|
|
||||||
let role,
|
|
||||||
currentUser,
|
|
||||||
anotherUser,
|
|
||||||
token,
|
|
||||||
flowOne,
|
|
||||||
stepOneForFlowOne,
|
|
||||||
stepTwoForFlowOne,
|
|
||||||
executionOne,
|
|
||||||
flowTwo,
|
|
||||||
stepOneForFlowTwo,
|
|
||||||
stepTwoForFlowTwo,
|
|
||||||
executionTwo,
|
|
||||||
flowThree,
|
|
||||||
stepOneForFlowThree,
|
|
||||||
stepTwoForFlowThree,
|
|
||||||
executionThree,
|
|
||||||
expectedResponseForExecutionOne,
|
|
||||||
expectedResponseForExecutionTwo,
|
|
||||||
expectedResponseForExecutionThree;
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
role = await createRole({
|
|
||||||
key: 'sample',
|
|
||||||
name: 'sample',
|
|
||||||
});
|
|
||||||
|
|
||||||
currentUser = await createUser({
|
|
||||||
roleId: role.id,
|
|
||||||
fullName: 'Current User',
|
|
||||||
});
|
|
||||||
|
|
||||||
anotherUser = await createUser();
|
|
||||||
|
|
||||||
token = createAuthTokenByUserId(currentUser.id);
|
|
||||||
|
|
||||||
flowOne = await createFlow({
|
|
||||||
userId: currentUser.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
stepOneForFlowOne = await createStep({
|
|
||||||
flowId: flowOne.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
stepTwoForFlowOne = await createStep({
|
|
||||||
flowId: flowOne.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
executionOne = await createExecution({
|
|
||||||
flowId: flowOne.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
await createExecutionStep({
|
|
||||||
executionId: executionOne.id,
|
|
||||||
stepId: stepOneForFlowOne.id,
|
|
||||||
status: 'success',
|
|
||||||
});
|
|
||||||
|
|
||||||
await createExecutionStep({
|
|
||||||
executionId: executionOne.id,
|
|
||||||
stepId: stepTwoForFlowOne.id,
|
|
||||||
status: 'success',
|
|
||||||
});
|
|
||||||
|
|
||||||
flowTwo = await createFlow({
|
|
||||||
userId: currentUser.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
stepOneForFlowTwo = await createStep({
|
|
||||||
flowId: flowTwo.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
stepTwoForFlowTwo = await createStep({
|
|
||||||
flowId: flowTwo.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
executionTwo = await createExecution({
|
|
||||||
flowId: flowTwo.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
await createExecutionStep({
|
|
||||||
executionId: executionTwo.id,
|
|
||||||
stepId: stepOneForFlowTwo.id,
|
|
||||||
status: 'success',
|
|
||||||
});
|
|
||||||
|
|
||||||
await createExecutionStep({
|
|
||||||
executionId: executionTwo.id,
|
|
||||||
stepId: stepTwoForFlowTwo.id,
|
|
||||||
status: 'failure',
|
|
||||||
});
|
|
||||||
|
|
||||||
flowThree = await createFlow({
|
|
||||||
userId: anotherUser.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
stepOneForFlowThree = await createStep({
|
|
||||||
flowId: flowThree.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
stepTwoForFlowThree = await createStep({
|
|
||||||
flowId: flowThree.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
executionThree = await createExecution({
|
|
||||||
flowId: flowThree.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
await createExecutionStep({
|
|
||||||
executionId: executionThree.id,
|
|
||||||
stepId: stepOneForFlowThree.id,
|
|
||||||
status: 'success',
|
|
||||||
});
|
|
||||||
|
|
||||||
await createExecutionStep({
|
|
||||||
executionId: executionThree.id,
|
|
||||||
stepId: stepTwoForFlowThree.id,
|
|
||||||
status: 'failure',
|
|
||||||
});
|
|
||||||
|
|
||||||
expectedResponseForExecutionOne = {
|
|
||||||
node: {
|
|
||||||
createdAt: executionOne.createdAt.getTime().toString(),
|
|
||||||
flow: {
|
|
||||||
active: flowOne.active,
|
|
||||||
id: flowOne.id,
|
|
||||||
name: flowOne.name,
|
|
||||||
steps: [
|
|
||||||
{
|
|
||||||
iconUrl: `${appConfig.baseUrl}/apps/${stepOneForFlowOne.appKey}/assets/favicon.svg`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
iconUrl: `${appConfig.baseUrl}/apps/${stepTwoForFlowOne.appKey}/assets/favicon.svg`,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
id: executionOne.id,
|
|
||||||
status: 'success',
|
|
||||||
testRun: executionOne.testRun,
|
|
||||||
updatedAt: executionOne.updatedAt.getTime().toString(),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
expectedResponseForExecutionTwo = {
|
|
||||||
node: {
|
|
||||||
createdAt: executionTwo.createdAt.getTime().toString(),
|
|
||||||
flow: {
|
|
||||||
active: flowTwo.active,
|
|
||||||
id: flowTwo.id,
|
|
||||||
name: flowTwo.name,
|
|
||||||
steps: [
|
|
||||||
{
|
|
||||||
iconUrl: `${appConfig.baseUrl}/apps/${stepTwoForFlowTwo.appKey}/assets/favicon.svg`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
iconUrl: `${appConfig.baseUrl}/apps/${stepTwoForFlowTwo.appKey}/assets/favicon.svg`,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
id: executionTwo.id,
|
|
||||||
status: 'failure',
|
|
||||||
testRun: executionTwo.testRun,
|
|
||||||
updatedAt: executionTwo.updatedAt.getTime().toString(),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
expectedResponseForExecutionThree = {
|
|
||||||
node: {
|
|
||||||
createdAt: executionThree.createdAt.getTime().toString(),
|
|
||||||
flow: {
|
|
||||||
active: flowThree.active,
|
|
||||||
id: flowThree.id,
|
|
||||||
name: flowThree.name,
|
|
||||||
steps: [
|
|
||||||
{
|
|
||||||
iconUrl: `${appConfig.baseUrl}/apps/${stepOneForFlowThree.appKey}/assets/favicon.svg`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
iconUrl: `${appConfig.baseUrl}/apps/${stepTwoForFlowThree.appKey}/assets/favicon.svg`,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
id: executionThree.id,
|
|
||||||
status: 'failure',
|
|
||||||
testRun: executionThree.testRun,
|
|
||||||
updatedAt: executionThree.updatedAt.getTime().toString(),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('and with isCreator condition', () => {
|
|
||||||
beforeEach(async () => {
|
|
||||||
await createPermission({
|
|
||||||
action: 'read',
|
|
||||||
subject: 'Execution',
|
|
||||||
roleId: role.id,
|
|
||||||
conditions: ['isCreator'],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return executions data of the current user', async () => {
|
|
||||||
const response = await request(app)
|
|
||||||
.post('/graphql')
|
|
||||||
.set('Authorization', token)
|
|
||||||
.send({ query })
|
|
||||||
.expect(200);
|
|
||||||
|
|
||||||
const expectedResponsePayload = {
|
|
||||||
data: {
|
|
||||||
getExecutions: {
|
|
||||||
edges: [
|
|
||||||
expectedResponseForExecutionTwo,
|
|
||||||
expectedResponseForExecutionOne,
|
|
||||||
],
|
|
||||||
pageInfo: { currentPage: 1, totalPages: 1 },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
expect(response.body).toEqual(expectedResponsePayload);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('and without isCreator condition', () => {
|
|
||||||
beforeEach(async () => {
|
|
||||||
await createPermission({
|
|
||||||
action: 'read',
|
|
||||||
subject: 'Execution',
|
|
||||||
roleId: role.id,
|
|
||||||
conditions: [],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return executions data of all users', async () => {
|
|
||||||
const response = await request(app)
|
|
||||||
.post('/graphql')
|
|
||||||
.set('Authorization', token)
|
|
||||||
.send({ query })
|
|
||||||
.expect(200);
|
|
||||||
|
|
||||||
const expectedResponsePayload = {
|
|
||||||
data: {
|
|
||||||
getExecutions: {
|
|
||||||
edges: [
|
|
||||||
expectedResponseForExecutionThree,
|
|
||||||
expectedResponseForExecutionTwo,
|
|
||||||
expectedResponseForExecutionOne,
|
|
||||||
],
|
|
||||||
pageInfo: { currentPage: 1, totalPages: 1 },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
expect(response.body).toEqual(expectedResponsePayload);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('and with filters', () => {
|
|
||||||
beforeEach(async () => {
|
|
||||||
await createPermission({
|
|
||||||
action: 'read',
|
|
||||||
subject: 'Execution',
|
|
||||||
roleId: role.id,
|
|
||||||
conditions: [],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return executions data for the specified flow', async () => {
|
|
||||||
const query = `
|
|
||||||
query {
|
|
||||||
getExecutions(limit: 10, offset: 0, filters: { flowId: "${flowOne.id}" }) {
|
|
||||||
pageInfo {
|
|
||||||
currentPage
|
|
||||||
totalPages
|
|
||||||
}
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
id
|
|
||||||
testRun
|
|
||||||
createdAt
|
|
||||||
updatedAt
|
|
||||||
status
|
|
||||||
flow {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
active
|
|
||||||
steps {
|
|
||||||
iconUrl
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const response = await request(app)
|
|
||||||
.post('/graphql')
|
|
||||||
.set('Authorization', token)
|
|
||||||
.send({ query })
|
|
||||||
.expect(200);
|
|
||||||
|
|
||||||
const expectedResponsePayload = {
|
|
||||||
data: {
|
|
||||||
getExecutions: {
|
|
||||||
edges: [expectedResponseForExecutionOne],
|
|
||||||
pageInfo: { currentPage: 1, totalPages: 1 },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
expect(response.body).toEqual(expectedResponsePayload);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return only executions data with success status', async () => {
|
|
||||||
const query = `
|
|
||||||
query {
|
|
||||||
getExecutions(limit: 10, offset: 0, filters: { status: "success" }) {
|
|
||||||
pageInfo {
|
|
||||||
currentPage
|
|
||||||
totalPages
|
|
||||||
}
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
id
|
|
||||||
testRun
|
|
||||||
createdAt
|
|
||||||
updatedAt
|
|
||||||
status
|
|
||||||
flow {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
active
|
|
||||||
steps {
|
|
||||||
iconUrl
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const response = await request(app)
|
|
||||||
.post('/graphql')
|
|
||||||
.set('Authorization', token)
|
|
||||||
.send({ query })
|
|
||||||
.expect(200);
|
|
||||||
|
|
||||||
const expectedResponsePayload = {
|
|
||||||
data: {
|
|
||||||
getExecutions: {
|
|
||||||
edges: [expectedResponseForExecutionOne],
|
|
||||||
pageInfo: { currentPage: 1, totalPages: 1 },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
expect(response.body).toEqual(expectedResponsePayload);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return only executions data within date range', async () => {
|
|
||||||
const createdAtFrom = executionOne.createdAt.getTime().toString();
|
|
||||||
|
|
||||||
const createdAtTo = executionOne.createdAt.getTime().toString();
|
|
||||||
|
|
||||||
const query = `
|
|
||||||
query {
|
|
||||||
getExecutions(limit: 10, offset: 0, filters: { createdAt: { from: "${createdAtFrom}", to: "${createdAtTo}" }}) {
|
|
||||||
pageInfo {
|
|
||||||
currentPage
|
|
||||||
totalPages
|
|
||||||
}
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
id
|
|
||||||
testRun
|
|
||||||
createdAt
|
|
||||||
updatedAt
|
|
||||||
status
|
|
||||||
flow {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
active
|
|
||||||
steps {
|
|
||||||
iconUrl
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const response = await request(app)
|
|
||||||
.post('/graphql')
|
|
||||||
.set('Authorization', token)
|
|
||||||
.send({ query })
|
|
||||||
.expect(200);
|
|
||||||
|
|
||||||
const expectedResponsePayload = {
|
|
||||||
data: {
|
|
||||||
getExecutions: {
|
|
||||||
edges: [expectedResponseForExecutionOne],
|
|
||||||
pageInfo: { currentPage: 1, totalPages: 1 },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
expect(response.body).toEqual(expectedResponsePayload);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
@@ -7,7 +7,6 @@ import getConnectedApps from './queries/get-connected-apps.js';
|
|||||||
import getCurrentUser from './queries/get-current-user.js';
|
import getCurrentUser from './queries/get-current-user.js';
|
||||||
import getDynamicData from './queries/get-dynamic-data.js';
|
import getDynamicData from './queries/get-dynamic-data.js';
|
||||||
import getDynamicFields from './queries/get-dynamic-fields.js';
|
import getDynamicFields from './queries/get-dynamic-fields.js';
|
||||||
import getExecutions from './queries/get-executions.js';
|
|
||||||
import getFlow from './queries/get-flow.js';
|
import getFlow from './queries/get-flow.js';
|
||||||
import getFlows from './queries/get-flows.js';
|
import getFlows from './queries/get-flows.js';
|
||||||
import getInvoices from './queries/get-invoices.ee.js';
|
import getInvoices from './queries/get-invoices.ee.js';
|
||||||
@@ -38,7 +37,6 @@ const queryResolvers = {
|
|||||||
getCurrentUser,
|
getCurrentUser,
|
||||||
getDynamicData,
|
getDynamicData,
|
||||||
getDynamicFields,
|
getDynamicFields,
|
||||||
getExecutions,
|
|
||||||
getFlow,
|
getFlow,
|
||||||
getFlows,
|
getFlows,
|
||||||
getInvoices,
|
getInvoices,
|
||||||
|
@@ -13,11 +13,6 @@ type Query {
|
|||||||
name: String
|
name: String
|
||||||
): FlowConnection
|
): FlowConnection
|
||||||
getStepWithTestExecutions(stepId: String!): [Step]
|
getStepWithTestExecutions(stepId: String!): [Step]
|
||||||
getExecutions(
|
|
||||||
limit: Int!
|
|
||||||
offset: Int!
|
|
||||||
filters: ExecutionFiltersInput
|
|
||||||
): ExecutionConnection
|
|
||||||
getDynamicData(
|
getDynamicData(
|
||||||
stepId: String!
|
stepId: String!
|
||||||
key: String!
|
key: String!
|
||||||
@@ -301,15 +296,6 @@ type Flow {
|
|||||||
status: FlowStatus
|
status: FlowStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
type Execution {
|
|
||||||
id: String
|
|
||||||
testRun: Boolean
|
|
||||||
createdAt: String
|
|
||||||
updatedAt: String
|
|
||||||
status: String
|
|
||||||
flow: Flow
|
|
||||||
}
|
|
||||||
|
|
||||||
type SamlAuthProvider {
|
type SamlAuthProvider {
|
||||||
id: String
|
id: String
|
||||||
name: String
|
name: String
|
||||||
@@ -609,19 +595,10 @@ type PageInfo {
|
|||||||
totalPages: Int!
|
totalPages: Int!
|
||||||
}
|
}
|
||||||
|
|
||||||
type ExecutionEdge {
|
|
||||||
node: Execution
|
|
||||||
}
|
|
||||||
|
|
||||||
type ExecutionStepEdge {
|
type ExecutionStepEdge {
|
||||||
node: ExecutionStep
|
node: ExecutionStep
|
||||||
}
|
}
|
||||||
|
|
||||||
type ExecutionConnection {
|
|
||||||
edges: [ExecutionEdge]
|
|
||||||
pageInfo: PageInfo
|
|
||||||
}
|
|
||||||
|
|
||||||
type ExecutionStepConnection {
|
type ExecutionStepConnection {
|
||||||
edges: [ExecutionStepEdge]
|
edges: [ExecutionStepEdge]
|
||||||
pageInfo: PageInfo
|
pageInfo: PageInfo
|
||||||
@@ -781,17 +758,6 @@ type Notification {
|
|||||||
description: String
|
description: String
|
||||||
}
|
}
|
||||||
|
|
||||||
input ExecutionCreatedAtFilterInput {
|
|
||||||
from: String
|
|
||||||
to: String
|
|
||||||
}
|
|
||||||
|
|
||||||
input ExecutionFiltersInput {
|
|
||||||
flowId: String
|
|
||||||
createdAt: ExecutionCreatedAtFilterInput
|
|
||||||
status: String
|
|
||||||
}
|
|
||||||
|
|
||||||
schema {
|
schema {
|
||||||
query: Query
|
query: Query
|
||||||
mutation: Mutation
|
mutation: Mutation
|
||||||
|
@@ -1,28 +0,0 @@
|
|||||||
import { gql } from '@apollo/client';
|
|
||||||
export const GET_EXECUTIONS = gql`
|
|
||||||
query GetExecutions($limit: Int!, $offset: Int!) {
|
|
||||||
getExecutions(limit: $limit, offset: $offset) {
|
|
||||||
pageInfo {
|
|
||||||
currentPage
|
|
||||||
totalPages
|
|
||||||
}
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
id
|
|
||||||
testRun
|
|
||||||
createdAt
|
|
||||||
updatedAt
|
|
||||||
status
|
|
||||||
flow {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
active
|
|
||||||
steps {
|
|
||||||
iconUrl
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
22
packages/web/src/hooks/useExecutions.js
Normal file
22
packages/web/src/hooks/useExecutions.js
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import { useQuery } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
import api from 'helpers/api';
|
||||||
|
|
||||||
|
export default function useExecutions({ page }, { refetchInterval } = {}) {
|
||||||
|
const query = useQuery({
|
||||||
|
queryKey: ['executions', page],
|
||||||
|
queryFn: async ({ signal }) => {
|
||||||
|
const { data } = await api.get(`/v1/executions`, {
|
||||||
|
params: {
|
||||||
|
page,
|
||||||
|
},
|
||||||
|
signal,
|
||||||
|
});
|
||||||
|
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
refetchInterval,
|
||||||
|
});
|
||||||
|
|
||||||
|
return query;
|
||||||
|
}
|
@@ -1,39 +1,33 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { Link, useSearchParams } from 'react-router-dom';
|
import { Link, useSearchParams } from 'react-router-dom';
|
||||||
import { useQuery } from '@apollo/client';
|
|
||||||
import Box from '@mui/material/Box';
|
import Box from '@mui/material/Box';
|
||||||
import Grid from '@mui/material/Grid';
|
import Grid from '@mui/material/Grid';
|
||||||
import CircularProgress from '@mui/material/CircularProgress';
|
import CircularProgress from '@mui/material/CircularProgress';
|
||||||
import Divider from '@mui/material/Divider';
|
import Divider from '@mui/material/Divider';
|
||||||
import Pagination from '@mui/material/Pagination';
|
import Pagination from '@mui/material/Pagination';
|
||||||
import PaginationItem from '@mui/material/PaginationItem';
|
import PaginationItem from '@mui/material/PaginationItem';
|
||||||
|
|
||||||
import NoResultFound from 'components/NoResultFound';
|
import NoResultFound from 'components/NoResultFound';
|
||||||
import ExecutionRow from 'components/ExecutionRow';
|
import ExecutionRow from 'components/ExecutionRow';
|
||||||
import Container from 'components/Container';
|
import Container from 'components/Container';
|
||||||
import PageTitle from 'components/PageTitle';
|
import PageTitle from 'components/PageTitle';
|
||||||
import useFormatMessage from 'hooks/useFormatMessage';
|
import useFormatMessage from 'hooks/useFormatMessage';
|
||||||
import { GET_EXECUTIONS } from 'graphql/queries/get-executions';
|
import useExecutions from 'hooks/useExecutions';
|
||||||
const EXECUTION_PER_PAGE = 10;
|
|
||||||
const getLimitAndOffset = (page) => ({
|
|
||||||
limit: EXECUTION_PER_PAGE,
|
|
||||||
offset: (page - 1) * EXECUTION_PER_PAGE,
|
|
||||||
});
|
|
||||||
export default function Executions() {
|
export default function Executions() {
|
||||||
const formatMessage = useFormatMessage();
|
const formatMessage = useFormatMessage();
|
||||||
const [searchParams, setSearchParams] = useSearchParams();
|
const [searchParams, setSearchParams] = useSearchParams();
|
||||||
const page = parseInt(searchParams.get('page') || '', 10) || 1;
|
const page = parseInt(searchParams.get('page') || '', 10) || 1;
|
||||||
const { data, refetch, loading } = useQuery(GET_EXECUTIONS, {
|
|
||||||
variables: getLimitAndOffset(page),
|
const { data, isLoading: isExecutionsLoading } = useExecutions(
|
||||||
fetchPolicy: 'cache-and-network',
|
{ page: page },
|
||||||
pollInterval: 5000,
|
{ refetchInterval: 5000 },
|
||||||
});
|
);
|
||||||
const getExecutions = data?.getExecutions || {};
|
|
||||||
const { pageInfo, edges } = getExecutions;
|
const { data: executions, meta: pageInfo } = data || {};
|
||||||
React.useEffect(() => {
|
|
||||||
refetch(getLimitAndOffset(page));
|
|
||||||
}, [refetch, page]);
|
|
||||||
const executions = edges?.map(({ node }) => node);
|
|
||||||
const hasExecutions = executions?.length;
|
const hasExecutions = executions?.length;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{ py: 3 }}>
|
<Box sx={{ py: 3 }}>
|
||||||
<Container>
|
<Container>
|
||||||
@@ -52,18 +46,18 @@ export default function Executions() {
|
|||||||
|
|
||||||
<Divider sx={{ mt: [2, 0], mb: 2 }} />
|
<Divider sx={{ mt: [2, 0], mb: 2 }} />
|
||||||
|
|
||||||
{loading && (
|
{isExecutionsLoading && (
|
||||||
<CircularProgress
|
<CircularProgress
|
||||||
data-test="executions-loader"
|
data-test="executions-loader"
|
||||||
sx={{ display: 'block', margin: '20px auto' }}
|
sx={{ display: 'block', margin: '20px auto' }}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!loading && !hasExecutions && (
|
{!isExecutionsLoading && !hasExecutions && (
|
||||||
<NoResultFound text={formatMessage('executions.noExecutions')} />
|
<NoResultFound text={formatMessage('executions.noExecutions')} />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!loading &&
|
{!isExecutionsLoading &&
|
||||||
executions?.map((execution) => (
|
executions?.map((execution) => (
|
||||||
<ExecutionRow key={execution.id} execution={execution} />
|
<ExecutionRow key={execution.id} execution={execution} />
|
||||||
))}
|
))}
|
||||||
|
Reference in New Issue
Block a user