Merge pull request #2340 from github/koesie10/show-extension-pack-name
Show extension pack name in data extensions editor
This commit is contained in:
@@ -88,6 +88,7 @@ export type BuiltInVsCodeCommands = {
|
||||
) => Promise<void>;
|
||||
"vscode.open": (uri: Uri) => Promise<void>;
|
||||
"vscode.openFolder": (uri: Uri) => Promise<void>;
|
||||
revealInExplorer: (uri: Uri) => Promise<void>;
|
||||
// We type the `config` property specifically as a CodeQL debug configuration, since that's the
|
||||
// only kinds we specify anyway.
|
||||
"workbench.action.debug.start": (options?: {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import {
|
||||
CancellationTokenSource,
|
||||
ExtensionContext,
|
||||
Uri,
|
||||
ViewColumn,
|
||||
window,
|
||||
workspace,
|
||||
@@ -34,6 +35,7 @@ import { readQueryResults, runQuery } from "./external-api-usage-query";
|
||||
import { createDataExtensionYaml, loadDataExtensionYaml } from "./yaml";
|
||||
import { ExternalApiUsage } from "./external-api-usage";
|
||||
import { ModeledMethod } from "./modeled-method";
|
||||
import { ExtensionPackModelFile } from "./extension-pack-picker";
|
||||
|
||||
function getQlSubmoduleFolder(): WorkspaceFolder | undefined {
|
||||
const workspaceFolder = workspace.workspaceFolders?.find(
|
||||
@@ -60,7 +62,7 @@ export class DataExtensionsEditorView extends AbstractWebview<
|
||||
private readonly queryRunner: QueryRunner,
|
||||
private readonly queryStorageDir: string,
|
||||
private readonly databaseItem: DatabaseItem,
|
||||
private readonly modelFilename: string,
|
||||
private readonly modelFile: ExtensionPackModelFile,
|
||||
) {
|
||||
super(ctx);
|
||||
}
|
||||
@@ -93,10 +95,17 @@ export class DataExtensionsEditorView extends AbstractWebview<
|
||||
case "viewLoaded":
|
||||
await this.onWebViewLoaded();
|
||||
|
||||
break;
|
||||
case "openExtensionPack":
|
||||
await this.app.commands.execute(
|
||||
"revealInExplorer",
|
||||
Uri.file(this.modelFile.extensionPack.path),
|
||||
);
|
||||
|
||||
break;
|
||||
case "openModelFile":
|
||||
await window.showTextDocument(
|
||||
await workspace.openTextDocument(this.modelFilename),
|
||||
await workspace.openTextDocument(this.modelFile.filename),
|
||||
);
|
||||
|
||||
break;
|
||||
@@ -127,7 +136,8 @@ export class DataExtensionsEditorView extends AbstractWebview<
|
||||
await Promise.all([
|
||||
this.postMessage({
|
||||
t: "setDataExtensionEditorInitialData",
|
||||
modelFilename: this.modelFilename,
|
||||
extensionPackName: this.modelFile.extensionPack.name,
|
||||
modelFilename: this.modelFile.filename,
|
||||
}),
|
||||
this.loadExternalApiUsages(),
|
||||
this.loadExistingModeledMethods(),
|
||||
@@ -164,28 +174,30 @@ export class DataExtensionsEditorView extends AbstractWebview<
|
||||
modeledMethods,
|
||||
);
|
||||
|
||||
await outputFile(this.modelFilename, yaml);
|
||||
await outputFile(this.modelFile.filename, yaml);
|
||||
|
||||
void extLogger.log(`Saved data extension YAML to ${this.modelFilename}`);
|
||||
void extLogger.log(
|
||||
`Saved data extension YAML to ${this.modelFile.filename}`,
|
||||
);
|
||||
}
|
||||
|
||||
protected async loadExistingModeledMethods(): Promise<void> {
|
||||
try {
|
||||
if (!(await pathExists(this.modelFilename))) {
|
||||
if (!(await pathExists(this.modelFile.filename))) {
|
||||
return;
|
||||
}
|
||||
|
||||
const yaml = await readFile(this.modelFilename, "utf8");
|
||||
const yaml = await readFile(this.modelFile.filename, "utf8");
|
||||
|
||||
const data = loadYaml(yaml, {
|
||||
filename: this.modelFilename,
|
||||
filename: this.modelFile.filename,
|
||||
});
|
||||
|
||||
const existingModeledMethods = loadDataExtensionYaml(data);
|
||||
|
||||
if (!existingModeledMethods) {
|
||||
void showAndLogErrorMessage(
|
||||
`Failed to parse data extension YAML ${this.modelFilename}.`,
|
||||
`Failed to parse data extension YAML ${this.modelFile.filename}.`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -197,7 +209,7 @@ export class DataExtensionsEditorView extends AbstractWebview<
|
||||
} catch (e: unknown) {
|
||||
void showAndLogErrorMessage(
|
||||
`Unable to read data extension YAML ${
|
||||
this.modelFilename
|
||||
this.modelFile.filename
|
||||
}: ${getErrorMessage(e)}`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -21,26 +21,36 @@ const packNameRegex = new RegExp(
|
||||
);
|
||||
const packNameLength = 128;
|
||||
|
||||
export interface ExtensionPack {
|
||||
name: string;
|
||||
path: string;
|
||||
}
|
||||
|
||||
export interface ExtensionPackModelFile {
|
||||
filename: string;
|
||||
extensionPack: ExtensionPack;
|
||||
}
|
||||
|
||||
export async function pickExtensionPackModelFile(
|
||||
cliServer: Pick<CodeQLCliServer, "resolveQlpacks" | "resolveExtensions">,
|
||||
databaseItem: Pick<DatabaseItem, "name" | "language">,
|
||||
progress: ProgressCallback,
|
||||
token: CancellationToken,
|
||||
): Promise<string | undefined> {
|
||||
const extensionPackPath = await pickExtensionPack(
|
||||
): Promise<ExtensionPackModelFile | undefined> {
|
||||
const extensionPack = await pickExtensionPack(
|
||||
cliServer,
|
||||
databaseItem,
|
||||
progress,
|
||||
token,
|
||||
);
|
||||
if (!extensionPackPath) {
|
||||
return;
|
||||
if (!extensionPack) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const modelFile = await pickModelFile(
|
||||
cliServer,
|
||||
databaseItem,
|
||||
extensionPackPath,
|
||||
extensionPack.path,
|
||||
progress,
|
||||
token,
|
||||
);
|
||||
@@ -48,7 +58,10 @@ export async function pickExtensionPackModelFile(
|
||||
return;
|
||||
}
|
||||
|
||||
return modelFile;
|
||||
return {
|
||||
filename: modelFile,
|
||||
extensionPack,
|
||||
};
|
||||
}
|
||||
|
||||
async function pickExtensionPack(
|
||||
@@ -56,7 +69,7 @@ async function pickExtensionPack(
|
||||
databaseItem: Pick<DatabaseItem, "name" | "language">,
|
||||
progress: ProgressCallback,
|
||||
token: CancellationToken,
|
||||
): Promise<string | undefined> {
|
||||
): Promise<ExtensionPack | undefined> {
|
||||
progress({
|
||||
message: "Resolving extension packs...",
|
||||
step: 1,
|
||||
@@ -117,7 +130,10 @@ async function pickExtensionPack(
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return extensionPackPaths[0];
|
||||
return {
|
||||
name: extensionPackOption.extensionPack,
|
||||
path: extensionPackPaths[0],
|
||||
};
|
||||
}
|
||||
|
||||
async function pickModelFile(
|
||||
@@ -186,7 +202,7 @@ async function pickModelFile(
|
||||
async function pickNewExtensionPack(
|
||||
databaseItem: Pick<DatabaseItem, "name" | "language">,
|
||||
token: CancellationToken,
|
||||
): Promise<string | undefined> {
|
||||
): Promise<ExtensionPack | undefined> {
|
||||
const workspaceFolders = getOnDiskWorkspaceFoldersObjects();
|
||||
const workspaceFolderOptions = workspaceFolders.map((folder) => ({
|
||||
label: folder.name,
|
||||
@@ -263,7 +279,10 @@ async function pickNewExtensionPack(
|
||||
}),
|
||||
);
|
||||
|
||||
return packPath;
|
||||
return {
|
||||
name: packName,
|
||||
path: packPath,
|
||||
};
|
||||
}
|
||||
|
||||
async function pickNewModelFile(
|
||||
|
||||
@@ -483,6 +483,7 @@ export type FromDataFlowPathsMessage = CommonFromViewMessages;
|
||||
|
||||
export interface SetDataExtensionEditorInitialDataMessage {
|
||||
t: "setDataExtensionEditorInitialData";
|
||||
extensionPackName: string;
|
||||
modelFilename: string;
|
||||
}
|
||||
|
||||
@@ -516,6 +517,10 @@ export interface JumpToUsageMessage {
|
||||
location: ResolvableLocationValue;
|
||||
}
|
||||
|
||||
export interface OpenExtensionPackMessage {
|
||||
t: "openExtensionPack";
|
||||
}
|
||||
|
||||
export interface OpenModelFileMessage {
|
||||
t: "openModelFile";
|
||||
}
|
||||
@@ -539,6 +544,7 @@ export type ToDataExtensionsEditorMessage =
|
||||
export type FromDataExtensionsEditorMessage =
|
||||
| ViewLoadedMsg
|
||||
| OpenModelFileMessage
|
||||
| OpenExtensionPackMessage
|
||||
| JumpToUsageMessage
|
||||
| SaveModeledMethods
|
||||
| GenerateExternalApiMessage;
|
||||
|
||||
@@ -15,6 +15,7 @@ const Template: ComponentStory<typeof DataExtensionsEditorComponent> = (
|
||||
|
||||
export const DataExtensionsEditor = Template.bind({});
|
||||
DataExtensionsEditor.args = {
|
||||
initialExtensionPackName: "codeql/sql2o-models",
|
||||
initialModelFilename:
|
||||
"/home/user/vscode-codeql-starter/codeql-custom-queries-java/sql2o/models/sql2o.yml",
|
||||
initialExternalApiUsages: [
|
||||
|
||||
@@ -47,16 +47,21 @@ const ProgressBar = styled.div<ProgressBarProps>`
|
||||
`;
|
||||
|
||||
type Props = {
|
||||
initialExtensionPackName?: string;
|
||||
initialModelFilename?: string;
|
||||
initialExternalApiUsages?: ExternalApiUsage[];
|
||||
initialModeledMethods?: Record<string, ModeledMethod>;
|
||||
};
|
||||
|
||||
export function DataExtensionsEditor({
|
||||
initialExtensionPackName,
|
||||
initialModelFilename,
|
||||
initialExternalApiUsages = [],
|
||||
initialModeledMethods = {},
|
||||
}: Props): JSX.Element {
|
||||
const [extensionPackName, setExtensionPackName] = useState<
|
||||
string | undefined
|
||||
>(initialExtensionPackName);
|
||||
const [modelFilename, setModelFilename] = useState<string | undefined>(
|
||||
initialModelFilename,
|
||||
);
|
||||
@@ -79,6 +84,7 @@ export function DataExtensionsEditor({
|
||||
const msg: ToDataExtensionsEditorMessage = evt.data;
|
||||
switch (msg.t) {
|
||||
case "setDataExtensionEditorInitialData":
|
||||
setExtensionPackName(msg.extensionPackName);
|
||||
setModelFilename(msg.modelFilename);
|
||||
break;
|
||||
case "setExternalApiUsages":
|
||||
@@ -150,6 +156,12 @@ export function DataExtensionsEditor({
|
||||
});
|
||||
}, []);
|
||||
|
||||
const onOpenExtensionPackClick = useCallback(() => {
|
||||
vscode.postMessage({
|
||||
t: "openExtensionPack",
|
||||
});
|
||||
}, []);
|
||||
|
||||
const onOpenModelFileClick = useCallback(() => {
|
||||
vscode.postMessage({
|
||||
t: "openModelFile",
|
||||
@@ -169,6 +181,12 @@ export function DataExtensionsEditor({
|
||||
<>
|
||||
<ViewTitle>Data extensions editor</ViewTitle>
|
||||
<DetailsContainer>
|
||||
{extensionPackName && (
|
||||
<LinkIconButton onClick={onOpenExtensionPackClick}>
|
||||
<span slot="start" className="codicon codicon-package"></span>
|
||||
{extensionPackName}
|
||||
</LinkIconButton>
|
||||
)}
|
||||
{modelFilename && (
|
||||
<LinkIconButton onClick={onOpenModelFileClick}>
|
||||
<span slot="start" className="codicon codicon-file-code"></span>
|
||||
|
||||
@@ -73,7 +73,13 @@ describe("pickExtensionPackModelFile", () => {
|
||||
progress,
|
||||
token,
|
||||
),
|
||||
).toEqual("/a/b/c/my-extension-pack/models/model.yml");
|
||||
).toEqual({
|
||||
filename: "/a/b/c/my-extension-pack/models/model.yml",
|
||||
extensionPack: {
|
||||
name: "my-extension-pack",
|
||||
path: "/a/b/c/my-extension-pack",
|
||||
},
|
||||
});
|
||||
expect(showQuickPickSpy).toHaveBeenCalledTimes(2);
|
||||
expect(showQuickPickSpy).toHaveBeenCalledWith(
|
||||
[
|
||||
@@ -174,7 +180,13 @@ describe("pickExtensionPackModelFile", () => {
|
||||
progress,
|
||||
token,
|
||||
),
|
||||
).toEqual(join(tmpDir.path, "models/my-model.yml"));
|
||||
).toEqual({
|
||||
filename: join(tmpDir.path, "models/my-model.yml"),
|
||||
extensionPack: {
|
||||
name: "my-extension-pack",
|
||||
path: tmpDir.path,
|
||||
},
|
||||
});
|
||||
expect(showQuickPickSpy).toHaveBeenCalledTimes(2);
|
||||
expect(showQuickPickSpy).toHaveBeenCalledWith(
|
||||
[
|
||||
@@ -264,7 +276,18 @@ describe("pickExtensionPackModelFile", () => {
|
||||
progress,
|
||||
token,
|
||||
),
|
||||
).toEqual(join(tmpDir.path, "my-extension-pack", "models", "my-model.yml"));
|
||||
).toEqual({
|
||||
filename: join(
|
||||
tmpDir.path,
|
||||
"my-extension-pack",
|
||||
"models",
|
||||
"my-model.yml",
|
||||
),
|
||||
extensionPack: {
|
||||
name: "my-extension-pack",
|
||||
path: join(tmpDir.path, "my-extension-pack"),
|
||||
},
|
||||
});
|
||||
expect(showQuickPickSpy).toHaveBeenCalledTimes(1);
|
||||
expect(showInputBoxSpy).toHaveBeenCalledTimes(2);
|
||||
expect(showInputBoxSpy).toHaveBeenCalledWith(
|
||||
@@ -329,7 +352,18 @@ describe("pickExtensionPackModelFile", () => {
|
||||
progress,
|
||||
token,
|
||||
),
|
||||
).toEqual(join(tmpDir.path, "my-extension-pack", "models", "my-model.yml"));
|
||||
).toEqual({
|
||||
filename: join(
|
||||
tmpDir.path,
|
||||
"my-extension-pack",
|
||||
"models",
|
||||
"my-model.yml",
|
||||
),
|
||||
extensionPack: {
|
||||
name: "my-extension-pack",
|
||||
path: join(tmpDir.path, "my-extension-pack"),
|
||||
},
|
||||
});
|
||||
expect(showQuickPickSpy).toHaveBeenCalledTimes(1);
|
||||
expect(showInputBoxSpy).toHaveBeenCalledTimes(2);
|
||||
expect(showInputBoxSpy).toHaveBeenCalledWith(
|
||||
@@ -508,7 +542,13 @@ describe("pickExtensionPackModelFile", () => {
|
||||
progress,
|
||||
token,
|
||||
),
|
||||
).toEqual(join(tmpDir.path, "models/my-model.yml"));
|
||||
).toEqual({
|
||||
filename: join(tmpDir.path, "models", "my-model.yml"),
|
||||
extensionPack: {
|
||||
name: "my-extension-pack",
|
||||
path: tmpDir.path,
|
||||
},
|
||||
});
|
||||
expect(showQuickPickSpy).toHaveBeenCalledTimes(1);
|
||||
expect(showInputBoxSpy).toHaveBeenCalledWith(
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user