mirror of
https://github.com/Kong/insomnia.git
synced 2026-10-25 08:09:06 -04:00
Fixes ApiSpec content syncing (#4443)
Co-authored-by: Opender Singh <opender.singh@konghq.com>
This commit is contained in:
1 parent
109eababcf
commit
c73d2c1c1d
2 files changed
+64
-43
No files matched your search
@@ -6,7 +6,6 @@ import styled from 'styled-components';
|
||||
|
||||
import { documentationLinks } from '../../common/documentation';
|
||||
import { selectFileOrFolder } from '../../common/select-file-or-folder';
|
||||
import * as models from '../../models';
|
||||
import { faint } from '../css/css-in-js';
|
||||
import { selectActiveApiSpec } from '../redux/selectors';
|
||||
import { showPrompt } from './modals';
|
||||
@@ -41,25 +40,10 @@ interface Props {
|
||||
onUpdateContents: (contents: string) => void;
|
||||
}
|
||||
|
||||
const useUpdateApiSpecContents = () => {
|
||||
const activeApiSpec = useSelector(selectActiveApiSpec);
|
||||
return useCallback(async (contents: string) => {
|
||||
if (!contents) {
|
||||
return;
|
||||
}
|
||||
if (!activeApiSpec) {
|
||||
return;
|
||||
}
|
||||
await models.apiSpec.update(activeApiSpec, { contents });
|
||||
}, [activeApiSpec]);
|
||||
};
|
||||
|
||||
const ImportSpecButton: FC<Props> = ({ onUpdateContents }) => {
|
||||
const updateApiSpecContents = useUpdateApiSpecContents();
|
||||
|
||||
const handleImportFile = useCallback(async () => {
|
||||
const { canceled, filePath } = await selectFileOrFolder({
|
||||
extensions: ['yml', 'yaml'],
|
||||
extensions: ['yml', 'yaml', 'json'],
|
||||
itemTypes: ['file'],
|
||||
});
|
||||
// Exit if no file selected
|
||||
@@ -68,8 +52,8 @@ const ImportSpecButton: FC<Props> = ({ onUpdateContents }) => {
|
||||
}
|
||||
|
||||
const contents = String(await fs.promises.readFile(filePath));
|
||||
await updateApiSpecContents(contents);
|
||||
}, [updateApiSpecContents]);
|
||||
onUpdateContents(contents);
|
||||
}, [onUpdateContents]);
|
||||
|
||||
const handleImportUri = useCallback(async () => {
|
||||
showPrompt({
|
||||
@@ -83,15 +67,14 @@ const ImportSpecButton: FC<Props> = ({ onUpdateContents }) => {
|
||||
return;
|
||||
}
|
||||
const contents = await response.text();
|
||||
await updateApiSpecContents(contents);
|
||||
onUpdateContents(contents);
|
||||
},
|
||||
});
|
||||
}, [updateApiSpecContents, onUpdateContents]);
|
||||
}, [onUpdateContents]);
|
||||
|
||||
const button = (
|
||||
<StyledButton variant="outlined" bg="surprise" className="margin-left">
|
||||
Import
|
||||
Import OpenAPI
|
||||
<i className="fa fa-caret-down pad-left-sm" />
|
||||
</StyledButton>
|
||||
);
|
||||
@@ -117,21 +100,19 @@ const ImportSpecButton: FC<Props> = ({ onUpdateContents }) => {
|
||||
const SecondaryAction: FC<Props> = ({ onUpdateContents }) => {
|
||||
const PETSTORE_EXAMPLE_URI = 'https://gist.githubusercontent.com/gschier/4e2278d5a50b4bbf1110755d9b48a9f9/raw/801c05266ae102bcb9288ab92c60f52d45557425/petstore-spec.yaml';
|
||||
|
||||
const updateApiSpecContents = useUpdateApiSpecContents();
|
||||
const onClick = useCallback(async () => {
|
||||
const response = await window.fetch(PETSTORE_EXAMPLE_URI);
|
||||
if (!response) {
|
||||
return;
|
||||
}
|
||||
const contents = await response.text();
|
||||
await updateApiSpecContents(contents);
|
||||
onUpdateContents(contents);
|
||||
}, [updateApiSpecContents, onUpdateContents]);
|
||||
}, [onUpdateContents]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div>
|
||||
Or import existing an OpenAPI spec or <ExampleButton onClick={onClick}>start from an example</ExampleButton>
|
||||
Or import an existing OpenAPI spec or <ExampleButton onClick={onClick}>start from an example</ExampleButton>
|
||||
</div>
|
||||
<ImportSpecButton onUpdateContents={onUpdateContents} />
|
||||
</div>
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { IRuleResult } from '@stoplight/spectral';
|
||||
import { Button, Notice, NoticeTable } from 'insomnia-components';
|
||||
import React, { createRef, FC, Fragment, ReactNode, RefObject, useCallback, useState } from 'react';
|
||||
import React, { createRef, FC, Fragment, ReactNode, RefObject, useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { useAsync, useDebounce } from 'react-use';
|
||||
import { useAsync } from 'react-use';
|
||||
import styled from 'styled-components';
|
||||
import SwaggerUI from 'swagger-ui-react';
|
||||
|
||||
import { parseApiSpec, ParsedApiSpec } from '../../common/api-specs';
|
||||
import { debounce } from '../../common/misc';
|
||||
import { initializeSpectral, isLintError } from '../../common/spectral';
|
||||
import * as models from '../../models/index';
|
||||
import { superFaint } from '../css/css-in-js';
|
||||
@@ -74,24 +75,65 @@ interface LintMessage {
|
||||
range: IRuleResult['range'];
|
||||
}
|
||||
|
||||
const RenderEditor: FC<{ editor: RefObject<UnconnectedCodeEditor> }> = ({ editor }) => {
|
||||
const useDesignEmptyState = () => {
|
||||
const activeApiSpec = useSelector(selectActiveApiSpec);
|
||||
const contents = activeApiSpec?.contents;
|
||||
|
||||
const [forceRefreshCounter, setForceRefreshCounter] = useState(0);
|
||||
const [lintMessages, setLintMessages] = useState<LintMessage[]>([]);
|
||||
const contents = activeApiSpec?.contents ?? '';
|
||||
const [contentsState, setContentsState] = useState(contents);
|
||||
const [shouldIncrementCounter, setShouldIncrementCounter] = useState(false);
|
||||
|
||||
const uniquenessKey = `${forceRefreshCounter}::${activeApiSpec?._id}`;
|
||||
useEffect(() => {
|
||||
if (contents && shouldIncrementCounter){
|
||||
setForceRefreshCounter(forceRefreshCounter => forceRefreshCounter + 1);
|
||||
setShouldIncrementCounter(false);
|
||||
}
|
||||
}, [contents, shouldIncrementCounter]);
|
||||
|
||||
useDebounce(async () => {
|
||||
const onUpdateContents = useCallback((value: string) => {
|
||||
if (!activeApiSpec) {
|
||||
return;
|
||||
}
|
||||
|
||||
await models.apiSpec.update({ ...activeApiSpec, contents: contentsState });
|
||||
setForceRefreshCounter(forceRefreshCounter => forceRefreshCounter + 1);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps -- this is a problem with react-use
|
||||
}, 500, [contentsState]);
|
||||
const fn = async () => {
|
||||
await models.apiSpec.update({ ...activeApiSpec, contents: value });
|
||||
};
|
||||
fn();
|
||||
|
||||
// Because we can't await activeApiSpec.contents to have propageted to redux, we flip a toggle to decide if we should do something when redux does eventually change
|
||||
setShouldIncrementCounter(true);
|
||||
}, [activeApiSpec]);
|
||||
|
||||
const emptyStateNode = contents ? null : (
|
||||
<DesignEmptyState
|
||||
onUpdateContents={onUpdateContents}
|
||||
/>
|
||||
);
|
||||
|
||||
const uniquenessKey = `${forceRefreshCounter}::${activeApiSpec?._id}`;
|
||||
return { uniquenessKey, emptyStateNode };
|
||||
};
|
||||
|
||||
const RenderEditor: FC<{ editor: RefObject<UnconnectedCodeEditor> }> = ({ editor }) => {
|
||||
const activeApiSpec = useSelector(selectActiveApiSpec);
|
||||
const [lintMessages, setLintMessages] = useState<LintMessage[]>([]);
|
||||
const contents = activeApiSpec?.contents ?? '';
|
||||
|
||||
const { uniquenessKey, emptyStateNode } = useDesignEmptyState();
|
||||
|
||||
const onCodeEditorChange = useMemo(() => {
|
||||
const handler = (contents: string) => {
|
||||
const fn = async () => {
|
||||
if (!activeApiSpec) {
|
||||
return;
|
||||
}
|
||||
|
||||
await models.apiSpec.update({ ...activeApiSpec, contents });
|
||||
};
|
||||
fn();
|
||||
};
|
||||
|
||||
return debounce(handler, 500);
|
||||
}, [activeApiSpec]);
|
||||
|
||||
useAsync(async () => {
|
||||
// Lint only if spec has content
|
||||
@@ -136,13 +178,11 @@ const RenderEditor: FC<{ editor: RefObject<UnconnectedCodeEditor> }> = ({ editor
|
||||
ref={editor}
|
||||
lintOptions={{ delay: 1000 }}
|
||||
mode="openapi"
|
||||
defaultValue={contentsState}
|
||||
onChange={setContentsState}
|
||||
defaultValue={contents}
|
||||
onChange={onCodeEditorChange}
|
||||
uniquenessKey={uniquenessKey}
|
||||
/>
|
||||
<DesignEmptyState
|
||||
onUpdateContents={setContentsState}
|
||||
/>
|
||||
{emptyStateNode}
|
||||
</div>
|
||||
{lintMessages.length > 0 && (
|
||||
<NoticeTable
|
||||
|
||||
Reference in new issue
Block a user