Skip to content

Commit 3cf163e

Browse files
committed
implement raw schema confirmation screen
1 parent 42660a7 commit 3cf163e

File tree

6 files changed

+194
-58
lines changed

6 files changed

+194
-58
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import React from 'react';
22

33
// TODO: More to come from CLOUDP-333853, CLOUDP-333854
4-
const FakerSchemaEditor = () => {
4+
const FakerSchemaEditorScreen = () => {
55
return (
66
<div data-testid="faker-schema-editor">
77
Schema Editor Content Placeholder
88
</div>
99
);
1010
};
1111

12-
export default FakerSchemaEditor;
12+
export default FakerSchemaEditorScreen;

packages/compass-collection/src/components/mock-data-generator-modal/mock-data-generator-modal.spec.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,8 @@ describe('MockDataGeneratorModal', () => {
222222
expect(screen.queryByTestId('faker-schema-editor')).to.not.exist;
223223
});
224224

225-
// todo: assert a user-friendly error is displayed (CLOUDP-333852)
225+
expect(screen.getByText('LLM Request failed. Please confirm again.')).to
226+
.exist;
226227
});
227228

228229
// todo: assert that closing then re-opening the modal after an LLM err removes the err message

packages/compass-collection/src/components/mock-data-generator-modal/mock-data-generator-modal.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ import {
2121
generateFakerMappings,
2222
mockDataGeneratorPreviousButtonClicked,
2323
} from '../../modules/collection-tab';
24-
import { default as SchemaConfirmationScreen } from './raw-schema-confirmation';
25-
import FakerSchemaEditor from './faker-schema-editor';
24+
import RawSchemaConfirmationScreen from './raw-schema-confirmation-screen';
25+
import FakerSchemaEditor from './faker-schema-editor-screen';
2626

2727
const footerStyles = css`
2828
flex-direction: row;
@@ -65,7 +65,7 @@ const MockDataGeneratorModal = ({
6565
let stepContent: React.ReactNode;
6666

6767
if (currentStep === MockDataGeneratorStep.SCHEMA_CONFIRMATION) {
68-
stepContent = <SchemaConfirmationScreen />;
68+
stepContent = <RawSchemaConfirmationScreen />;
6969
}
7070

7171
if (currentStep === MockDataGeneratorStep.SCHEMA_EDITOR) {
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
import React from 'react';
2+
import { connect } from 'react-redux';
3+
4+
import {
5+
css,
6+
palette,
7+
spacing,
8+
Banner,
9+
BannerVariant,
10+
Code,
11+
Body,
12+
Subtitle,
13+
} from '@mongodb-js/compass-components';
14+
15+
import { usePreference } from 'compass-preferences-model/provider';
16+
import toUserFriendlyFieldInfo from './to-user-friendly-field-info';
17+
import type { CollectionState } from '../../modules/collection-tab';
18+
import type { SchemaAnalysisState } from '../../schema-analysis-types';
19+
import type { MockDataGeneratorState } from './types';
20+
21+
interface RawSchemaConfirmationScreenProps {
22+
schemaAnalysis: SchemaAnalysisState;
23+
namespace: string;
24+
fakerSchemaGenerationStatus: MockDataGeneratorState['status'];
25+
}
26+
27+
const namespaceStyles = css({
28+
marginTop: spacing[100],
29+
marginBottom: spacing[500],
30+
});
31+
32+
const headingStyles = css({
33+
marginBottom: spacing[100],
34+
});
35+
36+
const descriptionStyles = css({
37+
marginBottom: spacing[300],
38+
});
39+
40+
const codeStyles = css({
41+
maxHeight: '30vh',
42+
});
43+
44+
const errorBannerStyles = css({
45+
marginTop: spacing[400],
46+
});
47+
48+
const errorBannerTextStyles = css({
49+
color: palette.red.dark2,
50+
});
51+
52+
// Note: Currently a placeholder. The final contents will be addressed by CLOUDP-333852
53+
const RawSchemaConfirmationScreen = (
54+
props: RawSchemaConfirmationScreenProps
55+
) => {
56+
let enableSampleDocumentPassing = usePreference(
57+
'enableGenAISampleDocumentPassing'
58+
);
59+
60+
const subtitleText = enableSampleDocumentPassing
61+
? 'Sample Documents Collected'
62+
: 'Document Schema Identified';
63+
64+
const descriptionText = enableSampleDocumentPassing
65+
? 'A sample of document values from your collection will be sent to an LLM for processing.'
66+
: 'We have identified the following schema from your documents. This schema will be sent to an LLM for processing.';
67+
68+
// todo: should establish if unfinished schema analysis edge case should be handled within the modal or
69+
// from the button opening the modal
70+
const schemaAnalysisIncompletePlaceholder = (
71+
<Body>Schema analysis has yet to complete successfully.</Body>
72+
);
73+
74+
return (
75+
<div data-testid="raw-schema-confirmation">
76+
{props.schemaAnalysis.status === 'complete' ? (
77+
<>
78+
<Body className={namespaceStyles}>{props.namespace}</Body>
79+
<Body
80+
as="h2"
81+
className={headingStyles}
82+
baseFontSize={16}
83+
weight="medium"
84+
>
85+
{subtitleText}
86+
</Body>
87+
<Body className={descriptionStyles}>{descriptionText}</Body>
88+
<Code language="javascript" copyable={false} className={codeStyles}>
89+
{enableSampleDocumentPassing
90+
? JSON.stringify(props.schemaAnalysis.sampleDocument, null, 4)
91+
: JSON.stringify(
92+
toUserFriendlyFieldInfo(props.schemaAnalysis.processedSchema),
93+
null,
94+
4
95+
)}
96+
</Code>
97+
{props.fakerSchemaGenerationStatus === 'error' && (
98+
<Banner
99+
variant={BannerVariant.Danger}
100+
className={errorBannerStyles}
101+
>
102+
<Body className={errorBannerTextStyles}>
103+
LLM Request failed. Please confirm again.
104+
</Body>
105+
</Banner>
106+
)}
107+
</>
108+
) : (
109+
schemaAnalysisIncompletePlaceholder
110+
)}
111+
</div>
112+
);
113+
};
114+
115+
const mapStateToProps = (state: CollectionState) => {
116+
const schemaAnalysis = state.schemaAnalysis;
117+
const fakerSchemaGenerationStatus = state.fakerSchemaGeneration.status;
118+
119+
return {
120+
schemaAnalysis,
121+
namespace: state.namespace,
122+
fakerSchemaGenerationStatus,
123+
};
124+
};
125+
126+
const ConnectedRawSchemaConfirmationScreen = connect(
127+
mapStateToProps,
128+
{}
129+
)(RawSchemaConfirmationScreen);
130+
131+
export default ConnectedRawSchemaConfirmationScreen;

packages/compass-collection/src/components/mock-data-generator-modal/raw-schema-confirmation.tsx

Lines changed: 0 additions & 52 deletions
This file was deleted.
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { processSchema } from '../../transform-schema-to-field-info';
2+
import type { FieldInfo } from '../../schema-analysis-types';
3+
4+
type UserFriendlyFieldInfoNode =
5+
| { [field: string]: UserFriendlyFieldInfoNode }
6+
| FieldInfo['type'];
7+
export type UserFriendlyFieldInfoTree = {
8+
[field: string]: UserFriendlyFieldInfoNode;
9+
};
10+
11+
/**
12+
* Usage is for display purposes only. The result is derived from the work of `processSchema`,
13+
* instead of directly derived from the `Schema` type from `mongodb-schema`, ensuring that what
14+
* the user sees in `RawSchemaConfirmationScreen` is constrained to what the LLM processes.
15+
*/
16+
export default function toUserFriendlyFieldInfo(
17+
input: ReturnType<typeof processSchema>
18+
): UserFriendlyFieldInfoTree {
19+
// ensure parent nodes are created before their children
20+
const sortedFieldPaths = Object.keys(input).sort(
21+
(f1, f2) => countSeparators(f1) - countSeparators(f2)
22+
);
23+
24+
// Assumes "." and "[]" placement is valid
25+
const result: UserFriendlyFieldInfoTree = {};
26+
for (const path of sortedFieldPaths) {
27+
const fieldParts = path.split('.');
28+
29+
let node = result;
30+
for (let i = 0; i < fieldParts.length; i++) {
31+
const part = fieldParts[i];
32+
33+
if (i === fieldParts.length - 1) {
34+
node[part] = input[path].type;
35+
break;
36+
}
37+
38+
if (typeof node[part] !== 'object' || node[part] === null) {
39+
node[part] = {};
40+
}
41+
node = node[part];
42+
}
43+
}
44+
return result;
45+
}
46+
47+
/**
48+
* note: assumes `processSchema` constructs field paths in this manner:
49+
* - 1+ "[]" can only appear at the end of field paths
50+
* - field keys do not contain "." or "[]"
51+
*/
52+
function countSeparators(input: string): number {
53+
const c1 = input.split('.').length - 1;
54+
const c2 = input.split('[]').length - 1;
55+
return c1 + c2;
56+
}

0 commit comments

Comments
 (0)