console: query auto population and usage

PR-URL: https://github.com/hasura/graphql-engine-mono/pull/6107
Co-authored-by: Rishichandra Wawhal <27274869+wawhal@users.noreply.github.com>
Co-authored-by: Abhijeet Khangarot <26903230+abhi40308@users.noreply.github.com>
GitOrigin-RevId: 1a6761de0db9f4ece8a26c9af54178c7f2461943
This commit is contained in:
nevermore 2022-09-30 22:02:50 +05:30 committed by hasura-bot
parent f7183302f5
commit c46a50d950
10 changed files with 42186 additions and 63 deletions

42084
console/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -48,6 +48,18 @@ const UNFOCUS_ROLE_HEADER = 'ApiExplorer/UNFOCUS_ROLE_HEADER';
const TRACK_RESPONSE_DETAILS = 'ApiExplorer/TRACK_RESPONSE_DETAILS';
const SET_FORCE_INTROSPECT_AT = 'ApiExplorer/FORCE_INTROSPECTION_AT';
export const setForceIntrospectAt = data => ({
type: SET_FORCE_INTROSPECT_AT,
data,
});
const SET_GRAPHIQL_QUERY = 'ApiExplorer/SET_GRAPHIQL_QUERY';
export const setGraphiQLQuery = data => ({
type: SET_GRAPHIQL_QUERY,
data,
});
let websocketSubscriptionClient;
const getSubscriptionInstance = (url, headers) => {
@ -721,6 +733,22 @@ const apiExplorerReducer = (state = defaultState, action) => {
},
},
};
case SET_FORCE_INTROSPECT_AT:
return {
...state,
graphiql: {
...state.graphiql,
forceIntrospectAt: action.data,
},
};
case SET_GRAPHIQL_QUERY:
return {
...state,
graphiql: {
...state.graphiql,
query: action.data,
},
};
default:
return state;
}

View File

@ -76,8 +76,16 @@ class GraphiQLWrapper extends Component {
};
render() {
const { numberOfTables, urlParams, headerFocus, dispatch, mode, loading } =
this.props;
const {
numberOfTables,
urlParams,
headerFocus,
dispatch,
mode,
loading,
query: queryFromProps,
forceIntrospectAt,
} = this.props;
const { codeExporterOpen, requestTrackingId } = this.state;
const graphqlNetworkData = this.props.data;
const { responseTime, responseSize, isResponseCached, responseTrackingId } =
@ -315,6 +323,8 @@ class GraphiQLWrapper extends Component {
numberOfTables={numberOfTables}
dispatch={dispatch}
mode={mode}
forceIntrospectAt={forceIntrospectAt}
query={queryFromProps}
/>
</div>
</GraphiQLErrorBoundary>
@ -329,11 +339,14 @@ GraphiQLWrapper.propTypes = {
headerFocus: PropTypes.bool.isRequired,
urlParams: PropTypes.object.isRequired,
response: PropTypes.object,
query: PropTypes.string,
};
const mapStateToProps = state => ({
mode: state.apiexplorer.mode,
loading: state.apiexplorer.loading,
query: state.apiexplorer.graphiql.query,
forceIntrospectAt: state.apiexplorer.graphiql.forceIntrospectAt,
});
const GraphiQLWrapperConnected = connect(mapStateToProps)(GraphiQLWrapper);

View File

@ -1,9 +1,13 @@
import { getLSItem, LS_KEYS } from '../../../../utils/localStorage';
import { getLSItem, setLSItem, LS_KEYS } from '../../../../utils/localStorage';
export const getGraphiQLQueryFromLocalStorage = () => {
return getLSItem(LS_KEYS.graphiqlQuery);
};
export const setGraphiQLQueryInLocalStorage = query => {
return setLSItem(LS_KEYS.graphiqlQuery, query);
};
export const clearCodeMirrorHints = () => {
const cmNodes = document.querySelectorAll('.CodeMirror-hints.graphiql');

View File

@ -29,7 +29,7 @@ class OneGraphExplorer extends React.Component {
explorerWidth: getExplorerWidth(),
explorerClientX: null,
schema: null,
query: undefined,
query: this.props.query || '',
isResizing: false,
previousIntrospectionHeaders: [],
};
@ -47,6 +47,7 @@ class OneGraphExplorer extends React.Component {
this.introspect();
return;
}
if (!headerFocus && !loading) {
if (
JSON.stringify(headers) !== JSON.stringify(previousIntrospectionHeaders)
@ -54,6 +55,21 @@ class OneGraphExplorer extends React.Component {
this.introspect();
}
}
// introspect by force if toggled through Redux
if (
this.props.forceIntrospectAt &&
this.props.forceIntrospectAt != prevProps.forceIntrospectAt
) {
this.introspect();
return;
}
// set query in graphiql through Redux
if (this.props.query != prevProps.query) {
// eslint-disable-next-line react/no-did-update-set-state
this.setState({ query: this.props.query });
}
}
setPersistedQuery() {

View File

@ -1,5 +1,6 @@
import GraphiQLExplorer from 'graphiql-explorer';
import { getLSItem, setLSItem, LS_KEYS } from '../../../../utils/localStorage';
import { setForceIntrospectAt, setGraphiQLQuery } from '../Actions';
export const makeDefaultArg = () => {
return false;
@ -49,3 +50,26 @@ export const getPersistedCodeExporterOpen = () => {
return false;
}
};
// Simulates the Run button click on the GraphiQL editor
export const clickRunQueryButton = () => {
const runQueryButton = document.getElementsByClassName('execute-button');
// trigger click
if (runQueryButton && runQueryButton[0]) {
runQueryButton[0].click();
} else {
// TODO throw Sentry alert
console.error('could not find run query button in the DOM');
}
};
// ATTENTION: use with care -- this function forces introspection in graphiql
export const forceGraphiQLIntrospection = dispatch => {
dispatch(setForceIntrospectAt(new Date().getTime().toString()));
};
// ATTENTION: use with care -- this function replaces the existing query in graphiql
export const forceChangeGraphiqlQuery = (query, dispatch) => {
dispatch(setGraphiQLQuery(query));
};

View File

@ -68,6 +68,10 @@ const defaultState = {
headerFocus: false,
mode: persistedGraphiqlMode,
loading: false,
graphiql: {
query: '',
forceIntrospectAt: undefined,
},
};
export default defaultState;

View File

@ -6,6 +6,7 @@ import { hasLuxFeatureAccess, isCloudConsole } from '@/utils/cloudConsole';
import { TopHeaderBar, ConnectDBScreen, TemplateSummary } from './components';
import { useWizardState } from './hooks';
import { NEON_TEMPLATE_BASE_PATH } from './constants';
import { GrowthExperimentsClient } from '../GrowthExperiments';
import { useFamiliaritySurveyData, HasuraFamiliaritySurvey } from '../Surveys';
@ -24,7 +25,10 @@ function Root(props: Props) {
const hasNeonAccess = hasLuxFeatureAccess(globals, 'NeonDatabaseIntegration');
// dialog cannot be reopened once closed
const { state, setState } = useWizardState(growthExperimentsClient);
const { state, setState } = useWizardState(
growthExperimentsClient,
hasNeonAccess
);
const {
showFamiliaritySurvey,
@ -33,8 +37,7 @@ function Root(props: Props) {
onOptionClick: familiaritySurveyOnOptionClick,
} = useFamiliaritySurveyData();
const templateBaseUrl =
'https://raw.githubusercontent.com/hasura/template-gallery/main/postgres/getting-started';
const templateBaseUrl = NEON_TEMPLATE_BASE_PATH;
const transitionToTemplateSummary = () => {
setState('template-summary');
@ -77,7 +80,11 @@ function Root(props: Props) {
}
case 'template-summary': {
return (
<TemplateSummary templateUrl={templateBaseUrl} dismiss={dismiss} />
<TemplateSummary
templateUrl={templateBaseUrl}
dismiss={dismiss}
dispatch={dispatch}
/>
);
}
case 'hidden':

View File

@ -1,8 +1,8 @@
import * as React from 'react';
import { useQuery } from 'react-query';
import { tracingTools } from '@/features/TracingTools';
import { Dispatch } from '@/types';
import {
NEON_ONBOARDING_QUERY_KEY,
staleTime,
templateSummaryRunQueryClickVariables,
templateSummaryRunQuerySkipVariables,
@ -11,6 +11,8 @@ import { QueryDialog } from './QueryDialog';
import {
fetchTemplateDataQueryFn,
getQueryFromSampleQueries,
runQueryInGraphiQL,
fillSampleQueryInGraphiQL,
emitOnboardingEvent,
} from '../../utils';
@ -34,25 +36,21 @@ query lookupArtist {
}
`;
// TODO use an actual function
const runSampleQueryInGraphiQL = (query: string) => {
return Promise.resolve(query);
};
type Props = {
templateUrl: string;
dismiss: VoidFunction;
dispatch: Dispatch;
};
export function TemplateSummary(props: Props) {
const { templateUrl, dismiss } = props;
const { templateUrl, dismiss, dispatch } = props;
const schemaImagePath = `${templateUrl}/diagram.png`;
const sampleQueriesPath = `${templateUrl}/sample.graphql`;
const [sampleQuery, setSampleQuery] = React.useState(defaultQuery);
useQuery({
queryKey: [NEON_ONBOARDING_QUERY_KEY, sampleQueriesPath],
const { data: sampleQueriesData } = useQuery({
queryKey: sampleQueriesPath,
queryFn: () => fetchTemplateDataQueryFn(sampleQueriesPath, {}),
staleTime,
onSuccess: (allQueries: string) => {
@ -86,13 +84,19 @@ export function TemplateSummary(props: Props) {
},
});
// this effect makes sure that the query is filled in GraphiQL as soon as possible
React.useEffect(() => {
if (sampleQueriesData) {
fillSampleQueryInGraphiQL(sampleQuery, dispatch);
}
}, [sampleQueriesData, sampleQuery]);
// this runs the query that is prefilled in graphiql
const onRunHandler = () => {
emitOnboardingEvent(templateSummaryRunQueryClickVariables);
runSampleQueryInGraphiQL(sampleQuery).then(() => {
dismiss();
});
runQueryInGraphiQL();
dismiss();
};
const onSkipHandler = () => {
emitOnboardingEvent(templateSummaryRunQuerySkipVariables);
dismiss();

View File

@ -1,8 +1,14 @@
import { parse, print } from 'graphql';
import { ExperimentConfig } from '@/features/GrowthExperiments';
import { Dispatch } from '@/types';
import { cloudDataServiceApiClient } from '@/hooks/cloudDataServiceApiClient';
import { Api } from '@/hooks/apiUtils';
import { HasuraMetadataV3 } from '@/metadata/types';
import {
clickRunQueryButton,
forceGraphiQLIntrospection,
forceChangeGraphiqlQuery,
} from '../../components/Services/ApiExplorer/OneGraphExplorer/utils';
import {
skippedOnboardingVariables,
onboardingCompleteVariables,
@ -189,3 +195,20 @@ export function getQueryFromSampleQueries(
definitions: [queryDef],
});
}
export const runQueryInGraphiQL = () => {
clickRunQueryButton();
};
export const fillSampleQueryInGraphiQL = (
query: string,
dispatch: Dispatch
) => {
forceGraphiQLIntrospection(dispatch);
// this timeout makes sure that there's a delay in setting query after introspection has been fired
// this timeout does not intend to wait for introspection to finish
setTimeout(() => {
forceChangeGraphiqlQuery(query, dispatch);
}, 500);
};