# Google Sheets

Lightweight, dependency-free in-memory fake of the Google Sheets API v4 for testing `googleapis` client code with zero cost and zero side effects.

Default port: `4613`

## Implemented Operations

### Internal parlel Endpoints

| Operation | Endpoint |
| --- | --- |
| Health check | `GET /_parlel/health` |
| Reset state | `POST /_parlel/reset` |
| Discovery ping | `GET /`, `GET /v4`, `GET /sheets/v4` |

### Spreadsheets

| googleapis method | Endpoint |
| --- | --- |
| `sheets.spreadsheets.create` | `POST /v4/spreadsheets` |
| `sheets.spreadsheets.get` | `GET /v4/spreadsheets/{spreadsheetId}` |
| `sheets.spreadsheets.getByDataFilter` | `POST /v4/spreadsheets/{spreadsheetId}:getByDataFilter` |
| `sheets.spreadsheets.batchUpdate` | `POST /v4/spreadsheets/{spreadsheetId}:batchUpdate` |

### Sheets

| googleapis method | Endpoint |
| --- | --- |
| `sheets.spreadsheets.sheets.copyTo` | `POST /v4/spreadsheets/{spreadsheetId}/sheets/{sheetId}:copyTo` |

### Values

| googleapis method | Endpoint |
| --- | --- |
| `sheets.spreadsheets.values.get` | `GET /v4/spreadsheets/{spreadsheetId}/values/{range}` |
| `sheets.spreadsheets.values.update` | `PUT /v4/spreadsheets/{spreadsheetId}/values/{range}` |
| `sheets.spreadsheets.values.append` | `POST /v4/spreadsheets/{spreadsheetId}/values/{range}:append` |
| `sheets.spreadsheets.values.clear` | `POST /v4/spreadsheets/{spreadsheetId}/values/{range}:clear` |
| `sheets.spreadsheets.values.batchGet` | `GET /v4/spreadsheets/{spreadsheetId}/values:batchGet` |
| `sheets.spreadsheets.values.batchUpdate` | `POST /v4/spreadsheets/{spreadsheetId}/values:batchUpdate` |
| `sheets.spreadsheets.values.batchClear` | `POST /v4/spreadsheets/{spreadsheetId}/values:batchClear` |
| `sheets.spreadsheets.values.batchGetByDataFilter` | `POST /v4/spreadsheets/{spreadsheetId}/values:batchGetByDataFilter` |
| `sheets.spreadsheets.values.batchUpdateByDataFilter` | `POST /v4/spreadsheets/{spreadsheetId}/values:batchUpdateByDataFilter` |
| `sheets.spreadsheets.values.batchClearByDataFilter` | `POST /v4/spreadsheets/{spreadsheetId}/values:batchClearByDataFilter` |

### Developer Metadata

| googleapis method | Endpoint |
| --- | --- |
| `sheets.spreadsheets.developerMetadata.get` | `GET /v4/spreadsheets/{spreadsheetId}/developerMetadata/{metadataId}` |
| `sheets.spreadsheets.developerMetadata.search` | `POST /v4/spreadsheets/{spreadsheetId}/developerMetadata:search` |

### Supported `spreadsheets.batchUpdate` Request Types

`addSheet`, `duplicateSheet`, `deleteSheet`, `updateSheetProperties`, `updateSpreadsheetProperties`, `updateCells`, `appendCells`, `repeatCell`, `copyPaste`, `setBasicFilter`, `clearBasicFilter`, `appendDimension`, `createDeveloperMetadata`, `updateDeveloperMetadata`, and `deleteDeveloperMetadata` mutate in-memory state.

Formatting and UI-oriented request types such as `autoResizeDimensions`, `mergeCells`, `unmergeCells`, `updateBorders`, filter view requests, chart requests, protected range requests, slicer requests, and similar batch requests are accepted as no-ops and return an empty reply object. This keeps common application code compatible without modeling visual-only Sheets behavior.

## Quick Start

Start the server from the repo:

```js
import { GoogleSheetsServer } from "./apps/parlel-pool/services/google-sheets/src/server.js";

const server = new GoogleSheetsServer(4613);
await server.start();
```

Connect with the real `googleapis` client:

```js
import { google } from "googleapis";

const sheets = google.sheets({
  version: "v4",
  auth: "test-token",
});

sheets.context._options.rootUrl = "http://127.0.0.1:4613/";

const created = await sheets.spreadsheets.create({
  requestBody: { properties: { title: "Local test" } },
});

await sheets.spreadsheets.values.update({
  spreadsheetId: created.data.spreadsheetId,
  range: "Sheet1!A1:B1",
  valueInputOption: "RAW",
  requestBody: { values: [["hello", "parlel"]] },
});
```

Reset state between tests:

```sh
curl -X POST http://127.0.0.1:4613/_parlel/reset
```

## Supported Features

| Feature | Status | Notes |
| --- | --- | --- |
| Google Sheets API v4 REST paths used by `googleapis` | Supported | Implemented under `/v4`; `/sheets/v4` is also accepted. |
| In-memory spreadsheet, sheet, values, and developer metadata state | Supported | State is ephemeral and resettable. |
| A1 ranges | Supported | Handles common ranges like `Sheet1!A1:B2`, quoted sheet names, whole-row/column-style bounds, and default first-sheet ranges. |
| Grid ranges in data filters | Supported | Used by data-filter value operations and batch requests. |
| `ROWS` and `COLUMNS` major dimensions | Supported | Values are transposed for `COLUMNS`. |
| Grid data in spreadsheet responses | Supported | Returned when `includeGridData=true` or requested by data-filter get. |
| Formatting-only batch requests | Intentionally no-op | Accepted for compatibility; visual formatting is not modeled. |
| Auth and IAM | Intentionally unsupported | Tokens are ignored. This fake is local-only. |
| Persistence | Intentionally unsupported | State disappears on reset or process exit. |
| Formula evaluation | Intentionally unsupported | Formula strings are stored; they are not calculated. |
| Real Google revision history, sharing, Drive integration | Intentionally unsupported | Outside the Sheets API surface needed for local tests. |

## Error Shapes

Errors use the Google JSON API envelope:

```json
{
  "error": {
    "code": 404,
    "message": "Requested entity was not found.",
    "status": "NOT_FOUND",
    "errors": [
      {
        "message": "Requested entity was not found.",
        "domain": "global",
        "reason": "notFound"
      }
    ]
  }
}
```

Common returned codes:

| HTTP status | Status | Reason examples |
| --- | --- | --- |
| `400` | `INVALID_ARGUMENT` | `badRequest`, `parseError` |
| `404` | `NOT_FOUND` | `notFound` |
| `405` | `METHOD_NOT_ALLOWED` | `methodNotAllowed` |
| `409` | `ALREADY_EXISTS` | `alreadyExists` |
| `500` | `INTERNAL` | `internalError` |
