Skip to content

Commit 1a1018f

Browse files
committed
wip
1 parent 16724bf commit 1a1018f

File tree

5 files changed

+103
-8
lines changed

5 files changed

+103
-8
lines changed

packages/compass-schema/src/components/export-schema-modal.tsx

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { type ChangeEvent, useCallback } from 'react';
1+
import React, { type ChangeEvent, useCallback, useMemo } from 'react';
22
import { connect } from 'react-redux';
33
import {
44
Button,
@@ -14,6 +14,8 @@ import {
1414
ErrorSummary,
1515
Label,
1616
CancelLoader,
17+
Link,
18+
SpinLoader,
1719
} from '@mongodb-js/compass-components';
1820
import { CodemirrorMultilineEditor } from '@mongodb-js/compass-editor';
1921

@@ -80,20 +82,24 @@ const ExportSchemaModal: React.FunctionComponent<{
8082
resultId?: string;
8183
exportFormat: SchemaFormat;
8284
exportedSchema?: string;
85+
blobToDownload?: Blob;
8386
onCancelSchemaExport: () => void;
8487
onChangeSchemaExportFormat: (format: SchemaFormat) => Promise<void>;
8588
onClose: () => void;
8689
onExportedSchemaCopied: () => void;
90+
onExportedSchema: () => void;
8791
}> = ({
8892
errorMessage,
8993
exportStatus,
9094
isOpen,
9195
exportFormat,
9296
exportedSchema,
97+
blobToDownload,
9398
onCancelSchemaExport,
9499
onChangeSchemaExportFormat,
95100
onClose,
96101
onExportedSchemaCopied,
102+
onExportedSchema,
97103
}) => {
98104
const onFormatOptionSelected = useCallback(
99105
(event: ChangeEvent<HTMLInputElement>) => {
@@ -104,6 +110,11 @@ const ExportSchemaModal: React.FunctionComponent<{
104110
[onChangeSchemaExportFormat]
105111
);
106112

113+
const downloadUrl = useMemo<string | undefined>(() => {
114+
if (!blobToDownload) return;
115+
return window.URL.createObjectURL(blobToDownload);
116+
}, [blobToDownload]);
117+
107118
return (
108119
<Modal open={isOpen} setOpen={onClose}>
109120
<ModalHeader title="Export Schema" />
@@ -177,14 +188,19 @@ const ExportSchemaModal: React.FunctionComponent<{
177188
<Button onClick={onClose} variant="default">
178189
Cancel
179190
</Button>
180-
<Button
181-
onClick={() => {
182-
/* TODO(COMPASS-8704): download and track with trackSchemaExported */
183-
}}
184-
variant="primary"
191+
<Link
192+
download="export.json" // TODO
193+
href={downloadUrl}
194+
onClick={onExportedSchema}
185195
>
186-
Export
187-
</Button>
196+
<Button
197+
variant="primary"
198+
isLoading={!downloadUrl}
199+
loadingIndicator={<SpinLoader />}
200+
>
201+
Export
202+
</Button>
203+
</Link>
188204
</ModalFooter>
189205
</Modal>
190206
);
@@ -197,9 +213,11 @@ export default connect(
197213
exportFormat: state.schemaExport.exportFormat,
198214
isOpen: state.schemaExport.isOpen,
199215
exportedSchema: state.schemaExport.exportedSchema,
216+
blobToDownload: state.schemaExport.blobToDownload,
200217
}),
201218
{
202219
onExportedSchemaCopied: trackSchemaExported,
220+
onExportedSchema: trackSchemaExported,
203221
onCancelSchemaExport: cancelExportSchema,
204222
onChangeSchemaExportFormat: changeExportSchemaFormat,
205223
onClose: closeExportSchema,

packages/compass-schema/src/stores/schema-export-reducer.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export type SchemaExportState = {
2727
exportFormat: SchemaFormat;
2828
errorMessage?: string;
2929
exportStatus: ExportStatus;
30+
blobToDownload?: Blob;
3031
};
3132

3233
const defaultSchemaFormat: SchemaFormat = 'standardJSON';
@@ -52,6 +53,7 @@ export const enum SchemaExportActions {
5253
changeExportSchemaFormatComplete = 'schema-service/schema-export/changeExportSchemaFormatComplete',
5354
changeExportSchemaFormatError = 'schema-service/schema-export/changeExportSchemaFormatError',
5455
cancelExportSchema = 'schema-service/schema-export/cancelExportSchema',
56+
schemaDownloadReady = 'schema-service/schema-export/schemaDownloadReady',
5557
}
5658

5759
export type OpenExportSchemaAction = {
@@ -105,6 +107,11 @@ export type ChangeExportSchemaFormatCompletedAction = {
105107
exportedSchema: string;
106108
};
107109

110+
export type schemaDownloadReadyAction = {
111+
type: SchemaExportActions.schemaDownloadReady;
112+
blob: Blob;
113+
};
114+
108115
export const cancelExportSchema = (): SchemaThunkAction<
109116
void,
110117
CancelExportSchemaAction
@@ -201,6 +208,37 @@ export const trackSchemaExported = (): SchemaThunkAction<void> => {
201208
};
202209
};
203210

211+
const prepareDownload = (): SchemaThunkAction<void> => {
212+
return (dispatch, getState, { track, connectionInfoRef }) => {
213+
let stage = 'initial';
214+
const { exportedSchema, exportFormat } = getState().schemaExport;
215+
216+
try {
217+
stage = 'stringify';
218+
// const stringified = JSON.stringify({ abc: 1 }, null, 2);
219+
220+
stage = 'blob';
221+
// TODO: schema is already stringified. are edge cases handled?
222+
// TODO: no schema case
223+
const blob = new Blob([exportedSchema || ''], {
224+
type: 'application/json',
225+
});
226+
dispatch({ type: SchemaExportActions.schemaDownloadReady, blob });
227+
} catch (error) {
228+
track(
229+
'Schema Export Download Failed',
230+
{
231+
has_schema: !!exportedSchema,
232+
schema_length: exportedSchema?.length || 0,
233+
format: exportFormat,
234+
stage,
235+
},
236+
connectionInfoRef.current
237+
);
238+
}
239+
};
240+
};
241+
204242
export const changeExportSchemaFormat = (
205243
exportFormat: SchemaFormat
206244
): SchemaThunkAction<
@@ -284,6 +322,7 @@ export const changeExportSchemaFormat = (
284322
type: SchemaExportActions.changeExportSchemaFormatComplete,
285323
exportedSchema,
286324
});
325+
dispatch(prepareDownload());
287326
};
288327
};
289328

@@ -404,6 +443,18 @@ export const schemaExportReducer: Reducer<SchemaExportState, Action> = (
404443
};
405444
}
406445

446+
if (
447+
isAction<schemaDownloadReadyAction>(
448+
action,
449+
SchemaExportActions.schemaDownloadReady
450+
)
451+
) {
452+
return {
453+
...state,
454+
blobToDownload: action.blob,
455+
};
456+
}
457+
407458
return state;
408459
};
409460

packages/compass-telemetry/src/telemetry-events.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2024,6 +2024,27 @@ type SchemaExportedEvent = ConnectionScopedEvent<{
20242024
};
20252025
}>;
20262026

2027+
/**
2028+
* This event is fired when user shares the schema.
2029+
*
2030+
* @category Schema
2031+
*/
2032+
type SchemaExportFailedEvent = ConnectionScopedEvent<{
2033+
name: 'Schema Export Download Failed';
2034+
payload: {
2035+
/**
2036+
* Indicates whether the schema was analyzed before sharing.
2037+
*/
2038+
has_schema: boolean;
2039+
2040+
schema_length: number;
2041+
2042+
format: 'standardJSON' | 'mongoDBJSON' | 'extendedJSON' | 'legacyJSON';
2043+
2044+
stage: string;
2045+
};
2046+
}>;
2047+
20272048
/**
20282049
* This event is fired when a user clicks to show the details of an operation.
20292050
*
@@ -2729,6 +2750,7 @@ export type TelemetryEvent =
27292750
| SchemaAnalysisCancelledEvent
27302751
| SchemaAnalyzedEvent
27312752
| SchemaExportedEvent
2753+
| SchemaExportFailedEvent
27322754
| SchemaValidationAddedEvent
27332755
| SchemaValidationEditedEvent
27342756
| SchemaValidationUpdatedEvent

packages/compass-web/sandbox/index.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ const App = () => {
127127
onActiveWorkspaceTabChange={updateCurrentTab}
128128
initialWorkspace={currentTab ?? undefined}
129129
initialPreferences={{
130+
enableExportSchema: true,
130131
enablePerformanceAdvisorBanner: isAtlas,
131132
enableAtlasSearchIndexes: !isAtlas,
132133
maximumNumberOfActiveConnections: isAtlas ? 10 : undefined,

packages/compass/src/app/utils/csp.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ const defaultCSP = {
6262
],
6363
'script-src': [
6464
"'self'",
65+
'blob:',
6566
'https://app.intercom.io',
6667
'https://widget.intercom.io',
6768
'https://js.intercomcdn.com',
@@ -100,6 +101,8 @@ function injectCSP() {
100101
return `${name} ${values.join(' ')}`;
101102
})
102103
.join('; ') + ';';
104+
105+
console.log({ cspContent });
103106
metaCSP.setAttribute('http-equiv', 'Content-Security-Policy');
104107
metaCSP.setAttribute('content', cspContent);
105108
document.head.prepend(metaCSP);

0 commit comments

Comments
 (0)