Add Activepieces integration for workflow automation
- Add Activepieces fork with SmoothSchedule custom piece - Create integrations app with Activepieces service layer - Add embed token endpoint for iframe integration - Create Automations page with embedded workflow builder - Add sidebar visibility fix for embed mode - Add list inactive customers endpoint to Public API - Include SmoothSchedule triggers: event created/updated/cancelled - Include SmoothSchedule actions: create/update/cancel events, list resources/services/customers 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
import { PrincipalType } from '@activepieces/shared'
|
||||
import { FastifyInstance } from 'fastify'
|
||||
import { initializeDatabase } from '../../../../../src/app/database'
|
||||
import { databaseConnection } from '../../../../../src/app/database/database-connection'
|
||||
import { setupServer } from '../../../../../src/app/server'
|
||||
import { generateMockToken } from '../../../../helpers/auth'
|
||||
import { mockAndSaveBasicSetup } from '../../../../helpers/mocks'
|
||||
let app: FastifyInstance | null = null
|
||||
|
||||
beforeAll(async () => {
|
||||
await initializeDatabase({ runMigrations: false })
|
||||
app = await setupServer()
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await databaseConnection().destroy()
|
||||
await app?.close()
|
||||
})
|
||||
|
||||
describe('List flow runs endpoint', () => {
|
||||
it('should return 200', async () => {
|
||||
// arrange
|
||||
const { mockPlatform, mockOwner, mockProject } = await mockAndSaveBasicSetup()
|
||||
|
||||
const testToken = await generateMockToken({
|
||||
type: PrincipalType.USER,
|
||||
id: mockOwner.id,
|
||||
projectId: mockProject.id,
|
||||
platform: {
|
||||
id: mockPlatform.id,
|
||||
},
|
||||
})
|
||||
|
||||
// act
|
||||
const response = await app?.inject({
|
||||
method: 'GET',
|
||||
url: '/v1/flow-runs',
|
||||
headers: {
|
||||
authorization: `Bearer ${testToken}`,
|
||||
},
|
||||
query: {
|
||||
projectId: mockProject.id,
|
||||
},
|
||||
})
|
||||
|
||||
// assert
|
||||
expect(response?.statusCode).toBe(200)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,69 @@
|
||||
import {
|
||||
apId,
|
||||
PrincipalType,
|
||||
} from '@activepieces/shared'
|
||||
import { FastifyInstance } from 'fastify'
|
||||
import { StatusCodes } from 'http-status-codes'
|
||||
import { initializeDatabase } from '../../../../src/app/database'
|
||||
import { databaseConnection } from '../../../../src/app/database/database-connection'
|
||||
import { setupServer } from '../../../../src/app/server'
|
||||
import { generateMockToken } from '../../../helpers/auth'
|
||||
import {
|
||||
createMockFlow,
|
||||
createMockFlowVersion,
|
||||
createMockProject,
|
||||
mockAndSaveBasicSetup,
|
||||
} from '../../../helpers/mocks'
|
||||
|
||||
let app: FastifyInstance | null = null
|
||||
|
||||
beforeAll(async () => {
|
||||
await initializeDatabase({ runMigrations: false })
|
||||
app = await setupServer()
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await databaseConnection().destroy()
|
||||
await app?.close()
|
||||
})
|
||||
|
||||
describe('Flow API for Worker', () => {
|
||||
describe('Get Flow from Worker', () => {
|
||||
it('List other flow for another project', async () => {
|
||||
// arrange
|
||||
const { mockPlatform, mockOwner, mockProject } = await mockAndSaveBasicSetup()
|
||||
|
||||
const mockProject2 = createMockProject({
|
||||
platformId: mockPlatform.id,
|
||||
ownerId: mockOwner.id,
|
||||
})
|
||||
|
||||
await databaseConnection().getRepository('project').save([mockProject2])
|
||||
|
||||
const mockFlow = createMockFlow({
|
||||
projectId: mockProject.id,
|
||||
})
|
||||
await databaseConnection().getRepository('flow').save([mockFlow])
|
||||
|
||||
const mockFlowVersion = createMockFlowVersion({
|
||||
flowId: mockFlow.id,
|
||||
})
|
||||
await databaseConnection().getRepository('flow_version').save([mockFlowVersion])
|
||||
|
||||
const mockToken = await generateMockToken({
|
||||
id: apId(),
|
||||
type: PrincipalType.WORKER,
|
||||
})
|
||||
|
||||
const response = await app?.inject({
|
||||
method: 'GET',
|
||||
url: `/v1/worker/flows/${mockFlowVersion.id}`,
|
||||
headers: {
|
||||
authorization: `Bearer ${mockToken}`,
|
||||
},
|
||||
})
|
||||
expect(response?.statusCode).toBe(StatusCodes.NOT_FOUND)
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
@@ -0,0 +1,646 @@
|
||||
import { WebhookRenewStrategy } from '@activepieces/pieces-framework'
|
||||
import {
|
||||
FlowOperationType,
|
||||
FlowStatus,
|
||||
FlowTriggerType,
|
||||
FlowVersionState,
|
||||
PackageType,
|
||||
PieceType,
|
||||
PopulatedFlow,
|
||||
PrincipalType,
|
||||
PropertyExecutionType,
|
||||
TriggerStrategy,
|
||||
TriggerTestStrategy,
|
||||
WebhookHandshakeStrategy,
|
||||
} from '@activepieces/shared'
|
||||
import { FastifyInstance } from 'fastify'
|
||||
import { StatusCodes } from 'http-status-codes'
|
||||
import { initializeDatabase } from '../../../../src/app/database'
|
||||
import { databaseConnection } from '../../../../src/app/database/database-connection'
|
||||
import { setupServer } from '../../../../src/app/server'
|
||||
import { generateMockToken } from '../../../helpers/auth'
|
||||
import {
|
||||
createMockFlow,
|
||||
createMockFlowVersion,
|
||||
createMockPieceMetadata,
|
||||
mockAndSaveBasicSetup,
|
||||
} from '../../../helpers/mocks'
|
||||
|
||||
let app: FastifyInstance | null = null
|
||||
|
||||
beforeAll(async () => {
|
||||
await initializeDatabase({ runMigrations: false })
|
||||
app = await setupServer()
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await databaseConnection().destroy()
|
||||
await app?.close()
|
||||
})
|
||||
|
||||
describe('Flow API', () => {
|
||||
describe('Create Flow endpoint', () => {
|
||||
it('Adds an empty flow', async () => {
|
||||
const { mockProject, mockOwner, mockPlatform } = await mockAndSaveBasicSetup()
|
||||
const mockToken = await generateMockToken({
|
||||
type: PrincipalType.USER,
|
||||
projectId: mockProject.id,
|
||||
id: mockOwner.id,
|
||||
platform: {
|
||||
id: mockPlatform.id,
|
||||
},
|
||||
})
|
||||
|
||||
const mockCreateFlowRequest = {
|
||||
displayName: 'test flow',
|
||||
projectId: mockProject.id,
|
||||
metadata: {
|
||||
foo: 'bar',
|
||||
},
|
||||
}
|
||||
|
||||
// act
|
||||
const response = await app?.inject({
|
||||
method: 'POST',
|
||||
url: '/v1/flows',
|
||||
query: {
|
||||
projectId: mockProject.id,
|
||||
},
|
||||
headers: {
|
||||
authorization: `Bearer ${mockToken}`,
|
||||
},
|
||||
body: mockCreateFlowRequest,
|
||||
})
|
||||
|
||||
// assert
|
||||
expect(response?.statusCode).toBe(StatusCodes.CREATED)
|
||||
const responseBody = response?.json()
|
||||
|
||||
expect(Object.keys(responseBody)).toHaveLength(12)
|
||||
expect(responseBody?.id).toHaveLength(21)
|
||||
expect(responseBody?.created).toBeDefined()
|
||||
expect(responseBody?.updated).toBeDefined()
|
||||
expect(responseBody?.projectId).toBe(mockProject.id)
|
||||
expect(responseBody?.folderId).toBeNull()
|
||||
expect(responseBody?.status).toBe('DISABLED')
|
||||
expect(responseBody?.publishedVersionId).toBeNull()
|
||||
expect(responseBody?.metadata).toMatchObject({ foo: 'bar' })
|
||||
expect(responseBody?.operationStatus).toBeDefined()
|
||||
|
||||
expect(Object.keys(responseBody?.version)).toHaveLength(13)
|
||||
expect(responseBody?.version?.id).toHaveLength(21)
|
||||
expect(responseBody?.version?.created).toBeDefined()
|
||||
expect(responseBody?.version?.updated).toBeDefined()
|
||||
expect(responseBody?.version?.updatedBy).toBeNull()
|
||||
expect(responseBody?.version?.flowId).toBe(responseBody?.id)
|
||||
expect(responseBody?.version?.displayName).toBe('test flow')
|
||||
expect(Object.keys(responseBody?.version?.trigger)).toHaveLength(5)
|
||||
expect(responseBody?.version?.trigger.type).toBe('EMPTY')
|
||||
expect(responseBody?.version?.trigger.name).toBe('trigger')
|
||||
expect(responseBody?.version?.trigger.settings).toMatchObject({})
|
||||
expect(responseBody?.version?.trigger.valid).toBe(false)
|
||||
expect(responseBody?.version?.trigger.displayName).toBe('Select Trigger')
|
||||
expect(responseBody?.version?.valid).toBe(false)
|
||||
expect(responseBody?.version?.state).toBe('DRAFT')
|
||||
})
|
||||
})
|
||||
|
||||
describe('Update status endpoint', () => {
|
||||
it('Enables a disabled Flow', async () => {
|
||||
// arrange
|
||||
const { mockProject, mockOwner, mockPlatform } = await mockAndSaveBasicSetup()
|
||||
|
||||
const mockPieceMetadata1 = createMockPieceMetadata({
|
||||
name: '@activepieces/piece-schedule',
|
||||
version: '0.1.5',
|
||||
triggers: {
|
||||
'every_hour': {
|
||||
'name': 'every_hour',
|
||||
'displayName': 'Every Hour',
|
||||
'description': 'Triggers the current flow every hour',
|
||||
'requireAuth': false,
|
||||
'props': {
|
||||
|
||||
},
|
||||
'type': TriggerStrategy.POLLING,
|
||||
'sampleData': {
|
||||
|
||||
},
|
||||
'testStrategy': TriggerTestStrategy.TEST_FUNCTION,
|
||||
},
|
||||
},
|
||||
pieceType: PieceType.OFFICIAL,
|
||||
packageType: PackageType.REGISTRY,
|
||||
})
|
||||
await databaseConnection()
|
||||
.getRepository('piece_metadata')
|
||||
.save([mockPieceMetadata1])
|
||||
|
||||
const mockFlow = createMockFlow({
|
||||
projectId: mockProject.id,
|
||||
status: FlowStatus.DISABLED,
|
||||
})
|
||||
await databaseConnection().getRepository('flow').save([mockFlow])
|
||||
|
||||
const mockFlowVersion = createMockFlowVersion({
|
||||
flowId: mockFlow.id,
|
||||
updatedBy: mockOwner.id,
|
||||
trigger: {
|
||||
type: FlowTriggerType.PIECE,
|
||||
settings: {
|
||||
pieceName: '@activepieces/piece-schedule',
|
||||
pieceVersion: '0.1.5',
|
||||
input: {
|
||||
run_on_weekends: false,
|
||||
},
|
||||
triggerName: 'every_hour',
|
||||
propertySettings: {
|
||||
'run_on_weekends': {
|
||||
type: PropertyExecutionType.MANUAL,
|
||||
},
|
||||
},
|
||||
},
|
||||
valid: true,
|
||||
name: 'trigger',
|
||||
displayName: 'Schedule',
|
||||
},
|
||||
})
|
||||
await databaseConnection()
|
||||
.getRepository('flow_version')
|
||||
.save([mockFlowVersion])
|
||||
|
||||
await databaseConnection().getRepository('flow').update(mockFlow.id, {
|
||||
publishedVersionId: mockFlowVersion.id,
|
||||
})
|
||||
|
||||
const mockToken = await generateMockToken({
|
||||
type: PrincipalType.USER,
|
||||
platform: {
|
||||
id: mockPlatform.id,
|
||||
},
|
||||
projectId: mockProject.id,
|
||||
id: mockOwner.id,
|
||||
})
|
||||
|
||||
|
||||
// act
|
||||
const response = await app?.inject({
|
||||
method: 'POST',
|
||||
url: `/v1/flows/${mockFlow.id}`,
|
||||
headers: {
|
||||
authorization: `Bearer ${mockToken}`,
|
||||
},
|
||||
body: {
|
||||
type: FlowOperationType.CHANGE_STATUS,
|
||||
request: {
|
||||
status: 'ENABLED',
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
// assert
|
||||
expect(response?.statusCode).toBe(StatusCodes.OK)
|
||||
const responseBody: PopulatedFlow | undefined = response?.json()
|
||||
expect(responseBody).toBeDefined()
|
||||
if (responseBody) {
|
||||
expect(responseBody.id).toBe(mockFlow.id)
|
||||
expect(responseBody.created).toBeDefined()
|
||||
expect(responseBody.updated).toBeDefined()
|
||||
expect(responseBody.projectId).toBe(mockProject.id)
|
||||
expect(responseBody.folderId).toBeNull()
|
||||
expect(responseBody.publishedVersionId).toBe(mockFlowVersion.id)
|
||||
expect(responseBody.metadata).toBeNull()
|
||||
expect(responseBody.operationStatus).toBe('ENABLING')
|
||||
expect(Object.keys(responseBody.version)).toHaveLength(13)
|
||||
expect(responseBody.version.id).toBe(mockFlowVersion.id)
|
||||
}
|
||||
})
|
||||
|
||||
it('Disables an enabled Flow', async () => {
|
||||
// arrange
|
||||
const { mockProject, mockOwner, mockPlatform } = await mockAndSaveBasicSetup()
|
||||
|
||||
const mockFlow = createMockFlow({
|
||||
projectId: mockProject.id,
|
||||
status: FlowStatus.ENABLED,
|
||||
})
|
||||
await databaseConnection().getRepository('flow').save([mockFlow])
|
||||
|
||||
const mockFlowVersion = createMockFlowVersion({
|
||||
flowId: mockFlow.id,
|
||||
updatedBy: mockOwner.id,
|
||||
})
|
||||
await databaseConnection()
|
||||
.getRepository('flow_version')
|
||||
.save([mockFlowVersion])
|
||||
|
||||
await databaseConnection().getRepository('flow').update(mockFlow.id, {
|
||||
publishedVersionId: mockFlowVersion.id,
|
||||
})
|
||||
|
||||
const mockToken = await generateMockToken({
|
||||
type: PrincipalType.USER,
|
||||
platform: {
|
||||
id: mockPlatform.id,
|
||||
},
|
||||
projectId: mockProject.id,
|
||||
id: mockOwner.id,
|
||||
})
|
||||
|
||||
const mockUpdateFlowStatusRequest = {
|
||||
type: FlowOperationType.CHANGE_STATUS,
|
||||
request: {
|
||||
status: 'DISABLED',
|
||||
},
|
||||
}
|
||||
|
||||
// act
|
||||
const response = await app?.inject({
|
||||
method: 'POST',
|
||||
url: `/v1/flows/${mockFlow.id}`,
|
||||
headers: {
|
||||
authorization: `Bearer ${mockToken}`,
|
||||
},
|
||||
body: mockUpdateFlowStatusRequest,
|
||||
})
|
||||
|
||||
// assert
|
||||
expect(response?.statusCode).toBe(StatusCodes.OK)
|
||||
const responseBody = response?.json()
|
||||
|
||||
expect(Object.keys(responseBody)).toHaveLength(12)
|
||||
expect(responseBody?.id).toBe(mockFlow.id)
|
||||
expect(responseBody?.created).toBeDefined()
|
||||
expect(responseBody?.updated).toBeDefined()
|
||||
expect(responseBody?.projectId).toBe(mockProject.id)
|
||||
expect(responseBody?.folderId).toBeNull()
|
||||
expect(responseBody?.status).toBe('ENABLED')
|
||||
expect(responseBody?.publishedVersionId).toBe(mockFlowVersion.id)
|
||||
expect(responseBody?.metadata).toBeNull()
|
||||
expect(responseBody?.operationStatus).toBe('DISABLING')
|
||||
|
||||
expect(Object.keys(responseBody?.version)).toHaveLength(13)
|
||||
expect(responseBody?.version?.id).toBe(mockFlowVersion.id)
|
||||
})
|
||||
})
|
||||
|
||||
describe('Update published version id endpoint', () => {
|
||||
it('Publishes latest draft version', async () => {
|
||||
// arrange
|
||||
const { mockProject, mockOwner, mockPlatform } = await mockAndSaveBasicSetup()
|
||||
|
||||
const mockPieceMetadata1 = createMockPieceMetadata({
|
||||
name: '@activepieces/piece-schedule',
|
||||
version: '0.1.5',
|
||||
triggers: {
|
||||
'every_hour': {
|
||||
'name': 'every_hour',
|
||||
'displayName': 'Every Hour',
|
||||
'description': 'Triggers the current flow every hour',
|
||||
'requireAuth': true,
|
||||
'props': {
|
||||
|
||||
},
|
||||
'type': TriggerStrategy.WEBHOOK,
|
||||
'handshakeConfiguration': {
|
||||
'strategy': WebhookHandshakeStrategy.NONE,
|
||||
},
|
||||
'renewConfiguration': {
|
||||
'strategy': WebhookRenewStrategy.NONE,
|
||||
},
|
||||
'sampleData': {
|
||||
|
||||
},
|
||||
'testStrategy': TriggerTestStrategy.TEST_FUNCTION,
|
||||
},
|
||||
},
|
||||
pieceType: PieceType.OFFICIAL,
|
||||
packageType: PackageType.REGISTRY,
|
||||
})
|
||||
await databaseConnection()
|
||||
.getRepository('piece_metadata')
|
||||
.save([mockPieceMetadata1])
|
||||
|
||||
const mockFlow = createMockFlow({
|
||||
projectId: mockProject.id,
|
||||
status: FlowStatus.DISABLED,
|
||||
})
|
||||
await databaseConnection().getRepository('flow').save([mockFlow])
|
||||
|
||||
const mockFlowVersion = createMockFlowVersion({
|
||||
flowId: mockFlow.id,
|
||||
updatedBy: mockOwner.id,
|
||||
state: FlowVersionState.DRAFT,
|
||||
trigger: {
|
||||
type: FlowTriggerType.PIECE,
|
||||
settings: {
|
||||
pieceName: '@activepieces/piece-schedule',
|
||||
pieceVersion: '0.1.5',
|
||||
input: {
|
||||
run_on_weekends: false,
|
||||
},
|
||||
triggerName: 'every_hour',
|
||||
propertySettings: {
|
||||
'run_on_weekends': {
|
||||
type: PropertyExecutionType.MANUAL,
|
||||
},
|
||||
},
|
||||
},
|
||||
valid: true,
|
||||
name: 'trigger',
|
||||
displayName: 'Schedule',
|
||||
},
|
||||
})
|
||||
await databaseConnection()
|
||||
.getRepository('flow_version')
|
||||
.save([mockFlowVersion])
|
||||
|
||||
const mockToken = await generateMockToken({
|
||||
id: mockOwner.id,
|
||||
type: PrincipalType.USER,
|
||||
projectId: mockProject.id,
|
||||
platform: {
|
||||
id: mockPlatform.id,
|
||||
},
|
||||
})
|
||||
|
||||
// act
|
||||
const response = await app?.inject({
|
||||
method: 'POST',
|
||||
url: `/v1/flows/${mockFlow.id}`,
|
||||
body: {
|
||||
type: FlowOperationType.LOCK_AND_PUBLISH,
|
||||
request: {},
|
||||
},
|
||||
headers: {
|
||||
authorization: `Bearer ${mockToken}`,
|
||||
},
|
||||
})
|
||||
|
||||
// assert
|
||||
expect(response?.statusCode).toBe(StatusCodes.OK)
|
||||
const responseBody: PopulatedFlow | undefined = response?.json()
|
||||
expect(responseBody).toBeDefined()
|
||||
if (responseBody) {
|
||||
expect(Object.keys(responseBody)).toHaveLength(12)
|
||||
expect(responseBody.id).toBe(mockFlow.id)
|
||||
expect(responseBody.created).toBeDefined()
|
||||
expect(responseBody.updated).toBeDefined()
|
||||
expect(responseBody.projectId).toBe(mockProject.id)
|
||||
expect(responseBody.folderId).toBeNull()
|
||||
expect(responseBody.status).toBe(mockFlow.status)
|
||||
expect(responseBody.publishedVersionId).toBe(mockFlowVersion.id)
|
||||
expect(responseBody.metadata).toBeNull()
|
||||
expect(responseBody.operationStatus).toBe('DISABLING')
|
||||
expect(Object.keys(responseBody.version)).toHaveLength(13)
|
||||
expect(responseBody.version.id).toBe(mockFlowVersion.id)
|
||||
expect(responseBody.version.state).toBe('LOCKED')
|
||||
}
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
describe('List Flows endpoint', () => {
|
||||
it('Filters Flows by status', async () => {
|
||||
// arrange
|
||||
const { mockProject, mockOwner, mockPlatform } = await mockAndSaveBasicSetup()
|
||||
|
||||
const mockEnabledFlow = createMockFlow({
|
||||
projectId: mockProject.id,
|
||||
status: FlowStatus.ENABLED,
|
||||
})
|
||||
const mockDisabledFlow = createMockFlow({
|
||||
projectId: mockProject.id,
|
||||
status: FlowStatus.DISABLED,
|
||||
})
|
||||
await databaseConnection()
|
||||
.getRepository('flow')
|
||||
.save([mockEnabledFlow, mockDisabledFlow])
|
||||
|
||||
const mockEnabledFlowVersion = createMockFlowVersion({
|
||||
flowId: mockEnabledFlow.id,
|
||||
})
|
||||
const mockDisabledFlowVersion = createMockFlowVersion({
|
||||
flowId: mockDisabledFlow.id,
|
||||
})
|
||||
await databaseConnection()
|
||||
.getRepository('flow_version')
|
||||
.save([mockEnabledFlowVersion, mockDisabledFlowVersion])
|
||||
|
||||
const mockToken = await generateMockToken({
|
||||
type: PrincipalType.USER,
|
||||
projectId: mockProject.id,
|
||||
id: mockOwner.id,
|
||||
platform: {
|
||||
id: mockPlatform.id,
|
||||
},
|
||||
})
|
||||
|
||||
// act
|
||||
const response = await app?.inject({
|
||||
method: 'GET',
|
||||
url: '/v1/flows',
|
||||
query: {
|
||||
projectId: mockProject.id,
|
||||
status: 'ENABLED',
|
||||
},
|
||||
headers: {
|
||||
authorization: `Bearer ${mockToken}`,
|
||||
},
|
||||
})
|
||||
|
||||
// assert
|
||||
expect(response?.statusCode).toBe(StatusCodes.OK)
|
||||
const responseBody = response?.json()
|
||||
|
||||
expect(responseBody.data).toHaveLength(1)
|
||||
expect(responseBody.data[0].id).toBe(mockEnabledFlow.id)
|
||||
})
|
||||
|
||||
it('Populates Flow version', async () => {
|
||||
// arrange
|
||||
const { mockProject, mockOwner, mockPlatform } = await mockAndSaveBasicSetup()
|
||||
|
||||
const mockFlow = createMockFlow({ projectId: mockProject.id })
|
||||
await databaseConnection().getRepository('flow').save([mockFlow])
|
||||
|
||||
const mockFlowVersion = createMockFlowVersion({ flowId: mockFlow.id })
|
||||
await databaseConnection()
|
||||
.getRepository('flow_version')
|
||||
.save([mockFlowVersion])
|
||||
|
||||
const mockToken = await generateMockToken({
|
||||
type: PrincipalType.USER,
|
||||
projectId: mockProject.id,
|
||||
id: mockOwner.id,
|
||||
platform: {
|
||||
id: mockPlatform.id,
|
||||
},
|
||||
})
|
||||
|
||||
// act
|
||||
const response = await app?.inject({
|
||||
method: 'GET',
|
||||
url: '/v1/flows',
|
||||
query: {
|
||||
projectId: mockProject.id,
|
||||
},
|
||||
headers: {
|
||||
authorization: `Bearer ${mockToken}`,
|
||||
},
|
||||
})
|
||||
|
||||
// assert
|
||||
expect(response?.statusCode).toBe(StatusCodes.OK)
|
||||
const responseBody = response?.json()
|
||||
|
||||
expect(responseBody?.data).toHaveLength(1)
|
||||
expect(responseBody?.data?.[0]?.id).toBe(mockFlow.id)
|
||||
expect(responseBody?.data?.[0]?.version?.id).toBe(mockFlowVersion.id)
|
||||
})
|
||||
|
||||
it('Fails if a flow with no version exists', async () => {
|
||||
// arrange
|
||||
const { mockProject, mockOwner, mockPlatform } = await mockAndSaveBasicSetup()
|
||||
|
||||
const mockFlow = createMockFlow({ projectId: mockProject.id })
|
||||
await databaseConnection().getRepository('flow').save([mockFlow])
|
||||
|
||||
const mockToken = await generateMockToken({
|
||||
type: PrincipalType.USER,
|
||||
projectId: mockProject.id,
|
||||
id: mockOwner.id,
|
||||
platform: {
|
||||
id: mockPlatform.id,
|
||||
},
|
||||
})
|
||||
|
||||
// act
|
||||
const response = await app?.inject({
|
||||
method: 'GET',
|
||||
url: '/v1/flows',
|
||||
query: {
|
||||
projectId: mockProject.id,
|
||||
},
|
||||
headers: {
|
||||
authorization: `Bearer ${mockToken}`,
|
||||
},
|
||||
})
|
||||
|
||||
// assert
|
||||
expect(response?.statusCode).toBe(StatusCodes.NOT_FOUND)
|
||||
const responseBody = response?.json()
|
||||
|
||||
expect(responseBody?.code).toBe('ENTITY_NOT_FOUND')
|
||||
expect(responseBody?.params?.entityType).toBe('FlowVersion')
|
||||
expect(responseBody?.params?.message).toBe(`flowId=${mockFlow.id}`)
|
||||
})
|
||||
})
|
||||
|
||||
describe('Update Metadata endpoint', () => {
|
||||
it('Updates flow metadata', async () => {
|
||||
// arrange
|
||||
const { mockProject, mockOwner, mockPlatform } = await mockAndSaveBasicSetup()
|
||||
|
||||
// create a flow with no metadata
|
||||
const mockFlow = createMockFlow({ projectId: mockProject.id })
|
||||
await databaseConnection().getRepository('flow').save([mockFlow])
|
||||
|
||||
const mockFlowVersion = createMockFlowVersion({ flowId: mockFlow.id })
|
||||
await databaseConnection()
|
||||
.getRepository('flow_version')
|
||||
.save([mockFlowVersion])
|
||||
|
||||
const mockToken = await generateMockToken({
|
||||
type: PrincipalType.USER,
|
||||
projectId: mockProject.id,
|
||||
id: mockOwner.id,
|
||||
platform: {
|
||||
id: mockPlatform.id,
|
||||
},
|
||||
})
|
||||
|
||||
const updatedMetadata = { foo: 'bar' }
|
||||
|
||||
// act
|
||||
const response = await app?.inject({
|
||||
method: 'POST',
|
||||
url: `/v1/flows/${mockFlow.id}`,
|
||||
headers: {
|
||||
authorization: `Bearer ${mockToken}`,
|
||||
},
|
||||
body: {
|
||||
type: FlowOperationType.UPDATE_METADATA,
|
||||
request: {
|
||||
metadata: updatedMetadata,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
// assert
|
||||
expect(response?.statusCode).toBe(StatusCodes.OK)
|
||||
const responseBody = response?.json()
|
||||
|
||||
expect(responseBody.id).toBe(mockFlow.id)
|
||||
expect(responseBody.metadata).toEqual(updatedMetadata)
|
||||
|
||||
// Verify metadata was actually persisted in the database
|
||||
const updatedFlow = await databaseConnection()
|
||||
.getRepository('flow')
|
||||
.findOneBy({ id: mockFlow.id })
|
||||
|
||||
expect(updatedFlow?.metadata).toEqual(updatedMetadata)
|
||||
})
|
||||
})
|
||||
|
||||
describe('Export Flow Template endpoint', () => {
|
||||
it('Exports a flow template using an API key', async () => {
|
||||
// arrange
|
||||
const { mockProject, mockOwner, mockPlatform } = await mockAndSaveBasicSetup()
|
||||
|
||||
const mockFlow = createMockFlow({
|
||||
projectId: mockProject.id,
|
||||
status: FlowStatus.ENABLED,
|
||||
})
|
||||
await databaseConnection().getRepository('flow').save([mockFlow])
|
||||
|
||||
const mockFlowVersion = createMockFlowVersion({
|
||||
flowId: mockFlow.id,
|
||||
updatedBy: mockOwner.id,
|
||||
})
|
||||
await databaseConnection()
|
||||
.getRepository('flow_version')
|
||||
.save([mockFlowVersion])
|
||||
|
||||
const mockApiKey = 'test_api_key'
|
||||
const mockToken = await generateMockToken({
|
||||
type: PrincipalType.SERVICE,
|
||||
projectId: mockProject.id,
|
||||
id: mockApiKey,
|
||||
platform: {
|
||||
id: mockPlatform.id,
|
||||
},
|
||||
})
|
||||
|
||||
// act
|
||||
const response = await app?.inject({
|
||||
method: 'GET',
|
||||
url: `/v1/flows/${mockFlow.id}/template`,
|
||||
headers: {
|
||||
authorization: `Bearer ${mockToken}`,
|
||||
},
|
||||
})
|
||||
|
||||
// assert
|
||||
expect(response?.statusCode).toBe(StatusCodes.OK)
|
||||
const responseBody = response?.json()
|
||||
|
||||
expect(responseBody).toHaveProperty('name')
|
||||
expect(responseBody).toHaveProperty('description')
|
||||
expect(responseBody).toHaveProperty('flows')
|
||||
expect(responseBody.flows).toHaveLength(1)
|
||||
expect(responseBody.flows[0]).toHaveProperty('trigger')
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,52 @@
|
||||
import { apId, PrincipalType } from '@activepieces/shared'
|
||||
import { FastifyInstance } from 'fastify'
|
||||
import { StatusCodes } from 'http-status-codes'
|
||||
import { initializeDatabase } from '../../../../src/app/database'
|
||||
import { databaseConnection } from '../../../../src/app/database/database-connection'
|
||||
import { setupServer } from '../../../../src/app/server'
|
||||
import { generateMockToken } from '../../../helpers/auth'
|
||||
import { mockAndSaveBasicSetup } from '../../../helpers/mocks'
|
||||
|
||||
let app: FastifyInstance | null = null
|
||||
|
||||
beforeAll(async () => {
|
||||
await initializeDatabase({ runMigrations: false })
|
||||
app = await setupServer()
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await databaseConnection().destroy()
|
||||
await app?.close()
|
||||
})
|
||||
|
||||
describe('Project Worker API', () => {
|
||||
describe('Get worker project endpoint', () => {
|
||||
it('Returns worker project', async () => {
|
||||
// arrange
|
||||
const { mockProject, mockPlatform } = await mockAndSaveBasicSetup()
|
||||
|
||||
const mockToken = await generateMockToken({
|
||||
type: PrincipalType.ENGINE,
|
||||
id: apId(),
|
||||
platform: {
|
||||
id: mockPlatform.id,
|
||||
},
|
||||
projectId: mockProject.id,
|
||||
})
|
||||
|
||||
// act
|
||||
const response = await app?.inject({
|
||||
method: 'GET',
|
||||
url: '/v1/worker/project',
|
||||
headers: {
|
||||
authorization: `Bearer ${mockToken}`,
|
||||
},
|
||||
})
|
||||
|
||||
// assert
|
||||
expect(response?.statusCode).toBe(StatusCodes.OK)
|
||||
const responseBody = response?.json()
|
||||
expect(responseBody?.id).toBe(mockProject.id)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,77 @@
|
||||
import { FlowStatus, PrincipalType } from '@activepieces/shared'
|
||||
import { FastifyInstance } from 'fastify'
|
||||
import { StatusCodes } from 'http-status-codes'
|
||||
import { initializeDatabase } from '../../../../src/app/database'
|
||||
import { databaseConnection } from '../../../../src/app/database/database-connection'
|
||||
import { setupServer } from '../../../../src/app/server'
|
||||
import { generateMockToken } from '../../../helpers/auth'
|
||||
import { createMockFlow, createMockFlowVersion, mockAndSaveBasicSetup } from '../../../helpers/mocks'
|
||||
|
||||
let app: FastifyInstance | null = null
|
||||
const MOCK_FLOW_ID = '8hfKOpm3kY1yAi1ApYOa1'
|
||||
beforeAll(async () => {
|
||||
await initializeDatabase({ runMigrations: false })
|
||||
app = await setupServer()
|
||||
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await databaseConnection().destroy()
|
||||
await app?.close()
|
||||
})
|
||||
|
||||
describe('Webhook Service', () => {
|
||||
it('should return GONE if the flow is not found', async () => {
|
||||
const { mockProject, mockOwner } = await mockAndSaveBasicSetup()
|
||||
const { mockPlatform } = await mockAndSaveBasicSetup()
|
||||
const mockToken = await generateMockToken({
|
||||
type: PrincipalType.USER,
|
||||
projectId: mockProject.id,
|
||||
id: mockOwner.id,
|
||||
platform: {
|
||||
id: mockPlatform.id,
|
||||
},
|
||||
})
|
||||
|
||||
const response = await app?.inject({
|
||||
method: 'GET',
|
||||
url: `/v1/webhooks/${MOCK_FLOW_ID}`,
|
||||
headers: {
|
||||
authorization: `Bearer ${mockToken}`,
|
||||
},
|
||||
})
|
||||
expect(response?.statusCode).toBe(StatusCodes.GONE)
|
||||
}),
|
||||
it('should return NOT FOUND if the flow is disabled', async () => {
|
||||
const { mockProject, mockPlatform } = await mockAndSaveBasicSetup()
|
||||
const { mockOwner } = await mockAndSaveBasicSetup()
|
||||
const mockFlow = createMockFlow({
|
||||
projectId: mockProject.id,
|
||||
status: FlowStatus.DISABLED,
|
||||
})
|
||||
await databaseConnection().getRepository('flow').save([mockFlow])
|
||||
const mockFlowVersion = createMockFlowVersion({
|
||||
flowId: mockFlow.id,
|
||||
})
|
||||
await databaseConnection().getRepository('flow_version').save([mockFlowVersion])
|
||||
await databaseConnection().getRepository('flow').update(mockFlow.id, {
|
||||
publishedVersionId: mockFlowVersion.id,
|
||||
})
|
||||
const mockToken = await generateMockToken({
|
||||
type: PrincipalType.USER,
|
||||
projectId: mockProject.id,
|
||||
platform: {
|
||||
id: mockPlatform.id,
|
||||
},
|
||||
id: mockOwner.id,
|
||||
})
|
||||
const response = await app?.inject({
|
||||
method: 'GET',
|
||||
url: `/v1/webhooks/${mockFlow.id}`,
|
||||
headers: {
|
||||
authorization: `Bearer ${mockToken}`,
|
||||
},
|
||||
})
|
||||
expect(response?.statusCode).toBe(StatusCodes.NOT_FOUND)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user