Skip to content

Commit 75d54af

Browse files
committed
Merge branch 'master' into bug/fetch-name-attribute
2 parents f74f16f + e718dd0 commit 75d54af

File tree

21 files changed

+947
-508
lines changed

21 files changed

+947
-508
lines changed

src/main/frontend/app/routes/editor/editor.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -248,9 +248,7 @@ export default function CodeEditor() {
248248
const contentType = response.headers.get('content-type')
249249
if (contentType && contentType.includes('application/json')) {
250250
const errorData = await response.json()
251-
toast.error(
252-
`Error saving configuration: ${errorData.title || errorData.error}\nDetails: ${errorData.details}`,
253-
)
251+
toast.error(`Error saving configuration: ${errorData.error}\nDetails: ${errorData.message}`)
254252
console.error('Something went wrong saving the configuration: ', errorData)
255253
} else {
256254
toast.error(`Error saving configuration. HTTP status: ${response.status}`)

src/main/frontend/app/routes/studio/canvas/flow.tsx

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ import { exportFlowToXml } from '~/routes/studio/flow-to-xml-parser'
2828
import useNodeContextStore from '~/stores/node-context-store'
2929
import CreateNodeModal from '~/components/flow/create-node-modal'
3030
import { useProjectStore } from "~/stores/project-store";
31+
import { toast, ToastContainer } from 'react-toastify'
32+
import { useTheme } from '~/hooks/use-theme'
3133

3234
export type FlowNode = FrankNode | ExitNode | StickyNote | GroupNode | Node
3335

@@ -45,6 +47,7 @@ const selector = (state: FlowState) => ({
4547
})
4648

4749
function FlowCanvas({ showNodeContextMenu }: Readonly<{ showNodeContextMenu: (b: boolean) => void }>) {
50+
const theme = useTheme()
4851
const [loading, setLoading] = useState(false)
4952
const { isEditing, setIsEditing, setParentId } = useNodeContextStore(
5053
useShallow((s) => ({
@@ -500,6 +503,36 @@ function FlowCanvas({ showNodeContextMenu }: Readonly<{ showNodeContextMenu: (b:
500503
URL.revokeObjectURL(url)
501504
}
502505

506+
const saveFlow = async () => {
507+
const flowData = reactFlow.toObject()
508+
const activeTabName = useTabStore.getState().activeTab
509+
const configName = useTabStore.getState().getTab(activeTabName)?.configurationName
510+
if (!configName) return
511+
512+
const xmlString = exportFlowToXml(flowData, activeTabName)
513+
514+
try {
515+
if (!project) return
516+
const response = await fetch(
517+
`/projects/${encodeURIComponent(project.name)}/${encodeURIComponent(configName)}/adapters/${encodeURIComponent(activeTabName)}`,
518+
{
519+
method: 'PUT',
520+
headers: { 'Content-Type': 'application/json' },
521+
body: JSON.stringify({ adapterXml: xmlString }),
522+
}
523+
)
524+
525+
if (!response.ok) {
526+
throw new Error(`HTTP error! Status: ${response.status}`)
527+
}
528+
529+
toast.success('Flow saved successfully!')
530+
} catch (error) {
531+
console.error('Failed to save XML:', error)
532+
toast.error(`Failed to save XML: ${error}`)
533+
}
534+
}
535+
503536
return (
504537
<div
505538
className="relative h-full w-full"
@@ -535,7 +568,16 @@ function FlowCanvas({ showNodeContextMenu }: Readonly<{ showNodeContextMenu: (b:
535568
>
536569
<Controls position="top-left" style={{ color: '#000' }}></Controls>
537570
<Background variant={BackgroundVariant.Dots} size={3} gap={100}></Background>
571+
<Panel position="top-center">
572+
<button
573+
className="border-border hover:bg-hover bg-background border p-2 hover:cursor-pointer"
574+
onClick={saveFlow}
575+
>
576+
Save XML
577+
</button>
578+
</Panel>
538579
</ReactFlow>
580+
<ToastContainer position="bottom-right" theme={theme} closeOnClick={true} />
539581
<CreateNodeModal
540582
isOpen={showModal}
541583
onClose={() => setShowModal(false)}

src/main/frontend/app/routes/studio/flow-to-xml-parser.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,15 +61,14 @@ export function exportFlowToXml(json: ReactFlowJson, adaptername: string): strin
6161

6262
const exitsXml = exitNodes.length > 0 ? ` <Exits>\n${generateExitsXml(exitNodes)}\n </Exits>` : ''
6363

64-
return `<Configuration>
64+
return `
6565
<Adapter name="${adaptername}" description="Auto-generated from React Flow JSON">
6666
${receivers.join('\n')}
6767
<Pipeline>
6868
${exitsXml}
6969
${pipelineParts.join('\n')}
7070
</Pipeline>
71-
</Adapter>
72-
</Configuration>`
71+
</Adapter>`
7372
}
7473

7574
function buildEdgeMaps(edges: Edge[]) {

src/main/frontend/app/routes/studio/studio.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,12 @@ import { SidebarSide } from '~/components/sidebars-layout/sidebar-layout-store'
1111
import SidebarLayout from '~/components/sidebars-layout/sidebar-layout'
1212
import useTabStore from '~/stores/tab-store'
1313
import { useShallow } from 'zustand/react/shallow'
14+
import { ToastContainer } from 'react-toastify'
15+
import { useTheme } from '~/hooks/use-theme'
1416

1517
export default function Studio() {
1618
const [showNodeContext, setShowNodeContext] = useState(false)
19+
const theme = useTheme()
1720
const nodeId = useNodeContextStore((state) => state.nodeId)
1821

1922
const { activeTab } = useTabStore(
@@ -39,7 +42,10 @@ export default function Studio() {
3942
<div className="border-b-border bg-background flex h-12 items-center border-b p-4">Path: {activeTab}</div>
4043

4144
{activeTab ? (
42-
<Flow showNodeContextMenu={setShowNodeContext} />
45+
<>
46+
<Flow showNodeContextMenu={setShowNodeContext} />
47+
<ToastContainer position="bottom-right" theme={theme} closeOnClick={true} />
48+
</>
4349
) : (
4450
<div className="text-muted-foreground flex h-full flex-col items-center justify-center p-8 text-center">
4551
<div className="border-border bg-background/40 max-w-md rounded-2xl border border-dashed p-10 shadow-inner backdrop-blur-sm">

src/main/frontend/app/routes/studio/xml-to-json-parser.ts

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,11 @@ export async function getAdapterNamesFromConfiguration(projectName: string, file
5151
})
5252
}
5353

54-
export async function getAdapterFromConfiguration(projectname: string, filename: string, adapterName: string): Promise<Element | null> {
54+
export async function getAdapterFromConfiguration(
55+
projectname: string,
56+
filename: string,
57+
adapterName: string,
58+
): Promise<Element | null> {
5559
const xmlString = await getXmlString(projectname, filename)
5660
const parser = new DOMParser()
5761
const xmlDoc = parser.parseFromString(xmlString, 'text/xml')
@@ -216,10 +220,20 @@ function convertAdapterToFlowNodes(adapter: any): FlowNode[] {
216220
const forwardElements = [...element.querySelectorAll('Forward')]
217221
const sourceHandles =
218222
forwardElements.length > 0
219-
? forwardElements.map((forward, index) => ({
220-
type: forward.getAttribute('name') || `forward${index + 1}`,
221-
index: index + 1,
222-
}))
223+
? forwardElements.map((forward, index) => {
224+
const path = forward.getAttribute('path') || ''
225+
const loweredPath = path.toLowerCase()
226+
// Only check for bad flows/forwards right now, could later be updated to also include exceptions and custom handles
227+
const type =
228+
loweredPath.includes('error') || loweredPath.includes('bad') || loweredPath.includes('fail')
229+
? 'failure'
230+
: 'success'
231+
232+
return {
233+
type,
234+
index: index + 1,
235+
}
236+
})
223237
: [
224238
{
225239
type: 'success',
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package org.frankframework.flow.configuration;
2+
3+
public class AdapterNotFoundException extends RuntimeException {
4+
public AdapterNotFoundException(String message){
5+
super(message);
6+
}
7+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package org.frankframework.flow.configuration;
2+
3+
public record AdapterUpdateDTO(String adapterXml) {
4+
}

src/main/java/org/frankframework/flow/exception/GlobalExceptionHandler.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package org.frankframework.flow.exception;
22

3+
import org.frankframework.flow.configuration.AdapterNotFoundException;
34
import org.frankframework.flow.configuration.ConfigurationNotFoundException;
5+
import org.frankframework.flow.project.InvalidXmlContentException;
46
import org.frankframework.flow.project.ProjectNotFoundException;
57
import org.frankframework.flow.projectsettings.InvalidFilterTypeException;
68
import org.springframework.http.HttpStatus;
@@ -38,4 +40,17 @@ public ResponseEntity<ErrorResponseDTO> handleInvalidFilter(InvalidFilterTypeExc
3840
return ResponseEntity.badRequest()
3941
.body(new ErrorResponseDTO("InvalidFilterType", ex.getMessage()));
4042
}
43+
44+
@ExceptionHandler(InvalidXmlContentException.class)
45+
public ResponseEntity<ErrorResponseDTO> handleInvalidXml(InvalidXmlContentException ex) {
46+
return ResponseEntity.badRequest()
47+
.body(new ErrorResponseDTO("InvalidXmlContent", ex.getMessage()));
48+
}
49+
50+
@ExceptionHandler(AdapterNotFoundException.class)
51+
public ResponseEntity<ErrorResponseDTO> handleAdapterNotFound(AdapterNotFoundException ex) {
52+
return ResponseEntity.status(HttpStatus.NOT_FOUND)
53+
.body(new ErrorResponseDTO("AdapterNotFound", ex.getMessage()));
54+
}
55+
4156
}

src/main/java/org/frankframework/flow/project/ErrorDTO.java

Lines changed: 0 additions & 3 deletions
This file was deleted.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package org.frankframework.flow.project;
2+
3+
public class InvalidXmlContentException extends RuntimeException {
4+
public InvalidXmlContentException(String message) {
5+
super(message);
6+
}
7+
}

0 commit comments

Comments
 (0)