\n
= {
+ Double: 'double',
+ Number: 'double',
+ String: 'string',
+ Document: 'object',
+ Array: 'array',
+ Binary: 'binData',
+ Undefined: 'undefined',
+ ObjectId: 'objectId',
+ Boolean: 'bool',
+ Date: 'date',
+ Null: 'null',
+ RegExp: 'regex',
+ DBRef: 'dbPointer',
+ BSONSymbol: 'symbol',
+ Symbol: 'symbol',
+ Code: 'javascript',
+ Int32: 'int',
+ Timestamp: 'timestamp',
+ Long: 'long',
+ Decimal128: 'decimal',
+ MinKey: 'minKey',
+ MaxKey: 'maxKey',
+};
+
+function processSchemaTypes(types: SchemaType[], settings: Settings): object {
+ if (types.length === 1) {
+ return processSchemaType(types[0], settings);
+ }
+
+ const hasComplexTypes = types.some((type: SchemaType) =>
+ ['Document', 'Array'].includes(type.name)
+ );
+
+ if (hasComplexTypes) {
+ return {
+ anyOf: types.map((t) => processSchemaType(t, settings)),
+ };
+ }
+
+ return {
+ bsonType: types.map((t) => typeToSchemaBsonTypeMap[t.name]),
+ };
+}
+
+function getRequiredFields(fields: SchemaField[]) {
+ const required = fields.filter((f) => f.probability === 1).map((f) => f.name);
+
+ return required.length ? required : undefined;
+}
+
+function processDocumentType(fields: SchemaField[], settings: Settings) {
+ const properties: { [key: string]: object } = {};
+
+ for (const field of fields) {
+ properties[field.name] = processSchemaTypes(field.types, settings);
+ }
+
+ return {
+ properties,
+ ...(settings.requireMandatoryProperties
+ ? { required: getRequiredFields(fields) }
+ : {}),
+
+ ...(settings.additionalProperties === false
+ ? { additionalProperties: false }
+ : {}),
+ };
+}
+
+function processSchemaType(type: SchemaType, settings: Settings): object {
+ const schemaBsonType = typeToSchemaBsonTypeMap[type.name];
+
+ if (!schemaBsonType) {
+ throw new Error(`Unrecognized type: "${type.name}"`);
+ }
+
+ if (type.name === 'Document') {
+ return {
+ bsonType: schemaBsonType,
+ ...processDocumentType(type.fields, settings),
+ };
+ }
+
+ if (type.name === 'Array') {
+ return {
+ bsonType: schemaBsonType,
+ items: processSchemaTypes(type.types, settings),
+ };
+ }
+
+ return { bsonType: schemaBsonType };
+}
+
+export function exportMongodbJSONSchema(
+ schema: Schema,
+ settings: Settings
+): object {
+ const fields = settings.includeId
+ ? schema.fields
+ : schema.fields.filter((f) => f.name !== '_id');
+
+ return {
+ $jsonSchema: {
+ ...processDocumentType(fields, settings),
+ },
+ };
+}
diff --git a/packages/compass-schema/src/components/schema-toolbar/schema-toolbar.tsx b/packages/compass-schema/src/components/schema-toolbar/schema-toolbar.tsx
index 38541c28b41..548b78c96d0 100644
--- a/packages/compass-schema/src/components/schema-toolbar/schema-toolbar.tsx
+++ b/packages/compass-schema/src/components/schema-toolbar/schema-toolbar.tsx
@@ -6,6 +6,8 @@ import {
WarningSummary,
css,
spacing,
+ Button,
+ Icon,
} from '@mongodb-js/compass-components';
import type { AnalysisState } from '../../constants/analysis-states';
@@ -63,6 +65,7 @@ type SchemaToolbarProps = {
localAppRegistry: AppRegistry;
onAnalyzeSchemaClicked: () => void;
onResetClicked: () => void;
+ onExportSchemaClicked: () => void;
sampleSize: number;
schemaResultId: string;
};
@@ -73,6 +76,7 @@ const SchemaToolbar: React.FunctionComponent = ({
isOutdated,
localAppRegistry,
onAnalyzeSchemaClicked,
+ onExportSchemaClicked,
onResetClicked,
sampleSize,
schemaResultId,
@@ -110,6 +114,16 @@ const SchemaToolbar: React.FunctionComponent = ({
{analysisState === ANALYSIS_STATE_COMPLETE && !isOutdated && (
+
+ }
+ data-testid="export-schema-button"
+ size="xsmall"
+ onClick={onExportSchemaClicked}
+ >
+ Export schema
+
+
{
this.geoLayers = {};
},
- getShareText() {
- if (this.state.schema !== null) {
- return `The schema definition of ${this.ns} has been copied to your clipboard in JSON format.`;
- }
- return 'Please Analyze the Schema First from the Schema Tab.';
+ openExportSchema() {
+ this.setState({
+ exportSchemaOpened: true,
+ });
},
- handleSchemaShare() {
- navigator.clipboard.writeText(
- JSON.stringify(this.state.schema, null, ' ')
- );
- ipc.call('app:show-info-dialog', 'Share Schema', this.getShareText());
+ closeExportSchema() {
+ this.setState({ exportSchemaOpened: false });
},
/**
@@ -158,6 +154,7 @@ const configureStore = (options = {}) => {
isActiveTab: false,
resultId: resultId(),
abortController: undefined,
+ exportSchemaOpened: false,
};
},
@@ -377,14 +374,6 @@ const configureStore = (options = {}) => {
store.onQueryChanged(state);
});
- /**
- * When `Share Schema as JSON` clicked in menu show a dialog message.
- */
- options.localAppRegistry.on(
- 'menu-share-schema-json',
- store.handleSchemaShare
- );
-
setLocalAppRegistry(store, options.localAppRegistry);
}
diff --git a/packages/compass/src/main/menu.ts b/packages/compass/src/main/menu.ts
index 9275c279e77..794d3b2613c 100644
--- a/packages/compass/src/main/menu.ts
+++ b/packages/compass/src/main/menu.ts
@@ -266,14 +266,6 @@ function helpSubMenu(
function collectionSubMenu(menuReadOnly: boolean): MenuItemConstructorOptions {
const subMenu = [];
- subMenu.push({
- label: '&Share Schema as JSON',
- accelerator: 'Alt+CmdOrCtrl+S',
- click() {
- ipcMain.broadcastFocused('window:menu-share-schema-json');
- },
- });
- subMenu.push(separator());
if (!preferences.getPreferences().readOnly && !menuReadOnly) {
subMenu.push({
label: '&Import Data',