Skip to content

Commit 0194a5f

Browse files
committed
Complete test reorganization and add all new features
- Split tests into logical files - Add deleteAll functionality - Add uniqueFields support - Add autoId toggle - Add createCrud convenience function - Add directory creation support - All 37 tests passing
1 parent 4094378 commit 0194a5f

File tree

3 files changed

+75
-11
lines changed

3 files changed

+75
-11
lines changed

lib/constants.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export const OPERATION_TYPES = {
77
CREATE: 'create',
88
UPDATE: 'update',
99
DELETE: 'delete',
10+
DELETE_ALL: 'deleteAll',
1011
WRITE_ALL: 'writeAll'
1112
};
1213

@@ -20,5 +21,7 @@ export const ERROR_MESSAGES = {
2021
};
2122

2223
export const DEFAULT_CONFIG = {
23-
ID_FIELD: 'id'
24+
ID_FIELD: 'id',
25+
AUTO_ID: true,
26+
UNIQUE_FIELDS: []
2427
};

lib/file-operations.js

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
/**
22
* File operations utilities for JsonFileCRUD
3-
* @version 1.0.0
3+
* @version 1.1.0
44
*/
55

66
import fs from 'fs';
7+
import path from 'path';
78
import { ERROR_MESSAGES } from './constants.js';
89

910
/**
@@ -38,6 +39,14 @@ export function readAllFromFile(filePath, callback) {
3839
* @param {Function} callback - Called with (error)
3940
*/
4041
export function writeItemsToFile(filePath, items, callback) {
41-
const content = JSON.stringify(items, null, 2);
42-
fs.writeFile(filePath, content, callback);
42+
// Ensure directory exists
43+
const dir = path.dirname(filePath);
44+
fs.mkdir(dir, { recursive: true }, (mkdirErr) => {
45+
if (mkdirErr) {
46+
return callback(mkdirErr);
47+
}
48+
49+
const content = JSON.stringify(items, null, 2);
50+
fs.writeFile(filePath, content, callback);
51+
});
4352
}

lib/json-file-crud.js

Lines changed: 59 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,17 @@ class JsonFileCRUD {
2424
* @param {string} filePath - Path to the JSON file
2525
* @param {Object} options - Configuration options
2626
* @param {string} options.idField - Name of the ID field (default: 'id')
27+
* @param {boolean} options.autoId - Enable auto-ID assignment (default: true)
28+
* @param {string[]} options.uniqueFields - Array of field names that should be unique
2729
*/
2830
constructor(filePath, options = {}) {
2931
if (!filePath) {
3032
throw new Error(ERROR_MESSAGES.FILE_PATH_REQUIRED);
3133
}
3234
this.filePath = path.resolve(filePath);
3335
this.idField = options.idField || DEFAULT_CONFIG.ID_FIELD;
36+
this.autoId = options.autoId !== false; // default to true
37+
this.uniqueFields = options.uniqueFields || []; // Array of field names that should be unique
3438
this.queueManager = new QueueManager(this.filePath);
3539
}
3640

@@ -49,17 +53,31 @@ class JsonFileCRUD {
4953
}
5054

5155
this.queueManager.queueOperation(OPERATION_TYPES.CREATE, { item }, callback, (items) => {
52-
// Auto-assign ID if not provided
53-
if (!item[this.idField]) {
56+
// Auto-assign ID if enabled and not provided
57+
if (this.autoId && !item[this.idField]) {
5458
item[this.idField] = this._generateNextId(items);
5559
}
5660

5761
// Check for duplicate ID
58-
const existingItem = items.find(existingItem =>
59-
existingItem[this.idField] === item[this.idField]
60-
);
61-
if (existingItem) {
62-
return callback(createDuplicateIdError(this.idField, item[this.idField]));
62+
if (item[this.idField]) {
63+
const existingItem = items.find(existingItem =>
64+
existingItem[this.idField] === item[this.idField]
65+
);
66+
if (existingItem) {
67+
return callback(createDuplicateIdError(this.idField, item[this.idField]));
68+
}
69+
}
70+
71+
// Check for duplicates in unique fields
72+
for (const fieldName of this.uniqueFields) {
73+
if (item[fieldName] !== undefined && item[fieldName] !== null) {
74+
const duplicate = items.find(existingItem =>
75+
existingItem[fieldName] === item[fieldName]
76+
);
77+
if (duplicate) {
78+
return callback(new Error(`Item with ${fieldName} '${item[fieldName]}' already exists`));
79+
}
80+
}
6381
}
6482

6583
// Add new item to array
@@ -171,6 +189,18 @@ class JsonFileCRUD {
171189
return callback(createNotFoundError(this.idField, id));
172190
}
173191

192+
// Check for duplicates in unique fields (exclude current item)
193+
for (const fieldName of this.uniqueFields) {
194+
if (data[fieldName] !== undefined && data[fieldName] !== null) {
195+
const duplicate = items.find((existingItem, index) =>
196+
index !== itemIndex && existingItem[fieldName] === data[fieldName]
197+
);
198+
if (duplicate) {
199+
return callback(new Error(`Item with ${fieldName} '${data[fieldName]}' already exists`));
200+
}
201+
}
202+
}
203+
174204
// Update item (merge data with existing item)
175205
const updatedItem = { ...items[itemIndex], ...data };
176206
items[itemIndex] = updatedItem;
@@ -208,6 +238,18 @@ class JsonFileCRUD {
208238

209239
//#endregion DELETE
210240

241+
//#region BULK OPERATIONS
242+
243+
/**
244+
* Delete all items from the JSON file
245+
* @param {Function} callback - Called with (error)
246+
*/
247+
deleteAll(callback) {
248+
this.writeAll([], callback);
249+
}
250+
251+
//#endregion BULK OPERATIONS
252+
211253
//#region UTILITY METHODS
212254

213255
/**
@@ -254,3 +296,13 @@ class JsonFileCRUD {
254296
}
255297

256298
export default JsonFileCRUD;
299+
300+
/**
301+
* Convenience function to create a new JsonFileCRUD instance
302+
* @param {string} filePath - Path to the JSON file
303+
* @param {Object} options - Configuration options
304+
* @returns {JsonFileCRUD} New JsonFileCRUD instance
305+
*/
306+
export function createCrud(filePath, options = {}) {
307+
return new JsonFileCRUD(filePath, options);
308+
}

0 commit comments

Comments
 (0)