Skip to content
11 changes: 11 additions & 0 deletions src/integrations/database/Database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,17 @@ export default class Database implements Driver
return this.#driver.searchRecords(type, query, fields, sort, limit, offset);
}

updateRecords(type: RecordType, query: RecordQuery, data: RecordData): Promise<void>
{

return this.#driver.updateRecords(type, query, data);
}

deleteRecords(type: RecordType, query: RecordQuery): Promise<void>
{
return this.#driver.deleteRecords(type, query);
}

clear(): Promise<void>
{
return this.#driver.clear();
Expand Down
2 changes: 2 additions & 0 deletions src/integrations/database/definitions/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,7 @@ export interface Driver
deleteRecord(type: RecordType, id: RecordId): Promise<void>;
findRecord(type: RecordType, query: RecordQuery, fields?: RecordField[], sort?: RecordSort): Promise<RecordData | undefined>;
searchRecords(type: RecordType, query: RecordQuery, fields?: RecordField[], sort?: RecordSort, limit?: number, offset?: number): Promise<RecordData[]>;
updateRecords(type: RecordType, query: RecordQuery, data: RecordData): Promise<void>;
deleteRecords(type: RecordType, query: RecordQuery): Promise<void>;
clear(): Promise<void>;
}
40 changes: 40 additions & 0 deletions src/integrations/database/implementations/memory/Memory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,46 @@ export default class Memory implements Driver
return limitedResult.map(records => this.#buildRecordData(records, fields));
}

async updateRecords(type: string, query: QueryStatement, data: RecordData): Promise<void>
{
const filterFunction = this.#buildFilterFunction(query);
const collection = this.#getCollection(type);
const records = collection.filter(filterFunction);
records.forEach(element =>
{
const record = collection.find(object => object.id === element.id);

if (record === undefined)
{
throw new RecordNotUpdated();
}

for (const key of Object.keys(data))
{
record[key] = data[key];
};

});
}

async deleteRecords(type: string, query: QueryStatement): Promise<void>
{
const filterFunction = this.#buildFilterFunction(query);
const collection = this.#getCollection(type);
const records = collection.filter(filterFunction);
records.forEach(element =>
{
const index = collection.findIndex(object => object.id === element.id);

if (index === -1)
{
throw new RecordNotFound();
}

collection.splice(index, 1);
});
}

async clear(): Promise<void>
{
this.#memory.clear();
Expand Down
24 changes: 24 additions & 0 deletions src/integrations/database/implementations/mongodb/MongoDb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,30 @@ export default class MongoDB implements Driver
return result.map(data => this.#buildRecordData(data, fields));
}

async updateRecords(type: RecordType, query: RecordQuery, data: RecordData): Promise<void>
{
const mongoQuery = this.#buildMongoQuery(query);

const collection = await this.#getCollection(type);
const result = await collection.updateMany(mongoQuery, { $set: data });
if (result.acknowledged === false)
{
throw new DatabaseError();
}
}

async deleteRecords(type: RecordType, query: RecordQuery): Promise<void>
{
const mongoQuery = this.#buildMongoQuery(query);

const collection = await this.#getCollection(type);
const result = await collection.deleteMany(mongoQuery);
if (result.acknowledged === false)
{
throw new DatabaseError();
}
}

async clear(): Promise<void>
{
return; // Deliberately not implemented
Expand Down
1 change: 1 addition & 0 deletions test/integrations/database/fixtures/queries.fixture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const { CALZONE, VEGETARIAN, HAWAII } = RECORDS.PIZZAS;

export const QUERIES: Record<string, RecordQuery> =
{
UPDATED: { size: { EQUALS: 40 } },
EMPTY: {},
NO_MATCH: { name: { EQUALS: 'Not existing' } },

Expand Down
25 changes: 25 additions & 0 deletions test/integrations/database/implementation.spec.ts
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tests are ordered in alphabetical order, but the order should be similar to the database defintion for easier maintenance.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

order changed

Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@

import { beforeEach, describe, expect, it } from 'vitest';

import type { RecordData } from '^/integrations/database';
import database, { RecordNotFound, RecordNotUpdated } from '^/integrations/database';

import { DATABASES, QUERIES, RECORDS, RECORD_TYPES, RESULTS, SORTS, VALUES } from './fixtures';
Expand Down Expand Up @@ -59,6 +60,18 @@ describe('integrations/database/implementation', () =>
});
});

describe('.deleteRecords', () =>
{
it('should delete all records matching the query', async () =>
{
await database.deleteRecords(RECORD_TYPES.PIZZAS, QUERIES.EQUALS);

const records = await database.searchRecords(RECORD_TYPES.PIZZAS, QUERIES.EQUALS);
expect(records).toHaveLength(0);
});

});

describe('.updateRecord', () =>
{
it('should update a record by id', async () =>
Expand All @@ -78,6 +91,18 @@ describe('integrations/database/implementation', () =>
});
});

describe('.updateRecords', () =>
{
it('should update all records matching the query', async () =>
{
const data: RecordData = { size: 40 };
await database.updateRecords(RECORD_TYPES.PIZZAS, QUERIES.EQUALS, data);

const records = await database.searchRecords(RECORD_TYPES.PIZZAS, QUERIES.UPDATED);
expect(records).toHaveLength(2);
});
});

describe('.findRecord', () =>
{
it('should return the first matched record', async () =>
Expand Down