Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
177 changes: 91 additions & 86 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,114 +7,119 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added
- Initial project setup
- AppSheetClient with full CRUD operations
- MockAppSheetClient for testing
- Schema-based usage with SchemaLoader and SchemaManager
- DynamicTable with runtime validation
- CLI tool for schema generation (inspect, init, add-table, validate)
- Multi-instance connection management
- runAsUserEmail feature for user context
- Comprehensive JSDoc documentation
- Jest test suite (48 tests)
- GitHub Actions CI workflow
- Semantic versioning setup

### Documentation
- README.md with usage examples
- CONTRIBUTING.md with versioning guidelines
- CLAUDE.md for Claude Code integration
- TypeDoc API documentation
- Comprehensive test documentation

## [0.1.0] - 2025-11-14
## [2.1.0] - 2024-11-24

### Added
- Initial release
- Basic AppSheet CRUD operations
- TypeScript support
- Schema management

---

## Version Format

- **[MAJOR.MINOR.PATCH]** - Released versions
- **[Unreleased]** - Upcoming changes not yet released
- **Per-Request User Context Support** ([#3](https://github.com/techdivision/appsheet/issues/3))
- Added optional `runAsUserEmail` parameter to `ConnectionManager.get()` method
- Added optional `runAsUserEmail` parameter to `SchemaManager.table()` method
- Enables multi-tenant MCP servers with per-request user context
- User-specific clients are created on-the-fly (lightweight, no caching)
- Overrides global `runAsUserEmail` from schema when provided
- 100% backward compatible - existing code works without changes

- **Enhanced Schema Configuration**
- Added optional `runAsUserEmail` field to `ConnectionDefinition` interface
- Allows setting global default user at connection level in schema

- **Comprehensive Test Coverage**
- Added 13 tests for `ConnectionManager` per-request user context
- Added 18 tests for `SchemaManager` per-request user context
- Total test suite: 157 tests across 6 test suites

- **Documentation Updates**
- Added "Per-Request User Context" section to CLAUDE.md
- Added "Multi-Tenant MCP Server" usage pattern
- Updated component descriptions with new feature details
- Added usage examples for ConnectionManager and SchemaManager

## Change Categories

- **Added** - New features
- **Changed** - Changes in existing functionality
- **Deprecated** - Soon-to-be removed features
- **Removed** - Removed features
- **Fixed** - Bug fixes
- **Security** - Security fixes

## Examples

### Patch Release (0.1.0 → 0.1.1)
### Changed

```markdown
## [0.1.1] - 2025-11-15
- **SchemaManager Architecture**
- Removed table client caching - `DynamicTable` instances now created on-the-fly
- Simplified `initialize()` method - only registers connections (no table pre-creation)
- Updated `getConnections()` and `getTables()` to work without cache
- More efficient for per-request user context use cases

### Fixed
- Fixed selector parsing for date fields
- Corrected error handling in retry logic

### Documentation
- Updated API documentation
```
- Fixed package.json version number ([#4](https://github.com/techdivision/appsheet/issues/4))
- Version corrected from `0.2.0` to `2.1.0` (proper SemVer)
- Reflects actual major version 2.0.0 release with breaking changes

### Minor Release (0.1.0 → 0.2.0)
### Technical Details

```markdown
## [0.2.0] - 2025-11-20
- **Breaking Changes**: None (fully backward compatible)
- **SemVer Level**: MINOR (new features, no breaking changes)
- **Migration Required**: No
- **Dependencies**: Added `ts-semver-detector@^0.3.1` (dev dependency)

## [2.0.0] - 2024-11-20

### Added
- New `findByIds()` method for batch retrieval
- Support for custom request headers
- Connection pooling support

- **AppSheet Field Type System** (SOSO-247)
- Support for all 27 AppSheet-specific field types
- Core types: Text, Number, Date, DateTime, Time, Duration, YesNo
- Specialized text: Name, Email, URL, Phone, Address
- Specialized numbers: Decimal, Percent, Price
- Selection types: Enum, EnumList
- Media types: Image, File, Drawing, Signature
- Tracking types: ChangeCounter, ChangeTimestamp, ChangeLocation
- Reference types: Ref, RefList
- Special types: Color, Show

- **Enhanced Validation System**
- Format validation for Email, URL, Phone fields
- Range validation for Percent type (0.00 to 1.00)
- Enum/EnumList value validation with `allowedValues`
- Required field validation for add operations
- Type-specific validation for all AppSheet types

- **Validator Architecture**
- `BaseTypeValidator`: JavaScript primitive type validation
- `FormatValidator`: Format-specific validation (Email, URL, Phone, Date, DateTime, Percent)
- `AppSheetTypeValidator`: Main orchestrator for field type validation

- **SchemaInspector Enhancements**
- Automatic detection of all 27 AppSheet field types from data
- Smart Enum detection based on unique value ratio
- Pattern detection for Email, URL, Phone, Date, DateTime, Percent
- Automatic extraction of `allowedValues` for Enum/EnumList fields

### Changed
- Improved error messages with more context

### Deprecated
- `oldMethod()` will be removed in v1.0.0
```
- **BREAKING**: Schema format now requires AppSheet-specific types
- Old generic types (`string`, `number`, `boolean`, etc.) no longer supported
- All fields must use full `FieldDefinition` object with `type` property
- Shorthand string format (`"email": "string"`) removed
- `enum` property renamed to `allowedValues`

### Major Release (0.2.0 → 1.0.0)
- **BREAKING**: Type validation is stricter and more comprehensive
- All field values validated against AppSheet type constraints
- Format validation enforced for specialized types

```markdown
## [1.0.0] - 2025-12-01
### Migration Guide

### Added
- Stable API release
- Full TypeScript type coverage
See [MIGRATION.md](./MIGRATION.md) for detailed upgrade instructions from v1.x to v2.0.0.

### Changed
- **BREAKING**: Client methods now return typed responses
- **BREAKING**: Renamed `findAll()` to `find()` with options
## [1.x.x] - Previous Releases

### Removed
- **BREAKING**: Removed deprecated `oldMethod()`
For changes in version 1.x.x, please refer to git history.

### Migration Guide
---

## Links

#### Client Method Changes
- [GitHub Repository](https://github.com/techdivision/appsheet)
- [Issue Tracker](https://github.com/techdivision/appsheet/issues)
- [Documentation](./CLAUDE.md)

Before (0.x.x):
```typescript
const rows = await client.findAll('Users');
```
## SemVer Policy

After (1.0.0):
```typescript
const result = await client.find({ tableName: 'Users' });
const rows = result.rows;
```
```
This project follows [Semantic Versioning](https://semver.org/):

[Unreleased]: https://github.com/techdivision/appsheet/compare/v0.1.0...HEAD
[0.1.0]: https://github.com/techdivision/appsheet/releases/tag/v0.1.0
- **MAJOR** version: Breaking changes, incompatible API changes
- **MINOR** version: New features, backward-compatible additions
- **PATCH** version: Bug fixes, backward-compatible fixes
102 changes: 100 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,13 +118,21 @@ npx appsheet inspect --help # After npm install (uses bin entry)
- Central management class that:
1. Validates loaded schema
2. Initializes ConnectionManager with all connections
3. Creates DynamicTable instances for each table
4. Provides `table<T>(connection, tableName)` method
3. Creates DynamicTable instances on-the-fly (no caching)
4. Provides `table<T>(connection, tableName, runAsUserEmail?)` method
- **Per-Request User Context**: Optional `runAsUserEmail` parameter on `table()` method
- Creates user-specific table clients on-the-fly without caching
- Enables multi-tenant MCP servers with per-request user context
- Backward compatible: omit parameter for default behavior
- Entry point for schema-based usage pattern

**ConnectionManager** (`src/utils/ConnectionManager.ts`)
- Manages multiple AppSheet app connections by name
- Enables multi-instance support (multiple apps in one project)
- **Per-Request User Context**: Optional `runAsUserEmail` parameter on `get()` method
- Creates user-specific clients on-the-fly without caching
- Overrides global `runAsUserEmail` from schema when provided
- Backward compatible: omit parameter to get default client
- Provides health check functionality

### CLI Tool
Expand Down Expand Up @@ -195,8 +203,31 @@ await client.findAll('TableName');
```typescript
const schema = SchemaLoader.fromYaml('./config/schema.yaml');
const db = new SchemaManager(schema);

// Default behavior (uses global user from schema if configured)
const table = db.table<Type>('connection', 'tableName');
await table.findAll();

// Per-request user context (new in v2.1.0)
const userTable = db.table<Type>('connection', 'tableName', 'user@example.com');
await userTable.findAll(); // Executes as user@example.com
```

**Pattern 3: Multi-Tenant MCP Server** (New in v2.1.0)
```typescript
// MCP Server with per-request user context
const schema = SchemaLoader.fromYaml('./config/schema.yaml');
const db = new SchemaManager(schema);

// Handler for MCP request with authenticated user
async function handleToolCall(toolName: string, params: any, userEmail: string) {
// Create user-specific table client on-the-fly
const table = db.table('worklog', 'worklogs', userEmail);

// All operations execute with user's permissions
const worklogs = await table.findAll();
return worklogs;
}
```

### Validation Examples
Expand Down Expand Up @@ -236,6 +267,73 @@ await table.add([{ discount: 1.5 }]);
// ❌ ValidationError: Field "discount" must be between 0.00 and 1.00
```

### Per-Request User Context (v2.1.0)

**Feature**: Execute operations with per-request user context in multi-tenant environments.

**Use Cases**:
- Multi-tenant MCP servers where different authenticated users make requests
- Row-level security and permissions enforcement by AppSheet
- User-specific audit trails and tracking
- User-based data filtering and access control

**ConnectionManager Usage**:
```typescript
const manager = new ConnectionManager();
manager.register({
name: 'worklog',
appId: 'app-id',
applicationAccessKey: 'key',
runAsUserEmail: 'default@example.com' // Optional global default
});

// Get default client (uses global user or no user)
const defaultClient = manager.get('worklog');

// Get user-specific client (creates new instance with user context)
const userClient = manager.get('worklog', 'user@example.com');
await userClient.findAll('worklogs'); // Executes as user@example.com
```

**SchemaManager Usage**:
```typescript
const schema = SchemaLoader.fromYaml('./config/schema.yaml');
const db = new SchemaManager(schema);

// Get default table client
const table = db.table('worklog', 'worklogs');

// Get user-specific table client (creates new instance)
const userTable = db.table('worklog', 'worklogs', 'user@example.com');
await userTable.findAll(); // Executes as user@example.com
```

**Important Notes**:
- User-specific clients are created on-the-fly (not cached) - this is a lightweight operation
- Parameter `runAsUserEmail` overrides any global `runAsUserEmail` from schema
- Omitting the parameter returns default client (backward compatible)
- Each call with `runAsUserEmail` creates a new client instance
- DynamicTable and AppSheetClient are lightweight, so on-the-fly creation is efficient

**MCP Server Example**:
```typescript
// Single SchemaManager instance for entire server
const db = new SchemaManager(SchemaLoader.fromYaml('./schema.yaml'));

// MCP tool handler with per-request user context
server.tool('list_worklogs', async (params, context) => {
// Extract user from MCP context (authentication handled by MCP framework)
const userEmail = context.user?.email;

// Create user-specific table client
const table = db.table('worklog', 'worklogs', userEmail);

// All operations execute with user's AppSheet permissions
const worklogs = await table.findAll();
return worklogs;
});
```

### Error Handling

All errors extend `AppSheetError` with specific subtypes:
Expand Down
Loading
Loading