Merge pull request #2423 from automatisch/flow-is-owner
feat: Implement isOwner flag for get flows API endpoint
This commit is contained in:
@@ -6,7 +6,7 @@ import { createUser } from '../../../../../test/factories/user.js';
|
|||||||
import { createFlow } from '../../../../../test/factories/flow.js';
|
import { createFlow } from '../../../../../test/factories/flow.js';
|
||||||
import { createStep } from '../../../../../test/factories/step.js';
|
import { createStep } from '../../../../../test/factories/step.js';
|
||||||
import { createPermission } from '../../../../../test/factories/permission.js';
|
import { createPermission } from '../../../../../test/factories/permission.js';
|
||||||
import getFlowsMock from '../../../../../test/mocks/rest/api/v1/flows/get-flows.js';
|
import getFlowsMock from '../../../../../test/mocks/rest/api/v1/apps/get-flows.js';
|
||||||
|
|
||||||
describe('GET /api/v1/apps/:appKey/flows', () => {
|
describe('GET /api/v1/apps/:appKey/flows', () => {
|
||||||
let currentUser, currentUserRole, token;
|
let currentUser, currentUserRole, token;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { createConnection } from '../../../../../test/factories/connection.js';
|
|||||||
import { createFlow } from '../../../../../test/factories/flow.js';
|
import { createFlow } from '../../../../../test/factories/flow.js';
|
||||||
import { createStep } from '../../../../../test/factories/step.js';
|
import { createStep } from '../../../../../test/factories/step.js';
|
||||||
import { createPermission } from '../../../../../test/factories/permission.js';
|
import { createPermission } from '../../../../../test/factories/permission.js';
|
||||||
import getFlowsMock from '../../../../../test/mocks/rest/api/v1/flows/get-flows.js';
|
import getFlowsMock from '../../../../../test/mocks/rest/api/v1/connections/get-flows.js';
|
||||||
|
|
||||||
describe('GET /api/v1/connections/:connectionId/flows', () => {
|
describe('GET /api/v1/connections/:connectionId/flows', () => {
|
||||||
let currentUser, currentUserRole, token;
|
let currentUser, currentUserRole, token;
|
||||||
|
|||||||
@@ -61,7 +61,8 @@ describe('GET /api/v1/flows', () => {
|
|||||||
actionStepFlowOne,
|
actionStepFlowOne,
|
||||||
triggerStepFlowTwo,
|
triggerStepFlowTwo,
|
||||||
actionStepFlowTwo,
|
actionStepFlowTwo,
|
||||||
]
|
],
|
||||||
|
currentUser.id
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(response.body).toStrictEqual(expectedPayload);
|
expect(response.body).toStrictEqual(expectedPayload);
|
||||||
@@ -111,7 +112,8 @@ describe('GET /api/v1/flows', () => {
|
|||||||
actionStepFlowOne,
|
actionStepFlowOne,
|
||||||
triggerStepFlowTwo,
|
triggerStepFlowTwo,
|
||||||
actionStepFlowTwo,
|
actionStepFlowTwo,
|
||||||
]
|
],
|
||||||
|
currentUser.id
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(response.body).toStrictEqual(expectedPayload);
|
expect(response.body).toStrictEqual(expectedPayload);
|
||||||
@@ -184,7 +186,8 @@ describe('GET /api/v1/flows', () => {
|
|||||||
actionStepFlowTwo,
|
actionStepFlowTwo,
|
||||||
triggerStepFlowThree,
|
triggerStepFlowThree,
|
||||||
actionStepFlowThree,
|
actionStepFlowThree,
|
||||||
]
|
],
|
||||||
|
currentUser.id
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(response.body).toStrictEqual(expectedPayload);
|
expect(response.body).toStrictEqual(expectedPayload);
|
||||||
@@ -270,7 +273,8 @@ describe('GET /api/v1/flows', () => {
|
|||||||
actionStepFlowThree,
|
actionStepFlowThree,
|
||||||
triggerStepFlowFour,
|
triggerStepFlowFour,
|
||||||
actionStepFlowFour,
|
actionStepFlowFour,
|
||||||
]
|
],
|
||||||
|
currentUser.id
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(response.body).toStrictEqual(expectedPayload);
|
expect(response.body).toStrictEqual(expectedPayload);
|
||||||
@@ -356,7 +360,8 @@ describe('GET /api/v1/flows', () => {
|
|||||||
actionStepFlowOne,
|
actionStepFlowOne,
|
||||||
triggerStepFlowTwo,
|
triggerStepFlowTwo,
|
||||||
actionStepFlowTwo,
|
actionStepFlowTwo,
|
||||||
]
|
],
|
||||||
|
currentUser.id
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(response.body).toStrictEqual(expectedPayload);
|
expect(response.body).toStrictEqual(expectedPayload);
|
||||||
|
|||||||
@@ -537,6 +537,8 @@ class User extends Base {
|
|||||||
.withGraphFetched({
|
.withGraphFetched({
|
||||||
steps: true,
|
steps: true,
|
||||||
})
|
})
|
||||||
|
.select('flows.*')
|
||||||
|
.select(Flow.raw('flows.user_id = ? as "isOwner"', [this.id]))
|
||||||
.where((builder) => {
|
.where((builder) => {
|
||||||
if (name) {
|
if (name) {
|
||||||
builder.where('flows.name', 'ilike', `%${name}%`);
|
builder.where('flows.name', 'ilike', `%${name}%`);
|
||||||
|
|||||||
@@ -1333,6 +1333,23 @@ describe('User model', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should return isOwner false if the flow is owned by another user', async () => {
|
||||||
|
const anotherUser = await createUser();
|
||||||
|
|
||||||
|
await createFlow({
|
||||||
|
userId: anotherUser.id,
|
||||||
|
folderId: folder.id,
|
||||||
|
active: true,
|
||||||
|
name: 'Another User Flow One',
|
||||||
|
});
|
||||||
|
|
||||||
|
const flows = await currentUser.getFlows({ onlyOwnedFlows: false }, [
|
||||||
|
folder.id,
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect(flows[0].isOwner).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
it('should return specified flows with all filters together', async () => {
|
it('should return specified flows with all filters together', async () => {
|
||||||
const flows = await currentUser.getFlows(
|
const flows = await currentUser.getFlows(
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -11,6 +11,10 @@ const flowSerializer = (flow) => {
|
|||||||
updatedAt: flow.updatedAt.getTime(),
|
updatedAt: flow.updatedAt.getTime(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if ('isOwner' in flow) {
|
||||||
|
flowData.isOwner = flow.isOwner;
|
||||||
|
}
|
||||||
|
|
||||||
if (flow.steps?.length > 0) {
|
if (flow.steps?.length > 0) {
|
||||||
flowData.steps = flow.steps.map((step) => stepSerializer(step));
|
flowData.steps = flow.steps.map((step) => stepSerializer(step));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,4 +43,22 @@ describe('flowSerializer', () => {
|
|||||||
|
|
||||||
expect(flowSerializer(flow)).toMatchObject(expectedPayload);
|
expect(flowSerializer(flow)).toMatchObject(expectedPayload);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('isOwner', () => {
|
||||||
|
it('should not be defined by default', async () => {
|
||||||
|
expect(flowSerializer(flow)).not.toHaveProperty('isOwner');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return true if the flow is owned by the current user', async () => {
|
||||||
|
flow.isOwner = true;
|
||||||
|
|
||||||
|
expect(flowSerializer(flow)).toMatchObject({ isOwner: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false if the flow is owned by another user', async () => {
|
||||||
|
flow.isOwner = false;
|
||||||
|
|
||||||
|
expect(flowSerializer(flow)).toMatchObject({ isOwner: false });
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
39
packages/backend/test/mocks/rest/api/v1/apps/get-flows.js
Normal file
39
packages/backend/test/mocks/rest/api/v1/apps/get-flows.js
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
const getFlowsMock = async (flows, steps) => {
|
||||||
|
const data = flows.map((flow) => {
|
||||||
|
const flowSteps = steps.filter((step) => step.flowId === flow.id);
|
||||||
|
|
||||||
|
return {
|
||||||
|
active: flow.active,
|
||||||
|
id: flow.id,
|
||||||
|
name: flow.name,
|
||||||
|
status: flow.active ? 'published' : 'draft',
|
||||||
|
createdAt: flow.createdAt.getTime(),
|
||||||
|
updatedAt: flow.updatedAt.getTime(),
|
||||||
|
steps: flowSteps.map((step) => ({
|
||||||
|
appKey: step.appKey,
|
||||||
|
iconUrl: step.iconUrl,
|
||||||
|
id: step.id,
|
||||||
|
key: step.key,
|
||||||
|
name: step.name,
|
||||||
|
parameters: step.parameters,
|
||||||
|
position: step.position,
|
||||||
|
status: step.status,
|
||||||
|
type: step.type,
|
||||||
|
webhookUrl: step.webhookUrl,
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: data,
|
||||||
|
meta: {
|
||||||
|
count: data.length,
|
||||||
|
currentPage: 1,
|
||||||
|
isArray: true,
|
||||||
|
totalPages: 1,
|
||||||
|
type: 'Flow',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default getFlowsMock;
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
const getFlowsMock = async (flows, steps) => {
|
||||||
|
const data = flows.map((flow) => {
|
||||||
|
const flowSteps = steps.filter((step) => step.flowId === flow.id);
|
||||||
|
|
||||||
|
return {
|
||||||
|
active: flow.active,
|
||||||
|
id: flow.id,
|
||||||
|
name: flow.name,
|
||||||
|
status: flow.active ? 'published' : 'draft',
|
||||||
|
createdAt: flow.createdAt.getTime(),
|
||||||
|
updatedAt: flow.updatedAt.getTime(),
|
||||||
|
steps: flowSteps.map((step) => ({
|
||||||
|
appKey: step.appKey,
|
||||||
|
iconUrl: step.iconUrl,
|
||||||
|
id: step.id,
|
||||||
|
key: step.key,
|
||||||
|
name: step.name,
|
||||||
|
parameters: step.parameters,
|
||||||
|
position: step.position,
|
||||||
|
status: step.status,
|
||||||
|
type: step.type,
|
||||||
|
webhookUrl: step.webhookUrl,
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: data,
|
||||||
|
meta: {
|
||||||
|
count: data.length,
|
||||||
|
currentPage: 1,
|
||||||
|
isArray: true,
|
||||||
|
totalPages: 1,
|
||||||
|
type: 'Flow',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default getFlowsMock;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
const getFlowsMock = async (flows, steps) => {
|
const getFlowsMock = async (flows, steps, currentUserId) => {
|
||||||
const data = flows.map((flow) => {
|
const data = flows.map((flow) => {
|
||||||
const flowSteps = steps.filter((step) => step.flowId === flow.id);
|
const flowSteps = steps.filter((step) => step.flowId === flow.id);
|
||||||
|
|
||||||
@@ -9,6 +9,7 @@ const getFlowsMock = async (flows, steps) => {
|
|||||||
status: flow.active ? 'published' : 'draft',
|
status: flow.active ? 'published' : 'draft',
|
||||||
createdAt: flow.createdAt.getTime(),
|
createdAt: flow.createdAt.getTime(),
|
||||||
updatedAt: flow.updatedAt.getTime(),
|
updatedAt: flow.updatedAt.getTime(),
|
||||||
|
isOwner: flow.userId === currentUserId,
|
||||||
steps: flowSteps.map((step) => ({
|
steps: flowSteps.map((step) => ({
|
||||||
appKey: step.appKey,
|
appKey: step.appKey,
|
||||||
iconUrl: step.iconUrl,
|
iconUrl: step.iconUrl,
|
||||||
|
|||||||
Reference in New Issue
Block a user