Skip to content

Commit

Permalink
feat(Async): Make the whole library async
Browse files Browse the repository at this point in the history
BREAKING CHANGE: Every method of the library is now async and returns a promise.

Fixes #444
  • Loading branch information
Belphemur committed Aug 1, 2022
1 parent e4760cb commit b99d784
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 76 deletions.
101 changes: 36 additions & 65 deletions src/JsonDB.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
import {KeyValue, merge, removeTrailingChar} from './lib/Utils'
import * as FS from 'fs'
import * as path from 'path'
import * as mkdirp from 'mkdirp'
import {DatabaseError, DataError} from './lib/Errors'
import {DBParentData} from './lib/DBParentData'
import {ArrayInfo} from './lib/ArrayInfo'
import {Config, JsonDBConfig} from './lib/JsonDBConfig'
import {JsonDBConfig} from './lib/JsonDBConfig'

type DataPath = Array<string>

Expand Down Expand Up @@ -41,8 +38,8 @@ export class JsonDB {
return path
}

private retrieveData(dataPath: DataPath, create: boolean = false) {
this.load()
private async retrieveData(dataPath: DataPath, create: boolean = false) : Promise<any> {
await this.load()

const thisDb = this

Expand Down Expand Up @@ -121,11 +118,11 @@ export class JsonDB {
return recursiveProcessDataPath(this.data, 0)
}

private getParentData(dataPath: string, create: boolean): DBParentData {
private async getParentData(dataPath: string, create: boolean): Promise<DBParentData> {
const path = this.processDataPath(dataPath)
const last = path.pop()
return new DBParentData(
this.retrieveData(path, create),
await this.retrieveData(path, create),
this,
dataPath,
last
Expand All @@ -136,7 +133,7 @@ export class JsonDB {
* Get the wanted data
* @param dataPath path of the data to retrieve
*/
public getData(dataPath: string): any {
public getData(dataPath: string): Promise<any> {
const path = this.processDataPath(dataPath)
return this.retrieveData(path, false)
}
Expand All @@ -145,17 +142,17 @@ export class JsonDB {
* Same as getData only here it's directly typed to your object
* @param dataPath path of the data to retrieve
*/
public getObject<T>(dataPath: string): T {
public getObject<T>(dataPath: string): Promise<T> {
return this.getData(dataPath)
}

/**
* Check for existing datapath
* @param dataPath
*/
public exists(dataPath: string): boolean {
public async exists(dataPath: string): Promise<boolean> {
try {
this.getData(dataPath)
await this.getData(dataPath)
return true
} catch (e) {
if (e instanceof DataError) {
Expand All @@ -169,13 +166,13 @@ export class JsonDB {
* Returns the number of element which constitutes the array
* @param dataPath
*/
public count(dataPath: string): number {
const result = this.getData(dataPath)
public async count(dataPath: string): Promise<number> {
const result = await this.getData(dataPath)
if (!Array.isArray(result)) {
throw new DataError(`DataPath: ${dataPath} is not an array.`, 11)
}
const path = this.processDataPath(dataPath)
const data = this.retrieveData(path, false)
const data = await this.retrieveData(path, false)
return data.length
}

Expand All @@ -185,12 +182,12 @@ export class JsonDB {
* @param searchValue value to look for in the dataPath
* @param propertyName name of the property to look for searchValue
*/
public getIndex(
public async getIndex(
dataPath: string,
searchValue: string | number,
propertyName: string = 'id'
): number {
const data = this.getArrayData(dataPath)
): Promise<number> {
const data = await this.getArrayData(dataPath)
return data
.map(function (element: any) {
return element[propertyName]
Expand All @@ -203,11 +200,11 @@ export class JsonDB {
* @param dataPath base dataPath from where to start searching
* @param searchValue value to look for in the dataPath
*/
public getIndexValue(dataPath: string, searchValue: string | number): number {
return this.getArrayData(dataPath).indexOf(searchValue)
public async getIndexValue(dataPath: string, searchValue: string | number): Promise<number> {
return (await this.getArrayData(dataPath)).indexOf(searchValue)
}

private getArrayData(dataPath: string) {
private getArrayData(dataPath: string) : Promise<any>{
const result = this.getData(dataPath)
if (!Array.isArray(result)) {
throw new DataError(`DataPath: ${dataPath} is not an array.`, 11)
Expand All @@ -221,8 +218,8 @@ export class JsonDB {
* @param rootPath base dataPath from where to start searching
* @param callback method to filter the result and find the wanted entry. Receive the entry and it's index.
*/
public filter<T>(rootPath: string, callback: FindCallback): T[] | undefined {
const result = this.getData(rootPath)
public async filter<T>(rootPath: string, callback: FindCallback): Promise<T[] | undefined> {
const result = await this.getData(rootPath)
if (Array.isArray(result)) {
return result.filter(callback) as T[]
}
Expand Down Expand Up @@ -253,8 +250,8 @@ export class JsonDB {
* @param rootPath base dataPath from where to start searching
* @param callback method to filter the result and find the wanted entry. Receive the entry and it's index.
*/
public find<T>(rootPath: string, callback: FindCallback): T | undefined {
const result = this.getData(rootPath)
public async find<T>(rootPath: string, callback: FindCallback): Promise<T | undefined> {
const result = await this.getData(rootPath)
if (Array.isArray(result)) {
return result.find(callback) as T
}
Expand Down Expand Up @@ -282,8 +279,8 @@ export class JsonDB {
* @param data data to push
* @param override overriding or not the data, if not, it will merge them
*/
public push(dataPath: string, data: any, override: boolean = true): void {
const dbData = this.getParentData(dataPath, true)
public async push(dataPath: string, data: any, override: boolean = true): Promise<void> {
const dbData = await this.getParentData(dataPath, true)
// if (!dbData) {
// throw new Error('Data not found')
// }
Expand Down Expand Up @@ -311,23 +308,23 @@ export class JsonDB {
dbData.setData(toSet)

if (this.config.saveOnPush) {
this.save()
await this.save()
}
}

/**
* Delete the data
* @param dataPath path leading to the data
*/
public delete(dataPath: string): void {
const dbData = this.getParentData(dataPath, true)
public async delete(dataPath: string): Promise<void> {
const dbData = await this.getParentData(dataPath, true)
// if (!dbData) {
// return
// }
dbData.delete()

if (this.config.saveOnPush) {
this.save()
await this.save()
}
}

Expand All @@ -343,26 +340,24 @@ export class JsonDB {
/**
* Reload the database from the file
*/
public reload(): void {
public async reload(): Promise<void> {
this.loaded = false
this.load()
await this.load()
}

/**
* Manually load the database
* It is automatically called when the first getData is done
*/
public load(): void {
public async load(): Promise<void> {
if (this.loaded) {
return
}
try {
const data = FS.readFileSync(this.config.filename, 'utf8')
this.data = JSON.parse(data)
this.data = await this.config.adapter.readAsync();
this.loaded = true
} catch (err) {
const error = new DatabaseError("Can't Load Database", 1, err)
throw error
throw new DatabaseError("Can't Load Database", 1, err)
}
}

Expand All @@ -371,39 +366,15 @@ export class JsonDB {
* By default you can't save the database if it's not loaded
* @param force force the save of the database
*/
public save(force?: boolean): void {
public async save(force?: boolean): Promise<void> {
force = force || false
if (!force && !this.loaded) {
throw new DatabaseError("DataBase not loaded. Can't write", 7)
}
let data = ''
try {
if (this.config.humanReadable) {
data = JSON.stringify(this.data, null, 4)
} else {
data = JSON.stringify(this.data)
}
if (this.config.syncOnSave) {
const buffer = Buffer.from(String(data), 'utf8')
const fd_tmp = FS.openSync(this.config.filename, 'w')
let offset = 0
let length = buffer.byteLength
try {
while (length > 0) {
const written = FS.writeSync(fd_tmp, buffer, offset, length)
offset += written
length -= written
}
} finally {
FS.fsyncSync(fd_tmp)
FS.closeSync(fd_tmp)
}
} else {
FS.writeFileSync(this.config.filename, data, 'utf8')
}
await this.config.adapter.writeAsync(this.data);
} catch (err) {
const error = new DatabaseError("Can't save the database", 2, err)
throw error
throw new DatabaseError("Can't save the database", 2, err)
}
}
}
9 changes: 2 additions & 7 deletions src/lib/JsonDBConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ export interface JsonDBConfig {
readonly saveOnPush: boolean,
readonly humanReadable: boolean,
readonly separator: string,
readonly syncOnSave: boolean
}

export class Config implements JsonDBConfig {
Expand All @@ -17,7 +16,6 @@ export class Config implements JsonDBConfig {
humanReadable: boolean
saveOnPush: boolean
separator: string
syncOnSave: boolean

constructor(filename: string, saveOnPush: boolean = true, humanReadable: boolean = false, separator: string = '/', syncOnSave: boolean = false) {
this.filename = filename
Expand All @@ -30,8 +28,7 @@ export class Config implements JsonDBConfig {
this.humanReadable = humanReadable
this.saveOnPush = saveOnPush
this.separator = separator
this.syncOnSave = syncOnSave
this.adapter = new JsonAdapter(new AtomicFileAdapter(this.filename));
this.adapter = new JsonAdapter(new AtomicFileAdapter(this.filename, syncOnSave));
}
}

Expand All @@ -40,14 +37,12 @@ export class ConfigWithAdapter implements JsonDBConfig {
readonly humanReadable: boolean;
readonly saveOnPush: boolean;
readonly separator: string;
readonly syncOnSave: boolean;


constructor(adapter: IAdapter<any>, humanReadable: boolean, saveOnPush: boolean, separator: string, syncOnSave: boolean) {
constructor(adapter: IAdapter<any>, humanReadable: boolean, saveOnPush: boolean, separator: string) {
this.adapter = adapter;
this.humanReadable = humanReadable;
this.saveOnPush = saveOnPush;
this.separator = separator;
this.syncOnSave = syncOnSave;
}
}
8 changes: 4 additions & 4 deletions test/adapter/adapters.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ describe('Adapter', () => {
const filename = "data/test.file";
const data = "Hello World";

const adapter = new AtomicFileAdapter(filename);
const adapter = new AtomicFileAdapter(filename, false);
await adapter.writeAsync(data);
const exists = await checkFileExists(filename);
expect(exists).toBeTruthy();
Expand All @@ -24,7 +24,7 @@ describe('Adapter', () => {
test('should return null data when file doesn\'t exists', async () => {
const filename = "data/test2.file";

const adapter = new AtomicFileAdapter(filename);
const adapter = new AtomicFileAdapter(filename, false);
const data = await adapter.readAsync();
expect(data).toBeNull();

Expand All @@ -34,7 +34,7 @@ describe('Adapter', () => {
const filename = "data/test.json";
const data = {Hello: "World", Foo: "Bar"};

const adapter = new JsonAdapter(new AtomicFileAdapter(filename), false);
const adapter = new JsonAdapter(new AtomicFileAdapter(filename, false), false);
await adapter.writeAsync(data);
const exists = await checkFileExists(filename);
expect(exists).toBeTruthy();
Expand All @@ -44,7 +44,7 @@ describe('Adapter', () => {
})
test('should create file when loading if it doesn\'t exists', async () => {
const filename = "data/test.json";
const adapter = new JsonAdapter(new AtomicFileAdapter(filename), false);
const adapter = new JsonAdapter(new AtomicFileAdapter(filename, false), false);
await adapter.readAsync();

const fileExists = await checkFileExists(filename);
Expand Down

0 comments on commit b99d784

Please sign in to comment.