-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(spreadsheets): ajout de la capacité à lire/écrire dans un spread…
…sheet
- Loading branch information
Showing
10 changed files
with
438 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
/** | ||
* See https://developers.google.com/sheets/api/reference/rest/v4/ValueInputOption?hl=fr | ||
*/ | ||
enum SheetValueInputOption { | ||
/** | ||
* Default input value. This value must not be used. | ||
*/ | ||
INPUT_VALUE_OPTION_UNSPECIFIED = 'INPUT_VALUE_OPTION_UNSPECIFIED', | ||
/** | ||
* The values the user has entered will not be parsed and will be stored as-is. | ||
*/ | ||
RAW = 'RAW', | ||
|
||
/** | ||
* The values will be parsed as if the user typed them into the UI. Numbers will stay as numbers, but strings may be converted to numbers, dates, etc. following the same rules that are applied when entering text into a cell via the Google Sheets UI. | ||
*/ | ||
USER_ENTERED = 'USER_ENTERED', | ||
} | ||
export default SheetValueInputOption; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
enum SheetValueRenderOption { | ||
/** | ||
* Values will be calculated & formatted in the response according to the cell's formatting. Formatting is based on the spreadsheet's locale, not the requesting user's locale. For example, if A1 is 1.23 and A2 is =A1 and formatted as currency, then A2 would return "$1.23". | ||
*/ | ||
FORMATTED_VALUE = 'FORMATTED_VALUE', | ||
|
||
/** | ||
* Values will be calculated, but not formatted in the reply. For example, if A1 is 1.23 and A2 is =A1 and formatted as currency, then A2 would return the number 1.23. | ||
*/ | ||
UNFORMATTED_VALUE = 'UNFORMATTED_VALUE', | ||
|
||
/** | ||
* Values will not be calculated. The reply will include the formulas. For example, if A1 is 1.23 and A2 is =A1 and formatted as currency, then A2 would return "=A1". | ||
*/ | ||
FORMULA = 'FORMULA', | ||
} | ||
export default SheetValueRenderOption; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
import { Injectable } from '@nestjs/common'; | ||
import * as auth from 'google-auth-library'; | ||
import { google, sheets_v4, drive_v3 } from 'googleapis'; | ||
import * as retry from 'async-retry'; | ||
import * as gaxios from 'gaxios'; | ||
import SheetValueRenderOption from '../models/SheetValueRenderOption'; | ||
import SheetValueInputOption from '../models/SheetValueInputOption'; | ||
const sheets = google.sheets({ version: 'v4' }); | ||
const drive = google.drive({ version: 'v3' }); | ||
|
||
@Injectable() | ||
export default class SheetService { | ||
readonly RETRY_STRATEGY: retry.Options = { | ||
minTimeout: 60000, // Wait for 1min due to sheet api quota limitation | ||
}; | ||
|
||
private authClient: | ||
| auth.Compute | ||
| auth.JWT | ||
| auth.UserRefreshClient | ||
| auth.BaseExternalAccountClient | ||
| auth.Impersonated | ||
| null = null; | ||
|
||
async getAuthClient(): Promise< | ||
| auth.Compute | ||
| auth.JWT | ||
| auth.UserRefreshClient | ||
| auth.BaseExternalAccountClient | ||
| auth.Impersonated | ||
> { | ||
if (!this.authClient) { | ||
this.authClient = await google.auth.getClient({ | ||
scopes: [ | ||
'https://www.googleapis.com/auth/spreadsheets', | ||
'https://www.googleapis.com/auth/drive', | ||
], | ||
}); | ||
} | ||
return this.authClient; | ||
} | ||
|
||
async copyFile(fileId: string, copyTitle: string): Promise<string> { | ||
const authClient = await this.getAuthClient(); | ||
|
||
const copyOptions: drive_v3.Params$Resource$Files$Copy = { | ||
auth: authClient, | ||
fileId: fileId, | ||
requestBody: { | ||
name: copyTitle, | ||
}, | ||
}; | ||
const copyResponse = await drive.files.copy(copyOptions); | ||
return copyResponse.data.id!; | ||
} | ||
|
||
async getRawDataFromSheet( | ||
spreadsheetId: string, | ||
range: string, | ||
valueRenderOption: SheetValueRenderOption = SheetValueRenderOption.FORMATTED_VALUE, | ||
): Promise<{ | ||
data: any[][] | null; | ||
}> { | ||
const authClient = await this.getAuthClient(); | ||
|
||
const sheetValues = await retry( | ||
async (bail, num): Promise<sheets_v4.Schema$ValueRange | undefined> => { | ||
try { | ||
const getOptions: sheets_v4.Params$Resource$Spreadsheets$Values$Get = | ||
{ | ||
auth: authClient, | ||
spreadsheetId: spreadsheetId, | ||
range: range, | ||
valueRenderOption: valueRenderOption, | ||
}; | ||
//logger.info(`Get raw data from sheet ${spreadsheetId} with range ${range} (attempt ${num})`); | ||
const sheetResponse = | ||
await sheets.spreadsheets.values.get(getOptions); | ||
return sheetResponse.data; | ||
} catch (error) { | ||
//logger.exception(error); | ||
if (error instanceof gaxios.GaxiosError) { | ||
//const gaxiosError = error as gaxios.GaxiosError; | ||
/*logger.error( | ||
`Error while retrieving sheet data: status ${gaxiosError.status}, code ${gaxiosError.code}, message: ${gaxiosError.message}` | ||
);*/ | ||
if (error.status === 429) { | ||
//logger.info(`Error due to api quota limitation, retrying`); | ||
throw error; | ||
} | ||
} | ||
bail(error as Error); | ||
} | ||
}, | ||
{}, | ||
); | ||
|
||
return { data: sheetValues?.values || null }; | ||
} | ||
|
||
async overwriteRawDataToSheet( | ||
spreadsheetId: string, | ||
range: string, | ||
data: any[][], | ||
valueInputOption?: SheetValueInputOption, | ||
) { | ||
const authClient = await this.getAuthClient(); | ||
await retry(async (bail, num): Promise<void> => { | ||
try { | ||
//logger.info(`Overwrite data to sheet ${spreadsheetId} (attempt ${num})`); | ||
await sheets.spreadsheets.values.update({ | ||
auth: authClient, | ||
spreadsheetId, | ||
range: range, | ||
valueInputOption: valueInputOption || SheetValueInputOption.RAW, | ||
requestBody: { | ||
values: data, | ||
}, | ||
}); | ||
} catch (error) { | ||
//logger.exception(error); | ||
if (error instanceof gaxios.GaxiosError) { | ||
//const gaxiosError = error as gaxios.GaxiosError; | ||
/*logger.error( | ||
`Error while overwriting sheet data: status ${gaxiosError.status}, code ${gaxiosError.code}, message: ${gaxiosError.message}` | ||
);*/ | ||
if (error.status === 429) { | ||
//logger.info(`Quota error, retrying`); | ||
throw error; | ||
} | ||
} | ||
// No need to trigger retry if it's not a quota error | ||
bail(error as Error); | ||
} | ||
}, this.RETRY_STRATEGY); | ||
} | ||
|
||
async testDownload() { | ||
console.log('Copying file...'); | ||
const fileCopyId = await this.copyFile( | ||
'1_l9NkgNIefC4BZLMhZ20GzrG3xlH2kxuH2vFzCgDFw8', | ||
'Trajectoire SNBC Territorialisée - Compute', | ||
); | ||
|
||
console.log('File copied with id:', fileCopyId); | ||
|
||
const epciCode = 200043495; | ||
// Write the epci code | ||
await this.overwriteRawDataToSheet(fileCopyId, 'Caract_territoire!F6', [ | ||
[epciCode], | ||
]); | ||
|
||
// Getting computed data from spreadsheet | ||
const data = await this.getRawDataFromSheet( | ||
fileCopyId, | ||
'Caract_territoire!F1239', | ||
SheetValueRenderOption.UNFORMATTED_VALUE, | ||
); | ||
|
||
return data; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import { Module } from '@nestjs/common'; | ||
import SheetService from './services/sheet.service'; | ||
|
||
@Module({ | ||
providers: [SheetService], | ||
exports: [SheetService], | ||
controllers: [], | ||
}) | ||
export class SheetModule {} |
Oops, something went wrong.