mirror of
https://github.com/unraid/api.git
synced 2025-12-31 13:39:52 -06:00
feat: allow deletion and creation of files with patches
This commit is contained in:
@@ -92,11 +92,12 @@ export abstract class FileModification {
|
|||||||
if (!patchContents.trim()) {
|
if (!patchContents.trim()) {
|
||||||
throw new Error('Patch contents are empty');
|
throw new Error('Patch contents are empty');
|
||||||
}
|
}
|
||||||
const currentContent = await readFile(this.filePath, 'utf8');
|
const currentContent = await readFile(this.filePath, 'utf8').catch(() => '');
|
||||||
const parsedPatch = parsePatch(patchContents)[0];
|
const parsedPatch = parsePatch(patchContents)[0];
|
||||||
if (!parsedPatch?.hunks.length) {
|
if (!parsedPatch?.hunks.length) {
|
||||||
throw new Error('Invalid Patch Format: No hunks found');
|
throw new Error('Invalid Patch Format: No hunks found');
|
||||||
}
|
}
|
||||||
|
|
||||||
const results = applyPatch(currentContent, parsedPatch);
|
const results = applyPatch(currentContent, parsedPatch);
|
||||||
if (results === false) {
|
if (results === false) {
|
||||||
throw new Error(`Failed to apply patch to ${this.filePath}`);
|
throw new Error(`Failed to apply patch to ${this.filePath}`);
|
||||||
@@ -178,7 +179,12 @@ export abstract class FileModification {
|
|||||||
throw new Error(`Failed to rollback patch from ${this.filePath}`);
|
throw new Error(`Failed to rollback patch from ${this.filePath}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
await writeFile(this.filePath, results);
|
if (results === '') {
|
||||||
|
// Delete the file if the patch results in an empty string
|
||||||
|
await unlink(this.filePath);
|
||||||
|
} else {
|
||||||
|
await writeFile(this.filePath, results);
|
||||||
|
}
|
||||||
|
|
||||||
// Clean up the patch file after successful rollback
|
// Clean up the patch file after successful rollback
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ const downloadOrRetrieveOriginalFile = async (filePath: string, fileUrl: string)
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return await readFile(filePath, 'utf-8');
|
return await readFile(filePath, 'utf-8').catch(() => '');
|
||||||
};
|
};
|
||||||
|
|
||||||
async function testModification(testCase: ModificationTestCase, patcher: FileModification) {
|
async function testModification(testCase: ModificationTestCase, patcher: FileModification) {
|
||||||
@@ -101,7 +101,7 @@ async function testModification(testCase: ModificationTestCase, patcher: FileMod
|
|||||||
|
|
||||||
// Rollback and verify original state
|
// Rollback and verify original state
|
||||||
await patcher.rollback();
|
await patcher.rollback();
|
||||||
const revertedContent = await readFile(filePath, 'utf-8');
|
const revertedContent = await readFile(filePath, 'utf-8').catch(() => '');
|
||||||
await expect(revertedContent).toMatch(originalContent);
|
await expect(revertedContent).toMatch(originalContent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
import { Logger } from '@nestjs/common';
|
import { Logger } from '@nestjs/common';
|
||||||
import { readFile } from 'node:fs/promises';
|
import { readFile } from 'node:fs/promises';
|
||||||
|
|
||||||
import { createPatch } from 'diff';
|
|
||||||
import { execa } from 'execa';
|
|
||||||
|
|
||||||
import { fileExists } from '@app/core/utils/files/file-exists';
|
import { fileExists } from '@app/core/utils/files/file-exists';
|
||||||
import {
|
import {
|
||||||
FileModification,
|
FileModification,
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
ShouldApplyWithReason,
|
ShouldApplyWithReason,
|
||||||
} from '@app/unraid-api/unraid-file-modifier/file-modification';
|
} from '@app/unraid-api/unraid-file-modifier/file-modification';
|
||||||
import { UnraidFileModificationService } from '@app/unraid-api/unraid-file-modifier/unraid-file-modifier.service';
|
import { UnraidFileModificationService } from '@app/unraid-api/unraid-file-modifier/unraid-file-modifier.service';
|
||||||
|
import { fileExistsSync } from '@app/core/utils/files/file-exists';
|
||||||
|
|
||||||
const FIXTURE_PATH = join(__dirname, 'modifications', '__test__', '__fixtures__', 'text-patch-file.txt');
|
const FIXTURE_PATH = join(__dirname, 'modifications', '__test__', '__fixtures__', 'text-patch-file.txt');
|
||||||
const ORIGINAL_CONTENT = 'original';
|
const ORIGINAL_CONTENT = 'original';
|
||||||
@@ -80,6 +81,25 @@ describe.sequential('FileModificationService', () => {
|
|||||||
await expect(service.applyModification(mod)).resolves.toBe(undefined);
|
await expect(service.applyModification(mod)).resolves.toBe(undefined);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should apply modification if file does not exist', async () => {
|
||||||
|
const mod = new TestFileModification(logger);
|
||||||
|
// @ts-expect-error - This is a protected method, but we need to mock it
|
||||||
|
mod.generatePatch = vi.fn().mockResolvedValue(createPatch(FIXTURE_PATH, '', 'modified'));
|
||||||
|
await fs.unlink(FIXTURE_PATH);
|
||||||
|
await expect(service.applyModification(mod)).resolves.toBe(undefined);
|
||||||
|
expect(mockLogger.warn).toHaveBeenCalledWith('Could not load pregenerated patch for: test');
|
||||||
|
expect(mockLogger.log).toHaveBeenCalledWith(
|
||||||
|
'Applying modification: test - Always Apply this mod'
|
||||||
|
);
|
||||||
|
expect(mockLogger.log).toHaveBeenCalledWith('Modification applied successfully: test');
|
||||||
|
const content = await fs.readFile(FIXTURE_PATH, 'utf-8');
|
||||||
|
expect(content).toBe('modified');
|
||||||
|
await service.rollbackAll();
|
||||||
|
expect(fileExistsSync(FIXTURE_PATH)).toBe(false);
|
||||||
|
expect(mockLogger.log).toHaveBeenCalledWith('Rolling back modification: test');
|
||||||
|
expect(mockLogger.log).toHaveBeenCalledWith('Successfully rolled back modification: test');
|
||||||
|
});
|
||||||
|
|
||||||
it('should not rollback any mods without loaded', async () => {
|
it('should not rollback any mods without loaded', async () => {
|
||||||
await expect(service.rollbackAll()).resolves.toBe(undefined);
|
await expect(service.rollbackAll()).resolves.toBe(undefined);
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user