Skip to content

Commit

Permalink
feat(compass-saved-aggregations-queries): update namespace for saved …
Browse files Browse the repository at this point in the history
…aggregations/queries (COMPASS-7665) (#5462)
  • Loading branch information
baileympearson authored Feb 27, 2024
1 parent f0655ab commit 7d3b82a
Show file tree
Hide file tree
Showing 3 changed files with 194 additions and 6 deletions.
113 changes: 113 additions & 0 deletions packages/compass-e2e-tests/tests/instance-my-queries-tab.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -244,4 +244,117 @@ describe('Instance my queries tab', function () {
const namespace = await browser.getActiveTabNamespace();
expect(namespace).to.equal('test.numbers');
});

context(
'when a user has a saved query associated with a collection that does not exist',
function () {
const favoriteQueryName = 'list of numbers greater than 10 - query';
const newCollectionName = 'numbers-renamed';

/** saves a query and renames the collection associated with the query, so that the query must be opened with the "select namespace" modal */
async function setup() {
// Run a query
await browser.navigateToCollectionTab('test', 'numbers', 'Documents');
await browser.runFindOperation('Documents', `{i: {$gt: 10}}`, {
limit: '10',
});
await browser.clickVisible(Selectors.QueryBarHistoryButton);

// Wait for the popover to show
const history = await browser.$(Selectors.QueryBarHistory);
await history.waitForDisplayed();

// wait for the recent item to show.
const recentCard = await browser.$(Selectors.QueryHistoryRecentItem);
await recentCard.waitForDisplayed();

// Save the ran query
await browser.hover(Selectors.QueryHistoryRecentItem);
await browser.clickVisible(Selectors.QueryHistoryFavoriteAnItemButton);
await browser.setValueVisible(
Selectors.QueryHistoryFavoriteItemNameField,
favoriteQueryName
);
await browser.clickVisible(
Selectors.QueryHistorySaveFavoriteItemButton
);

await browser.closeWorkspaceTabs();
await browser.navigateToInstanceTab('Databases');
await browser.navigateToInstanceTab('My Queries');

// open the menu
await openMenuForQueryItem(browser, favoriteQueryName);

// copy to clipboard
await browser.clickVisible(Selectors.SavedItemMenuItemCopy);

if (process.env.COMPASS_E2E_DISABLE_CLIPBOARD_USAGE !== 'true') {
await browser.waitUntil(
async () => {
const text = (await clipboard.read())
.replace(/\s+/g, ' ')
.replace(/\n/g, '');
const isValid =
text ===
'{ "collation": null, "filter": { "i": { "$gt": 10 } }, "limit": 10, "project": null, "skip": null, "sort": null }';
if (!isValid) {
console.log(text);
}
return isValid;
},
{ timeoutMsg: 'Expected copy to clipboard to work' }
);
}

// rename the collection associated with the query to force the open item modal
await browser.shellEval('use test');
await browser.shellEval(
`db.numbers.renameCollection('${newCollectionName}')`
);
await browser.clickVisible(Selectors.SidebarRefreshDatabasesButton);
}
beforeEach(setup);

it('users can permanently associate a new namespace for an aggregation/query', async function () {
await browser.navigateToInstanceTab('My Queries');
// browse to the query
await browser.clickVisible(Selectors.myQueriesItem(favoriteQueryName));

// the open item modal - select a new collection
const openModal = await browser.$(Selectors.OpenSavedItemModal);
await openModal.waitForDisplayed();
await browser.selectOption(
Selectors.OpenSavedItemDatabaseField,
'test'
);
await browser.selectOption(
Selectors.OpenSavedItemCollectionField,
newCollectionName
);

await browser.clickParent(
'[data-testid="update-query-aggregation-checkbox"]'
);

const confirmOpenButton = await browser.$(
Selectors.OpenSavedItemModalConfirmButton
);
await confirmOpenButton.waitForEnabled();

await confirmOpenButton.click();
await openModal.waitForDisplayed({ reverse: true });

await browser.navigateToInstanceTab('My Queries');

const [databaseNameElement, collectionNameElement] = [
await browser.$('span=test'),
await browser.$(`span=${newCollectionName}`),
];

await databaseNameElement.waitForDisplayed();
await collectionNameElement.waitForDisplayed();
});
}
);
});
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react';
import {
Checkbox,
FormModal,
Option,
Select,
Expand All @@ -14,6 +15,7 @@ import {
openSelectedItem,
selectCollection,
selectDatabase,
updateItemNamespaceChecked,
} from '../stores/open-item';

type AsyncItemsSelectProps = {
Expand Down Expand Up @@ -98,15 +100,18 @@ type OpenItemModalProps = {
itemName: string;
isModalOpen: boolean;
isSubmitDisabled: boolean;
updateItemNamespace: boolean;
onSubmit(): void;
onClose(): void;
onUpdateNamespaceChecked(checked: boolean): void;
};

const modalContent = css({
display: 'grid',
gridTemplateAreas: `
'description description'
'database collection'
'checkbox checkbox'
`,
gridAutoColumns: '1fr',
rowGap: spacing[4],
Expand All @@ -125,20 +130,26 @@ const collectionSelect = css({
gridArea: 'collection',
});

const checkbox = css({
gridArea: 'checkbox',
});

const OpenItemModal: React.FunctionComponent<OpenItemModalProps> = ({
namespace,
itemType,
itemName,
isModalOpen,
isSubmitDisabled,
updateItemNamespace,
onClose,
onSubmit,
onUpdateNamespaceChecked,
}) => {
return (
<FormModal
open={isModalOpen}
onCancel={onClose}
onSubmit={onSubmit}
onSubmit={() => onSubmit()}
title="Select a Namespace"
submitButtonText="Open"
submitDisabled={isSubmitDisabled}
Expand All @@ -161,6 +172,15 @@ const OpenItemModal: React.FunctionComponent<OpenItemModalProps> = ({
label="Collection"
></CollectionSelect>
</div>
<Checkbox
className={checkbox}
checked={updateItemNamespace}
onChange={(event) => {
onUpdateNamespaceChecked(event.target.checked);
}}
label={`Update this ${itemType} with the newly selected namespace`}
data-testid="update-query-aggregation-checkbox"
/>
</div>
</FormModal>
);
Expand All @@ -169,7 +189,12 @@ const OpenItemModal: React.FunctionComponent<OpenItemModalProps> = ({
const mapState: MapStateToProps<
Pick<
OpenItemModalProps,
'isModalOpen' | 'isSubmitDisabled' | 'namespace' | 'itemType' | 'itemName'
| 'isModalOpen'
| 'isSubmitDisabled'
| 'namespace'
| 'itemType'
| 'itemName'
| 'updateItemNamespace'
>,
Record<string, never>,
RootState
Expand All @@ -179,6 +204,7 @@ const mapState: MapStateToProps<
selectedDatabase,
selectedCollection,
selectedItem: item,
updateItemNamespace,
},
}) => {
return {
Expand All @@ -187,15 +213,17 @@ const mapState: MapStateToProps<
namespace: `${item?.database ?? ''}.${item?.collection ?? ''}`,
itemName: item?.name ?? '',
itemType: item?.type ?? '',
updateItemNamespace,
};
};

const mapDispatch: MapDispatchToProps<
Pick<OpenItemModalProps, 'onSubmit' | 'onClose'>,
Pick<OpenItemModalProps, 'onSubmit' | 'onClose' | 'onUpdateNamespaceChecked'>,
Record<string, never>
> = {
onSubmit: openSelectedItem,
onClose: closeModal,
onUpdateNamespaceChecked: updateItemNamespaceChecked,
};

export default connect(mapState, mapDispatch)(OpenItemModal);
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export type State = {
collections: string[];
selectedCollection: string | null;
collectionsStatus: Status;
updateItemNamespace: boolean;
};

const INITIAL_STATE: State = {
Expand All @@ -26,6 +27,7 @@ const INITIAL_STATE: State = {
collections: [],
selectedCollection: null,
collectionsStatus: 'initial',
updateItemNamespace: false,
};

export enum ActionTypes {
Expand All @@ -40,6 +42,7 @@ export enum ActionTypes {
LoadCollections = 'compass-saved-aggregations-queries/loadCollections',
LoadCollectionsSuccess = 'compass-saved-aggregations-queries/loadCollectionsSuccess',
LoadCollectionsError = 'compass-saved-aggregations-queries/loadCollectionsError',
UpdateNamespaceChecked = 'compass-saved-aggregations-queries/updateNamespaceChecked',
}

type OpenModalAction = {
Expand Down Expand Up @@ -92,6 +95,11 @@ type LoadCollectionsErrorAction = {
type: ActionTypes.LoadCollectionsError;
};

type UpdateNamespaceChecked = {
type: ActionTypes.UpdateNamespaceChecked;
updateItemNamespace: boolean;
};

export type Actions =
| OpenModalAction
| CloseModalAction
Expand All @@ -103,7 +111,8 @@ export type Actions =
| SelectCollectionAction
| LoadCollectionsAction
| LoadCollectionsErrorAction
| LoadCollectionsSuccessAction;
| LoadCollectionsSuccessAction
| UpdateNamespaceChecked;

const reducer: Reducer<State> = (state = INITIAL_STATE, action) => {
switch (action.type) {
Expand Down Expand Up @@ -165,11 +174,21 @@ const reducer: Reducer<State> = (state = INITIAL_STATE, action) => {
collections: action.collections,
collectionsStatus: 'ready',
};
case ActionTypes.UpdateNamespaceChecked:
return {
...state,
updateItemNamespace: action.updateItemNamespace,
};
default:
return state;
}
};

export const updateItemNamespaceChecked = (updateItemNamespace: boolean) => ({
type: ActionTypes.UpdateNamespaceChecked,
updateItemNamespace,
});

const openModal =
(selectedItem: Item): SavedQueryAggregationThunkAction<Promise<void>> =>
async (dispatch, _getState, { instance, dataService }) => {
Expand Down Expand Up @@ -249,16 +268,44 @@ export const openSavedItem =
dispatch(openItem(item, database, collection));
};

export const updateNamespaceChecked =
(updateNamespaceChecked: boolean): SavedQueryAggregationThunkAction<void> =>
(dispatch) => {
dispatch({
type: ActionTypes.UpdateNamespaceChecked,
updateNamespaceChecked,
});
};

export const openSelectedItem =
(): SavedQueryAggregationThunkAction<void> => (dispatch, getState) => {
(): SavedQueryAggregationThunkAction<Promise<void>> =>
async (dispatch, getState, { queryStorage, pipelineStorage }) => {
const {
openItem: { selectedItem, selectedDatabase, selectedCollection },
openItem: {
selectedItem,
selectedDatabase,
selectedCollection,
updateItemNamespace,
},
} = getState();

if (!selectedItem || !selectedDatabase || !selectedCollection) {
return;
}

if (updateItemNamespace) {
const id = selectedItem.id;
const newNamespace = `${selectedDatabase}.${selectedCollection}`;

if (selectedItem.type === 'aggregation') {
await pipelineStorage?.updateAttributes(id, {
namespace: newNamespace,
});
} else if (selectedItem.type === 'query') {
await queryStorage?.updateAttributes(id, { _ns: newNamespace });
}
}

dispatch({ type: ActionTypes.CloseModal });
dispatch(openItem(selectedItem, selectedDatabase, selectedCollection));
};
Expand Down

0 comments on commit 7d3b82a

Please sign in to comment.