mirror of
https://github.com/Kong/insomnia.git
synced 2026-08-04 11:52:33 -04:00
feat: adds logic to enable cunstom lint rules for cloud/git sync projects
Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
@@ -258,6 +258,7 @@ const main: Window['main'] = {
|
||||
curlRequest: options => invokeWithNormalizedError('curlRequest', options),
|
||||
cancelCurlRequest: options => ipcRenderer.send('cancelCurlRequest', options),
|
||||
writeFile: options => invokeWithNormalizedError('writeFile', options),
|
||||
deleteFile: options => invokeWithNormalizedError('deleteFile', options),
|
||||
writeResponseBodyToFile: options => invokeWithNormalizedError('writeResponseBodyToFile', options),
|
||||
getAuthHeader: (renderedRequest: RenderedRequest, url: string): Promise<RequestHeader | undefined> =>
|
||||
invokeWithNormalizedError('getAuthHeader', renderedRequest, url),
|
||||
|
||||
@@ -11,13 +11,13 @@ export const canDuplicate = true;
|
||||
|
||||
export const canSync = true;
|
||||
|
||||
export const optionalKeys = ['rulesetFilePath'];
|
||||
export const optionalKeys = ['rulesetContent'];
|
||||
|
||||
export interface BaseApiSpec {
|
||||
fileName: string;
|
||||
contentType: 'json' | 'yaml';
|
||||
contents: string;
|
||||
rulesetFilePath?: string;
|
||||
rulesetContent?: string; // This is the content of the spectral ruleset file for linting API specs. It is stored in the DB to support cloud sync, but also written to disk for spectral to use when linting.
|
||||
}
|
||||
|
||||
export type ApiSpec = BaseModel & BaseApiSpec;
|
||||
|
||||
@@ -148,6 +148,7 @@ export type HandleChannels =
|
||||
| 'webSocket.open'
|
||||
| 'webSocket.readyState'
|
||||
| 'writeFile'
|
||||
| 'deleteFile'
|
||||
| 'writeResponseBodyToFile';
|
||||
|
||||
export const ipcMainHandle = (
|
||||
|
||||
@@ -153,6 +153,7 @@ export interface RendererToMainBridgeAPI {
|
||||
parseImport: typeof convert;
|
||||
multipartBufferToArray: (options: { bodyBuffer: Buffer; contentType: string }) => Promise<Part[]>;
|
||||
writeFile: (options: { path: string; content: string | Buffer }) => Promise<string>;
|
||||
deleteFile: (options: { path: string }) => Promise<void>;
|
||||
writeResponseBodyToFile: (options: {
|
||||
sourcePath: string;
|
||||
destinationPath: string;
|
||||
@@ -321,6 +322,13 @@ export function registerMainHandlers() {
|
||||
throw new Error(err);
|
||||
}
|
||||
});
|
||||
ipcMainHandle('deleteFile', async (_, options: { path: string }) => {
|
||||
try {
|
||||
await fs.promises.unlink(options.path);
|
||||
} catch (err) {
|
||||
throw new Error(err);
|
||||
}
|
||||
});
|
||||
ipcMainHandle('writeResponseBodyToFile', writeResponseBodyToFile);
|
||||
ipcMainHandle('getAuthHeader', (_, renderedRequest: RenderedRequest, url: string) => {
|
||||
return getAuthHeaderInMain(renderedRequest, url);
|
||||
|
||||
@@ -195,6 +195,17 @@ const Component = ({ params }: Route.ComponentProps) => {
|
||||
const [isSpecPaneOpen, setIsSpecPaneOpen] = useState(Boolean(parsedSpec));
|
||||
const [rulesetPath, setRulesetPath] = useState<string>('');
|
||||
|
||||
// This determines what path to write the ruleset content to when the user selects a custom ruleset file or when the rulesetContent in the DB changes.
|
||||
// For git sync projects, this will be the git working directory so that it appears in the staging modal and can be committed.
|
||||
// For cloud/local projects, this will be a per-workspace path — rulesetContent in the DB handles syncing for these projects.
|
||||
// Spectral requires a file path on disk to lint with a ruleset. Ref: lint-process.mjs.
|
||||
const rulesetWritePath = useMemo(
|
||||
() =>
|
||||
gitSyncRulesetPath ||
|
||||
window.path.join(window.app.getPath('userData'), `workspaces/${workspaceId}/.spectral.yaml`),
|
||||
[gitSyncRulesetPath, workspaceId],
|
||||
);
|
||||
|
||||
const { components, info, servers, paths } = parsedSpec || {};
|
||||
const { requestBodies, responses, parameters, headers, schemas, securitySchemes } = components || {};
|
||||
|
||||
@@ -262,8 +273,20 @@ const Component = ({ params }: Route.ComponentProps) => {
|
||||
}, [rulesetPath]);
|
||||
|
||||
useEffect(() => {
|
||||
setRulesetPath(apiSpec.rulesetFilePath || gitSyncRulesetPath || '');
|
||||
}, [apiSpec, gitSyncRulesetPath]);
|
||||
const syncRulesetToDisk = async () => {
|
||||
if (apiSpec.rulesetContent) {
|
||||
// Write the stored content to the correct path for this project type and use it for linting.
|
||||
await window.main.writeFile({ path: rulesetWritePath, content: apiSpec.rulesetContent });
|
||||
setRulesetPath(rulesetWritePath);
|
||||
} else {
|
||||
// No ruleset content — for git sync, fall back to any .spectral.yaml already present in the repo (e.g. committed by another user).
|
||||
// For cloud/local projects, default to OAS ruleset.
|
||||
setRulesetPath(gitSyncRulesetPath || '');
|
||||
}
|
||||
};
|
||||
|
||||
syncRulesetToDisk();
|
||||
}, [apiSpec.rulesetContent, rulesetWritePath, gitSyncRulesetPath]);
|
||||
|
||||
reactUse.useUnmount(() => {
|
||||
// delete the helper to avoid it run multiple times when user enter the page next time
|
||||
@@ -411,15 +434,24 @@ const Component = ({ params }: Route.ComponentProps) => {
|
||||
const handleSelectSpectralFile = async () => {
|
||||
const { filePath, canceled } = await selectFileOrFolder({
|
||||
itemTypes: ['file'],
|
||||
extensions: ['yaml', 'yml'],
|
||||
extensions: ['yaml'],
|
||||
showHiddenFiles: true,
|
||||
});
|
||||
|
||||
if (canceled || !filePath) {
|
||||
return;
|
||||
}
|
||||
await services.apiSpec.update(apiSpec, { rulesetFilePath: filePath });
|
||||
setRulesetPath(filePath);
|
||||
|
||||
if (window.path.basename(filePath) !== '.spectral.yaml') {
|
||||
showError({ title: 'Invalid File', message: 'Please select a file named .spectral.yaml' });
|
||||
return;
|
||||
}
|
||||
|
||||
const content = await window.main.insecureReadFile({ path: filePath });
|
||||
|
||||
await window.main.writeFile({ path: rulesetWritePath, content });
|
||||
await services.apiSpec.update(apiSpec, { rulesetContent: content });
|
||||
setRulesetPath(rulesetWritePath);
|
||||
};
|
||||
|
||||
const handleUnselectSpectralFile = async () => {
|
||||
@@ -432,8 +464,9 @@ const Component = ({ params }: Route.ComponentProps) => {
|
||||
noText: 'Cancel',
|
||||
onDone: async (confirmed: boolean) => {
|
||||
if (confirmed) {
|
||||
setRulesetPath(gitSyncRulesetPath || '');
|
||||
await services.apiSpec.update(apiSpec, { rulesetFilePath: '' });
|
||||
await window.main.deleteFile({ path: rulesetWritePath });
|
||||
await services.apiSpec.update(apiSpec, { rulesetContent: '' });
|
||||
setRulesetPath('');
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -1101,7 +1134,7 @@ const Component = ({ params }: Route.ComponentProps) => {
|
||||
</div>
|
||||
</Tooltip>
|
||||
</TooltipTrigger>
|
||||
{apiSpec.rulesetFilePath === rulesetPath && (
|
||||
{!!apiSpec.rulesetContent && (
|
||||
<TooltipTrigger>
|
||||
<Button onPress={handleUnselectSpectralFile}>
|
||||
<Icon icon="xmark" />
|
||||
|
||||
Reference in New Issue
Block a user