diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index 271ea19..ff7b790 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -61,7 +61,7 @@ export default defineConfig({ text: 'Reference', items: [ { text: 'Error Handling', link: '/en/reference/error-handling' }, - { text: 'Operations', link: '/en/reference/operations' } + { text: 'Types', link: '/en/reference/types' } ] }, { diff --git a/docs/en/concepts/data-operations.md b/docs/en/concepts/data-operations.md index 91fabd1..17a0bd7 100644 --- a/docs/en/concepts/data-operations.md +++ b/docs/en/concepts/data-operations.md @@ -1,11 +1,44 @@ # Data Operations -HolySheets! provides a set of CRUD-like operations: +HolySheets! provides a set of CRUD-like operations to interact with your Google Sheets data: -- **findFirst**, **findMany**, **findAll**: Query rows. -- **insert**: Insert new records. -- **updateFirst**, **updateMany**: Update existing rows. -- **deleteFirst**, **deleteMany**: Delete records. -- **clearFirst**, **clearMany**: Clear cell values without removing rows. +- **Finding Data** -All operations can optionally return metadata such as affected ranges and operation duration. + - [`findFirst`](/en/guides/finding-data.md#findfirst): Retrieves the first matching record. + - [`findMany`](/en/guides/finding-data.md#findmany): Retrieves multiple matching records. + - [`findAll`](/en/guides/finding-data.md#findall): Retrieves all records. + +- **Inserting Data** + + - [`insert`](/en/guides/inserting-data.md): Inserts new records. + +- **Updating Data** + + - [`updateFirst`](/en/guides/updating-data.md#updatefirst): Updates the first matching record. + - [`updateMany`](/en/guides/updating-data.md#updatemany): Updates all matching records. + +- **Deleting Data** + + - [`deleteFirst`](/en/guides/deleting-data.md#deletefirst): Deletes the first matching record. + - [`deleteMany`](/en/guides/deleting-data.md#deletemany): Deletes all matching records. + +- **Clearing Data** + - [`clearFirst`](/en/guides/clearing-data.md#clearfirst): Clears the first matching record’s cell contents (row not removed). + - [`clearMany`](/en/guides/clearing-data.md#clearmany): Clears all matching records’ cell contents (rows not removed). + +All operations can optionally return metadata such as affected ranges and operation duration. To include metadata in your responses, pass `{ includeMetadata: true }` in the second parameter of any operation method. + +```typescript +// Example: Finding the first record named "Alice" and including metadata +const result = await holySheetsInstance.findFirst( + { + where: { name: 'Alice' } + }, + { + includeMetadata: true + } +) + +console.log(result.data) // The matching record +console.log(result.metadata) // Optional metadata (if requested) +``` diff --git a/docs/en/concepts/where-filters.md b/docs/en/concepts/where-filters.md index 3ee3b6d..5cd80c8 100644 --- a/docs/en/concepts/where-filters.md +++ b/docs/en/concepts/where-filters.md @@ -19,6 +19,19 @@ | `endsWith` | Ends with a specified substring | `{ email: { endsWith: '@example.com' } }` | | `search` | Matches a regular expression pattern | `{ name: { search: '^Pro.*' } }` | +> **Note**: When multiple filters are set on the **same field**, they are combined with an **AND** operation. For instance: +> +> ```typescript +> // Both conditions must be met: +> // The 'role' field is not 'admin', AND it starts with 'senior' +> where: { +> role: { +> not: 'admin', +> startsWith: 'senior' +> } +> } +> ``` + ## equals Filters records where the specified field exactly matches the provided value. diff --git a/docs/en/configuration.md b/docs/en/configuration.md index ff724f5..87fa687 100644 --- a/docs/en/configuration.md +++ b/docs/en/configuration.md @@ -1,7 +1,141 @@ # Configuration -To configure HolySheets!: +**HolySheets!** requires a few key parameters and setup steps before you can begin interacting with your Google Sheets data. Below is a more detailed overview of the necessary configurations. -- **spreadsheetId**: The ID of your Google Spreadsheet. -- **auth**: A Google Auth client (JWT or OAuth2) with appropriate permissions. -- **Sheets Title**: Call `base(sheetTitle)` to target a specific sheet. +--- + +## Quick Start + +1. **Install HolySheets!** + +```bash +pnpm install holysheets +``` + +2. **Obtain Google Credentials** + + - Create a **Google Cloud project** and enable the _Google Sheets API_. + - Generate the appropriate **credentials** (JWT or OAuth2) with permission to access the intended spreadsheet. + - For more information about authentication check [JWT](/en/authentication/jwt) and [OAuth](/en/authentication/oauth) + +3. **Initialize HolySheets** + +Pass both `spreadsheetId` and `auth` to the **HolySheets** constructor: + +```Typescript +import HolySheets from 'holysheets' +import { authClient } from './your-auth-setup' + +const holySheetsInstance = new HolySheets({ + spreadsheetId: 'your-spreadsheet-id', + auth: authClient +}) +``` + +--- + +## Essential Configuration Parameters + +### `spreadsheetId` + +- **Description**: The unique ID of your Google Spreadsheet, which can be found in the spreadsheet’s URL. +- **Example**: + +```text +https://docs.google.com/spreadsheets/d/1AbCDefGhIJkLMNOPQRS_TUVWXYZ/edit#gid=0 + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +``` + +Everything between `d/` and `/edit` is your `spreadsheetId`. + +--- + +### `auth` + +- **Description**: An authenticated Google client capable of calling the Sheets API. +- **Supported Auth Types**: + - **JWT (Service Account)**: Commonly used for server-to-server communications. + - **OAuth2**: Used when you need user consent to access their Google Sheets. +- **Example (JWT)**: + +```Typescript +import { google } from 'googleapis' + +const authClient = new google.auth.JWT(serviceAccountEmail, null, privateKey, [ + 'https://www.googleapis.com/auth/spreadsheets' +]) +``` + +- **Example (OAuth2)**: + +```Typescript +import { google } from 'googleapis' + +const authClient = new google.auth.OAuth2(clientId, clientSecret, redirectUri) +// Then use authClient.getToken(...) and authClient.setCredentials(...) as needed +``` + +--- + +## Specifying a Sheet + +Once you have an instance of **HolySheets**, you can target a particular sheet (tab) within your spreadsheet by calling `base(sheetTitle)`: + +```Typescript +const userSheet = holySheetsInstance.base('Users') + +// Now all operations (find, insert, etc.) are performed against the "Users" sheet. +``` + +**Note**: If you omit `base(...)` or don’t specify a `sheet`, operations typically fail because the system doesn’t know which tab to target. + +--- + +## Additional Tips + +- **Permissions**: Your auth client must have permission to read/write the specified spreadsheet (depending on the operations you intend to perform). +- **Sheet Titles**: Keep your sheet (tab) names descriptive (e.g., `Orders`, `Users`, `Inventory`). You’ll use these names in `base(sheetTitle)`. +- **Environment Variables**: Consider storing your `spreadsheetId` and auth credentials in environment variables or a secure vault for better security and portability. + +--- + +## Putting It All Together + +```Typescript +// Example of a final setup: +import { google } from 'googleapis' +import HolySheets from 'holysheets' + +async function createHolySheetsInstance() { + // 1. Auth - Service Account example + const authClient = new google.auth.JWT( + process.env.GOOGLE_CLIENT_EMAIL, + undefined, + (process.env.GOOGLE_PRIVATE_KEY || '').replace(/\\n/g, '\n'), + ['https://www.googleapis.com/auth/spreadsheets'] + ) + + // 2. Construct HolySheets + const holySheetsInstance = new HolySheets({ + spreadsheetId: process.env.SPREADSHEET_ID as string, + auth: authClient + }) + + // 3. Select the "Users" sheet + const userSheet = holySheetsInstance.base('Users') + + // 4. Perform an operation (e.g., insert) + const insertResult = await userSheet.insert({ + data: [ + { name: 'Alice', age: 30 }, + { name: 'Bob', age: 25 } + ] + }) + + console.log(insertResult) +} + +createHolySheetsInstance().catch(console.error) +``` + +Following these steps ensures that **HolySheets!** is properly configured and ready to handle your data operations on Google Sheets. diff --git a/docs/en/examples/advanced-queries.md b/docs/en/examples/advanced-queries.md index ab6b5c6..4b2b1fd 100644 --- a/docs/en/examples/advanced-queries.md +++ b/docs/en/examples/advanced-queries.md @@ -1,17 +1,133 @@ -# Advanced Queries Example +# Advanced Queries -```Typescript -// Find users with email ending in @example.com and age > 30 -const results = await users.findMany({ +This guide explores more in-depth features and patterns when working with HolySheets!, including complex filtering, metadata handling, and batch updates. + +--- + +## 1. Using Multiple Filters in `where` (Logical AND) + +```typescript +await holySheetsInstance.base('Orders').findMany( + { + where: { + status: { not: 'cancelled' }, + total: { gt: 100 } + } + }, + { + includeMetadata: true + } +) +``` + +**Explanation**: + +- Both filters must be satisfied (logical AND). +- Fetches orders where `status` is NOT `'cancelled'` **and** `total` is greater than `100`. +- Includes metadata to inspect the affected ranges and operation duration. + +--- + +## 2. Combining Filter Types + +```typescript +await holySheetsInstance.base('Products').findMany({ where: { - email: { endsWith: '@example.com' }, - age: { gt: 30 } + category: { in: ['Electronics', 'Apparel'] }, + name: { startsWith: 'Pro' } } }) +``` + +**Explanation**: + +- Filters the `Products` sheet for rows in the categories `'Electronics'` or `'Apparel'`, **and** names starting with `'Pro'`. + +--- + +## 3. Performing Batch Updates with `updateMany` + +```typescript +await holySheetsInstance.base('Users').updateMany({ + where: { + status: 'active' + }, + data: { status: 'inactive' } +}) +``` + +**Explanation**: + +- Finds all users where `status` equals `'active'`, then sets `status` to `'inactive'`. +- Be cautious: **all** matching rows will be updated. + +--- + +## 4. Bulk Clearing with `clearMany` -// Update multiple inactive users at once -await users.updateMany({ - where: { status: { equals: 'inactive' } }, - data: { status: 'active' } +```typescript +await holySheetsInstance.base('Logs').clearMany({ + where: { + level: { in: ['debug', 'trace'] } + } }) ``` + +**Explanation**: + +- Clears all rows in the `Logs` sheet that have `level` `'debug'` or `'trace'`. +- Does not delete rows, only empties their contents. + +--- + +## 5. Retrieving All Records with Metadata + +```typescript +const allRecords = await holySheetsInstance.base('Inventory').findAll( + { + includeEmptyRows: true + }, + { + includeMetadata: true + } +) +console.log(allRecords.metadata) +``` + +**Explanation**: + +- Retrieves every row from the `Inventory` sheet, **including** empty rows. +- Returns metadata about the operation, such as execution time and affected ranges. + +--- + +## 6. Advanced Error Handling + +You can catch and log errors when an operation fails: + +```typescript +try { + await holySheetsInstance.base('Payments').deleteMany({ + where: { status: 'failed' } + }) +} catch (error) { + console.error('Error deleting failed payments:', error) +} +``` + +**Explanation**: + +- If the Sheets API call fails or no permission is granted, an error is thrown. +- Log or handle this error in your application logic. + +--- + +## Tips for Scaling + +1. **Use Metadata**: Inspect operation duration, record counts, and any errors for performance insights or auditing. +2. **Refine Filters**: Use multiple filters (`gt`, `lt`, `not`, etc.) to narrow down large data sets. +3. **Optimize Sheets**: Regularly review the size of your spreadsheets and consider splitting data into multiple tabs for better performance. + +--- + +Check [Basic Queries](./basic-queries.md) if you need a refresher on the fundamentals. diff --git a/docs/en/examples/basic-queries.md b/docs/en/examples/basic-queries.md index 6afbcea..8b2c2ad 100644 --- a/docs/en/examples/basic-queries.md +++ b/docs/en/examples/basic-queries.md @@ -1,12 +1,83 @@ -# Basic Queries Example +# Basic Queries -```Typescript -const users = sheets.base<{ name: string; email: string }>('Users') +This guide demonstrates how to perform the most common HolySheets! operations with minimal configuration. -// Insert a user -await users.insert({ data: [{ name: 'Diana', email: 'diana@example.com' }] }) +--- -// Find a user by exact name -const diana = await users.findFirst({ where: { name: { equals: 'Diana' } } }) -console.log(diana.data) +## 1. Insert a Single Record + +```typescript +await holySheetsInstance.base('Users').insert({ + data: [{ name: 'Alice', age: 30 }] +}) +``` + +**Explanation**: + +- Calls `base('Users')` to target the `Users` sheet. +- Inserts a single record with `name` and `age` fields. + +--- + +## 2. Find the First Matching Record + +```typescript +const alice = await holySheetsInstance.base('Users').findFirst({ + where: { name: 'Alice' } +}) +console.log(alice.data) +``` + +**Explanation**: + +- Retrieves the first row in the `Users` sheet where the `name` column equals `'Alice'`. + +--- + +## 3. Update the First Matching Record + +```typescript +await holySheetsInstance.base('Users').updateFirst({ + where: { name: 'Alice' }, + data: { age: 31 } +}) +``` + +**Explanation**: + +- Finds the first record matching `name === 'Alice'`. +- Updates her `age` to `31`. + +--- + +## 4. Delete the First Matching Record + +```typescript +await holySheetsInstance.base('Users').deleteFirst({ + where: { name: 'Alice' } +}) ``` + +**Explanation**: + +- Finds the first `Users` row where `name === 'Alice'` and deletes that row. + +--- + +## 5. Clearing a Single Record + +```typescript +await holySheetsInstance.base('Users').clearFirst({ + where: { name: 'Bob' } +}) +``` + +**Explanation**: + +- Clears all cell contents for the first row matching `name === 'Bob'`, but retains the empty row. + +--- + +## Next Steps + +Check out [Advanced Queries](./advanced-queries.md) for more filtering techniques, metadata usage, and bulk updates. diff --git a/docs/en/reference/operations.md b/docs/en/reference/operations.md deleted file mode 100644 index 52d5733..0000000 --- a/docs/en/reference/operations.md +++ /dev/null @@ -1,12 +0,0 @@ -# Operations Reference - -**Base Method**: `base(sheetName)`: Select target sheet. -**CRUD**: - -- `findFirst`, `findMany` -- `insert` -- `updateFirst`, `updateMany` -- `deleteFirst`, `deleteMany` -- `clearFirst`, `clearMany` - -See each guide for usage examples. diff --git a/docs/en/reference/types.md b/docs/en/reference/types.md new file mode 100644 index 0000000..d402074 --- /dev/null +++ b/docs/en/reference/types.md @@ -0,0 +1,190 @@ +# Types + +Below is a quick-reference table for the most common **HolySheets!** types, followed by detailed explanations. + +| **Type** | **Description** | +| ---------------------------------- | ------------------------------------------------------------------------------ | +| `CellValue` | Allowed cell data types (string, number, boolean, null). | +| `RecordType` | Represents a single row of data (keys = column headers). | +| `WhereClause` | Defines conditions for filtering rows (keys = columns; values = filters). | +| `SelectClause` | Determines which columns to return (`true` to include, `false` to omit). | +| `OperationConfigs` | Configurations for including metadata and other optional features. | +| `OperationMetadata` | Contains details about a completed operation (e.g., duration, status, errors). | +| `SanitizedOperationResult` | Return type for single-record operations (e.g., `findFirst`). | +| `SanitizedBatchOperationResult` | Return type for multi-record operations (e.g., `findMany`). | + +--- + +## `CellValue` + +Represents the type of data allowed in a single cell. By default, **HolySheets!** supports strings, numbers, booleans, and null values. + +```ts +export type CellValue = string | number | boolean | null +``` + +--- + +## `RecordType` + +A generic type representing a single record (row) in the sheet. Each key corresponds to a column header, and each value must be a valid `CellValue`. + +```ts +type RecordType = Record +``` + +> **Note**: When you instantiate **HolySheets!**, you can pass your own custom interface (instead of a plain record) if you want strongly typed columns. + +--- + +## `WhereClause` + +Defines conditions for filtering rows. Each key must match a column name in `RecordType`, and the value can be either: + +- A simple string (e.g., `'Alice'` for an equals check), or +- An object specifying one or more filters (e.g., `{ not: 'Bob', startsWith: 'A' }`). + +```ts +export type WhereClause = { + [column in keyof RecordType]?: string | WhereCondition +} +``` + +Where `WhereCondition` includes supported filters like `equals`, `lt`, `gt`, `contains`, etc. + +--- + +## `SelectClause` + +Specifies which columns to include in the result. Each key corresponds to a column in `RecordType`, and the value is a boolean indicating whether to include that column. + +```ts +export type SelectClause = Partial<{ + [column in keyof RecordType]: boolean +}> +``` + +Example usage: + +```ts +select: { name: true, age: true, status: false } +``` + +This includes only `name` and `age` columns in the returned data, excluding `status`. + +--- + +## `OperationConfigs` + +Allows you to configure additional behaviors for each operation. Currently, the most common config is: + +- **`includeMetadata`** (boolean): When `true`, the operation returns extra metadata (e.g., duration, affected rows). + +```ts +export type OperationConfigs = { + includeMetadata: boolean +} +``` + +Usage example: + +```ts +await holySheetsInstance.findMany( + { where: { status: 'active' } }, + { includeMetadata: true } +) +``` + +--- + +## `OperationMetadata` + +When `includeMetadata` is `true` in an operation, **HolySheets!** returns an `OperationMetadata` object detailing the results. This can include information such as the duration of the operation, number of records affected, and any errors. + +### Interface + +```ts +export interface OperationMetadata { + operationId: string + timestamp: string // e.g. "2024-01-01T12:00:00.000Z" + duration: string // e.g. "50ms" + recordsAffected: number // how many rows were inserted/updated/etc. + status: 'success' | 'failure' + operationType: OperationType // e.g. 'insert', 'update', 'delete', 'find', 'clear' + spreadsheetId: string + sheetId: string // the name of the sheet + ranges: string[] // Affected ranges, if any + error?: string // error message if status = 'failure' + userId?: string // optional user ID or similar, if available +} +``` + +### Key Fields + +| **Field** | **Type** | **Description** | +| ----------------- | ----------------------------- | -------------------------------------------------------------------------- | +| `operationId` | `string` | A unique identifier for the operation. | +| `timestamp` | `string` | ISO-formatted date/time string when the operation occurred. | +| `duration` | `string` | How long the operation took to complete, e.g., `"50ms"`. | +| `recordsAffected` | `number` | How many records were inserted, updated, or deleted. | +| `status` | `'success' \| 'failure'` | Whether the operation succeeded or failed. | +| `operationType` | `OperationType` | The type of operation performed, e.g., `'insert'`, `'update'`, `'delete'`. | +| `spreadsheetId` | `string` | The Google Sheets spreadsheet ID. | +| `sheetId` | `string` | The name of the specific sheet targeted by this operation. | +| `ranges` | `string[]` | An array of ranges that were affected or checked during the operation. | +| `error` | `string?` | An error message if the operation failed. | +| `userId` | `string?` | An optional user identifier for audit or logging purposes. | + +--- + +## `SanitizedOperationResult` + +Represents the result of a **single-record operation** (like `findFirst`, `updateFirst`, `deleteFirst`, etc.). It includes: + +- **`data`**: The returned record (or `undefined` if none found). +- **`metadata`**: Optional metadata (if `includeMetadata` was `true`). + +```ts +export interface SanitizedOperationResult { + data: T | undefined + metadata?: OperationMetadata +} +``` + +--- + +## `SanitizedBatchOperationResult` + +Represents the result of a **multi-record operation** (like `findMany`, `updateMany`, `deleteMany`, etc.). It includes: + +- **`data`**: An array of records (or `undefined` if none found). +- **`metadata`**: Optional metadata (if `includeMetadata` was `true`). + +```ts +export interface SanitizedBatchOperationResult { + data: T[] | undefined + metadata?: OperationMetadata +} +``` + +--- + +## Additional Notes + +- **Strongly Typed Columns**: You can replace `RecordType` with your own interface or type if your columns are well-defined. For example: + + ```ts + interface UserRecord { + id: number + name: string + email: string + isActive: boolean + } + + // Then use it with HolySheets: + const holySheets = new HolySheets({ spreadsheetId, auth }) + ``` + +- **Flexible Filtering**: `WhereClause` offers powerful filtering via filter objects (e.g., `{ equals: 'Alice', startsWith: 'Al' }`), which are combined with AND logic if multiple filters exist on the same field. + +- **Metadata**: Set `{ includeMetadata: true }` in `OperationConfigs` to receive additional details about your operation. The returned `OperationMetadata` object can help with logging, auditing, or performance monitoring.