bodevops-features
v1.0.8
Published
BoDevOps features library - utilities for Google Drive, Google Sheets, and iDrive e2
Maintainers
Readme
BoDevOps Features
A collection of framework-agnostic TypeScript utilities for Google Drive, Google Sheets, and iDrive e2 operations.
Features
- 🚀 Framework-agnostic - Works with any TypeScript/JavaScript project
- 📦 Tree-shakeable - Import only what you need
- 🔒 Type-safe - Full TypeScript support with no
anytypes - 📚 Well-documented - Comprehensive JSDoc comments in English
- ✨ Easy to use - Clean, intuitive API design
Installation
npm install bodevops-featuresModules
🗂️ Google Drive (GGDrive)
Manage files and folders in Google Drive with ease.
Features:
- Upload files to Drive
- Create folder hierarchies
- Share files and folders
- Transfer ownership
- Get storage quota information
- List files in folders
- Delete files
📊 Google Sheets (GGSheet)
Read, write, and manage Google Spreadsheet data.
Features:
- Read sheet data
- Export data (Append/Overwrite modes)
- Update cells, rows, and columns
- Find row by value
- Delete rows
- Convert sheet data to typed objects
- Column name/index conversion utilities
Quick Start
Google Drive
import { GGDrive } from 'bodevops-features';
// Initialize client
const driveClient = new GGDrive.GoogleDriveClient({
keyFilePath: './service-account.json',
});
// Get storage information
const storage = await driveClient.getStorageInfo();
console.log(`Used: ${storage.formattedUsed} / ${storage.formattedTotal}`);
// Upload a file
const result = await driveClient.uploadFile({
localFilePath: './document.pdf',
driveFolder: 'MyFolder/SubFolder',
fileName: 'uploaded-document.pdf',
});
console.log(`View at: ${result.webViewLink}`);
// Upload and share with someone
const sharedResult = await driveClient.uploadFileAndShare({
localFilePath: './report.pdf',
driveFolder: 'SharedReports',
shareWithEmail: '[email protected]',
role: 'writer',
});
// List files in a folder
const files = await driveClient.listFilesInFolder({ folderId: 'root' });
for (const file of files) {
console.log(`${file.name} (${file.mimeType})`);
}Google Sheets
import { GGSheet } from 'bodevops-features';
// Initialize client
const sheetClient = new GGSheet.GoogleSheetClient({
keyFilePath: './service-account.json',
});
// Read data from a sheet
const data = await sheetClient.getValues({
sheetUrl: 'https://docs.google.com/spreadsheets/d/1abc123...',
sheetName: 'Sheet1',
});
// Convert to typed objects
interface Person {
name: string;
email: string;
age: string;
}
const people = GGSheet.convertValueSheet<Person>({
values: data,
rowOffset: 0, // First row is header
});
// Export data (Overwrite mode)
await sheetClient.export({
sheetUrl: 'https://docs.google.com/spreadsheets/d/1abc123...',
sheetName: 'Sheet1',
listCols: ['Name', 'Email', 'Age'],
valsExport: [
['John Doe', '[email protected]', '30'],
['Jane Smith', '[email protected]', '25'],
],
typeExport: GGSheet.ETypeExport.Overwrite,
});
// Update specific cells
await sheetClient.updateValuesMultiCells({
sheetUrl: 'https://docs.google.com/spreadsheets/d/1abc123...',
sheetName: 'Sheet1',
cells: [
{ row: 0, col: 0, content: 'Updated Value' },
{ row: 1, col: 1, content: 'Another Update' },
],
rowOffset: 0,
});
// Find a row by value
const rowIndex = await sheetClient.getIdxRow({
sheetUrl: 'https://docs.google.com/spreadsheets/d/1abc123...',
sheetName: 'Sheet1',
colName: 'A',
value: 'John Doe',
});Authentication
Both Google Drive and Google Sheets require a Google Service Account for authentication.
Creating a Service Account
- Go to Google Cloud Console
- Create a new project or select an existing one
- Enable the Google Drive API and Google Sheets API
- Create a Service Account:
- Go to "IAM & Admin" → "Service Accounts"
- Click "Create Service Account"
- Fill in the details and create
- Create a JSON key:
- Click on the created service account
- Go to "Keys" tab
- Click "Add Key" → "Create new key"
- Select "JSON" and download
Using Credentials
// Option 1: Using key file path
const client = new GGDrive.GoogleDriveClient({
keyFilePath: './service-account.json',
});
// Option 2: Using credentials object
const client = new GGDrive.GoogleDriveClient({
credentials: {
type: 'service_account',
project_id: 'your-project-id',
private_key: '-----BEGIN PRIVATE KEY-----\n...',
client_email: '[email protected]',
// ... other fields
},
});API Reference
Google Drive
GoogleDriveClient
Constructor:
new GoogleDriveClient(config: IGoogleDriveConfig)Methods:
| Method | Description |
| ------------------------------- | ----------------------------------- |
| getStorageInfo() | Get Drive storage quota information |
| uploadFile(params) | Upload a file to Drive |
| uploadFileAndShare(params) | Upload and share with email |
| deleteFile(params) | Delete a file from Drive |
| listFilesInFolder(params) | List files in a folder |
| makeFilePublic(params) | Make file accessible via link |
| transferFileOwnership(params) | Transfer file to another user |
| shareFolderWithEmail(params) | Share folder with permissions |
| getFolderIdByPath(params) | Get folder ID from path string |
| fileExistsInFolder(params) | Check if file exists |
Utility Functions:
formatBytes({ bytes })- Convert bytes to human-readable formatnormalizeFilePath({ filePath })- Normalize file pathvalidateFileExists({ filePath })- Check if file existsgetFileInfo({ filePath })- Get file metadata
Google Sheets
GoogleSheetClient
Constructor:
new GoogleSheetClient(config: IGoogleSheetConfig)Methods:
| Method | Description |
| ---------------------------------------- | ------------------------------ |
| getSheetInfo(params) | Get spreadsheet metadata |
| getValues(params) | Read all values from sheet |
| getIdxRow(params) | Find row index by value |
| export(params) | Export data (Append/Overwrite) |
| updateValuesMultiCells(params) | Update specific cells |
| updateValuesMultiColsByRow(params) | Update columns in a row |
| updateValuesMultiRowsByCol(params) | Update rows in a column |
| updateValuesMultiRowsMultiCols(params) | Batch update range |
| deleteRowSheet(params) | Delete a row |
Utility Functions:
convertIndexToColumnName({ columnIndex })- Convert 0 → "A", 26 → "AA"convertColumnNameToIndex({ columnName })- Convert "A" → 0, "AA" → 26convertValueSheet({ values, rowOffset })- Parse sheet to typed objectsgetSheetIdFromUrl({ sheetUrl })- Extract spreadsheet IDcalculateActualRow({ dataRowIndex, rowOffset })- Calculate sheet row number
Enums:
ETypeExport.Append- Append data to existingETypeExport.Overwrite- Replace all data
Advanced Examples
Google Drive: Batch Upload with Progress
const files = ['file1.pdf', 'file2.pdf', 'file3.pdf'];
for (const file of files) {
const result = await driveClient.uploadFile({
localFilePath: `./documents/${file}`,
driveFolder: 'Uploads/2024',
fileName: file,
});
console.log(`✓ Uploaded: ${result.name}`);
}Google Sheets: Export Database Query Results
import { GGSheet } from 'bodevops-features';
// Assume you have query results
const users = await database.query('SELECT name, email, created_at FROM users');
// Map to column definitions
const colsMapping = {
name: 'Full Name',
email: 'Email Address',
created_at: 'Registration Date',
};
// Extract columns and values
const { listCols, valsExport } = GGSheet.getListColsAndValsExport({
colsForSheet: colsMapping,
resultItems: users,
});
// Export to sheet
await sheetClient.export({
sheetUrl: 'https://docs.google.com/spreadsheets/d/...',
sheetName: 'Users',
listCols,
valsExport,
typeExport: GGSheet.ETypeExport.Overwrite,
});Google Sheets: Update Multiple Rows
// Update column C for rows 0-4
await sheetClient.updateValuesMultiRowsByCol({
sheetUrl: 'https://docs.google.com/spreadsheets/d/...',
sheetName: 'Sheet1',
col: 2, // Column C (0-based)
values: [
{ row: 0, content: 'Status: Complete' },
{ row: 1, content: 'Status: Pending' },
{ row: 2, content: 'Status: Complete' },
{ row: 3, content: 'Status: Failed' },
{ row: 4, content: 'Status: Complete' },
],
});Google Sheets Update & Delete Methods - Detailed Guide
📝 Understanding rowOffset
All update and delete methods support a rowOffset parameter to handle different sheet structures:
rowOffset: 0(default): Header at row 1, data starts at row 2rowOffset: 1: Header at row 1, skip row 2, data starts at row 3rowOffset: 2: Header at row 1, skip rows 2-3, data starts at row 4
Example Sheet Structure:
Row 1: [Name, Email, Status] ← Header
Row 2: [John, [email protected], Active] ← Data row 0 (with rowOffset=0)
Row 3: [Jane, [email protected], Pending] ← Data row 1 (with rowOffset=0)1️⃣ updateValuesMultiCells() - Update Specific Cells
Update multiple cells at specific row and column positions.
Use Case: Update scattered cells across the sheet
await sheetClient.updateValuesMultiCells({
sheetUrl: 'https://docs.google.com/spreadsheets/d/...',
sheetName: 'Sheet1',
cells: [
{ row: 0, col: 0, content: 'John Doe' }, // A2
{ row: 0, col: 2, content: 'Active' }, // C2
{ row: 1, col: 1, content: '[email protected]' }, // B3
{ row: 5, col: 3, content: 'Updated' }, // D7
],
rowOffset: 0, // Optional, default is 0
});Parameters:
cells: Array of{ row, col, content }objectsrow: 0-based data row indexcol: 0-based column index (0=A, 1=B, 2=C, ...)content: String value to write
2️⃣ updateValuesMultiColsByRow() - Update Multiple Columns in One Row
Update several columns in a single row.
Use Case: Update a complete user record
// Update row 3 (data row index 2) - columns A, B, C
await sheetClient.updateValuesMultiColsByRow({
sheetUrl: 'https://docs.google.com/spreadsheets/d/...',
sheetName: 'Users',
row: 2, // Data row index (actual sheet row 4 with rowOffset=0)
values: [
{ col: 0, content: 'Updated Name' }, // Column A
{ col: 1, content: '[email protected]' }, // Column B
{ col: 2, content: 'Active' }, // Column C
{ col: 4, content: '2026-01-07' }, // Column E
],
rowOffset: 0,
});Real-world Example:
// Update user status and last login
await sheetClient.updateValuesMultiColsByRow({
sheetUrl: SHEET_URL,
sheetName: 'Users',
row: userId,
values: [
{ col: 5, content: 'Online' }, // Status column
{ col: 6, content: new Date().toISOString() }, // Last login column
],
});3️⃣ updateValuesMultiRowsByCol() - Update Multiple Rows in One Column
Update several rows in a single column.
Use Case: Batch update status for multiple items
// Update status column (C) for multiple rows
await sheetClient.updateValuesMultiRowsByCol({
sheetUrl: 'https://docs.google.com/spreadsheets/d/...',
sheetName: 'Tasks',
col: 2, // Column C (0-based)
values: [
{ row: 0, content: 'Completed' },
{ row: 1, content: 'In Progress' },
{ row: 2, content: 'Completed' },
{ row: 5, content: 'Pending' },
{ row: 8, content: 'Completed' },
],
rowOffset: 0,
});Real-world Example:
// Mark all selected tasks as completed
const completedTaskIds = [0, 3, 5, 7];
await sheetClient.updateValuesMultiRowsByCol({
sheetUrl: SHEET_URL,
sheetName: 'Tasks',
col: 3, // Status column
values: completedTaskIds.map((taskId) => ({
row: taskId,
content: 'Completed ✓',
})),
});4️⃣ updateValuesMultiRowsMultiCols() - Batch Update a Range
Update a rectangular range of cells (multiple rows and columns).
Use Case: Update a table section or paste data block
// Update a 3x3 range starting at row 0, column 0
await sheetClient.updateValuesMultiRowsMultiCols({
sheetUrl: 'https://docs.google.com/spreadsheets/d/...',
sheetName: 'Data',
values: [
['A1', 'B1', 'C1'],
['A2', 'B2', 'C2'],
['A3', 'B3', 'C3'],
],
startRow: 0, // Start at data row 0 (sheet row 2)
startCol: 0, // Start at column A
rowOffset: 0,
});Advanced Example with Custom Range:
// Update columns D-F (indices 3-5) for rows 10-15
await sheetClient.updateValuesMultiRowsMultiCols({
sheetUrl: SHEET_URL,
sheetName: 'Report',
values: [
['Q1', '1000', '95%'],
['Q2', '1200', '98%'],
['Q3', '1100', '96%'],
['Q4', '1300', '99%'],
],
startRow: 10, // Data row 10
endRow: 13, // Data row 13 (4 rows total)
startCol: 3, // Column D
rowOffset: 0,
});Paste Clipboard Data:
// Paste a copied table from Excel/Sheets
const clipboardData = [
['Product', 'Price', 'Stock'],
['Item A', '100', '50'],
['Item B', '200', '30'],
['Item C', '150', '40'],
];
await sheetClient.updateValuesMultiRowsMultiCols({
sheetUrl: SHEET_URL,
sheetName: 'Inventory',
values: clipboardData,
startRow: 0,
startCol: 0,
});5️⃣ deleteRowSheet() - Delete a Row
Delete a specific row from the sheet.
Use Case: Remove a record from the sheet
// Delete data row 5 (actual sheet row 7 with rowOffset=0)
await sheetClient.deleteRowSheet({
sheetUrl: 'https://docs.google.com/spreadsheets/d/...',
sheetName: 'Users',
row: 5, // Data row index
rowOffset: 0,
});Real-world Example:
// Delete user by finding their row first
const userEmail = '[email protected]';
// Find the row
const rowIndex = await sheetClient.getIdxRow({
sheetUrl: SHEET_URL,
sheetName: 'Users',
colName: 'B', // Email column
value: userEmail,
});
if (rowIndex >= 0) {
// Delete the row
await sheetClient.deleteRowSheet({
sheetUrl: SHEET_URL,
sheetName: 'Users',
row: rowIndex,
});
console.log(`Deleted user: ${userEmail}`);
}⚠️ Important Notes:
- Deleting a row shifts all rows below it up by one
- The row index is 0-based for data rows (excluding header)
- Cannot be undone - use with caution!
🎯 Method Selection Guide
| Scenario | Recommended Method |
| ----------------------------------- | ---------------------------------- |
| Update 1-2 specific cells | updateValuesMultiCells() |
| Update entire user record (one row) | updateValuesMultiColsByRow() |
| Batch update status column | updateValuesMultiRowsByCol() |
| Update a table section/range | updateValuesMultiRowsMultiCols() |
| Remove a record | deleteRowSheet() |
💡 Pro Tips
1. Batch Operations for Performance:
// ❌ Bad: Multiple individual updates
for (const item of items) {
await sheetClient.updateValuesMultiCells({
sheetUrl: SHEET_URL,
sheetName: 'Data',
cells: [{ row: item.id, col: 2, content: item.status }],
});
}
// ✅ Good: Single batch update
await sheetClient.updateValuesMultiRowsByCol({
sheetUrl: SHEET_URL,
sheetName: 'Data',
col: 2,
values: items.map((item) => ({ row: item.id, content: item.status })),
});2. Handle rowOffset Correctly:
// If your sheet has a description row after header:
// Row 1: [Name, Email, Status] ← Header
// Row 2: [Enter name, Enter email, ...] ← Description
// Row 3: [John, [email protected], Active] ← First data row
await sheetClient.updateValuesMultiCells({
sheetUrl: SHEET_URL,
sheetName: 'Sheet1',
cells: [{ row: 0, col: 0, content: 'Updated' }],
rowOffset: 1, // Skip the description row
});
// This updates row 3 (first data row)3. Validate Before Delete:
// Always confirm before deleting
const confirmDelete = await getUserConfirmation();
if (confirmDelete) {
await sheetClient.deleteRowSheet({
sheetUrl: SHEET_URL,
sheetName: 'Users',
row: rowIndex,
});
}TypeScript Support
This library is written in TypeScript and provides full type definitions.
import { GGDrive, GGSheet } from 'bodevops-features';
// All types are exported
type UploadResult = GGDrive.IUploadFileResult;
type SheetInfo = GGSheet.ISpreadsheetInfo;
type ExportMode = GGSheet.ETypeExport;Error Handling
All methods throw descriptive errors. Wrap calls in try-catch:
try {
const result = await driveClient.uploadFile({
localFilePath: './document.pdf',
driveFolder: 'MyFolder',
});
} catch (error) {
console.error('Upload failed:', error.message);
// Handle error appropriately
}Contributing
Contributions are welcome! Please follow these guidelines:
- Fork the repository
- Create a feature branch
- Write clean, documented code
- Add tests if applicable
- Submit a pull request
License
MIT © BoDevOps
Support
- 📧 Email: [email protected]
- 🐛 Issues: GitHub Issues
- 📖 Documentation: Full API Docs
Changelog
v1.0.0 (2026-01-07)
- ✨ Initial release
- 🗂️ Google Drive module
- 📊 Google Sheets module
- 📚 Full TypeScript support
- 📝 Comprehensive documentation
