diff --git a/.changeset/sour-turkeys-roll.md b/.changeset/sour-turkeys-roll.md new file mode 100644 index 00000000000..dd4bd54a260 --- /dev/null +++ b/.changeset/sour-turkeys-roll.md @@ -0,0 +1,5 @@ +--- +'@finos/legend-query-builder': patch +--- + +Add milestoning setup in Query Option diff --git a/.changeset/stupid-panthers-search.md b/.changeset/stupid-panthers-search.md new file mode 100644 index 00000000000..21429dd3eca --- /dev/null +++ b/.changeset/stupid-panthers-search.md @@ -0,0 +1,3 @@ +--- +'@finos/legend-application-query-bootstrap': patch +--- diff --git a/packages/legend-application-query-bootstrap/style/index.scss b/packages/legend-application-query-bootstrap/style/index.scss index cff822ee84d..3bbc8b230b5 100644 --- a/packages/legend-application-query-bootstrap/style/index.scss +++ b/packages/legend-application-query-bootstrap/style/index.scss @@ -1335,6 +1335,10 @@ color: var(--color-legacylight-light-dark-grey-200); } + .query-builder__projection__result-modifier-prompt__divider { + color: var(--color-legacylight-light-dark-grey-200); + } + .query-builder-property-expression-badge__content { color: var(--color-legacylight-dark-grey-200); } @@ -1840,4 +1844,15 @@ } } } + + .query-builder__projection__modal { + ::-webkit-scrollbar-thumb { + height: 5rem; + background: var(--color-legacylight-light-grey-300); + outline-offset: -0.2rem; + border-radius: 0.4rem; + outline-color: var(--color-legacylight-light-grey-100); + border-color: var(--color-legacylight-light-grey-100); + } + } } diff --git a/packages/legend-query-builder/src/components/QueryBuilderSideBar.tsx b/packages/legend-query-builder/src/components/QueryBuilderSideBar.tsx index f213fd2931d..fd0784683d7 100644 --- a/packages/legend-query-builder/src/components/QueryBuilderSideBar.tsx +++ b/packages/legend-query-builder/src/components/QueryBuilderSideBar.tsx @@ -47,7 +47,6 @@ import { getPackageableElementOptionFormatter, type PackageableElementOption, } from '@finos/legend-lego/graph-editor'; -import { MilestoningParametersEditor } from './explorer/QueryBuilderMilestoningEditor.js'; import type { QueryBuilder_LegendApplicationPlugin_Extension } from '../stores/QueryBuilder_LegendApplicationPlugin_Extension.js'; export const getParameterValue = ( @@ -120,7 +119,6 @@ export const QueryBuilderClassSelector = observer( noMatchMessage?: string | undefined; }) => { const { queryBuilderState, classes, onClassChange, noMatchMessage } = props; - const milestoningState = queryBuilderState.milestoningState; const applicationStore = useApplicationStore(); // class @@ -151,10 +149,6 @@ export const QueryBuilderClassSelector = observer( onClassChange?.(val.value); }; - // milestoning - const showMilestoningEditor = (): void => - milestoningState.setShowMilestoningEditor(true); - return (
@@ -192,22 +186,6 @@ export const QueryBuilderClassSelector = observer( .TEMPORARY__isLightColorThemeEnabled, })} /> - {queryBuilderState.isQuerySupported && ( - - )} - {milestoningState.isMilestonedQuery && ( - - )}
); diff --git a/packages/legend-query-builder/src/components/__tests__/QueryBuilderFetchStructure.test.tsx b/packages/legend-query-builder/src/components/__tests__/QueryBuilderFetchStructure.test.tsx index 388b59c8b7e..bf68d767075 100644 --- a/packages/legend-query-builder/src/components/__tests__/QueryBuilderFetchStructure.test.tsx +++ b/packages/legend-query-builder/src/components/__tests__/QueryBuilderFetchStructure.test.tsx @@ -270,9 +270,7 @@ test( const queryBuilder = await waitFor(() => renderResult.getByTestId(QUERY_BUILDER_TEST_ID.QUERY_BUILDER), ); - fireEvent.click( - getByTitle(queryBuilder, 'Configure result set modifiers...'), - ); + fireEvent.click(getByTitle(queryBuilder, 'Configure Query Options...')); const modal = await waitFor(() => renderResult.getByRole('dialog')); await waitFor(() => fireEvent.click(getByText(modal, 'Add Value'))); @@ -315,7 +313,7 @@ test( fireEvent.click(getByTitle(queryBuilder, 'Clear all projection columns')); const closeModal = await waitFor(() => renderResult.getByRole('dialog')); fireEvent.click(getByText(closeModal, 'Proceed')); - await waitFor(() => renderResult.getByText('Query Options')); + await waitFor(() => renderResult.getByText('Set Query Options')); const queryBuilderResultModifierPrompt = await waitFor(() => renderResult.getByTestId( QUERY_BUILDER_TEST_ID.QUERY_BUILDER_TDS_RESULT_MODIFIER_PROMPT, diff --git a/packages/legend-query-builder/src/components/__tests__/QueryBuilderMilestoning.test.tsx b/packages/legend-query-builder/src/components/__tests__/QueryBuilderMilestoning.test.tsx index 5c2f1c71bf9..7b9673cb208 100644 --- a/packages/legend-query-builder/src/components/__tests__/QueryBuilderMilestoning.test.tsx +++ b/packages/legend-query-builder/src/components/__tests__/QueryBuilderMilestoning.test.tsx @@ -56,6 +56,7 @@ import type { Entity } from '@finos/legend-storage'; import { TEST__setUpQueryBuilder } from '../__test-utils__/QueryBuilderComponentTestUtils.js'; import { QUERY_BUILDER_TEST_ID } from '../../__lib__/QueryBuilderTesting.js'; import { TEST_DATA__ModelCoverageAnalysisResult_Milestoning } from '../../stores/__tests__/TEST_DATA__ModelCoverageAnalysisResult.js'; +import { getParameterNameInput } from './QueryBuilderParametersPanel.test.js'; type QueryComparisonTestCase = [ string, @@ -465,6 +466,140 @@ test( }, ); +test( + integrationTest( + 'Query builder result modifier panel displays milestoning dates', + ), + async () => { + const { renderResult, queryBuilderState } = await TEST__setUpQueryBuilder( + TEST_MilestoningModel, + stub_RawLambda(), + 'my::map', + 'my::runtime', + TEST_DATA__ModelCoverageAnalysisResult_Milestoning, + ); + const _personClass = + queryBuilderState.graphManagerState.graph.getClass('my::Person'); + await act(async () => { + queryBuilderState.changeClass(_personClass); + }); + const queryBuilderSetup = await waitFor(() => + renderResult.getByTestId(QUERY_BUILDER_TEST_ID.QUERY_BUILDER_SETUP), + ); + await waitFor(() => getAllByText(queryBuilderSetup, 'Person')); + + const resultModifierPromptPanel = await waitFor(() => + renderResult.getByTestId( + QUERY_BUILDER_TEST_ID.QUERY_BUILDER_TDS_RESULT_MODIFIER_PROMPT, + ), + ); + await waitFor(() => + getAllByText(resultModifierPromptPanel, 'Business Date'), + ); + await waitFor(() => + getAllByText(resultModifierPromptPanel, 'businessDate'), + ); + + const queryOptionsButton = await waitFor(() => + renderResult.getByRole('button', { name: 'Query Options' }), + ); + fireEvent.click(queryOptionsButton); + const allVersionsToggle = await renderResult.findByText( + 'Query All Milestoned Versions of the Root Class', + ); + fireEvent.click(allVersionsToggle); + const applyButton = (await renderResult.findByRole('button', { + name: 'Apply', + })) as HTMLButtonElement; + await waitFor(() => fireEvent.click(applyButton)); + await waitFor(() => + getAllByText(resultModifierPromptPanel, 'All Versions'), + ); + await waitFor(() => getAllByText(resultModifierPromptPanel, 'Yes')); + fireEvent.click(queryOptionsButton); + const allVersionInRangeToggle = await renderResult.findByText( + 'Optionally apply a date range to get All Versions for', + ); + fireEvent.click(allVersionInRangeToggle); + fireEvent.click(renderResult.getByRole('button', { name: 'Apply' })); + await waitFor(() => + getAllByText(resultModifierPromptPanel, '(startDate - endDate)'), + ); + fireEvent.click(queryOptionsButton); + fireEvent.click(allVersionInRangeToggle); + const cancelButton = (await renderResult.findByRole('button', { + name: 'Cancel', + })) as HTMLButtonElement; + fireEvent.click(cancelButton); + await waitFor(() => + getAllByText(resultModifierPromptPanel, '(startDate - endDate)'), + ); + }, +); + +test( + integrationTest( + 'Query builder result modifier panel displays correct milestoning names after renaming milestoning parameters', + ), + async () => { + const { renderResult, queryBuilderState } = await TEST__setUpQueryBuilder( + TEST_MilestoningModel, + stub_RawLambda(), + 'my::map', + 'my::runtime', + TEST_DATA__ModelCoverageAnalysisResult_Milestoning, + ); + const _personClass = + queryBuilderState.graphManagerState.graph.getClass('my::Person'); + await act(async () => { + queryBuilderState.changeClass(_personClass); + }); + const queryBuilderSetup = await waitFor(() => + renderResult.getByTestId(QUERY_BUILDER_TEST_ID.QUERY_BUILDER_SETUP), + ); + await waitFor(() => getAllByText(queryBuilderSetup, 'Person')); + + const parameterPanel = await waitFor(() => + renderResult.getByTestId(QUERY_BUILDER_TEST_ID.QUERY_BUILDER_PARAMETERS), + ); + + const resultModifierPromptPanel = await waitFor(() => + renderResult.getByTestId( + QUERY_BUILDER_TEST_ID.QUERY_BUILDER_TDS_RESULT_MODIFIER_PROMPT, + ), + ); + await waitFor(() => + getAllByText(resultModifierPromptPanel, 'Business Date'), + ); + await waitFor(() => + getAllByText(resultModifierPromptPanel, 'businessDate'), + ); + fireEvent.click(getByText(parameterPanel, 'businessDate')); + const parameterNameInput = getParameterNameInput(renderResult); + fireEvent.change(parameterNameInput, { + target: { value: 'businessDateRenamed' }, + }); + const updateButton = (await renderResult.findByRole('button', { + name: 'Update', + })) as HTMLButtonElement; + fireEvent.click(updateButton); + await waitFor(() => + getAllByText(resultModifierPromptPanel, 'businessDateRenamed'), + ); + const queryOptionsButton = await waitFor(() => + renderResult.getByRole('button', { name: 'Query Options' }), + ); + fireEvent.click(queryOptionsButton); + const cancelButton = (await renderResult.findByRole('button', { + name: 'Cancel', + })) as HTMLButtonElement; + fireEvent.click(cancelButton); + await waitFor(() => + getAllByText(resultModifierPromptPanel, 'businessDateRenamed'), + ); + }, +); + type QueryGetAllVersionsTestCase = [ string, { @@ -571,14 +706,23 @@ describe( ); }); - renderResult.getByTitle('Edit Milestoning Parameters'); - fireEvent.click(renderResult.getByTitle('Edit Milestoning Parameters')); + const queryOptionsButton = await waitFor(() => + renderResult.getByRole('button', { name: 'Query Options' }), + ); + fireEvent.click(queryOptionsButton); const dialog = await waitFor(() => renderResult.getByRole('dialog')); fireEvent.click( getByText(dialog, 'Query All Milestoned Versions of the Root Class'), ); + + const applyButton = (await renderResult.findByRole('button', { + name: 'Apply', + })) as HTMLButtonElement; + + await waitFor(() => fireEvent.click(applyButton)); + const receivedOutput = queryBuilderState.buildQuery(); // Compare input JSON and output JSON for building a query @@ -666,11 +810,11 @@ describe( ); }); - renderResult.getByTitle('Edit Milestoning Parameters'); - fireEvent.click(renderResult.getByTitle('Edit Milestoning Parameters')); - + const queryOptionsButton = await waitFor(() => + renderResult.getByRole('button', { name: 'Query Options' }), + ); + fireEvent.click(queryOptionsButton); const dialog = await waitFor(() => renderResult.getByRole('dialog')); - // Check if we are setting start date and end date when allVersionsInRange() is selected fireEvent.click( getByText( @@ -678,6 +822,7 @@ describe( 'Optionally apply a date range to get All Versions for', ), ); + expect(getAllByText(dialog, 'startDate').length).toBe(2); expect(getAllByText(dialog, 'endDate').length).toBe(2); diff --git a/packages/legend-query-builder/src/components/__tests__/QueryBuilderParametersPanel.test.tsx b/packages/legend-query-builder/src/components/__tests__/QueryBuilderParametersPanel.test.tsx index 43e2afc0ce5..d7b179410eb 100644 --- a/packages/legend-query-builder/src/components/__tests__/QueryBuilderParametersPanel.test.tsx +++ b/packages/legend-query-builder/src/components/__tests__/QueryBuilderParametersPanel.test.tsx @@ -51,7 +51,9 @@ import { } from '@finos/legend-lego/code-editor/test'; import { guaranteeNonNullable } from '@finos/legend-shared'; -const getParameterNameInput = (renderResult: RenderResult): HTMLInputElement => +export const getParameterNameInput = ( + renderResult: RenderResult, +): HTMLInputElement => getByRole( guaranteeNonNullable( renderResult.getByText('Parameter Name').parentElement, diff --git a/packages/legend-query-builder/src/components/__tests__/QueryBuilderResultModifierPanel.test.tsx b/packages/legend-query-builder/src/components/__tests__/QueryBuilderResultModifierPanel.test.tsx index 804f45be11b..f033b73253a 100644 --- a/packages/legend-query-builder/src/components/__tests__/QueryBuilderResultModifierPanel.test.tsx +++ b/packages/legend-query-builder/src/components/__tests__/QueryBuilderResultModifierPanel.test.tsx @@ -91,7 +91,8 @@ describe('QueryBuilderResultModifierPanel', () => { ), async () => { // Open Query Options panel - const queryOptionsButton = await renderResult.findByText('Query Options'); + const queryOptionsButton = + await renderResult.findByText('Set Query Options'); fireEvent.click(queryOptionsButton); const resultModifierPanel = await renderResult.findByTestId( QUERY_BUILDER_TEST_ID.QUERY_BUILDER_RESULT_MODIFIER_PANEL, @@ -144,7 +145,8 @@ describe('QueryBuilderResultModifierPanel', () => { ), async () => { // Open Query Options panel - const queryOptionsButton = await renderResult.findByText('Query Options'); + const queryOptionsButton = + await renderResult.findByText('Set Query Options'); fireEvent.click(queryOptionsButton); const resultModifierPanel = await renderResult.findByTestId( QUERY_BUILDER_TEST_ID.QUERY_BUILDER_RESULT_MODIFIER_PANEL, @@ -193,7 +195,8 @@ describe('QueryBuilderResultModifierPanel', () => { ), async () => { // Open Query Options panel - const queryOptionsButton = await renderResult.findByText('Query Options'); + const queryOptionsButton = + await renderResult.findByText('Set Query Options'); fireEvent.click(queryOptionsButton); const resultModifierPanel = await renderResult.findByTestId( QUERY_BUILDER_TEST_ID.QUERY_BUILDER_RESULT_MODIFIER_PANEL, @@ -255,7 +258,8 @@ describe('QueryBuilderResultModifierPanel', () => { ), async () => { // Open Query Options panel - const queryOptionsButton = await renderResult.findByText('Query Options'); + const queryOptionsButton = + await renderResult.findByText('Set Query Options'); fireEvent.click(queryOptionsButton); const resultModifierPanel = await renderResult.findByTestId( QUERY_BUILDER_TEST_ID.QUERY_BUILDER_RESULT_MODIFIER_PANEL, @@ -292,7 +296,8 @@ describe('QueryBuilderResultModifierPanel', () => { ), async () => { // Open Query Options panel - const queryOptionsButton = await renderResult.findByText('Query Options'); + const queryOptionsButton = + await renderResult.findByText('Set Query Options'); fireEvent.click(queryOptionsButton); const resultModifierPanel = await renderResult.findByTestId( QUERY_BUILDER_TEST_ID.QUERY_BUILDER_RESULT_MODIFIER_PANEL, @@ -347,7 +352,8 @@ describe('QueryBuilderResultModifierPanel', () => { ), async () => { // Open Query Options panel - const queryOptionsButton = await renderResult.findByText('Query Options'); + const queryOptionsButton = + await renderResult.findByText('Set Query Options'); fireEvent.click(queryOptionsButton); const resultModifierPanel = await renderResult.findByTestId( QUERY_BUILDER_TEST_ID.QUERY_BUILDER_RESULT_MODIFIER_PANEL, @@ -383,7 +389,8 @@ describe('QueryBuilderResultModifierPanel', () => { ), async () => { // Open Query Options panel - const queryOptionsButton = await renderResult.findByText('Query Options'); + const queryOptionsButton = + await renderResult.findByText('Set Query Options'); fireEvent.click(queryOptionsButton); const resultModifierPanel = await renderResult.findByTestId( QUERY_BUILDER_TEST_ID.QUERY_BUILDER_RESULT_MODIFIER_PANEL, @@ -425,7 +432,8 @@ describe('QueryBuilderResultModifierPanel', () => { ), async () => { // Open Query Options panel - const queryOptionsButton = await renderResult.findByText('Query Options'); + const queryOptionsButton = + await renderResult.findByText('Set Query Options'); fireEvent.click(queryOptionsButton); const resultModifierPanel = await renderResult.findByTestId( QUERY_BUILDER_TEST_ID.QUERY_BUILDER_RESULT_MODIFIER_PANEL, @@ -469,7 +477,8 @@ describe('QueryBuilderResultModifierPanel', () => { ), async () => { // Open Query Options panel - const queryOptionsButton = await renderResult.findByText('Query Options'); + const queryOptionsButton = + await renderResult.findByText('Set Query Options'); fireEvent.click(queryOptionsButton); const resultModifierPanel = await renderResult.findByTestId( QUERY_BUILDER_TEST_ID.QUERY_BUILDER_RESULT_MODIFIER_PANEL, @@ -524,7 +533,8 @@ describe('QueryBuilderResultModifierPanel', () => { ), async () => { // Open Query Options panel - const queryOptionsButton = await renderResult.findByText('Query Options'); + const queryOptionsButton = + await renderResult.findByText('Set Query Options'); fireEvent.click(queryOptionsButton); const resultModifierPanel = await renderResult.findByTestId( QUERY_BUILDER_TEST_ID.QUERY_BUILDER_RESULT_MODIFIER_PANEL, diff --git a/packages/legend-query-builder/src/components/__tests__/QueryBuilderWatermark.test.tsx b/packages/legend-query-builder/src/components/__tests__/QueryBuilderWatermark.test.tsx index ab2c66f8c3b..a6ba4ebda1a 100644 --- a/packages/legend-query-builder/src/components/__tests__/QueryBuilderWatermark.test.tsx +++ b/packages/legend-query-builder/src/components/__tests__/QueryBuilderWatermark.test.tsx @@ -47,7 +47,7 @@ test( // Open Query Options modal const queryOptionsButton = await waitFor(() => - renderResult.getByRole('button', { name: 'Query Options' }), + renderResult.getByRole('button', { name: 'Set Query Options' }), ); fireEvent.click(queryOptionsButton); fireEvent.click( @@ -103,7 +103,7 @@ test( // Open Query Options modal, enable and save watermark const queryOptionsButton = await waitFor(() => - renderResult.getByRole('button', { name: 'Query Options' }), + renderResult.getByRole('button', { name: 'Set Query Options' }), ); fireEvent.click(queryOptionsButton); fireEvent.click( @@ -183,7 +183,7 @@ test( // Open watermark modal const queryOptionsButton = await waitFor(() => - renderResult.getByRole('button', { name: 'Query Options' }), + renderResult.getByRole('button', { name: 'Set Query Options' }), ); fireEvent.click(queryOptionsButton); await waitFor(() => renderResult.getByText('Watermark')); diff --git a/packages/legend-query-builder/src/components/explorer/QueryBuilderMilestoningEditor.tsx b/packages/legend-query-builder/src/components/explorer/QueryBuilderMilestoningEditor.tsx index f69e6989c5e..6dba90f3cd7 100644 --- a/packages/legend-query-builder/src/components/explorer/QueryBuilderMilestoningEditor.tsx +++ b/packages/legend-query-builder/src/components/explorer/QueryBuilderMilestoningEditor.tsx @@ -19,7 +19,6 @@ import type { QueryBuilderState } from '../../stores/QueryBuilderState.js'; import { useCallback } from 'react'; import { type ValueSpecification, - type VariableExpression, GenericType, GenericTypeExplicitReference, observe_PrimitiveInstanceValue, @@ -27,19 +26,8 @@ import { PRIMITIVE_TYPE, PrimitiveType, } from '@finos/legend-graph'; -import { guaranteeNonNullable } from '@finos/legend-shared'; import { useDrop } from 'react-dnd'; -import { - Dialog, - Modal, - ModalBody, - ModalFooter, - ModalFooterButton, - ModalHeader, - PanelEntryDropZonePlaceholder, - PanelFormBooleanField, - PanelFormSection, -} from '@finos/legend-art'; +import { PanelEntryDropZonePlaceholder } from '@finos/legend-art'; import { generateDefaultValueForPrimitiveType } from '../../stores/QueryBuilderValueSpecificationHelper.js'; import { BasicValueSpecificationEditor, @@ -47,9 +35,8 @@ import { QUERY_BUILDER_VARIABLE_DND_TYPE, } from '../shared/BasicValueSpecificationEditor.js'; import { instanceValue_setValues } from '../../stores/shared/ValueSpecificationModifierHelper.js'; -import { VariableSelector } from '../shared/QueryBuilderVariableSelector.js'; -const MilestoningParameterEditor = observer( +export const MilestoningParameterEditor = observer( (props: { queryBuilderState: QueryBuilderState; parameter: ValueSpecification; @@ -125,240 +112,3 @@ const MilestoningParameterEditor = observer( ); }, ); - -const BiTemporalMilestoningEditor = observer( - (props: { queryBuilderState: QueryBuilderState }) => { - const { queryBuilderState } = props; - return ( - <> - -
- Processing Date -
- - queryBuilderState.milestoningState.setProcessingDate(val) - } - /> -
- -
- Business Date -
- - queryBuilderState.milestoningState.setBusinessDate(val) - } - /> -
- - ); - }, -); - -const BusinessTemporalMilestoningEditor = observer( - (props: { queryBuilderState: QueryBuilderState }) => { - const { queryBuilderState } = props; - return ( - -
- Business Date -
- - queryBuilderState.milestoningState.setBusinessDate(val) - } - /> -
- ); - }, -); - -const ProcessingTemporalMilestoningEditor = observer( - (props: { queryBuilderState: QueryBuilderState }) => { - const { queryBuilderState } = props; - return ( - -
- Processing Date -
- - queryBuilderState.milestoningState.setProcessingDate(val) - } - /> -
- ); - }, -); - -const TemporalMilestoningEditor: React.FC<{ - queryBuilderState: QueryBuilderState; -}> = (props) => { - const { queryBuilderState } = props; - - if ( - queryBuilderState.milestoningState.processingDate && - queryBuilderState.milestoningState.businessDate - ) { - return ( - - ); - } else if (queryBuilderState.milestoningState.businessDate) { - return ( - - ); - } else if (queryBuilderState.milestoningState.processingDate) { - return ( - - ); - } else { - return null; - } -}; - -const AllVersionsInRangelMilestoningParametersEditor = observer( - (props: { queryBuilderState: QueryBuilderState }) => { - const { queryBuilderState } = props; - - return ( -
- -
- Start Date -
- - queryBuilderState.milestoningState.setStartDate(val) - } - /> -
- -
- End Date -
- - queryBuilderState.milestoningState.setEndDate(val) - } - /> -
-
- ); - }, -); - -export const MilestoningParametersEditor = observer( - (props: { queryBuilderState: QueryBuilderState }) => { - const { queryBuilderState } = props; - const applicationStore = queryBuilderState.applicationStore; - const milestoningState = queryBuilderState.milestoningState; - const close = (): void => milestoningState.setShowMilestoningEditor(false); - const isCompatibleMilestoningParameter = ( - variable: VariableExpression, - ): boolean => - variable.genericType?.value.rawType.name === PRIMITIVE_TYPE.STRICTDATE || - variable.genericType?.value.rawType.name === PRIMITIVE_TYPE.LATESTDATE || - variable.genericType?.value.rawType.name === PRIMITIVE_TYPE.DATE || - variable.genericType?.value.rawType.name === PRIMITIVE_TYPE.DATETIME; - - return ( - - - - - {milestoningState.isCurrentClassMilestoned && ( - - milestoningState.setAllVersions(value) - } - /> - )} - {milestoningState.isAllVersionsEnabled && - milestoningState.isCurrentClassSupportsVersionsInRange && ( - <> - - milestoningState.setAllVersionsInRange(value) - } - /> - - {milestoningState.isAllVersionsInRangeEnabled && ( - - )} - - )} - - -
- List of compatible milestoning parameters -
-
-
- -
-
- - - -
-
- ); - }, -); diff --git a/packages/legend-query-builder/src/components/fetch-structure/QueryBuilderResultModifierPanel.tsx b/packages/legend-query-builder/src/components/fetch-structure/QueryBuilderResultModifierPanel.tsx index 99e87884edf..1b6acf33ee0 100644 --- a/packages/legend-query-builder/src/components/fetch-structure/QueryBuilderResultModifierPanel.tsx +++ b/packages/legend-query-builder/src/components/fetch-structure/QueryBuilderResultModifierPanel.tsx @@ -33,6 +33,8 @@ import { PanelFormSection, ArrowUpIcon, ArrowDownIcon, + PanelFormBooleanField, + PanelFormListItems, } from '@finos/legend-art'; import { SortColumnState } from '../../stores/fetch-structure/tds/QueryResultSetModifierState.js'; import { @@ -41,21 +43,36 @@ import { deepClone, deleteEntry, guaranteeNonNullable, + isNonNullable, } from '@finos/legend-shared'; import { useApplicationStore } from '@finos/legend-application'; import type { QueryBuilderTDSState } from '../../stores/fetch-structure/tds/QueryBuilderTDSState.js'; import type { QueryBuilderTDSColumnState } from '../../stores/fetch-structure/tds/QueryBuilderTDSColumnState.js'; -import { COLUMN_SORT_TYPE } from '../../graph/QueryBuilderMetaModelConst.js'; +import { + COLUMN_SORT_TYPE, + QUERY_BUILDER_SUPPORTED_GET_ALL_FUNCTIONS, +} from '../../graph/QueryBuilderMetaModelConst.js'; import { useCallback, useEffect, useState } from 'react'; import type { QueryBuilderProjectionColumnState } from '../../stores/fetch-structure/tds/projection/QueryBuilderProjectionColumnState.js'; import { QUERY_BUILDER_TEST_ID } from '../../__lib__/QueryBuilderTesting.js'; -import { VariableSelector } from '../shared/QueryBuilderVariableSelector.js'; +import { + VariableSelector, + VariableViewer, +} from '../shared/QueryBuilderVariableSelector.js'; import { type ValueSpecification, - type VariableExpression, + VariableExpression, PrimitiveType, Multiplicity, areMultiplicitiesEqual, + PRIMITIVE_TYPE, + MILESTONING_START_DATE_PARAMETER_NAME, + GenericType, + GenericTypeExplicitReference, + MILESTONING_END_DATE_PARAMETER_NAME, + getMilestoneTemporalStereotype, + BUSINESS_DATE_MILESTONING_PROPERTY_NAME, + PROCESSING_DATE_MILESTONING_PROPERTY_NAME, } from '@finos/legend-graph'; import { BasicValueSpecificationEditor, @@ -63,6 +80,9 @@ import { type QueryBuilderVariableDragSource, } from '../shared/BasicValueSpecificationEditor.js'; import { useDrop } from 'react-dnd'; +import { MilestoningParameterEditor } from '../explorer/QueryBuilderMilestoningEditor.js'; +import { QueryBuilderSimpleConstantExpressionState } from '../../stores/QueryBuilderConstantsState.js'; +import { LambdaParameterState } from '../../stores/shared/LambdaParameterState.js'; const ColumnSortEditor = observer( (props: { @@ -275,21 +295,6 @@ export const QueryResultModifierModal = observer( stateSlice, ]); - // Handle user actions - const closeModal = (): void => resultSetModifierState.setShowModal(false); - const applyChanges = (): void => { - resultSetModifierState.setSortColumns(sortColumns); - resultSetModifierState.setDistinct(distinct); - resultSetModifierState.setLimit(limitResults); - if (slice[0] !== undefined && slice[1] !== undefined) { - resultSetModifierState.setSlice([slice[0], slice[1]]); - } else { - resultSetModifierState.setSlice(undefined); - } - resultSetModifierState.setShowModal(false); - watermarkState.setValue(watermarkValue); - }; - const handleLimitResultsChange: React.ChangeEventHandler< HTMLInputElement > = (event) => { @@ -380,6 +385,273 @@ export const QueryResultModifierModal = observer( [handleDrop], ); + //milestoning config + const isCompatibleMilestoningParameter = ( + variable: VariableExpression, + ): boolean => + variable.genericType?.value.rawType.name === PRIMITIVE_TYPE.STRICTDATE || + variable.genericType?.value.rawType.name === PRIMITIVE_TYPE.LATESTDATE || + variable.genericType?.value.rawType.name === PRIMITIVE_TYPE.DATE || + variable.genericType?.value.rawType.name === PRIMITIVE_TYPE.DATETIME; + + const milestoningState = tdsState.queryBuilderState.milestoningState; + const [parameterStates, setParameterStates] = useState( + milestoningState.queryBuilderState.parametersState.parameterStates, + ); + const filteredParameterStates = parameterStates.filter((p) => + isCompatibleMilestoningParameter(p.parameter), + ); + const filteredConstantState = + milestoningState.queryBuilderState.constantState.constants.filter((c) => + isCompatibleMilestoningParameter(c.variable), + ); + + const [isAllVersionsEnabled, setIsAllVersionsEnabled] = useState( + milestoningState.isAllVersionsEnabled, + ); + const [isAllVersionsInRangeEnabled, setIsAllVersionsInRangeEnabled] = + useState(milestoningState.isAllVersionsInRangeEnabled); + const [businessDate, setBusinessDate] = useState( + milestoningState.businessDate, + ); + const [processingDate, setProcessingDate] = useState( + milestoningState.processingDate, + ); + const [startDate, setStartDate] = useState(milestoningState.startDate); + const [endDate, setEndDate] = useState(milestoningState.endDate); + + const shouldFilterMilestoningParamIfNotUsed = ( + param: ValueSpecification | undefined, + resetParameter: (val: ValueSpecification | undefined) => void, + ): ((lambdaParamState: LambdaParameterState) => boolean) => { + if (param && param instanceof VariableExpression) { + if ( + !milestoningState.queryBuilderState.isVariableUsed(param, { + exculdeMilestoningState: true, + }) + ) { + resetParameter(undefined); + return (lambdaParamState: LambdaParameterState) => + lambdaParamState.parameter.name !== param.name; + } + } + return (lambdaParamState: LambdaParameterState) => true; + }; + + const setAllVersions = (value: boolean | undefined): void => { + if (value) { + //clean all unused milestoning parameters e.g. businessDate, ..., endDate. + setParameterStates([ + ...parameterStates.filter( + (ps) => + shouldFilterMilestoningParamIfNotUsed( + businessDate, + setBusinessDate, + )(ps) && + shouldFilterMilestoningParamIfNotUsed( + processingDate, + setProcessingDate, + )(ps) && + shouldFilterMilestoningParamIfNotUsed( + startDate, + setStartDate, + )(ps) && + shouldFilterMilestoningParamIfNotUsed(endDate, setEndDate)(ps), + ), + ]); + } else { + //get or initialize getAll() parameters + setIsAllVersionsInRangeEnabled(false); + let newParamStates: LambdaParameterState[] = []; + if ( + (!businessDate || !processingDate) && + milestoningState.queryBuilderState.class + ) { + const stereotype = getMilestoneTemporalStereotype( + milestoningState.queryBuilderState.class, + milestoningState.queryBuilderState.graphManagerState.graph, + ); + if (stereotype) { + const existingParamStates = parameterStates.map( + (ps) => ps.variableName, + ); + newParamStates = milestoningState + .getMilestoningImplementation(stereotype) + .buildParameterStatesFromMilestoningParameters() + .filter((ps) => !existingParamStates.includes(ps.parameter.name)); + } + } + const allParamStates = [...newParamStates, ...parameterStates].filter( + (ps) => + shouldFilterMilestoningParamIfNotUsed( + startDate, + setStartDate, + )(ps) && + shouldFilterMilestoningParamIfNotUsed(endDate, setEndDate)(ps), + ); + setParameterStates(allParamStates); + if (!businessDate) { + setBusinessDate( + allParamStates.find( + (ps) => + ps.variableName === BUSINESS_DATE_MILESTONING_PROPERTY_NAME, + )?.parameter, + ); + } + if (!processingDate) { + setProcessingDate( + allParamStates.find( + (ps) => + ps.variableName === PROCESSING_DATE_MILESTONING_PROPERTY_NAME, + )?.parameter, + ); + } + } + setIsAllVersionsEnabled(Boolean(value)); + }; + + const buildAllVersionsInRangeParameters = (): void => { + let startDateParameterState; + let endDateParameterState; + if (!(startDate && startDate instanceof VariableExpression)) { + const startDateVar = new VariableExpression( + MILESTONING_START_DATE_PARAMETER_NAME, + Multiplicity.ONE, + GenericTypeExplicitReference.create( + new GenericType(PrimitiveType.DATE), + ), + ); + setStartDate(startDateVar); + startDateParameterState = new LambdaParameterState( + startDateVar, + milestoningState.queryBuilderState.observerContext, + milestoningState.queryBuilderState.graphManagerState.graph, + ); + startDateParameterState.mockParameterValue(); + } + if (!(endDate && endDate instanceof VariableExpression)) { + const endDateVar = new VariableExpression( + MILESTONING_END_DATE_PARAMETER_NAME, + Multiplicity.ONE, + GenericTypeExplicitReference.create( + new GenericType(PrimitiveType.DATE), + ), + ); + setEndDate(endDateVar); + endDateParameterState = new LambdaParameterState( + endDateVar, + milestoningState.queryBuilderState.observerContext, + milestoningState.queryBuilderState.graphManagerState.graph, + ); + endDateParameterState.mockParameterValue(); + } + setParameterStates([ + ...parameterStates.filter( + (ps) => + shouldFilterMilestoningParamIfNotUsed( + businessDate, + setBusinessDate, + )(ps) && + shouldFilterMilestoningParamIfNotUsed( + processingDate, + setProcessingDate, + )(ps), + ), + ...[startDateParameterState, endDateParameterState].filter( + isNonNullable, + ), + ]); + }; + + const setAllVersionsInRange = (value: boolean | undefined): void => { + if (value) { + buildAllVersionsInRangeParameters(); + } else { + setAllVersions(true); + } + setIsAllVersionsInRangeEnabled(Boolean(value)); + }; + + const resetMilestoningConfig = (): void => { + setIsAllVersionsEnabled(milestoningState.isAllVersionsEnabled); + setIsAllVersionsInRangeEnabled( + milestoningState.isAllVersionsInRangeEnabled, + ); + setEndDate(milestoningState.endDate); + setBusinessDate(milestoningState.businessDate); + setProcessingDate(milestoningState.processingDate); + setParameterStates( + milestoningState.queryBuilderState.parametersState.parameterStates, + ); + }; + + // Handle user actions + const closeModal = (): void => { + resetMilestoningConfig(); + resultSetModifierState.setShowModal(false); + }; + + const applyChanges = (): void => { + resultSetModifierState.setSortColumns(sortColumns); + resultSetModifierState.setDistinct(distinct); + resultSetModifierState.setLimit(limitResults); + if (slice[0] !== undefined && slice[1] !== undefined) { + resultSetModifierState.setSlice([slice[0], slice[1]]); + } else { + resultSetModifierState.setSlice(undefined); + } + resultSetModifierState.setShowModal(false); + watermarkState.setValue(watermarkValue); + milestoningState.queryBuilderState.parametersState.setParameters( + parameterStates, + ); + if (isAllVersionsInRangeEnabled) { + milestoningState.setStartDate(startDate); + milestoningState.setEndDate(endDate); + milestoningState.clearGetAllParameters(); + milestoningState.queryBuilderState.setGetAllFunction( + QUERY_BUILDER_SUPPORTED_GET_ALL_FUNCTIONS.GET_ALL_VERSIONS_IN_RANGE, + ); + } else if (isAllVersionsEnabled) { + milestoningState.clearGetAllParameters(); + milestoningState.queryBuilderState.setGetAllFunction( + QUERY_BUILDER_SUPPORTED_GET_ALL_FUNCTIONS.GET_ALL_VERSIONS, + ); + } else if ( + tdsState.queryBuilderState.milestoningState.isMilestonedQuery + ) { + milestoningState.clearAllVersionsInRangeParameters(); + milestoningState.setBusinessDate(businessDate); + milestoningState.setProcessingDate(processingDate); + milestoningState.queryBuilderState.setGetAllFunction( + QUERY_BUILDER_SUPPORTED_GET_ALL_FUNCTIONS.GET_ALL, + ); + } + milestoningState.updateQueryBuilderState(); + }; + + useEffect(() => { + setIsAllVersionsEnabled(milestoningState.isAllVersionsEnabled); + setIsAllVersionsInRangeEnabled( + milestoningState.isAllVersionsInRangeEnabled, + ); + setEndDate(milestoningState.endDate); + setBusinessDate(milestoningState.businessDate); + setProcessingDate(milestoningState.processingDate); + setParameterStates( + milestoningState.queryBuilderState.parametersState.parameterStates, + ); + }, [ + milestoningState.isAllVersionsEnabled, + milestoningState.isAllVersionsInRangeEnabled, + milestoningState.queryBuilderState.parametersState.parameterStates, + milestoningState.businessDate, + milestoningState.processingDate, + milestoningState.startDate, + milestoningState.endDate, + milestoningState.queryBuilderState.class, + ]); + return ( - +
+ {tdsState.queryBuilderState.milestoningState + .isMilestonedQuery && ( + <> +
+ Milestoning +
+ {milestoningState.isCurrentClassMilestoned && ( + + )} + {isAllVersionsEnabled && + milestoningState.isCurrentClassSupportsVersionsInRange && ( + <> + + {isAllVersionsInRangeEnabled && + startDate && + endDate && ( +
+ +
+ Start Date +
+ setStartDate(val)} + /> +
+ +
+ End Date +
+ setEndDate(val)} + /> +
+
+ )} + + )} + {!isAllVersionsEnabled && ( + <> + {processingDate && ( + +
+ Processing Date +
+ + setProcessingDate(val) + } + /> +
+ )} + {businessDate && ( + +
+ Business Date +
+ + setBusinessDate(val) + } + /> +
+ )} + + )} + +
+ List of compatible milestoning parameters +
+
+
+ + {filteredParameterStates.length === 0 && ( + <> No available parameters + )} + {filteredParameterStates.map((pState) => ( + + ))} + + {Boolean(filteredConstantState.length) && ( + + {filteredConstantState.map((constantState) => ( + + ))} + + )} +
+
+ Other +
+ + )}
+
+ {tdsState.isQueryOptionsSet && ' - '} +
+ {tdsState.queryBuilderState.milestoningState.businessDate && ( +
+
+ Business Date +
+ +
+ )} + {tdsState.queryBuilderState.milestoningState.processingDate && ( +
+
+ Processing Date +
+ +
+ )} + {tdsState.queryBuilderState.milestoningState + .isAllVersionsEnabled && + !tdsState.queryBuilderState.milestoningState + .isAllVersionsInRangeEnabled && ( +
+
+ All Versions +
+ +
+ )} + {tdsState.queryBuilderState.milestoningState + .isAllVersionsInRangeEnabled && + tdsState.queryBuilderState.milestoningState.startDate && + tdsState.queryBuilderState.milestoningState.endDate && ( +
+
+ All Versions +
+ +
+ )}
{tdsState.resultSetModifierState.limit && (
Max Rows
-
- {tdsState.resultSetModifierState.limit} -
+
+ {tdsState.resultSetModifierState.limit} +
+
)} {tdsState.resultSetModifierState.distinct && ( @@ -1166,12 +1257,14 @@ export const QueryBuilderTDSPanel = observer(
Eliminate Duplicate Rows
-
- Yes -
+
+ Yes +
+
)} {tdsState.resultSetModifierState.sortColumns.length > 0 && ( @@ -1181,13 +1274,15 @@ export const QueryBuilderTDSPanel = observer( {tdsState.resultSetModifierState.sortColumns.map( (columnState) => ( -
- {`${columnState.columnState.columnName} ${columnState.sortType}`} -
+
+ {`${columnState.columnState.columnName} ${columnState.sortType}`} +
+ ), )} @@ -1197,12 +1292,14 @@ export const QueryBuilderTDSPanel = observer(
Slice
-
- {`${tdsState.resultSetModifierState.slice[0]},${tdsState.resultSetModifierState.slice[1]}`} -
+
+ {`${tdsState.resultSetModifierState.slice[0]},${tdsState.resultSetModifierState.slice[1]}`} +
+ )} {tdsState.queryBuilderState.watermarkState.value && ( diff --git a/packages/legend-query-builder/src/stores/QueryBuilderValueSpecificationHelper.ts b/packages/legend-query-builder/src/stores/QueryBuilderValueSpecificationHelper.ts index 4b02a09448d..63fe77306ac 100644 --- a/packages/legend-query-builder/src/stores/QueryBuilderValueSpecificationHelper.ts +++ b/packages/legend-query-builder/src/stores/QueryBuilderValueSpecificationHelper.ts @@ -63,6 +63,7 @@ import { validateMilestoningPropertyExpressionChain, } from './milestoning/QueryBuilderMilestoningHelper.js'; import { instanceValue_setValues } from './shared/ValueSpecificationModifierHelper.js'; +import type { QueryBuilderState } from './QueryBuilderState.js'; export const getNonCollectionValueSpecificationType = ( valueSpecification: ValueSpecification, @@ -329,6 +330,7 @@ export const buildGenericLambdaFunctionInstanceValue = ( export const validatePropertyExpressionChain = ( propertyExpression: AbstractPropertyExpression, graph: PureModel, + queryBuilderState: QueryBuilderState, ): void => { if ( propertyExpression.func.value.genericType.value.rawType instanceof Class && @@ -355,6 +357,7 @@ export const validatePropertyExpressionChain = ( sourceStereotype, targetStereotype, propertyExpression, + queryBuilderState, ); } } diff --git a/packages/legend-query-builder/src/stores/fetch-structure/tds/QueryBuilderTDSState.ts b/packages/legend-query-builder/src/stores/fetch-structure/tds/QueryBuilderTDSState.ts index db5e009975f..bab75cdd1ae 100644 --- a/packages/legend-query-builder/src/stores/fetch-structure/tds/QueryBuilderTDSState.ts +++ b/packages/legend-query-builder/src/stores/fetch-structure/tds/QueryBuilderTDSState.ts @@ -146,6 +146,7 @@ export class QueryBuilderTDSState TEMPORARY__showPostFetchStructurePanel: computed, derivations: computed, hasParserError: computed, + isQueryOptionsSet: computed, addColumn: action, moveColumn: action, removeAllColumns: action, @@ -356,6 +357,16 @@ export class QueryBuilderTDSState return fetchStructureValidationIssues; } + get isQueryOptionsSet(): boolean { + return ( + this.resultSetModifierState.limit !== undefined || + this.queryBuilderState.milestoningState.isMilestonedQuery || + this.resultSetModifierState.slice !== undefined || + this.resultSetModifierState.sortColumns.length > 0 || + this.resultSetModifierState.distinct + ); + } + get tdsColumns(): QueryBuilderTDSColumnState[] { const aggregationStateCols = this.aggregationState.columns.map( (c) => c.projectionColumnState, diff --git a/packages/legend-query-builder/src/stores/fetch-structure/tds/projection/QueryBuilderProjectionStateBuilder.ts b/packages/legend-query-builder/src/stores/fetch-structure/tds/projection/QueryBuilderProjectionStateBuilder.ts index bbe3997c5c9..2117b109c3e 100644 --- a/packages/legend-query-builder/src/stores/fetch-structure/tds/projection/QueryBuilderProjectionStateBuilder.ts +++ b/packages/legend-query-builder/src/stores/fetch-structure/tds/projection/QueryBuilderProjectionStateBuilder.ts @@ -159,6 +159,7 @@ export const processTDSProjectionColumnPropertyExpression = ( validatePropertyExpressionChain( currentPropertyExpression, queryBuilderState.graphManagerState.graph, + queryBuilderState, ); currentPropertyExpression = guaranteeNonNullable( currentPropertyExpression.parametersValues[0], diff --git a/packages/legend-query-builder/src/stores/filter/QueryBuilderFilterStateBuilder.ts b/packages/legend-query-builder/src/stores/filter/QueryBuilderFilterStateBuilder.ts index 90dcc6b7ff4..7b3fa30919a 100644 --- a/packages/legend-query-builder/src/stores/filter/QueryBuilderFilterStateBuilder.ts +++ b/packages/legend-query-builder/src/stores/filter/QueryBuilderFilterStateBuilder.ts @@ -309,6 +309,7 @@ const processFilterTree = ( validatePropertyExpressionChain( currentPropertyExpression, filterState.queryBuilderState.graphManagerState.graph, + filterState.queryBuilderState, ); } } diff --git a/packages/legend-query-builder/src/stores/milestoning/QueryBuilderBitemporalMilestoningImplementation.ts b/packages/legend-query-builder/src/stores/milestoning/QueryBuilderBitemporalMilestoningImplementation.ts index 859607dc040..3a33bb5500c 100644 --- a/packages/legend-query-builder/src/stores/milestoning/QueryBuilderBitemporalMilestoningImplementation.ts +++ b/packages/legend-query-builder/src/stores/milestoning/QueryBuilderBitemporalMilestoningImplementation.ts @@ -23,18 +23,21 @@ import { MILESTONING_STEREOTYPE, INTERNAL__PropagatedValue, PrimitiveType, + VariableExpression, } from '@finos/legend-graph'; import { UnsupportedOperationError, assertTrue, guaranteeNonNullable, guaranteeType, + isNonNullable, } from '@finos/legend-shared'; import { getParameterValue } from '../../components/QueryBuilderSideBar.js'; import { QUERY_BUILDER_SUPPORTED_FUNCTIONS } from '../../graph/QueryBuilderMetaModelConst.js'; import type { QueryBuilderDerivedPropertyExpressionState } from '../QueryBuilderPropertyEditorState.js'; import { createSupportedFunctionExpression } from '../shared/ValueSpecificationEditorHelper.js'; import { QueryBuilderMilestoningImplementation } from './QueryBuilderMilestoningImplementation.js'; +import type { LambdaParameterState } from '../shared/LambdaParameterState.js'; export class QueryBuilderBitemporalMilestoningImplementation extends QueryBuilderMilestoningImplementation { getMilestoningDate(index?: number): ValueSpecification | undefined { @@ -70,6 +73,26 @@ export class QueryBuilderBitemporalMilestoningImplementation extends QueryBuilde this.milestoningState.queryBuilderState.setShowParametersPanel(true); } + buildParameterStatesFromMilestoningParameters(): LambdaParameterState[] { + const businessState = + this.milestoningState.buildParameterStateFromMilestoningParameter( + this.milestoningState.businessDate && + this.milestoningState.businessDate instanceof VariableExpression + ? this.milestoningState.businessDate.name + : BUSINESS_DATE_MILESTONING_PROPERTY_NAME, + ); + + const processingState = + this.milestoningState.buildParameterStateFromMilestoningParameter( + this.milestoningState.processingDate && + this.milestoningState.processingDate instanceof VariableExpression + ? this.milestoningState.processingDate.name + : BUSINESS_DATE_MILESTONING_PROPERTY_NAME, + ); + + return [businessState, processingState].filter(isNonNullable); + } + processGetAllParamaters(parameterValues: ValueSpecification[]): void { assertTrue( parameterValues.length === 3, diff --git a/packages/legend-query-builder/src/stores/milestoning/QueryBuilderBusinessTemporalMilestoningImplementation.ts b/packages/legend-query-builder/src/stores/milestoning/QueryBuilderBusinessTemporalMilestoningImplementation.ts index 0206070ac1d..2d0ec326660 100644 --- a/packages/legend-query-builder/src/stores/milestoning/QueryBuilderBusinessTemporalMilestoningImplementation.ts +++ b/packages/legend-query-builder/src/stores/milestoning/QueryBuilderBusinessTemporalMilestoningImplementation.ts @@ -22,6 +22,7 @@ import { MILESTONING_STEREOTYPE, INTERNAL__PropagatedValue, PrimitiveType, + VariableExpression, } from '@finos/legend-graph'; import { UnsupportedOperationError, @@ -32,6 +33,7 @@ import { getParameterValue } from '../../components/QueryBuilderSideBar.js'; import { QUERY_BUILDER_SUPPORTED_FUNCTIONS } from '../../graph/QueryBuilderMetaModelConst.js'; import { createSupportedFunctionExpression } from '../shared/ValueSpecificationEditorHelper.js'; import { QueryBuilderMilestoningImplementation } from './QueryBuilderMilestoningImplementation.js'; +import type { LambdaParameterState } from '../shared/LambdaParameterState.js'; export class QueryBuilderBusinessTemporalMilestoningImplementation extends QueryBuilderMilestoningImplementation { getMilestoningDate(): ValueSpecification | undefined { @@ -54,6 +56,17 @@ export class QueryBuilderBusinessTemporalMilestoningImplementation extends Query this.milestoningState.queryBuilderState.setShowParametersPanel(true); } + buildParameterStatesFromMilestoningParameters(): LambdaParameterState[] { + const state = + this.milestoningState.buildParameterStateFromMilestoningParameter( + this.milestoningState.businessDate && + this.milestoningState.businessDate instanceof VariableExpression + ? this.milestoningState.businessDate.name + : BUSINESS_DATE_MILESTONING_PROPERTY_NAME, + ); + return state ? [state] : []; + } + processGetAllParamaters(parameterValues: ValueSpecification[]): void { assertTrue( parameterValues.length === 2, diff --git a/packages/legend-query-builder/src/stores/milestoning/QueryBuilderMilestoningHelper.ts b/packages/legend-query-builder/src/stores/milestoning/QueryBuilderMilestoningHelper.ts index 0b77c3cf106..48d238cf497 100644 --- a/packages/legend-query-builder/src/stores/milestoning/QueryBuilderMilestoningHelper.ts +++ b/packages/legend-query-builder/src/stores/milestoning/QueryBuilderMilestoningHelper.ts @@ -294,6 +294,7 @@ export const validateMilestoningPropertyExpressionChain = ( sourceStereotype: MILESTONING_STEREOTYPE | undefined, targetStereotype: MILESTONING_STEREOTYPE, propertyExpression: AbstractPropertyExpression, + queryBuilderState: QueryBuilderState, ): void => { if ( sourceStereotype !== MILESTONING_STEREOTYPE.BITEMPORAL && @@ -316,10 +317,27 @@ export const validateMilestoningPropertyExpressionChain = ( `Property of milestoning sterotype '${MILESTONING_STEREOTYPE.BITEMPORAL}' should not have more than two parameters`, ); } + queryBuilderState.milestoningState.setProcessingDate( + propertyExpression.parametersValues[1], + ); + queryBuilderState.milestoningState.setBusinessDate( + propertyExpression.parametersValues[2], + ); } else if (propertyExpression.parametersValues.length !== 2) { throw new UnsupportedOperationError( `Property of milestoning sterotype '${targetStereotype}' should have exactly one parameter`, ); } + if (targetStereotype === MILESTONING_STEREOTYPE.BUSINESS_TEMPORAL) { + queryBuilderState.milestoningState.setBusinessDate( + propertyExpression.parametersValues[1], + ); + } else if ( + targetStereotype === MILESTONING_STEREOTYPE.PROCESSING_TEMPORAL + ) { + queryBuilderState.milestoningState.setProcessingDate( + propertyExpression.parametersValues[1], + ); + } } }; diff --git a/packages/legend-query-builder/src/stores/milestoning/QueryBuilderMilestoningImplementation.ts b/packages/legend-query-builder/src/stores/milestoning/QueryBuilderMilestoningImplementation.ts index 8344973ead4..9596999b9c7 100644 --- a/packages/legend-query-builder/src/stores/milestoning/QueryBuilderMilestoningImplementation.ts +++ b/packages/legend-query-builder/src/stores/milestoning/QueryBuilderMilestoningImplementation.ts @@ -22,6 +22,7 @@ import type { } from '@finos/legend-graph'; import type { QueryBuilderDerivedPropertyExpressionState } from '../QueryBuilderPropertyEditorState.js'; import type { QueryBuilderMilestoningState } from './QueryBuilderMilestoningState.js'; +import type { LambdaParameterState } from '../shared/LambdaParameterState.js'; export abstract class QueryBuilderMilestoningImplementation { milestoningState: QueryBuilderMilestoningState; @@ -88,4 +89,6 @@ export abstract class QueryBuilderMilestoningImplementation { idx?: number, derivedPropertyExpressionState?: QueryBuilderDerivedPropertyExpressionState, ): ValueSpecification; + + abstract buildParameterStatesFromMilestoningParameters(): LambdaParameterState[]; } diff --git a/packages/legend-query-builder/src/stores/milestoning/QueryBuilderMilestoningState.ts b/packages/legend-query-builder/src/stores/milestoning/QueryBuilderMilestoningState.ts index 40cfb9c6486..055aa9b623e 100644 --- a/packages/legend-query-builder/src/stores/milestoning/QueryBuilderMilestoningState.ts +++ b/packages/legend-query-builder/src/stores/milestoning/QueryBuilderMilestoningState.ts @@ -75,7 +75,7 @@ export class QueryBuilderMilestoningState implements Hashable { startDate: observable, endDate: observable, showMilestoningEditor: observable, - isMilestonedQuery: computed, + setProcessingDate: action, setBusinessDate: action, setStartDate: action, @@ -87,6 +87,10 @@ export class QueryBuilderMilestoningState implements Hashable { initializeAllVersionsInRangeParameters: action, clearAllVersionsInRangeParameters: action, clearGetAllParameters: action, + + isAllVersionsEnabled: computed, + isAllVersionsInRangeEnabled: computed, + isMilestonedQuery: computed, hashCode: computed, }); @@ -380,9 +384,9 @@ export class QueryBuilderMilestoningState implements Hashable { this.queryBuilderState.parametersState.parameterStates.find( (p) => p.parameter === this.startDate, ); - this.queryBuilderState.parametersState.removeParameter( - guaranteeNonNullable(paramState), - ); + if (paramState) { + this.queryBuilderState.parametersState.removeParameter(paramState); + } } if ( this.endDate instanceof VariableExpression && @@ -394,9 +398,9 @@ export class QueryBuilderMilestoningState implements Hashable { this.queryBuilderState.parametersState.parameterStates.find( (p) => p.parameter === this.endDate, ); - this.queryBuilderState.parametersState.removeParameter( - guaranteeNonNullable(paramState), - ); + if (paramState) { + this.queryBuilderState.parametersState.removeParameter(paramState); + } } this.setStartDate(undefined); this.setEndDate(undefined); @@ -422,6 +426,36 @@ export class QueryBuilderMilestoningState implements Hashable { } } + buildParameterStateFromMilestoningParameter( + parameterName: string, + ): LambdaParameterState | undefined { + const milestoningParameter = new VariableExpression( + parameterName, + Multiplicity.ONE, + GenericTypeExplicitReference.create(new GenericType(PrimitiveType.DATE)), + ); + const paramState = + this.queryBuilderState.parametersState.parameterStates.find( + (p) => p.variableName === parameterName, + ); + if (paramState) { + return paramState; + } else if ( + !this.queryBuilderState.constantState.constants.find( + (c) => c.variable.name === parameterName, + ) + ) { + const variableState = new LambdaParameterState( + milestoningParameter, + this.queryBuilderState.observerContext, + this.queryBuilderState.graphManagerState.graph, + ); + variableState.mockParameterValue(); + return variableState; + } + return undefined; + } + buildMilestoningParameter(parameterName: string): ValueSpecification { const milestoningParameter = new VariableExpression( parameterName, diff --git a/packages/legend-query-builder/src/stores/milestoning/QueryBuilderProcessingTemporalMilestoningImplementation.ts b/packages/legend-query-builder/src/stores/milestoning/QueryBuilderProcessingTemporalMilestoningImplementation.ts index 13788e13be1..0dc9592e593 100644 --- a/packages/legend-query-builder/src/stores/milestoning/QueryBuilderProcessingTemporalMilestoningImplementation.ts +++ b/packages/legend-query-builder/src/stores/milestoning/QueryBuilderProcessingTemporalMilestoningImplementation.ts @@ -21,6 +21,7 @@ import { type AbstractPropertyExpression, INTERNAL__PropagatedValue, PrimitiveType, + VariableExpression, } from '@finos/legend-graph'; import { UnsupportedOperationError, @@ -31,6 +32,7 @@ import { getParameterValue } from '../../components/QueryBuilderSideBar.js'; import { QUERY_BUILDER_SUPPORTED_FUNCTIONS } from '../../graph/QueryBuilderMetaModelConst.js'; import { createSupportedFunctionExpression } from '../shared/ValueSpecificationEditorHelper.js'; import { QueryBuilderMilestoningImplementation } from './QueryBuilderMilestoningImplementation.js'; +import type { LambdaParameterState } from '../shared/LambdaParameterState.js'; export class QueryBuilderProcessingTemporalMilestoningImplementation extends QueryBuilderMilestoningImplementation { getMilestoningDate(): ValueSpecification | undefined { @@ -53,6 +55,17 @@ export class QueryBuilderProcessingTemporalMilestoningImplementation extends Que this.milestoningState.queryBuilderState.setShowParametersPanel(true); } + buildParameterStatesFromMilestoningParameters(): LambdaParameterState[] { + const state = + this.milestoningState.buildParameterStateFromMilestoningParameter( + this.milestoningState.processingDate && + this.milestoningState.processingDate instanceof VariableExpression + ? this.milestoningState.processingDate.name + : PROCESSING_DATE_MILESTONING_PROPERTY_NAME, + ); + return state ? [state] : []; + } + processGetAllParamaters(parameterValues: ValueSpecification[]): void { assertTrue( parameterValues.length === 2, diff --git a/packages/legend-query-builder/style/_query-builder-projection.scss b/packages/legend-query-builder/style/_query-builder-projection.scss index 781ffdeb7b6..7e8842b4f35 100644 --- a/packages/legend-query-builder/style/_query-builder-projection.scss +++ b/packages/legend-query-builder/style/_query-builder-projection.scss @@ -48,25 +48,27 @@ @include flexVCenter; height: 2.2rem; - background: var(--color-blue-200); padding: 1rem; - border-radius: 0.2rem 0 0 0.2rem; + text-underline-position: under; } &__label__icon { - font-size: 1.2rem; - color: var(--color-light-grey-180); + font-size: 1.3rem; + margin-right: 0.7rem; } &__label__title { - margin-left: 0.7rem; white-space: nowrap; - color: var(--color-light-grey-180); - font-size: 1.2rem; + font-size: 1.3rem; font-weight: 500; } } + &__divider { + color: var(--color-light-grey-200); + margin-right: 1rem; + } + &__group { @include flexVCenter; @@ -82,20 +84,6 @@ line-height: 1.8rem; border-radius: 0.1rem; font-size: 1.1rem; - margin-left: 0.5rem; - } - - &__content { - color: var(--color-light-blue-50); - font-weight: 500; - font-size: 1.2rem; - padding-left: 0.5rem; - cursor: pointer; - overflow: hidden; - text-overflow: ellipsis; - text-decoration: underline; - text-decoration-style: dotted; - white-space: nowrap; } } } @@ -347,7 +335,7 @@ } &__modal { - height: 60vh; + height: 80vh; width: 50vw; &__body { @@ -363,6 +351,12 @@ background: unset; } + &__section-name { + font-size: 1.8rem; + font-weight: 700; + padding: 1rem; + } + &__sort { display: flex; width: 100%;