mirror of
https://github.com/graphql-hive/console
synced 2026-04-21 14:37:17 +00:00
refactor(laboratory): rename preflight-script to just preflight (#6470)
This commit is contained in:
parent
9eeaf7a9a6
commit
4c2550551e
10 changed files with 165 additions and 168 deletions
5
.vscode/settings.json
vendored
5
.vscode/settings.json
vendored
|
|
@ -2,7 +2,10 @@
|
|||
"search.exclude": {
|
||||
"**/dist": true,
|
||||
"**/pnpm-lock.yaml": true,
|
||||
"**/*.tsbuildinfo": true
|
||||
"**/*.tsbuildinfo": true,
|
||||
"**/*.next": true,
|
||||
"**/*.turbo": true,
|
||||
"**/web/docs/out": true
|
||||
},
|
||||
"commands.commands": [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import { dedent } from '../support/testkit';
|
||||
|
||||
const selectors = {
|
||||
buttonPreflightScript: '[aria-label*="Preflight Script"]',
|
||||
buttonModalCy: 'preflight-script-modal-button',
|
||||
buttonToggleCy: 'toggle-preflight-script',
|
||||
buttonGraphiQLPreflight: '[aria-label*="Preflight Script"]',
|
||||
buttonModalCy: 'preflight-modal-button',
|
||||
buttonToggleCy: 'toggle-preflight',
|
||||
buttonHeaders: '[data-name="headers"]',
|
||||
headersEditor: {
|
||||
textArea: '.graphiql-editor-tool .graphiql-editor:last-child textarea',
|
||||
|
|
@ -13,7 +13,7 @@ const selectors = {
|
|||
},
|
||||
|
||||
modal: {
|
||||
buttonSubmitCy: 'preflight-script-modal-submit',
|
||||
buttonSubmitCy: 'preflight-modal-submit',
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -27,7 +27,7 @@ beforeEach(() => {
|
|||
cy.setCookie('sRefreshToken', refreshToken);
|
||||
data.slug = slug;
|
||||
cy.visit(`/${slug}/laboratory`);
|
||||
cy.get(selectors.buttonPreflightScript).click();
|
||||
cy.get(selectors.buttonGraphiQLPreflight).click();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -52,7 +52,7 @@ function setMonacoEditorContents(editorCyName: string, text: string) {
|
|||
}
|
||||
|
||||
function setEditorScript(script: string) {
|
||||
setMonacoEditorContents('preflight-script-editor', script);
|
||||
setMonacoEditorContents('preflight-editor', script);
|
||||
}
|
||||
|
||||
describe('Laboratory > Preflight Script', () => {
|
||||
|
|
@ -60,22 +60,19 @@ describe('Laboratory > Preflight Script', () => {
|
|||
it('regression: loads even if local storage is set to {}', () => {
|
||||
window.localStorage.setItem('hive:laboratory:environment', '{}');
|
||||
cy.visit(`/${data.slug}/laboratory`);
|
||||
cy.get(selectors.buttonPreflightScript).click();
|
||||
cy.get(selectors.buttonGraphiQLPreflight).click();
|
||||
});
|
||||
it('mini script editor is read only', () => {
|
||||
cy.dataCy('toggle-preflight-script').click();
|
||||
cy.dataCy('toggle-preflight').click();
|
||||
// Wait loading disappears
|
||||
cy.dataCy('preflight-script-editor-mini').should('not.contain', 'Loading');
|
||||
cy.dataCy('preflight-editor-mini').should('not.contain', 'Loading');
|
||||
// Click
|
||||
cy.dataCy('preflight-script-editor-mini').click();
|
||||
cy.dataCy('preflight-editor-mini').click();
|
||||
// And type
|
||||
cy.dataCy('preflight-script-editor-mini').within(() => {
|
||||
cy.dataCy('preflight-editor-mini').within(() => {
|
||||
cy.get('textarea').type('🐝', { force: true });
|
||||
});
|
||||
cy.dataCy('preflight-script-editor-mini').should(
|
||||
'have.text',
|
||||
'Cannot edit in read-only editor',
|
||||
);
|
||||
cy.dataCy('preflight-editor-mini').should('have.text', 'Cannot edit in read-only editor');
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -84,25 +81,25 @@ describe('Preflight Script Modal', () => {
|
|||
const env = '{"foo":123}';
|
||||
|
||||
beforeEach(() => {
|
||||
cy.dataCy('preflight-script-modal-button').click();
|
||||
cy.dataCy('preflight-modal-button').click();
|
||||
setMonacoEditorContents('env-editor', env);
|
||||
});
|
||||
|
||||
it('save script and environment variables when submitting', () => {
|
||||
setEditorScript(script);
|
||||
cy.dataCy('preflight-script-modal-submit').click();
|
||||
cy.dataCy('preflight-modal-submit').click();
|
||||
cy.dataCy('env-editor-mini').should('have.text', env);
|
||||
cy.dataCy('toggle-preflight-script').click();
|
||||
cy.dataCy('preflight-script-editor-mini').should('have.text', script);
|
||||
cy.dataCy('toggle-preflight').click();
|
||||
cy.dataCy('preflight-editor-mini').should('have.text', script);
|
||||
cy.reload();
|
||||
cy.get('[aria-label*="Preflight Script"]').click();
|
||||
cy.dataCy('env-editor-mini').should('have.text', env);
|
||||
cy.dataCy('preflight-script-editor-mini').should('have.text', script);
|
||||
cy.dataCy('preflight-editor-mini').should('have.text', script);
|
||||
});
|
||||
|
||||
it('logs show console/error information', () => {
|
||||
setEditorScript(script);
|
||||
cy.dataCy('run-preflight-script').click();
|
||||
cy.dataCy('run-preflight').click();
|
||||
cy.dataCy('console-output').should('contain', 'log: Hello_world (1:1)');
|
||||
|
||||
setEditorScript(
|
||||
|
|
@ -112,7 +109,7 @@ console.error('Fatal')
|
|||
throw new TypeError('Test')`,
|
||||
);
|
||||
|
||||
cy.dataCy('run-preflight-script').click();
|
||||
cy.dataCy('run-preflight').click();
|
||||
// First log previous log message
|
||||
cy.dataCy('console-output').should('contain', 'log: Hello_world (1:1)');
|
||||
// After the new logs
|
||||
|
|
@ -125,7 +122,7 @@ throw new TypeError('Test')`,
|
|||
it('prompt and pass the awaited response', () => {
|
||||
setEditorScript(script);
|
||||
|
||||
cy.dataCy('run-preflight-script').click();
|
||||
cy.dataCy('run-preflight').click();
|
||||
cy.dataCy('console-output').should('contain', 'log: Hello_world (1:1)');
|
||||
|
||||
setEditorScript(
|
||||
|
|
@ -135,7 +132,7 @@ throw new TypeError('Test')`,
|
|||
`,
|
||||
);
|
||||
|
||||
cy.dataCy('run-preflight-script').click();
|
||||
cy.dataCy('run-preflight').click();
|
||||
cy.dataCy('prompt').get('input').type('test-username');
|
||||
cy.dataCy('prompt').get('form').submit();
|
||||
|
||||
|
|
@ -153,7 +150,7 @@ throw new TypeError('Test')`,
|
|||
it('prompt and cancel', () => {
|
||||
setEditorScript(script);
|
||||
|
||||
cy.dataCy('run-preflight-script').click();
|
||||
cy.dataCy('run-preflight').click();
|
||||
cy.dataCy('console-output').should('contain', 'log: Hello_world (1:1)');
|
||||
|
||||
setEditorScript(
|
||||
|
|
@ -163,7 +160,7 @@ throw new TypeError('Test')`,
|
|||
`,
|
||||
);
|
||||
|
||||
cy.dataCy('run-preflight-script').click();
|
||||
cy.dataCy('run-preflight').click();
|
||||
cy.dataCy('prompt').get('input').type('test-username');
|
||||
cy.dataCy('prompt').get('[data-cy="prompt-cancel"]').click();
|
||||
|
||||
|
|
@ -181,7 +178,7 @@ throw new TypeError('Test')`,
|
|||
it('script execution updates environment variables', () => {
|
||||
setEditorScript(`lab.environment.set('my-test', "TROLOLOL")`);
|
||||
|
||||
cy.dataCy('run-preflight-script').click();
|
||||
cy.dataCy('run-preflight').click();
|
||||
cy.dataCy('env-editor').should(
|
||||
'include.text',
|
||||
// replace space with
|
||||
|
|
@ -191,7 +188,7 @@ throw new TypeError('Test')`,
|
|||
|
||||
it('`crypto-js` can be used for generating hashes', () => {
|
||||
setEditorScript('console.log(lab.CryptoJS.SHA256("🐝"))');
|
||||
cy.dataCy('run-preflight-script').click();
|
||||
cy.dataCy('run-preflight').click();
|
||||
cy.dataCy('console-output').should('contain', 'info: Using crypto-js version:');
|
||||
cy.dataCy('console-output').should(
|
||||
'contain',
|
||||
|
|
@ -201,13 +198,13 @@ throw new TypeError('Test')`,
|
|||
|
||||
it('scripts can not use `eval`', () => {
|
||||
setEditorScript('eval()');
|
||||
cy.dataCy('preflight-script-modal-submit').click();
|
||||
cy.dataCy('preflight-modal-submit').click();
|
||||
cy.get('body').contains('Usage of dangerous statement like eval() or Function("").');
|
||||
});
|
||||
|
||||
it('invalid code is rejected and can not be saved', () => {
|
||||
setEditorScript('🐝');
|
||||
cy.dataCy('preflight-script-modal-submit').click();
|
||||
cy.dataCy('preflight-modal-submit').click();
|
||||
cy.get('body').contains("[1:1]: Illegal character '}");
|
||||
});
|
||||
});
|
||||
|
|
@ -289,7 +286,7 @@ describe('Execution', () => {
|
|||
});
|
||||
|
||||
it('header placeholders are substituted with environment variables', () => {
|
||||
cy.dataCy('toggle-preflight-script').click();
|
||||
cy.dataCy('toggle-preflight').click();
|
||||
cy.get('[data-name="headers"]').click();
|
||||
cy.get('.graphiql-editor-tool .graphiql-editor:last-child textarea').type(
|
||||
'{ "__test": "{{foo}} bar {{nonExist}}" }',
|
||||
|
|
@ -316,7 +313,7 @@ describe('Execution', () => {
|
|||
});
|
||||
|
||||
it('executed script updates update env editor and substitute headers', () => {
|
||||
cy.dataCy('toggle-preflight-script').click();
|
||||
cy.dataCy('toggle-preflight').click();
|
||||
cy.get('[data-name="headers"]').click();
|
||||
cy.get('.graphiql-editor-tool .graphiql-editor:last-child textarea').type(
|
||||
'{ "__test": "{{foo}}" }',
|
||||
|
|
@ -325,9 +322,9 @@ describe('Execution', () => {
|
|||
parseSpecialCharSequences: false,
|
||||
},
|
||||
);
|
||||
cy.dataCy('preflight-script-modal-button').click();
|
||||
setMonacoEditorContents('preflight-script-editor', `lab.environment.set('foo', '92')`);
|
||||
cy.dataCy('preflight-script-modal-submit').click();
|
||||
cy.dataCy('preflight-modal-button').click();
|
||||
setMonacoEditorContents('preflight-editor', `lab.environment.set('foo', '92')`);
|
||||
cy.dataCy('preflight-modal-submit').click();
|
||||
|
||||
cy.intercept({
|
||||
method: 'POST',
|
||||
|
|
@ -340,7 +337,7 @@ describe('Execution', () => {
|
|||
});
|
||||
|
||||
it('execute, prompt and use it in headers', () => {
|
||||
cy.dataCy('toggle-preflight-script').click();
|
||||
cy.dataCy('toggle-preflight').click();
|
||||
|
||||
cy.get('[data-name="headers"]').click();
|
||||
cy.get('[data-name="headers"]').click();
|
||||
|
|
@ -352,15 +349,15 @@ describe('Execution', () => {
|
|||
},
|
||||
);
|
||||
|
||||
cy.dataCy('preflight-script-modal-button').click();
|
||||
cy.dataCy('preflight-modal-button').click();
|
||||
setMonacoEditorContents(
|
||||
'preflight-script-editor',
|
||||
'preflight-editor',
|
||||
dedent`
|
||||
const username = await lab.prompt('Enter your username');
|
||||
lab.environment.set('username', username);
|
||||
`,
|
||||
);
|
||||
cy.dataCy('preflight-script-modal-submit').click();
|
||||
cy.dataCy('preflight-modal-submit').click();
|
||||
|
||||
cy.intercept({
|
||||
method: 'POST',
|
||||
|
|
@ -385,11 +382,11 @@ describe('Execution', () => {
|
|||
parseSpecialCharSequences: false,
|
||||
},
|
||||
);
|
||||
cy.dataCy('preflight-script-modal-button').click();
|
||||
setMonacoEditorContents('preflight-script-editor', `lab.environment.set('foo', 92)`);
|
||||
cy.dataCy('preflight-modal-button').click();
|
||||
setMonacoEditorContents('preflight-editor', `lab.environment.set('foo', 92)`);
|
||||
setMonacoEditorContents('env-editor', `{"foo":10}`);
|
||||
|
||||
cy.dataCy('preflight-script-modal-submit').click();
|
||||
cy.dataCy('preflight-modal-submit').click();
|
||||
|
||||
cy.intercept({
|
||||
method: 'POST',
|
||||
|
|
@ -402,11 +399,11 @@ describe('Execution', () => {
|
|||
});
|
||||
|
||||
it('logs are visible when opened', () => {
|
||||
cy.dataCy('toggle-preflight-script').click();
|
||||
cy.dataCy('toggle-preflight').click();
|
||||
|
||||
cy.dataCy('preflight-script-modal-button').click();
|
||||
cy.dataCy('preflight-modal-button').click();
|
||||
setMonacoEditorContents(
|
||||
'preflight-script-editor',
|
||||
'preflight-editor',
|
||||
dedent`
|
||||
console.info(1)
|
||||
console.warn(true)
|
||||
|
|
@ -414,18 +411,18 @@ describe('Execution', () => {
|
|||
throw new TypeError('Test')
|
||||
`,
|
||||
);
|
||||
cy.dataCy('preflight-script-modal-submit').click();
|
||||
cy.dataCy('preflight-modal-submit').click();
|
||||
|
||||
cy.intercept({
|
||||
method: 'POST',
|
||||
}).as('post');
|
||||
|
||||
// shows no logs before executing
|
||||
cy.get('#preflight-script-logs button[data-cy="trigger"]').click({
|
||||
cy.get('#preflight-logs button[data-cy="trigger"]').click({
|
||||
// it's because the button is not fully visible on the screen
|
||||
force: true,
|
||||
});
|
||||
cy.get('#preflight-script-logs [data-cy="logs"]').should(
|
||||
cy.get('#preflight-logs [data-cy="logs"]').should(
|
||||
'contain',
|
||||
['No logs available', 'Execute a query to see logs'].join(''),
|
||||
);
|
||||
|
|
@ -433,7 +430,7 @@ describe('Execution', () => {
|
|||
cy.get('.graphiql-execute-button').click();
|
||||
cy.wait('@post');
|
||||
|
||||
cy.get('#preflight-script-logs [data-cy="logs"]').should(
|
||||
cy.get('#preflight-logs [data-cy="logs"]').should(
|
||||
'contain',
|
||||
[
|
||||
'log: Running script...',
|
||||
|
|
@ -447,11 +444,11 @@ describe('Execution', () => {
|
|||
});
|
||||
|
||||
it('logs are cleared when requested', () => {
|
||||
cy.dataCy('toggle-preflight-script').click();
|
||||
cy.dataCy('toggle-preflight').click();
|
||||
|
||||
cy.dataCy('preflight-script-modal-button').click();
|
||||
cy.dataCy('preflight-modal-button').click();
|
||||
setMonacoEditorContents(
|
||||
'preflight-script-editor',
|
||||
'preflight-editor',
|
||||
dedent`
|
||||
console.info(1)
|
||||
console.warn(true)
|
||||
|
|
@ -459,7 +456,7 @@ describe('Execution', () => {
|
|||
throw new TypeError('Test')
|
||||
`,
|
||||
);
|
||||
cy.dataCy('preflight-script-modal-submit').click();
|
||||
cy.dataCy('preflight-modal-submit').click();
|
||||
|
||||
cy.intercept({
|
||||
method: 'POST',
|
||||
|
|
@ -468,12 +465,12 @@ describe('Execution', () => {
|
|||
cy.wait('@post');
|
||||
|
||||
// open logs
|
||||
cy.get('#preflight-script-logs button[data-cy="trigger"]').click({
|
||||
cy.get('#preflight-logs button[data-cy="trigger"]').click({
|
||||
// it's because the button is not fully visible on the screen
|
||||
force: true,
|
||||
});
|
||||
|
||||
cy.get('#preflight-script-logs [data-cy="logs"]').should(
|
||||
cy.get('#preflight-logs [data-cy="logs"]').should(
|
||||
'contain',
|
||||
[
|
||||
'log: Running script...',
|
||||
|
|
@ -485,8 +482,8 @@ describe('Execution', () => {
|
|||
].join(''),
|
||||
);
|
||||
|
||||
cy.get('#preflight-script-logs button[data-cy="erase-logs"]').click();
|
||||
cy.get('#preflight-script-logs [data-cy="logs"]').should(
|
||||
cy.get('#preflight-logs button[data-cy="erase-logs"]').click();
|
||||
cy.get('#preflight-logs [data-cy="logs"]').should(
|
||||
'contain',
|
||||
['No logs available', 'Execute a query to see logs'].join(''),
|
||||
);
|
||||
|
|
@ -8,6 +8,6 @@
|
|||
<a href="https://www.youtube.com/watch?v=CMNry4PE93Y" rel="nofollow">I like turtles</a>
|
||||
<a href="https://www.youtube.com/watch?v=XOi2jFIhZhA" rel="nofollow">Wheatherboi</a>
|
||||
|
||||
<script type="module" src="./src/lib/preflight-sandbox/preflight-worker-embed.ts"></script>
|
||||
<script type="module" src="./src/lib/preflight/preflight-worker-embed.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -38,12 +38,9 @@ import { cn } from '../utils';
|
|||
import labApiDefinitionRaw from './lab-api-declaration?raw';
|
||||
import { IFrameEvents, LogMessage } from './shared-types';
|
||||
|
||||
export type PreflightScriptResultData = Omit<
|
||||
IFrameEvents.Outgoing.EventData.Result,
|
||||
'type' | 'runId'
|
||||
>;
|
||||
export type PreflightResultData = Omit<IFrameEvents.Outgoing.EventData.Result, 'type' | 'runId'>;
|
||||
|
||||
export const preflightScriptPlugin: GraphiQLPlugin = {
|
||||
export const preflightPlugin: GraphiQLPlugin = {
|
||||
icon: () => (
|
||||
<svg
|
||||
viewBox="0 0 256 256"
|
||||
|
|
@ -59,7 +56,7 @@ export const preflightScriptPlugin: GraphiQLPlugin = {
|
|||
</svg>
|
||||
),
|
||||
title: 'Preflight Script',
|
||||
content: PreflightScriptContent,
|
||||
content: PreflightContent,
|
||||
};
|
||||
|
||||
const classes = {
|
||||
|
|
@ -147,14 +144,16 @@ export const enum PreflightWorkerState {
|
|||
ready,
|
||||
}
|
||||
|
||||
export function usePreflightScript(args: {
|
||||
export function usePreflight(args: {
|
||||
target: FragmentType<typeof PreflightScript_TargetFragment> | null;
|
||||
}) {
|
||||
const iframeRef = useRef<HTMLIFrameElement>(null);
|
||||
const prompt = usePromptManager();
|
||||
|
||||
const target = useFragment(PreflightScript_TargetFragment, args.target);
|
||||
const [isPreflightScriptEnabled, setIsPreflightScriptEnabled] = useLocalStorageJson(
|
||||
const [isEnabled, setIsEnabled] = useLocalStorageJson(
|
||||
// todo: ability to pass historical keys for seamless gradual migration to new key names.
|
||||
// 'hive:laboratory:isPreflightEnabled',
|
||||
'hive:laboratory:isPreflightScriptEnabled',
|
||||
z.boolean().default(false),
|
||||
);
|
||||
|
|
@ -170,13 +169,13 @@ export function usePreflightScript(args: {
|
|||
const [state, setState] = useState<PreflightWorkerState>(PreflightWorkerState.ready);
|
||||
const [logs, setLogs] = useState<LogRecord[]>([]);
|
||||
|
||||
const currentRun = useRef<null | Function>(null);
|
||||
const abortExecutionRef = useRef<null | (() => void)>(null);
|
||||
|
||||
async function execute(
|
||||
script = target?.preflightScript?.sourceCode ?? '',
|
||||
isPreview = false,
|
||||
): Promise<PreflightScriptResultData> {
|
||||
const resultEnvironmentVariablesDecoded: PreflightScriptResultData['environmentVariables'] =
|
||||
): Promise<PreflightResultData> {
|
||||
const resultEnvironmentVariablesDecoded: PreflightResultData['environmentVariables'] =
|
||||
Kit.tryOr(
|
||||
() => JSON.parse(latestEnvironmentVariablesRef.current),
|
||||
// todo: find a better solution than blowing away the user's
|
||||
|
|
@ -192,14 +191,14 @@ export function usePreflightScript(args: {
|
|||
//
|
||||
() => ({}),
|
||||
);
|
||||
const result: PreflightScriptResultData = {
|
||||
const result: PreflightResultData = {
|
||||
request: {
|
||||
headers: [],
|
||||
},
|
||||
environmentVariables: resultEnvironmentVariablesDecoded,
|
||||
};
|
||||
|
||||
if (isPreview === false && !isPreflightScriptEnabled) {
|
||||
if (isPreview === false && !isEnabled) {
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -226,7 +225,7 @@ export function usePreflightScript(args: {
|
|||
type: IFrameEvents.Incoming.Event.run,
|
||||
id,
|
||||
script,
|
||||
// Preflight Script has read/write relationship with environment variables.
|
||||
// Preflight has read/write relationship with environment variables.
|
||||
environmentVariables: result.environmentVariables,
|
||||
} satisfies IFrameEvents.Incoming.EventData,
|
||||
'*',
|
||||
|
|
@ -356,7 +355,7 @@ export function usePreflightScript(args: {
|
|||
|
||||
// Window message events can be emitted from unknowable sources.
|
||||
// For example when our e2e tests runs within Cypress GUI, we see a `MessageEvent` with `.data` of `{ vscodeScheduleAsyncWork: 3 }`.
|
||||
// Since we cannot know if the event source is Preflight Script, we cannot perform an exhaustive check.
|
||||
// Since we cannot know if the event source is Preflight, we cannot perform an exhaustive check.
|
||||
//
|
||||
// Kit.neverCase(ev.data);
|
||||
//
|
||||
|
|
@ -367,7 +366,7 @@ export function usePreflightScript(args: {
|
|||
}
|
||||
|
||||
window.addEventListener('message', eventHandler);
|
||||
currentRun.current = () => {
|
||||
abortExecutionRef.current = () => {
|
||||
contentWindow.postMessage({
|
||||
type: IFrameEvents.Incoming.Event.abort,
|
||||
id,
|
||||
|
|
@ -375,7 +374,7 @@ export function usePreflightScript(args: {
|
|||
|
||||
closedOpenedPrompts();
|
||||
|
||||
currentRun.current = null;
|
||||
abortExecutionRef.current = null;
|
||||
};
|
||||
|
||||
await isFinishedD.promise;
|
||||
|
|
@ -407,24 +406,24 @@ export function usePreflightScript(args: {
|
|||
}
|
||||
}
|
||||
|
||||
function abort() {
|
||||
currentRun.current?.();
|
||||
function abortExecution() {
|
||||
abortExecutionRef.current?.();
|
||||
}
|
||||
|
||||
// terminate worker when leaving laboratory
|
||||
useEffect(
|
||||
() => () => {
|
||||
currentRun.current?.();
|
||||
abortExecutionRef.current?.();
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
return {
|
||||
execute,
|
||||
abort,
|
||||
isPreflightScriptEnabled,
|
||||
setIsPreflightScriptEnabled,
|
||||
script: target?.preflightScript?.sourceCode ?? '',
|
||||
abortExecution,
|
||||
isEnabled,
|
||||
setIsEnabled,
|
||||
content: target?.preflightScript?.sourceCode ?? '',
|
||||
environmentVariables,
|
||||
setEnvironmentVariables,
|
||||
state,
|
||||
|
|
@ -447,15 +446,15 @@ export function usePreflightScript(args: {
|
|||
} as const;
|
||||
}
|
||||
|
||||
type PreflightScriptObject = ReturnType<typeof usePreflightScript>;
|
||||
type PreflightObject = ReturnType<typeof usePreflight>;
|
||||
|
||||
const PreflightScriptContext = createContext<PreflightScriptObject | null>(null);
|
||||
export const PreflightScriptProvider = PreflightScriptContext.Provider;
|
||||
const PreflightContext = createContext<PreflightObject | null>(null);
|
||||
export const PreflightProvider = PreflightContext.Provider;
|
||||
|
||||
function PreflightScriptContent() {
|
||||
const preflightScript = useContext(PreflightScriptContext);
|
||||
if (preflightScript === null) {
|
||||
throw new Error('PreflightScriptContent used outside PreflightScriptContext.Provider');
|
||||
function PreflightContent() {
|
||||
const preflight = useContext(PreflightContext);
|
||||
if (preflight === null) {
|
||||
throw new Error('PreflightContent used outside PreflightContext.Provider');
|
||||
}
|
||||
|
||||
const [showModal, toggleShowModal] = useToggle();
|
||||
|
|
@ -467,7 +466,7 @@ function PreflightScriptContent() {
|
|||
|
||||
const { toast } = useToast();
|
||||
|
||||
const handleScriptChange = useCallback(async (newValue = '') => {
|
||||
const handleContentChange = useCallback(async (newValue = '') => {
|
||||
const { data, error } = await mutate({
|
||||
input: {
|
||||
selector: params,
|
||||
|
|
@ -494,24 +493,24 @@ function PreflightScriptContent() {
|
|||
|
||||
return (
|
||||
<>
|
||||
<PreflightScriptModal
|
||||
<PreflightModal
|
||||
// to unmount on submit/close
|
||||
key={String(showModal)}
|
||||
isOpen={showModal}
|
||||
toggle={toggleShowModal}
|
||||
scriptValue={preflightScript.script}
|
||||
executeScript={value =>
|
||||
preflightScript.execute(value, true).catch(err => {
|
||||
execute={value =>
|
||||
preflight.execute(value, true).catch(err => {
|
||||
console.error(err);
|
||||
})
|
||||
}
|
||||
state={preflightScript.state}
|
||||
abortScriptRun={preflightScript.abort}
|
||||
logs={preflightScript.logs}
|
||||
clearLogs={preflightScript.clearLogs}
|
||||
onScriptValueChange={handleScriptChange}
|
||||
envValue={preflightScript.environmentVariables}
|
||||
onEnvValueChange={preflightScript.setEnvironmentVariables}
|
||||
state={preflight.state}
|
||||
abortExecution={preflight.abortExecution}
|
||||
logs={preflight.logs}
|
||||
clearLogs={preflight.clearLogs}
|
||||
content={preflight.content}
|
||||
onContentChange={handleContentChange}
|
||||
envValue={preflight.environmentVariables}
|
||||
onEnvValueChange={preflight.setEnvironmentVariables}
|
||||
/>
|
||||
<div className="graphiql-doc-explorer-title flex items-center justify-between gap-4">
|
||||
Preflight Script
|
||||
|
|
@ -520,7 +519,7 @@ function PreflightScriptContent() {
|
|||
size="icon-sm"
|
||||
className="size-auto gap-1"
|
||||
onClick={toggleShowModal}
|
||||
data-cy="preflight-script-modal-button"
|
||||
data-cy="preflight-modal-button"
|
||||
>
|
||||
<Pencil1Icon className="shrink-0" />
|
||||
Edit
|
||||
|
|
@ -536,13 +535,11 @@ function PreflightScriptContent() {
|
|||
size="sm"
|
||||
variant="outline"
|
||||
className="mt-3"
|
||||
onClick={() =>
|
||||
preflightScript.setIsPreflightScriptEnabled(!preflightScript.isPreflightScriptEnabled)
|
||||
}
|
||||
data-cy="toggle-preflight-script"
|
||||
onClick={() => preflight.setIsEnabled(!preflight.isEnabled)}
|
||||
data-cy="toggle-preflight"
|
||||
>
|
||||
<PowerIcon className="mr-2 size-4" />
|
||||
{preflightScript.isPreflightScriptEnabled ? 'On' : 'Off'}
|
||||
{preflight.isEnabled ? 'On' : 'Off'}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
|
|
@ -554,7 +551,7 @@ function PreflightScriptContent() {
|
|||
</EditorTitle>
|
||||
<Subtitle className="mb-3 cursor-not-allowed">Read-only view of the script</Subtitle>
|
||||
<div className="relative">
|
||||
{preflightScript.isPreflightScriptEnabled ? null : (
|
||||
{preflight.isEnabled ? null : (
|
||||
<div className="absolute inset-0 z-20 flex items-center justify-center bg-[#030711]/90 p-4 text-white">
|
||||
<div className="rounded-md bg-[#0f1520] p-4 text-sm">
|
||||
Preflight Script is disabled and will not be executed
|
||||
|
|
@ -563,11 +560,11 @@ function PreflightScriptContent() {
|
|||
)}
|
||||
<MonacoEditor
|
||||
height={128}
|
||||
value={preflightScript.script}
|
||||
value={preflight.content}
|
||||
{...monacoProps.script}
|
||||
className={cn(classes.monacoMini, 'z-10')}
|
||||
wrapperProps={{
|
||||
['data-cy']: 'preflight-script-editor-mini',
|
||||
['data-cy']: 'preflight-editor-mini',
|
||||
}}
|
||||
options={{
|
||||
...monacoProps.script.options,
|
||||
|
|
@ -592,8 +589,8 @@ function PreflightScriptContent() {
|
|||
</Subtitle>
|
||||
<MonacoEditor
|
||||
height={128}
|
||||
value={preflightScript.environmentVariables}
|
||||
onChange={value => preflightScript.setEnvironmentVariables(value ?? '')}
|
||||
value={preflight.environmentVariables}
|
||||
onChange={value => preflight.setEnvironmentVariables(value ?? '')}
|
||||
{...monacoProps.env}
|
||||
className={classes.monacoMini}
|
||||
wrapperProps={{
|
||||
|
|
@ -604,28 +601,28 @@ function PreflightScriptContent() {
|
|||
);
|
||||
}
|
||||
|
||||
function PreflightScriptModal({
|
||||
function PreflightModal({
|
||||
isOpen,
|
||||
toggle,
|
||||
scriptValue,
|
||||
executeScript,
|
||||
content,
|
||||
state,
|
||||
abortScriptRun,
|
||||
execute,
|
||||
abortExecution,
|
||||
logs,
|
||||
clearLogs,
|
||||
onScriptValueChange,
|
||||
onContentChange,
|
||||
envValue,
|
||||
onEnvValueChange,
|
||||
}: {
|
||||
isOpen: boolean;
|
||||
toggle: () => void;
|
||||
scriptValue?: string;
|
||||
executeScript: (script: string) => void;
|
||||
content?: string;
|
||||
state: PreflightWorkerState;
|
||||
abortScriptRun: () => void;
|
||||
execute: (script: string) => void;
|
||||
abortExecution: () => void;
|
||||
logs: Array<LogRecord>;
|
||||
clearLogs: () => void;
|
||||
onScriptValueChange: (value: string) => void;
|
||||
onContentChange: (value: string) => void;
|
||||
envValue: string;
|
||||
onEnvValueChange: (value: string) => void;
|
||||
}) {
|
||||
|
|
@ -653,7 +650,7 @@ function PreflightScriptModal({
|
|||
}, []);
|
||||
|
||||
const handleSubmit = useCallback(() => {
|
||||
onScriptValueChange(scriptEditorRef.current?.getValue() ?? '');
|
||||
onContentChange(scriptEditorRef.current?.getValue() ?? '');
|
||||
onEnvValueChange(envEditorRef.current?.getValue() ?? '');
|
||||
toggle();
|
||||
}, []);
|
||||
|
|
@ -668,7 +665,7 @@ function PreflightScriptModal({
|
|||
open={isOpen}
|
||||
onOpenChange={open => {
|
||||
if (!open) {
|
||||
abortScriptRun();
|
||||
abortExecution();
|
||||
}
|
||||
toggle();
|
||||
}}
|
||||
|
|
@ -706,13 +703,13 @@ function PreflightScriptModal({
|
|||
className="size-auto gap-1"
|
||||
onClick={() => {
|
||||
if (state === PreflightWorkerState.running) {
|
||||
abortScriptRun();
|
||||
abortExecution();
|
||||
return;
|
||||
}
|
||||
|
||||
executeScript(scriptEditorRef.current?.getValue() ?? '');
|
||||
execute(scriptEditorRef.current?.getValue() ?? '');
|
||||
}}
|
||||
data-cy="run-preflight-script"
|
||||
data-cy="run-preflight"
|
||||
>
|
||||
{state === PreflightWorkerState.running && (
|
||||
<>
|
||||
|
|
@ -729,7 +726,7 @@ function PreflightScriptModal({
|
|||
</Button>
|
||||
</div>
|
||||
<MonacoEditor
|
||||
value={scriptValue}
|
||||
value={content}
|
||||
beforeMount={handleMonacoEditorBeforeMount}
|
||||
onMount={handleScriptEditorDidMount}
|
||||
{...monacoProps.script}
|
||||
|
|
@ -738,7 +735,7 @@ function PreflightScriptModal({
|
|||
wordWrap: 'wordWrapColumn',
|
||||
}}
|
||||
wrapperProps={{
|
||||
['data-cy']: 'preflight-script-editor',
|
||||
['data-cy']: 'preflight-editor',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -792,14 +789,14 @@ function PreflightScriptModal({
|
|||
Changes made to this Preflight Script will apply to all users on your team using this
|
||||
target.
|
||||
</p>
|
||||
<Button type="button" onClick={toggle} data-cy="preflight-script-modal-cancel">
|
||||
<Button type="button" onClick={toggle} data-cy="preflight-modal-cancel">
|
||||
Close
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
variant="primary"
|
||||
onClick={handleSubmit}
|
||||
data-cy="preflight-script-modal-submit"
|
||||
data-cy="preflight-modal-submit"
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
|
||||
// The content of this file is used as a string
|
||||
// and feed into the context of the Monaco Editor and the Preflight Script.
|
||||
// and feed into the context of the Monaco Editor and Preflight.
|
||||
// The lack of `declare const lab: LabAPI` is intentional, to avoid messing up the global scope
|
||||
// of the web app codebase.
|
||||
// This could be a string in `graphiql-plugin.tsx`, but it's better to keep it in a separate file,
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import { Kit } from '../kit';
|
||||
import PreflightWorker from './preflight-script-worker?worker&inline';
|
||||
import PreflightWorker from './preflight-worker?worker&inline';
|
||||
import { IFrameEvents, WorkerEvents } from './shared-types';
|
||||
|
||||
function postMessage(data: IFrameEvents.Outgoing.EventData) {
|
||||
|
|
@ -73,7 +73,7 @@ function handleEvent(data: IFrameEvents.Incoming.EventData) {
|
|||
type: IFrameEvents.Outgoing.Event.error,
|
||||
runId,
|
||||
error: {
|
||||
message: `Preflight script execution timed out after ${PREFLIGHT_TIMEOUT / 1000} seconds`,
|
||||
message: `Preflight execution timed out after ${PREFLIGHT_TIMEOUT / 1000} seconds`,
|
||||
},
|
||||
});
|
||||
terminate();
|
||||
|
|
@ -94,7 +94,7 @@ async function execute(args: WorkerEvents.Incoming.EventData): Promise<void> {
|
|||
columnOffset: 'console.'.length,
|
||||
});
|
||||
// The messages should be streamed to the main thread as they occur not gathered and send to
|
||||
// the main thread at the end of the execution of the preflight script
|
||||
// the main thread at the end of preflight execution
|
||||
// const message: LogMessage = { level, message };
|
||||
postMessage({
|
||||
type: 'log',
|
||||
|
|
@ -38,11 +38,11 @@ import { useResetState } from '@/lib/hooks/use-reset-state';
|
|||
import {
|
||||
LogLine,
|
||||
LogRecord,
|
||||
preflightScriptPlugin,
|
||||
PreflightScriptProvider,
|
||||
PreflightScriptResultData,
|
||||
usePreflightScript,
|
||||
} from '@/lib/preflight-sandbox/graphiql-plugin';
|
||||
preflightPlugin,
|
||||
PreflightProvider,
|
||||
PreflightResultData,
|
||||
usePreflight,
|
||||
} from '@/lib/preflight/graphiql-plugin';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { explorerPlugin } from '@graphiql/plugin-explorer';
|
||||
import {
|
||||
|
|
@ -64,7 +64,7 @@ import { Kit } from '@/lib/kit';
|
|||
const explorer = explorerPlugin();
|
||||
|
||||
// Declare outside components, otherwise while clicking on field in explorer operationCollectionsPlugin will be open
|
||||
const plugins = [explorer, operationCollectionsPlugin, preflightScriptPlugin];
|
||||
const plugins = [explorer, operationCollectionsPlugin, preflightPlugin];
|
||||
|
||||
function Share(): ReactElement | null {
|
||||
const label = 'Share query';
|
||||
|
|
@ -315,7 +315,7 @@ function LaboratoryPageContent(props: {
|
|||
const mockEndpoint = `${location.origin}/api/lab/${props.organizationSlug}/${props.projectSlug}/${props.targetSlug}`;
|
||||
const target = query.data?.target;
|
||||
|
||||
const preflightScript = usePreflightScript({ target: target ?? null });
|
||||
const preflight = usePreflight({ target: target ?? null });
|
||||
|
||||
const fetcher = useMemo<Fetcher>(() => {
|
||||
return async (params, opts) => {
|
||||
|
|
@ -324,17 +324,17 @@ function LaboratoryPageContent(props: {
|
|||
mockEndpoint;
|
||||
|
||||
return new Repeater(async (push, stop) => {
|
||||
let hasFinishedPreflightScript = false;
|
||||
let isPreflightExecutionDone = false;
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
stop.then(() => {
|
||||
if (!hasFinishedPreflightScript) {
|
||||
preflightScript.abort();
|
||||
if (!isPreflightExecutionDone) {
|
||||
preflight.abortExecution();
|
||||
}
|
||||
});
|
||||
|
||||
let preflightData: PreflightScriptResultData;
|
||||
let preflightResultData: PreflightResultData;
|
||||
try {
|
||||
preflightData = await preflightScript.execute();
|
||||
preflightResultData = await preflight.execute();
|
||||
} catch (err: unknown) {
|
||||
if (err instanceof Error === false) {
|
||||
throw err;
|
||||
|
|
@ -347,22 +347,25 @@ function LaboratoryPageContent(props: {
|
|||
null,
|
||||
2,
|
||||
);
|
||||
const error = new Error(`Error during preflight script execution:\n\n${formatError}`);
|
||||
const error = new Error(`Error during preflight execution:\n\n${formatError}`);
|
||||
// We only want to expose the error message, not the whole stack trace.
|
||||
delete error.stack;
|
||||
stop(error);
|
||||
return;
|
||||
} finally {
|
||||
hasFinishedPreflightScript = true;
|
||||
isPreflightExecutionDone = true;
|
||||
}
|
||||
|
||||
const headers = {
|
||||
// We want to prevent users from interpolating environment variables into
|
||||
// their preflight script headers. So, apply substitution BEFORE merging
|
||||
// their preflight headers. So, apply substitution BEFORE merging
|
||||
// in preflight headers.
|
||||
//
|
||||
...substituteVariablesInHeaders(opts?.headers ?? {}, preflightData.environmentVariables),
|
||||
...Object.fromEntries(preflightData.request.headers),
|
||||
...substituteVariablesInHeaders(
|
||||
opts?.headers ?? {},
|
||||
preflightResultData.environmentVariables,
|
||||
),
|
||||
...Object.fromEntries(preflightResultData.request.headers),
|
||||
};
|
||||
|
||||
const graphiqlFetcher = createGraphiQLFetcher({ url, fetch });
|
||||
|
|
@ -396,8 +399,8 @@ function LaboratoryPageContent(props: {
|
|||
}, [
|
||||
target?.graphqlEndpointUrl,
|
||||
actualSelectedApiEndpoint,
|
||||
preflightScript.execute,
|
||||
preflightScript.isPreflightScriptEnabled,
|
||||
preflight.execute,
|
||||
preflight.isEnabled,
|
||||
]);
|
||||
|
||||
const FullScreenIcon = isFullScreen ? ExitFullScreenIcon : EnterFullScreenIcon;
|
||||
|
|
@ -450,7 +453,7 @@ function LaboratoryPageContent(props: {
|
|||
|
||||
return (
|
||||
<>
|
||||
{preflightScript.iframeElement}
|
||||
{preflight.iframeElement}
|
||||
<div className="flex py-6">
|
||||
<div className="flex-1">
|
||||
<Title>Laboratory</Title>
|
||||
|
|
@ -568,28 +571,28 @@ function LaboratoryPageContent(props: {
|
|||
margin-top: 15px;
|
||||
}
|
||||
|
||||
#preflight-script-logs {
|
||||
#preflight-logs {
|
||||
background-color: hsl(var(--color-base));
|
||||
border-radius: var(--border-radius-12);
|
||||
box-shadow: var(--popover-box-shadow);
|
||||
color: hsla(var(--color-neutral), var(--alpha-tertiary));
|
||||
}
|
||||
|
||||
#preflight-script-logs h2 {
|
||||
#preflight-logs h2 {
|
||||
color: hsla(var(--color-neutral), var(--alpha-secondary));
|
||||
}
|
||||
|
||||
#preflight-script-logs button[data-state="open"] > h2 {
|
||||
#preflight-logs button[data-state="open"] > h2 {
|
||||
color: hsl(var(--color-neutral));
|
||||
}
|
||||
|
||||
#preflight-script-logs > div {
|
||||
#preflight-logs > div {
|
||||
border-color: hsl(var(--border));
|
||||
}
|
||||
`}</style>
|
||||
</Helmet>
|
||||
{!query.fetching && !query.stale && (
|
||||
<PreflightScriptProvider value={preflightScript}>
|
||||
<PreflightProvider value={preflight}>
|
||||
<GraphiQL
|
||||
fetcher={fetcher}
|
||||
shouldPersistHeaders
|
||||
|
|
@ -631,16 +634,13 @@ function LaboratoryPageContent(props: {
|
|||
</GraphiQL.Toolbar>
|
||||
<GraphiQL.Footer>
|
||||
<div>
|
||||
{preflightScript.isPreflightScriptEnabled ? (
|
||||
<PreflightScriptLogs
|
||||
logs={preflightScript.logs}
|
||||
onClear={preflightScript.clearLogs}
|
||||
/>
|
||||
{preflight.isEnabled ? (
|
||||
<PreflightLogs logs={preflight.logs} onClear={preflight.clearLogs} />
|
||||
) : null}
|
||||
</div>
|
||||
</GraphiQL.Footer>
|
||||
</GraphiQL>
|
||||
</PreflightScriptProvider>
|
||||
</PreflightProvider>
|
||||
)}
|
||||
<ConnectLabModal
|
||||
endpoint={mockEndpoint}
|
||||
|
|
@ -703,7 +703,7 @@ function useApiTabValueState(graphqlEndpointUrl: string | null) {
|
|||
] as const;
|
||||
}
|
||||
|
||||
function PreflightScriptLogs(props: { logs: LogRecord[]; onClear: () => void }) {
|
||||
function PreflightLogs(props: { logs: LogRecord[]; onClear: () => void }) {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const consoleRef = useRef<HTMLDivElement>(null);
|
||||
useEffect(() => {
|
||||
|
|
@ -716,7 +716,7 @@ function PreflightScriptLogs(props: { logs: LogRecord[]; onClear: () => void })
|
|||
open={isOpen}
|
||||
onOpenChange={setIsOpen}
|
||||
className={cn('flex max-h-[200px] w-full flex-col overflow-hidden bg-[#030711]')}
|
||||
id="preflight-script-logs"
|
||||
id="preflight-logs"
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
|
|
|
|||
Loading…
Reference in a new issue