mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-10-26 10:20:54 +03:00
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:
parent
f7183302f5
commit
c46a50d950
42084
console/package-lock.json
generated
42084
console/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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');
|
||||
|
||||
|
@ -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() {
|
||||
|
@ -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));
|
||||
};
|
||||
|
@ -68,6 +68,10 @@ const defaultState = {
|
||||
headerFocus: false,
|
||||
mode: persistedGraphiqlMode,
|
||||
loading: false,
|
||||
graphiql: {
|
||||
query: '',
|
||||
forceIntrospectAt: undefined,
|
||||
},
|
||||
};
|
||||
|
||||
export default defaultState;
|
||||
|
@ -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':
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user