Skip to content

Commit

Permalink
DeleteAll method (#9)
Browse files Browse the repository at this point in the history
  • Loading branch information
joseffffff committed May 23, 2024
1 parent 2fa0695 commit 04320fd
Show file tree
Hide file tree
Showing 3 changed files with 161 additions and 43 deletions.
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,3 +154,21 @@ await orm.delete(entityToDelete);
- This method deletes the row in which the entity was persisted.
- It internally fetches the sheet data to find which row needs to delete.
- Quota retries are automatically handled to manage API rate limits.

### `deleteAll(entities: T[])`

Deletes the provided entities from the spreadsheet.

```typescript
const myEntities: YourEntity[] = await orm.all();

const entitiesToDelete: YourEntity = myEntities.filter(e => e.shouldBeDeleted());

await orm.delete(entitiesToDelete);
```

- **Parameters**:
- `entities`: The entity array to delete in the sheet.
- **Remarks**:
- It internally fetches the sheet data to find which row needs to delete.
- Quota retries are automatically handled to manage API rate limits.
88 changes: 45 additions & 43 deletions src/GoogleSpreadsheetsOrm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,30 +75,7 @@ export class GoogleSpreadsheetsOrm<T extends BaseModel> {
* @returns A Promise that resolves when the row deletion process is completed successfully.
*/
public async delete(entity: T): Promise<void> {
const { data } = await this.findTableData();
const rowNumber = this.rowNumber(data, entity);

const sheetId = await this.fetchSheetDetails().then(sheetDetails => sheetDetails.properties?.sheetId);

await this.sheetsClientProvider.handleQuotaRetries(sheetsClient =>
sheetsClient.spreadsheets.batchUpdate({
spreadsheetId: this.options.spreadsheetId,
requestBody: {
requests: [
{
deleteDimension: {
range: {
sheetId,
dimension: 'ROWS',
startIndex: rowNumber - 1, // index, not a rowNumber here
endIndex: rowNumber, // exclusive, to delete just one row
},
},
},
],
},
}),
);
return this.deleteAll([entity]);
}

/**
Expand Down Expand Up @@ -141,6 +118,50 @@ export class GoogleSpreadsheetsOrm<T extends BaseModel> {
);
}

/**
* Deletes the rows associated with the provided entities in the specified sheet.
*
* @param entities - An array of entities objects to delete
*
* @remarks
* @remarks
* It internally retrieves all data from the specified sheet.
* Quota retries are automatically handled to manage API rate limits.
*
* @returns A Promise that resolves when all the row deletion processes are completed successfully.
*/
public async deleteAll(entities: T[]): Promise<void> {
if (entities.length === 0) {
return;
}

const { data } = await this.findTableData();
const rowNumbers = entities
.map(entity => this.rowNumber(data, entity))
// rows are deleted from bottom to top
.sort((a, b) => b - a);

const sheetId = await this.fetchSheetDetails().then(sheetDetails => sheetDetails.properties?.sheetId);

await this.sheetsClientProvider.handleQuotaRetries(sheetsClient =>
sheetsClient.spreadsheets.batchUpdate({
spreadsheetId: this.options.spreadsheetId,
requestBody: {
requests: rowNumbers.map(rowNumber => ({
deleteDimension: {
range: {
sheetId,
dimension: 'ROWS',
startIndex: rowNumber - 1, // index, not a rowNumber here, so -1
endIndex: rowNumber, // exclusive, to delete just one row
},
},
})),
},
}),
);
}

// public updateAll(entities: T[]): boolean {
// if (entities.length === 0) {
// return true;
Expand Down Expand Up @@ -182,25 +203,6 @@ export class GoogleSpreadsheetsOrm<T extends BaseModel> {
return sheetDetails;
}

// public deleteAll(entities: T[]): boolean {
// if (entities.length === 0) {
// return true;
// }
//
// const sheet = this.sheet();
//
// const data = this.allSheetDataFromSheet(sheet);
// data.shift(); // Delete headers
//
// const rowNumbers = entities.map(entity => this.rowNumber(data, entity)).sort((a, b) => b - a);
//
// this.ioTimingsReporter.measureTime(InputOutputOperation.DB_DELETE_ALL, () =>
// rowNumbers.forEach(rowNumber => sheet.deleteRow(rowNumber)),
// );
//
// return true;
// }
//
private rowNumber(data: ParsedSpreadsheetCellValue[][], entity: T): number {
const index = data.findIndex(row => row[0] === entity.id);

Expand Down
98 changes: 98 additions & 0 deletions tests/GoogleSpreadsheetsOrm.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,104 @@ describe(GoogleSpreadsheetsOrm.name, () => {
);
});

test('deleteAll method should correctly delete many rows', async () => {
mockValuesResponse([
['id', 'createdAt', 'name', 'jsonField', 'current', 'year'],
[
'ae222b54-182f-4958-b77f-26a3a04dff34', // id
'29/12/2023 17:47:04', // createdAt
'John Doe', // name
// language=json
'{"a":"b","c":[1,2,3]}', // jsonField
'true', // current
'2023', // year
],
[
'ae222b54-182f-4958-b77f-26a3a04dff35', // id
'29/12/2023 17:47:04', // createdAt
'John Doe', // name
// language=json
'{"a":"b","c":[1,2,3]}', // jsonField
'true', // current
'2023', // year
],
]);

mockSpreadsheetDetailsResponse({
data: {
sheets: [
{
properties: {
title: SHEET,
sheetId: 1234,
},
},
],
},
} as never);

const entitiesToDelete: TestEntity[] = [
{
id: 'ae222b54-182f-4958-b77f-26a3a04dff34',
createdAt: new Date('2023-12-29 17:47:04'),
name: 'John Doe',
jsonField: {
a: 'b',
c: [1, 2, 3],
},
current: true,
year: 2023,
},
{
id: 'ae222b54-182f-4958-b77f-26a3a04dff35',
createdAt: new Date('2023-12-29 17:47:04'),
name: 'John Doe',
jsonField: {
a: 'b',
c: [1, 2, 3],
},
current: true,
year: 2023,
},
];

await sut.deleteAll(entitiesToDelete);

expect(getBatchUpdateUsedSheetClient()?.spreadsheets.batchUpdate).toHaveBeenCalledWith({
spreadsheetId: SPREADSHEET_ID,
requestBody: {
requests: [
{
deleteDimension: {
range: {
sheetId: 1234,
dimension: 'ROWS',
startIndex: 2, // row 3
endIndex: 3,
},
},
},
{
deleteDimension: {
range: {
sheetId: 1234,
dimension: 'ROWS',
startIndex: 1, // row 3
endIndex: 2,
},
},
},
],
},
});
});

test('deleteAll does not delete anything if no entities are passed', async () => {
await sut.deleteAll([]);
// @ts-ignore
expect(sheetClients.every(client => client.spreadsheets.batchUpdate.mock.calls.length === 0)).toBeTruthy();
});

function mockValuesResponse(rawValues: string[][]): void {
sheetClients
.map(s => s.spreadsheets.values as MockProxy<sheets_v4.Resource$Spreadsheets$Values>)
Expand Down

0 comments on commit 04320fd

Please sign in to comment.