Skip to content

feat: update cache for all property files if they point to the same changelog when contexts are loaded #74

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ All notable changes to the "Liquibase" extension will be documented in this file

The format is based on [Keep a Changelog](http://keepachangelog.com/) and and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## 1.1.1

### Added

- Added update mechanism to cache when contexts are loaded and point to the same changelog so every property file is updated and does not have to be loaded for each file

## 1.1.0

### Changed
Expand Down
6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"description": "Execute Liquibase commands on changelogs and create liquibase.properties files",
"license": "MIT",
"publisher": "adito",
"version": "1.1.0",
"version": "1.1.1",
"engines": {
"vscode": "^1.84.0"
},
Expand Down
2 changes: 1 addition & 1 deletion src/cache/CacheHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ export class CacheHandler {
* @param changelogLocation - the location of the changelog
* @returns the sorted loaded contexts and the selected contexts of the given connection and changelog
*/
readContexts(connectionLocation: string, changelogLocation: string): ContextSelection {
public readContexts(connectionLocation: string, changelogLocation: string): ContextSelection {
const existingChangelog = this.getChangelog(connectionLocation, changelogLocation).existingChangelog;

if (existingChangelog?.contexts) {
Expand Down
81 changes: 72 additions & 9 deletions src/executeJar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,15 @@ import { cacheHandler, libFolder, resourcePath } from "./extension";
import { getClasspathSeparator } from "./utilities/osUtilities";
import {
getClearOutputChannelOnStartSetting,
getDefaultDatabaseForConfiguration,
getLiquibaseFolder,
getOpenOutputChannelOnCommandStartSetting,
} from "./handleLiquibaseSettings";
import { getWorkFolder } from "./handleChangelogFileInput";
import { getPathOfConfiguration, readLiquibaseConfigurationNames } from "./configuration/handle/readConfiguration";
import { readFullValues } from "./configuration/data/readFromProperties";
import { getCustomDrivers } from "./utilities/customDriverUtilities";
import { ContextSelection } from "./cache/CacheHandler";

/**
* The error that was given during the process execution.
Expand Down Expand Up @@ -159,13 +165,9 @@ export function executeJarAsync<ErrorCode extends number>(
* Loads all contexts from a changelog file.
*
* @param changelogFile - the absolute path to the changelog file
* @param liquibasePropertiesPath - the path to the liquibase properties file
* @returns the quick pick items containing all changelogs
*/
export async function loadContextsFromChangelogFile(
changelogFile: string,
liquibasePropertiesPath: string
): Promise<vscode.QuickPickItem[]> {
export async function loadContextsFromChangelogFile(changelogFile: string): Promise<vscode.QuickPickItem[]> {
if (!fs.existsSync(changelogFile)) {
Logger.getLogger().debug({ message: `File ${changelogFile} does not exist for context search` });
return [];
Expand Down Expand Up @@ -212,10 +214,9 @@ export async function loadContextsFromChangelogFile(
Logger.getLogger().info({ message: "No contexts found. You can continue normal", notifyUser: true });
}

// save the loaded context into the cache
cacheHandler.saveContexts(liquibasePropertiesPath, changelogFile, {
loadedContexts: contexts,
});
// save the loaded contexts to the cache
// this will save the contexts to all property files that point to the same changelog file
setContextForCache(contexts, changelogFile);

// transform the elements to an quick pick array
const contextValues: vscode.QuickPickItem[] = contexts.map((pContext: string) => {
Expand All @@ -240,6 +241,68 @@ export async function loadContextsFromChangelogFile(
reject(error as Error);
}
});

/**
* Sets the context for all files that point to the same changelog file.
*
* @param contexts - the contexts that were loaded from the changelog file
* @param changelogFile - the absolute path to the changelog file
*/
function setContextForCache(contexts: string[], changelogFile: string): void {
readLiquibaseConfigurationNames()
.then((names) => {
if (!names) {
return;
}
for (const configurationName of names) {
getPathOfConfiguration(configurationName)
.then((propertyFile) => {
if (!propertyFile) {
return;
}

const data = readFullValues(configurationName, propertyFile, {
defaultDatabaseForConfiguration: getDefaultDatabaseForConfiguration(),
liquibaseDirectoryInProject: getLiquibaseFolder(),
customDrivers: getCustomDrivers(),
});

//here you have to use the absolute path to the changelog file
const previousSelectedContexts: ContextSelection = cacheHandler.readContexts(
propertyFile,
path.join(getWorkFolder(), data.changelogFile)
);

const changelogsFromCache = cacheHandler.readChangelogs(propertyFile);

if (
data.changelogFile.includes(path.relative(getWorkFolder(), changelogFile)) ||
changelogsFromCache.includes(changelogFile)
) {
// save the loaded context into the cache
cacheHandler.saveContexts(propertyFile, changelogFile, {
loadedContexts: contexts,
selectedContexts: previousSelectedContexts.selectedContexts ?? [""],
});
}
})
.catch((error) => {
Logger.getLogger().error({
message: `Error while reading configuration ${configurationName}`,
error,
notifyUser: true,
});
});
}
})
.catch((error) => {
Logger.getLogger().error({
message: "Error while reading configuration names",
error,
notifyUser: true,
});
});
}
}

/**
Expand Down
4 changes: 2 additions & 2 deletions src/handleContexts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ async function readContextValues(currentResults: DialogValues): Promise<vscode.Q

if (changelog) {
// we are in a right click menu, read the contexts from this file
return await loadContextsFromChangelogFile(changelog, liquibasePropertiesPath);
return await loadContextsFromChangelogFile(changelog);
}

// Read Liquibase changelog lines from properties file content
Expand All @@ -266,7 +266,7 @@ async function readContextValues(currentResults: DialogValues): Promise<vscode.Q
if (changelogs) {
// Read and parse the specified XML file
const possibleFile = path.join(getLiquibaseFolder(), path.normalize(changelogs.trim()));
const contexts = await loadContextsFromChangelogFile(possibleFile, liquibasePropertiesPath);
const contexts = await loadContextsFromChangelogFile(possibleFile);
contextValues.push(...contexts);
}

Expand Down
9 changes: 7 additions & 2 deletions src/test/e2e/LiquibaseGUITestUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ export class LiquibaseGUITestUtils {
await DockerTestUtils.startContainer();
}

this.removeContentOfFolder(path.join(this.WORKSPACE_PATH, "data", "liquibase"));

// open the workspace
await this.openWorkspace();

Expand Down Expand Up @@ -288,10 +290,13 @@ export class LiquibaseGUITestUtils {
* @param folder - the folder
*/
static removeContentOfFolder(folder: string): void {
for (const file of fs.readdirSync(folder)) {
fs.rmSync(path.join(folder, file), { recursive: true, force: true });
if (fs.existsSync(folder)) {
for (const file of fs.readdirSync(folder)) {
fs.rmSync(path.join(folder, file), { recursive: true, force: true });
}
}
}

//#endregion

//#region command end
Expand Down
1 change: 1 addition & 0 deletions src/test/e2e/commands/open-cache-file.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ suite("open cache", () => {
// sanitize the result to remove the lastUsed from the changelogs
cacheForKey.changelogs.forEach((pChangelog) => {
pChangelog.lastUsed = 1;
pChangelog.path = pChangelog.path.toLowerCase();
});

assert.deepStrictEqual(cacheForKey, expectedConnectionFromCache);
Expand Down
81 changes: 81 additions & 0 deletions src/test/e2e/executeJar.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import assert from "assert";
import { TextEditor } from "vscode-extension-tester";
import { ContextOptions } from "../../constants";
import { DockerTestUtils } from "../suite/DockerTestUtils";
import { LiquibaseGUITestUtils } from "./LiquibaseGUITestUtils";
import { Connection } from "../../cache";

/**
* Test suite for executeJar.
*/
suite("executeJar", function () {
/**
* The names of the configurations that were created during the setup.
*/
let configurationName: string;
let configurationNameDupe: string;
const clock = 1744016763539;

/**
* Set up the test suite.
*/
suiteSetup(async function () {
// create configuration that is used for the update
configurationName = await LiquibaseGUITestUtils.setupTests({ startContainer: true, addChangelog: true });
// create a second configuration that is NOT used for the update but gets the cache entry updated
configurationNameDupe = await LiquibaseGUITestUtils.setupTests({ startContainer: false, addChangelog: true });

await DockerTestUtils.resetDB();
});

/**
* Test case for loading contexts and checking if all property files are updated that point to the same changelog.
*/
test("should insert loadedContext to all property-files which point to the same changelog", async function () {
const expectedConnectionFromCache: Connection = {
changelogs: [
{
path: LiquibaseGUITestUtils.CHANGELOG_FILE.toLowerCase(),
lastUsed: clock,
contexts: {
loadedContexts: ["bar", "baz", "foo"],
selectedContexts: [""],
},
},
],
};

await LiquibaseGUITestUtils.executeUpdate(configurationName, ContextOptions.LOAD_ALL_CONTEXT, "foo");

// open the cache file
await LiquibaseGUITestUtils.startCommandExecution({
command: "Cache: Open the file with the recently loaded elements",
});

// get the text from the editor
const text = await new TextEditor().getText();
const cache = JSON.parse(text);

// get the key that was NOT used for the update but should be updated in the cache
const key = Object.keys(cache).filter((pKey) => pKey.includes(configurationNameDupe))[0];
const cacheForKey: Connection = cache[key];
// sanitize the result to remove the lastUsed from the changelogs and to lowercase the path
cacheForKey.changelogs.forEach((pChangelog) => {
pChangelog.lastUsed = clock;
pChangelog.path = pChangelog.path.toLowerCase();
});

assert.deepStrictEqual(
cacheForKey,
expectedConnectionFromCache,
"cache was not updated correctly after loading contexts"
);
});

/**
* Clean up after the test suite.
*/
suiteTeardown(async () => {
await DockerTestUtils.stopAndRemoveContainer();
});
});
Loading