Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add query assist to query enhancements plugin #14

Merged
merged 10 commits into from
Jun 25, 2024
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
/build
/target
/target
/coverage/
25 changes: 25 additions & 0 deletions babel.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

// babelrc doesn't respect NODE_PATH anymore but using require does.
// Alternative to install them locally in node_modules
module.exports = function (api) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure, i'm getting

ERROR [queryEnhancements] depends on [@osd/babel-preset] but it's not using the local package. Update its package.json to the expected value below.
 info Additional debugging info:

 │ info actual: "@osd/babel-preset": "1.0.0"
 │      expected: "@osd/babel-preset": "link:../../packages/osd-babel-preset"

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh wow. we might need to open a bug into core.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// ensure env is test so that this config won't impact build or dev server
if (api.env('test')) {
return {
presets: [
require('@babel/preset-env', {
useBuiltIns: false,
targets: {
node: 'current',
},
}),
require('@babel/preset-react'),
require('@babel/preset-typescript'),
],
};
}
return {};
};
18 changes: 18 additions & 0 deletions common/query_assist/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { TimeRange } from '../../../../src/plugins/data/common';

export const ERROR_DETAILS = { GUARDRAILS_TRIGGERED: 'guardrails triggered' };
joshuali925 marked this conversation as resolved.
Show resolved Hide resolved

export const SUPPORTED_LANGUAGES = ['PPL'] as const;
joshuali925 marked this conversation as resolved.
Show resolved Hide resolved

export interface QueryAssistResponse {
joshuali925 marked this conversation as resolved.
Show resolved Hide resolved
query: string;
timeRange?: TimeRange;
}

export interface QueryAssistParameters {
question: string;
index: string;
language: string;
// for MDS
dataSourceId?: string;
}
2 changes: 1 addition & 1 deletion opensearch_dashboards.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@
"ui": true,
"requiredPlugins": ["data"],
"optionalPlugins": ["dataSource", "dataSourceManagement"],
"requiredBundles": ["opensearchDashboardsUtils"]
"requiredBundles": ["opensearchDashboardsUtils", "opensearchDashboardsReact"]
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"scripts": {
"build": "yarn plugin-helpers build",
"plugin-helpers": "node ../../scripts/plugin_helpers",
"test": "../../node_modules/.bin/jest --config ./test/jest.config.js",
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

similar to below will yarn test:jest not capture these tests?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in queryEnhancements yarn test:jest is not defined

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah i mean like like if i was in the root repo and ran like

yarn test:jest {pathtotest} does it work?

"osd": "node ../../scripts/osd"
},
"dependencies": {},
Expand Down
18 changes: 18 additions & 0 deletions public/assets/query_assist_logo.svg
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: is this technically a mark?
nit: i think we can either remove the logo or call it mark to match the pattern:
https://github.com/opensearch-project/OpenSearch-Dashboards/blob/main/src/core/server/core_app/assets/logos/opensearch_mark.svg

so like query_assist_mark.svg

and to match the current strucutre can we add duplicates for light and dark ? im not sure if there is a light mode or dark mode. might need ux feedback.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

opensearch_mark is inside the logo directory, i'm not really sure what the difference is between a mark and a logo, but i'll rename it to mark. currently we only have one version, i can duplicate but why are there three versions: mark, mark_on_light, mark_on_dark?

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions public/plugin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import moment from 'moment';
import { CoreSetup, CoreStart, Plugin } from '../../../src/core/public';
import { IStorageWrapper, Storage } from '../../../src/plugins/opensearch_dashboards_utils/public';
import { createQueryAssistExtension } from './query_assist';
import { PPLSearchInterceptor, SQLSearchInterceptor } from './search';
import { setData, setStorage } from './services';
import {
Expand Down Expand Up @@ -84,6 +85,12 @@ export class QueryEnhancementsPlugin
},
});

data.__enhance({
ui: {
queryEditorExtension: createQueryAssistExtension(core.http),
},
});

return {};
}

Expand Down
163 changes: 163 additions & 0 deletions public/query_assist/components/__snapshots__/call_outs.test.tsx.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`CallOuts spec should display empty_index call out 1`] = `
<div>
<div
class="euiCallOut euiCallOut--warning euiCallOut--small"
data-test-subj="query-assist-empty-index-callout"
>
<div
class="euiCallOutHeader"
>
<svg
aria-hidden="true"
class="euiIcon euiIcon--medium euiIcon--inherit euiCallOutHeader__icon"
focusable="false"
height="16"
role="img"
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M7.5 11.508 7.468 8H6.25V7h2.401l.03 3.508H9.8v1H7.5Zm-.25-6.202a.83.83 0 0 1 .207-.577c.137-.153.334-.229.59-.229.256 0 .454.076.594.23.14.152.209.345.209.576 0 .228-.07.417-.21.568-.14.15-.337.226-.593.226-.256 0-.453-.075-.59-.226a.81.81 0 0 1-.207-.568ZM8 13A5 5 0 1 0 8 3a5 5 0 0 0 0 10Zm0 1A6 6 0 1 1 8 2a6 6 0 0 1 0 12Z"
fill-rule="evenodd"
/>
</svg>
<span
class="euiCallOutHeader__title"
>
<span>
Select a data source or index to ask a question.
</span>
</span>
</div>
</div>
</div>
`;

exports[`CallOuts spec should display empty_query call out 1`] = `
<div>
<div
class="euiCallOut euiCallOut--warning euiCallOut--small"
data-test-subj="query-assist-empty-query-callout"
>
<div
class="euiCallOutHeader"
>
<svg
aria-hidden="true"
class="euiIcon euiIcon--medium euiIcon--inherit euiIcon-isLoading euiCallOutHeader__icon"
focusable="false"
height="16"
role="img"
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M5.277 10.088c.02.014.04.03.057.047.582.55 1.134.812 1.666.812.586 0 1.84-.293 3.713-.88L9 6.212V2H7v4.212l-1.723 3.876Zm-.438.987L3.539 14h8.922l-1.32-2.969C9.096 11.677 7.733 12 7 12c-.74 0-1.463-.315-2.161-.925ZM6 2H5V1h6v1h-1v4l3.375 7.594A1 1 0 0 1 12.461 15H3.54a1 1 0 0 1-.914-1.406L6 6V2Z"
/>
</svg>
<span
class="euiCallOutHeader__title"
>
<span>
Enter a natural language question to automatically generate a query to view results.
</span>
</span>
</div>
</div>
</div>
`;

exports[`CallOuts spec should display invalid_query call out 1`] = `
<div>
<div
class="euiCallOut euiCallOut--danger euiCallOut--small"
data-test-subj="query-assist-guard-callout"
>
<div
class="euiCallOutHeader"
>
<svg
aria-hidden="true"
class="euiIcon euiIcon--medium euiIcon--inherit euiIcon-isLoading euiCallOutHeader__icon"
focusable="false"
height="16"
role="img"
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M5.277 10.088c.02.014.04.03.057.047.582.55 1.134.812 1.666.812.586 0 1.84-.293 3.713-.88L9 6.212V2H7v4.212l-1.723 3.876Zm-.438.987L3.539 14h8.922l-1.32-2.969C9.096 11.677 7.733 12 7 12c-.74 0-1.463-.315-2.161-.925ZM6 2H5V1h6v1h-1v4l3.375 7.594A1 1 0 0 1 12.461 15H3.54a1 1 0 0 1-.914-1.406L6 6V2Z"
/>
</svg>
<span
class="euiCallOutHeader__title"
>
<span>
I am unable to respond to this query. Try another question.
</span>
</span>
</div>
</div>
</div>
`;

exports[`CallOuts spec should display query_generated call out 1`] = `
<div>
<div
class="euiCallOut euiCallOut--success euiCallOut--small"
data-test-subj="query-assist-query-generated-callout"
>
<div
class="euiCallOutHeader"
>
<svg
aria-hidden="true"
class="euiIcon euiIcon--medium euiIcon--inherit euiIcon-isLoading euiCallOutHeader__icon"
focusable="false"
height="16"
role="img"
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M5.277 10.088c.02.014.04.03.057.047.582.55 1.134.812 1.666.812.586 0 1.84-.293 3.713-.88L9 6.212V2H7v4.212l-1.723 3.876Zm-.438.987L3.539 14h8.922l-1.32-2.969C9.096 11.677 7.733 12 7 12c-.74 0-1.463-.315-2.161-.925ZM6 2H5V1h6v1h-1v4l3.375 7.594A1 1 0 0 1 12.461 15H3.54a1 1 0 0 1-.914-1.406L6 6V2Z"
/>
</svg>
<span
class="euiCallOutHeader__title"
>
<span>
test lang query generated. If there are any issues with the response, try adding more context to the question or a new question to submit.
</span>
</span>
</div>
<button
aria-label="dismissible_icon"
class="euiButtonIcon euiButtonIcon--primary euiButtonIcon--empty euiButtonIcon--xSmall euiCallOut__closeIcon"
data-test-subj="closeCallOutButton"
type="button"
>
<svg
aria-hidden="true"
class="euiIcon euiIcon--medium euiIcon--inherit euiIcon-isLoading euiButtonIcon__icon"
focusable="false"
height="16"
role="img"
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M5.277 10.088c.02.014.04.03.057.047.582.55 1.134.812 1.666.812.586 0 1.84-.293 3.713-.88L9 6.212V2H7v4.212l-1.723 3.876Zm-.438.987L3.539 14h8.922l-1.32-2.969C9.096 11.677 7.733 12 7 12c-.74 0-1.463-.315-2.161-.925ZM6 2H5V1h6v1h-1v4l3.375 7.594A1 1 0 0 1 12.461 15H3.54a1 1 0 0 1-.914-1.406L6 6V2Z"
/>
</svg>
</button>
</div>
</div>
`;
46 changes: 46 additions & 0 deletions public/query_assist/components/call_outs.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { render } from '@testing-library/react';
import React, { ComponentProps } from 'react';
import { QueryAssistCallOut } from './call_outs';

type Props = ComponentProps<typeof QueryAssistCallOut>;

const renderCallOut = (overrideProps: Partial<Props> = {}) => {
const props: Props = Object.assign<Props, Partial<Props>>(
{
type: 'empty_query',
language: 'test lang',
onDismiss: jest.fn(),
},
overrideProps
);
const component = render(<QueryAssistCallOut {...props} />);
return { component, props: props as jest.MockedObjectDeep<Props> };
};

describe('CallOuts spec', () => {
it('should display nothing if type is invalid', () => {
// @ts-expect-error testing invalid type
const { component } = renderCallOut({ type: '' });
expect(component.container).toBeEmptyDOMElement();
});

it('should display empty_query call out', () => {
const { component } = renderCallOut({ type: 'empty_query' });
expect(component.container).toMatchSnapshot();
});

it('should display empty_index call out', () => {
const { component } = renderCallOut({ type: 'empty_index' });
expect(component.container).toMatchSnapshot();
});

it('should display invalid_query call out', () => {
const { component } = renderCallOut({ type: 'invalid_query' });
expect(component.container).toMatchSnapshot();
});

it('should display query_generated call out', () => {
const { component } = renderCallOut({ type: 'query_generated' });
expect(component.container).toMatchSnapshot();
});
});
Loading