Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 49 additions & 32 deletions extensions/ql-vscode/src/skeleton-query-wizard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -253,11 +253,19 @@ export class SkeletonQueryWizard {
}

private async selectOrDownloadDatabase() {
if (this.language === undefined) {
throw new Error("Language is undefined");
}

if (this.qlPackStoragePath === undefined) {
throw new Error("QL Pack storage path is undefined");
}

const existingDatabaseItem = await this.findExistingDatabaseItem();
const existingDatabaseItem =
await SkeletonQueryWizard.findExistingDatabaseItem(
this.language,
this.databaseManager.databaseItems,
);

if (existingDatabaseItem) {
// select the found database
Expand All @@ -268,59 +276,68 @@ export class SkeletonQueryWizard {
}
}

public async findDatabaseItemByNwo(
public static async findDatabaseItemByNwo(
language: string,
databaseNwo: string,
databaseItems: readonly DatabaseItem[],
): Promise<DatabaseItem | undefined> {
const dbItems = databaseItems || [];
const dbs = dbItems.filter(
(db) =>
db.language === language &&
db.name === databaseNwo &&
db.error === undefined,
const dbs = databaseItems.filter(
(db) => db.language === language && db.name === databaseNwo,
);

if (dbs.length === 0) {
return undefined;
}
return dbs[0];
return dbs.pop();
}

public async findDatabaseItemByLanguage(
public static async findDatabaseItemByLanguage(
language: string,
databaseItems: readonly DatabaseItem[],
): Promise<DatabaseItem | undefined> {
const dbItems = databaseItems || [];
const dbs = dbItems.filter(
(db) => db.language === language && db.error === undefined,
);
if (dbs.length === 0) {
return undefined;
}
return dbs[0];
const dbs = databaseItems.filter((db) => db.language === language);

return dbs.pop();
}

private async findExistingDatabaseItem() {
if (this.language === undefined) {
throw new Error("Language is undefined");
}
public static async findExistingDatabaseItem(
language: string,
databaseItems: readonly DatabaseItem[],
): Promise<DatabaseItem | undefined> {
const defaultDatabaseNwo = QUERY_LANGUAGE_TO_DATABASE_REPO[language];

const defaultDatabaseNwo = QUERY_LANGUAGE_TO_DATABASE_REPO[this.language];
const dbItems = await SkeletonQueryWizard.sortDatabaseItemsByDateAdded(
databaseItems,
);

const defaultDatabaseItem = await this.findDatabaseItemByNwo(
this.language,
const defaultDatabaseItem = await SkeletonQueryWizard.findDatabaseItemByNwo(
language,
defaultDatabaseNwo,
this.databaseManager.databaseItems,
dbItems,
);

if (defaultDatabaseItem !== undefined) {
return defaultDatabaseItem;
}

return await this.findDatabaseItemByLanguage(
this.language,
this.databaseManager.databaseItems,
return await SkeletonQueryWizard.findDatabaseItemByLanguage(
language,
dbItems,
);
}

public static async sortDatabaseItemsByDateAdded(
databaseItems: readonly DatabaseItem[],
) {
const validDbItems = databaseItems.filter((db) => db.error === undefined);

return validDbItems.sort((a, b) => {
if (a.dateAdded === undefined) {
return -1;
}

if (b.dateAdded === undefined) {
return 1;
}

return a.dateAdded - b.dateAdded;
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,7 @@ describe("SkeletonQueryWizard", () => {

jest.spyOn(mockDbItem, "name", "get").mockReturnValue("mock-name");

const databaseItem = await wizard.findDatabaseItemByNwo(
const databaseItem = await SkeletonQueryWizard.findDatabaseItemByNwo(
mockDbItem.language,
mockDbItem.name,
[mockDbItem, mockDbItem2],
Expand All @@ -330,45 +330,14 @@ describe("SkeletonQueryWizard", () => {
JSON.stringify(mockDbItem),
);
});

it("should ignore databases with errors", async () => {
const mockDbItem = createMockDB(dir, {
language: "ruby",
dateAdded: 123,
} as FullDatabaseOptions);
const mockDbItem2 = createMockDB(dir, {
language: "javascript",
} as FullDatabaseOptions);
const mockDbItem3 = createMockDB(dir, {
language: "ruby",
dateAdded: 345,
} as FullDatabaseOptions);

jest.spyOn(mockDbItem, "name", "get").mockReturnValue("mock-name");
jest.spyOn(mockDbItem3, "name", "get").mockReturnValue(mockDbItem.name);

jest
.spyOn(mockDbItem, "error", "get")
.mockReturnValue(asError("database go boom!"));

const databaseItem = await wizard.findDatabaseItemByNwo(
mockDbItem.language,
mockDbItem.name,
[mockDbItem, mockDbItem2, mockDbItem3],
);

expect(JSON.stringify(databaseItem)).toEqual(
JSON.stringify(mockDbItem3),
);
});
});

describe("when the item doesn't exist", () => {
it("should return nothing", async () => {
const mockDbItem = createMockDB(dir);
const mockDbItem2 = createMockDB(dir);

const databaseItem = await wizard.findDatabaseItemByNwo(
const databaseItem = await SkeletonQueryWizard.findDatabaseItemByNwo(
"ruby",
"mock-nwo",
[mockDbItem, mockDbItem2],
Expand All @@ -389,50 +358,26 @@ describe("SkeletonQueryWizard", () => {
language: "javascript",
} as FullDatabaseOptions);

const databaseItem = await wizard.findDatabaseItemByLanguage("ruby", [
mockDbItem,
mockDbItem2,
]);
const databaseItem =
await SkeletonQueryWizard.findDatabaseItemByLanguage("ruby", [
mockDbItem,
mockDbItem2,
]);

expect(databaseItem).toEqual(mockDbItem);
});

it("should ignore databases with errors", async () => {
const mockDbItem = createMockDB(dir, {
language: "ruby",
} as FullDatabaseOptions);
const mockDbItem2 = createMockDB(dir, {
language: "javascript",
} as FullDatabaseOptions);
const mockDbItem3 = createMockDB(dir, {
language: "ruby",
} as FullDatabaseOptions);

jest
.spyOn(mockDbItem, "error", "get")
.mockReturnValue(asError("database go boom!"));

const databaseItem = await wizard.findDatabaseItemByLanguage("ruby", [
mockDbItem,
mockDbItem2,
mockDbItem3,
]);

expect(JSON.stringify(databaseItem)).toEqual(
JSON.stringify(mockDbItem3),
);
});
});

describe("when the item doesn't exist", () => {
it("should return nothing", async () => {
const mockDbItem = createMockDB(dir);
const mockDbItem2 = createMockDB(dir);

const databaseItem = await wizard.findDatabaseItemByLanguage("ruby", [
mockDbItem,
mockDbItem2,
]);
const databaseItem =
await SkeletonQueryWizard.findDatabaseItemByLanguage("ruby", [
mockDbItem,
mockDbItem2,
]);

expect(databaseItem).toBeUndefined();
});
Expand Down Expand Up @@ -550,4 +495,136 @@ describe("SkeletonQueryWizard", () => {
});
});
});

describe("sortDatabaseItemsByDateAdded", () => {
describe("should return a sorted list", () => {
it("should sort the items by dateAdded", async () => {
const mockDbItem = createMockDB(dir, {
dateAdded: 678,
} as FullDatabaseOptions);
const mockDbItem2 = createMockDB(dir, {
dateAdded: 123,
} as FullDatabaseOptions);
const mockDbItem3 = createMockDB(dir, {
dateAdded: undefined,
} as FullDatabaseOptions);
const mockDbItem4 = createMockDB(dir, {
dateAdded: 345,
} as FullDatabaseOptions);

const sortedList =
await SkeletonQueryWizard.sortDatabaseItemsByDateAdded([
mockDbItem,
mockDbItem2,
mockDbItem3,
mockDbItem4,
]);

expect(sortedList).toEqual([
mockDbItem3,
mockDbItem2,
mockDbItem4,
mockDbItem,
]);
});

it("should ignore databases with errors", async () => {
const mockDbItem = createMockDB(dir, {
dateAdded: 678,
} as FullDatabaseOptions);
const mockDbItem2 = createMockDB(dir, {
dateAdded: undefined,
} as FullDatabaseOptions);
const mockDbItem3 = createMockDB(dir, {
dateAdded: 345,
} as FullDatabaseOptions);
const mockDbItem4 = createMockDB(dir, {
dateAdded: 123,
} as FullDatabaseOptions);

jest
.spyOn(mockDbItem, "error", "get")
.mockReturnValue(asError("database go boom!"));

const sortedList =
await SkeletonQueryWizard.sortDatabaseItemsByDateAdded([
mockDbItem,
mockDbItem2,
mockDbItem3,
mockDbItem4,
]);

expect(sortedList).toEqual([mockDbItem2, mockDbItem4, mockDbItem3]);
});
});
});

describe("findExistingDatabaseItem", () => {
describe("when there are multiple items with the same name", () => {
it("should choose the latest one", async () => {
const mockDbItem = createMockDB(dir, {
language: "javascript",
dateAdded: 456,
} as FullDatabaseOptions);
const mockDbItem2 = createMockDB(dir, {
language: "ruby",
dateAdded: 789,
} as FullDatabaseOptions);
const mockDbItem3 = createMockDB(dir, {
language: "javascript",
dateAdded: 123,
} as FullDatabaseOptions);
const mockDbItem4 = createMockDB(dir, {
language: "javascript",
dateAdded: undefined,
} as FullDatabaseOptions);

jest
.spyOn(mockDbItem, "name", "get")
.mockReturnValue(QUERY_LANGUAGE_TO_DATABASE_REPO["javascript"]);
jest
.spyOn(mockDbItem2, "name", "get")
.mockReturnValue(QUERY_LANGUAGE_TO_DATABASE_REPO["javascript"]);

const databaseItem = await SkeletonQueryWizard.findExistingDatabaseItem(
"javascript",
[mockDbItem, mockDbItem2, mockDbItem3, mockDbItem4],
);

expect(JSON.stringify(databaseItem)).toEqual(
JSON.stringify(mockDbItem),
);
});
});

describe("when there are multiple items with the same language", () => {
it("should choose the latest one", async () => {
const mockDbItem = createMockDB(dir, {
language: "ruby",
dateAdded: 789,
} as FullDatabaseOptions);
const mockDbItem2 = createMockDB(dir, {
language: "javascript",
dateAdded: 456,
} as FullDatabaseOptions);
const mockDbItem3 = createMockDB(dir, {
language: "ruby",
dateAdded: 123,
} as FullDatabaseOptions);
const mockDbItem4 = createMockDB(dir, {
language: "javascript",
dateAdded: undefined,
} as FullDatabaseOptions);

const databaseItem = await SkeletonQueryWizard.findExistingDatabaseItem(
"javascript",
[mockDbItem, mockDbItem2, mockDbItem3, mockDbItem4],
);

expect(JSON.stringify(databaseItem)).toEqual(
JSON.stringify(mockDbItem2),
);
});
});
});
});