Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/export import stage 2 #3063

Merged
merged 11 commits into from
Sep 10, 2024
5 changes: 2 additions & 3 deletions packages/server/src/Interface.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { IAction } from 'flowise-components'
import { ICommonObject, IFileUpload, INode, INodeData as INodeDataFromComponent, INodeParams } from 'flowise-components'
import { IAction, ICommonObject, IFileUpload, INode, INodeData as INodeDataFromComponent, INodeParams } from 'flowise-components'

export type MessageType = 'apiMessage' | 'userMessage'

export type ChatflowType = 'CHATFLOW' | 'MULTIAGENT'
export type ChatflowType = 'CHATFLOW' | 'MULTIAGENT' | 'ALL'

export enum chatType {
INTERNAL = 'INTERNAL',
Expand Down
28 changes: 28 additions & 0 deletions packages/server/src/controllers/export-import/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { NextFunction, Request, Response } from 'express'
import { ChatFlow } from '../../database/entities/ChatFlow'
import { Tool } from '../../database/entities/Tool'
import exportImportService from '../../services/export-import'

const exportData = async (req: Request, res: Response, next: NextFunction) => {
try {
const apiResponse = await exportImportService.exportData(exportImportService.convertExportInput(req.body))
return res.json(apiResponse)
} catch (error) {
next(error)
}
}

const importAll = async (req: Request, res: Response, next: NextFunction) => {
try {
const importData: { Tool: Partial<Tool>[]; ChatFlow: Partial<ChatFlow>[] } = req.body
await exportImportService.importAll(importData)
return res.json({ message: 'success' })
} catch (error) {
next(error)
}
}

export default {
exportData,
importAll
}
9 changes: 9 additions & 0 deletions packages/server/src/routes/export-import/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import express from 'express'
import exportImportController from '../../controllers/export-import'
const router = express.Router()

router.post('/export', exportImportController.exportData)

router.post('/import/all', exportImportController.importAll)

export default router
2 changes: 2 additions & 0 deletions packages/server/src/routes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import componentsCredentialsRouter from './components-credentials'
import componentsCredentialsIconRouter from './components-credentials-icon'
import credentialsRouter from './credentials'
import documentStoreRouter from './documentstore'
import exportImportRouter from './export-import'
import feedbackRouter from './feedback'
import fetchLinksRouter from './fetch-links'
import flowConfigRouter from './flow-config'
Expand Down Expand Up @@ -53,6 +54,7 @@ router.use('/components-credentials-icon', componentsCredentialsIconRouter)
router.use('/chatflows-uploads', chatflowsUploadsRouter)
router.use('/credentials', credentialsRouter)
router.use('/document-store', documentStoreRouter)
router.use('/export-import', exportImportRouter)
router.use('/feedback', feedbackRouter)
router.use('/fetch-links', fetchLinksRouter)
router.use('/flow-config', flowConfigRouter)
Expand Down
7 changes: 4 additions & 3 deletions packages/server/src/services/chatflows/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { removeFolderFromStorage } from 'flowise-components'
import { StatusCodes } from 'http-status-codes'
import { ChatflowType, IChatFlow, IReactFlowObject } from '../../Interface'
import { ChatflowType, IReactFlowObject } from '../../Interface'
import { ChatFlow } from '../../database/entities/ChatFlow'
import { ChatMessage } from '../../database/entities/ChatMessage'
import { ChatMessageFeedback } from '../../database/entities/ChatMessageFeedback'
Expand Down Expand Up @@ -103,12 +103,14 @@ const deleteChatflow = async (chatflowId: string): Promise<any> => {
}
}

const getAllChatflows = async (type?: ChatflowType): Promise<IChatFlow[]> => {
const getAllChatflows = async (type?: ChatflowType): Promise<ChatFlow[]> => {
try {
const appServer = getRunningExpressApp()
const dbResponse = await appServer.AppDataSource.getRepository(ChatFlow).find()
if (type === 'MULTIAGENT') {
return dbResponse.filter((chatflow) => chatflow.type === type)
} else if (type === 'ALL') {
return dbResponse
}
return dbResponse.filter((chatflow) => chatflow.type === 'CHATFLOW' || !chatflow.type)
} catch (error) {
Expand Down Expand Up @@ -234,7 +236,6 @@ const importChatflows = async (newChatflows: Partial<ChatFlow>[]): Promise<any>
newChatflow.id = undefined
newChatflow.name += ' with new id'
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

with new id

}
newChatflow.type = 'CHATFLOW'
newChatflow.flowData = JSON.stringify(JSON.parse(flowData))
return newChatflow
})
Expand Down
79 changes: 79 additions & 0 deletions packages/server/src/services/export-import/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { StatusCodes } from 'http-status-codes'
import { ChatFlow } from '../../database/entities/ChatFlow'
import { Tool } from '../../database/entities/Tool'
import { InternalFlowiseError } from '../../errors/internalFlowiseError'
import { getErrorMessage } from '../../errors/utils'
import { getRunningExpressApp } from '../../utils/getRunningExpressApp'
import chatflowService from '../chatflows'
import toolsService from '../tools'

type ExportInput = { tool: boolean; chatflow: boolean; multiagent: boolean }

const convertExportInput = (body: any): ExportInput => {
try {
if (typeof body.tool !== 'boolean' || typeof body.chatflow !== 'boolean' || typeof body.multiagent !== 'boolean')
throw new Error('Invalid ExportInput object in request body')
return body as ExportInput
} catch (error) {
throw new InternalFlowiseError(
StatusCodes.INTERNAL_SERVER_ERROR,
`Error: exportImportService.convertExportInput - ${getErrorMessage(error)}`
)
}
}

const FileDefaultName = 'ExportData.json'
const exportData = async (exportInput: ExportInput): Promise<{ FileDefaultName: string; Tool: Tool[]; ChatFlow: ChatFlow[] }> => {
try {
// step 1 - get all tool
let allTool: Tool[] = []
if (exportInput.tool === true) allTool = await toolsService.getAllTools()

// step 2 - get all ChatFlow
let allChatflow: ChatFlow[] = []
if (exportInput.chatflow === true) allChatflow = await chatflowService.getAllChatflows('CHATFLOW')

// step 3 - get all MultiAgent
let allMultiAgent: ChatFlow[] = []
if (exportInput.multiagent === true) allMultiAgent = await chatflowService.getAllChatflows('MULTIAGENT')

return { FileDefaultName, Tool: allTool, ChatFlow: [...allChatflow, ...allMultiAgent] }
} catch (error) {
throw new InternalFlowiseError(
StatusCodes.INTERNAL_SERVER_ERROR,
`Error: exportImportService.exportData - ${getErrorMessage(error)}`
)
}
}

const importAll = async (importData: { Tool: Partial<Tool>[]; ChatFlow: Partial<ChatFlow>[] }) => {
try {
const appServer = getRunningExpressApp()
const queryRunner = appServer.AppDataSource.createQueryRunner()

try {
queryRunner.startTransaction()
// step 1 - importTools
if (importData.Tool.length > 0) await toolsService.importTools(importData.Tool)
// step 2 - importChatflows
if (importData.ChatFlow.length > 0) await chatflowService.importChatflows(importData.ChatFlow)
queryRunner.commitTransaction()
} catch (error) {
queryRunner.rollbackTransaction()
throw error
} finally {
queryRunner.release()
}
} catch (error) {
throw new InternalFlowiseError(
StatusCodes.INTERNAL_SERVER_ERROR,
`Error: exportImportService.importAll - ${getErrorMessage(error)}`
)
}
}

export default {
convertExportInput,
exportData,
importAll
}
56 changes: 52 additions & 4 deletions packages/server/src/services/tools/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { StatusCodes } from 'http-status-codes'
import { getRunningExpressApp } from '../../utils/getRunningExpressApp'
import { Tool } from '../../database/entities/Tool'
import { getAppVersion } from '../../utils'
import { InternalFlowiseError } from '../../errors/internalFlowiseError'
import { getErrorMessage } from '../../errors/utils'
import { getAppVersion } from '../../utils'
import { getRunningExpressApp } from '../../utils/getRunningExpressApp'

const createTool = async (requestBody: any): Promise<any> => {
try {
Expand Down Expand Up @@ -35,7 +35,7 @@ const deleteTool = async (toolId: string): Promise<any> => {
}
}

const getAllTools = async (): Promise<any> => {
const getAllTools = async (): Promise<Tool[]> => {
try {
const appServer = getRunningExpressApp()
const dbResponse = await appServer.AppDataSource.getRepository(Tool).find()
Expand Down Expand Up @@ -79,10 +79,58 @@ const updateTool = async (toolId: string, toolBody: any): Promise<any> => {
}
}

const importTools = async (newTools: Partial<Tool>[]) => {
try {
const appServer = getRunningExpressApp()

// step 1 - check whether file tools array is zero
if (newTools.length == 0) throw new Error('No tools in this file.')

// step 2 - check whether ids are duplicate in database
let ids = '('
let count: number = 0
const lastCount = newTools.length - 1
newTools.forEach((newTools) => {
ids += `'${newTools.id}'`
if (lastCount != count) ids += ','
if (lastCount == count) ids += ')'
count += 1
})

const selectResponse = await appServer.AppDataSource.getRepository(Tool)
.createQueryBuilder('t')
.select('t.id')
.where(`t.id IN ${ids}`)
.getMany()
const foundIds = selectResponse.map((response) => {
return response.id
})

// step 3 - remove ids that are only duplicate
const prepTools: Partial<Tool>[] = newTools.map((newTool) => {
let id: string = ''
if (newTool.id) id = newTool.id
if (foundIds.includes(id)) {
newTool.id = undefined
newTool.name += ' with new id'
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

with new id

}
return newTool
})

// step 4 - transactional insert array of entities
const insertResponse = await appServer.AppDataSource.getRepository(Tool).insert(prepTools)

return insertResponse
} catch (error) {
throw new InternalFlowiseError(StatusCodes.INTERNAL_SERVER_ERROR, `Error: toolsService.importTools - ${getErrorMessage(error)}`)
}
}

export default {
createTool,
deleteTool,
getAllTools,
getToolById,
updateTool
updateTool,
importTools
}
10 changes: 10 additions & 0 deletions packages/ui/src/api/exportimport.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import client from './client'

const exportAll = () => client.get('/export-import/export/all')
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@HenryHengZJ

Remember to modify this part to

const exportData = (body) => client.post('/export-import/export', body)

Body example:

{
    "tool": true,
    "chatflow": true,
    "multiagent": true
}


const importAll = (body) => client.post('/export-import/import/all', body)

export default {
exportAll,
importAll
}
Loading
Loading