@@ -8,8 +8,15 @@ import {
88} from '@patternfly/react-core' ;
99import { MenuToggleElement } from '@patternfly/react-core/dist/esm/components/MenuToggle/MenuToggle' ;
1010import { EllipsisVIcon } from '@patternfly/react-icons' ;
11+ import TOML from 'smol-toml' ;
1112
13+ // The hosted UI exports JSON, while the Cockpit plugin exports TOML.
14+ // Because the blueprint formats differ, using the 'backendApi'
15+ // abstraction would be misleading. Import and handle each environment
16+ // separately.
1217import { selectSelectedBlueprintId } from '../../store/BlueprintSlice' ;
18+ import { useLazyExportBlueprintCockpitQuery } from '../../store/cockpit/cockpitApi' ;
19+ import type { Blueprint as CockpitExportResponse } from '../../store/cockpit/composerCloudApi' ;
1320import { useAppSelector } from '../../store/hooks' ;
1421import {
1522 BlueprintExportResponse ,
@@ -30,6 +37,7 @@ export const BlueprintActionsMenu: React.FunctionComponent<
3037 } ;
3138
3239 const [ trigger ] = useLazyExportBlueprintQuery ( ) ;
40+ const [ cockpitTrigger ] = useLazyExportBlueprintCockpitQuery ( ) ;
3341 const selectedBlueprintId = useAppSelector ( selectSelectedBlueprintId ) ;
3442 if ( selectedBlueprintId === undefined ) {
3543 return null ;
@@ -41,6 +49,15 @@ export const BlueprintActionsMenu: React.FunctionComponent<
4149 handleExportBlueprint ( response . name , response ) ;
4250 } ) ;
4351 } ;
52+
53+ const handleCockpitClick = ( ) => {
54+ cockpitTrigger ( { id : selectedBlueprintId } )
55+ . unwrap ( )
56+ . then ( ( response : CockpitExportResponse ) => {
57+ handleExportBlueprint ( response . name , response ) ;
58+ } ) ;
59+ } ;
60+
4461 return (
4562 < Dropdown
4663 isOpen = { showBlueprintActionsMenu }
@@ -62,8 +79,10 @@ export const BlueprintActionsMenu: React.FunctionComponent<
6279 ) }
6380 >
6481 < DropdownList >
65- < DropdownItem onClick = { handleClick } >
66- Download blueprint (.json)
82+ < DropdownItem
83+ onClick = { process . env . IS_ON_PREMISE ? handleCockpitClick : handleClick }
84+ >
85+ Download blueprint ({ process . env . IS_ON_PREMISE ? '.toml' : '.json' } )
6786 </ DropdownItem >
6887 < DropdownItem onClick = { ( ) => setShowDeleteModal ( true ) } >
6988 Delete blueprint
@@ -75,15 +94,28 @@ export const BlueprintActionsMenu: React.FunctionComponent<
7594
7695async function handleExportBlueprint (
7796 blueprintName : string ,
78- blueprint : BlueprintExportResponse ,
97+ blueprint : BlueprintExportResponse | CockpitExportResponse ,
7998) {
80- const jsonData = JSON . stringify ( blueprint , null , 2 ) ;
81- const blob = new Blob ( [ jsonData ] , { type : 'application/json' } ) ;
99+ const data = process . env . IS_ON_PREMISE
100+ ? TOML . stringify ( blueprint )
101+ : JSON . stringify ( blueprint , null , 2 ) ;
102+ const mime = process . env . IS_ON_PREMISE
103+ ? 'application/octet-stream'
104+ : 'application/json' ;
105+ const blob = new Blob ( [ data ] , { type : mime } ) ;
82106
83107 const url = URL . createObjectURL ( blob ) ;
84- const link = document . createElement ( 'a' ) ;
108+ // In cockpit we're running in an iframe, the current content-security policy
109+ // (set in cockpit/public/manifest.json) only allows resources from the same origin as the
110+ // document (which is unique to the iframe). So create the element in the parent document.
111+ const link = process . env . IS_ON_PREMISE
112+ ? window . parent . document . createElement ( 'a' )
113+ : document . createElement ( 'a' ) ;
85114 link . href = url ;
86- link . download = blueprintName . replace ( / \s / g, '_' ) . toLowerCase ( ) + '.json' ;
115+ link . download =
116+ blueprintName . replace ( / \s / g, '_' ) . toLowerCase ( ) + process . env . IS_ON_PREMISE
117+ ? '.toml'
118+ : '.json' ;
87119 link . click ( ) ;
88120 URL . revokeObjectURL ( url ) ;
89121}
0 commit comments