Skip to content

Commit 9b5fc1c

Browse files
authored
Merge pull request #5 from techdivision/develop
Release v2.1.0: Per-Request User Context Support
2 parents e6db6cf + c5e3097 commit 9b5fc1c

File tree

10 files changed

+2028
-123
lines changed

10 files changed

+2028
-123
lines changed

CHANGELOG.md

Lines changed: 91 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -7,114 +7,119 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10-
### Added
11-
- Initial project setup
12-
- AppSheetClient with full CRUD operations
13-
- MockAppSheetClient for testing
14-
- Schema-based usage with SchemaLoader and SchemaManager
15-
- DynamicTable with runtime validation
16-
- CLI tool for schema generation (inspect, init, add-table, validate)
17-
- Multi-instance connection management
18-
- runAsUserEmail feature for user context
19-
- Comprehensive JSDoc documentation
20-
- Jest test suite (48 tests)
21-
- GitHub Actions CI workflow
22-
- Semantic versioning setup
23-
24-
### Documentation
25-
- README.md with usage examples
26-
- CONTRIBUTING.md with versioning guidelines
27-
- CLAUDE.md for Claude Code integration
28-
- TypeDoc API documentation
29-
- Comprehensive test documentation
30-
31-
## [0.1.0] - 2025-11-14
10+
## [2.1.0] - 2024-11-24
3211

3312
### Added
34-
- Initial release
35-
- Basic AppSheet CRUD operations
36-
- TypeScript support
37-
- Schema management
38-
39-
---
40-
41-
## Version Format
4213

43-
- **[MAJOR.MINOR.PATCH]** - Released versions
44-
- **[Unreleased]** - Upcoming changes not yet released
14+
- **Per-Request User Context Support** ([#3](https://github.com/techdivision/appsheet/issues/3))
15+
- Added optional `runAsUserEmail` parameter to `ConnectionManager.get()` method
16+
- Added optional `runAsUserEmail` parameter to `SchemaManager.table()` method
17+
- Enables multi-tenant MCP servers with per-request user context
18+
- User-specific clients are created on-the-fly (lightweight, no caching)
19+
- Overrides global `runAsUserEmail` from schema when provided
20+
- 100% backward compatible - existing code works without changes
21+
22+
- **Enhanced Schema Configuration**
23+
- Added optional `runAsUserEmail` field to `ConnectionDefinition` interface
24+
- Allows setting global default user at connection level in schema
25+
26+
- **Comprehensive Test Coverage**
27+
- Added 13 tests for `ConnectionManager` per-request user context
28+
- Added 18 tests for `SchemaManager` per-request user context
29+
- Total test suite: 157 tests across 6 test suites
30+
31+
- **Documentation Updates**
32+
- Added "Per-Request User Context" section to CLAUDE.md
33+
- Added "Multi-Tenant MCP Server" usage pattern
34+
- Updated component descriptions with new feature details
35+
- Added usage examples for ConnectionManager and SchemaManager
4536

46-
## Change Categories
47-
48-
- **Added** - New features
49-
- **Changed** - Changes in existing functionality
50-
- **Deprecated** - Soon-to-be removed features
51-
- **Removed** - Removed features
52-
- **Fixed** - Bug fixes
53-
- **Security** - Security fixes
54-
55-
## Examples
56-
57-
### Patch Release (0.1.0 → 0.1.1)
37+
### Changed
5838

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

6245
### Fixed
63-
- Fixed selector parsing for date fields
64-
- Corrected error handling in retry logic
6546

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

70-
### Minor Release (0.1.0 → 0.2.0)
51+
### Technical Details
7152

72-
```markdown
73-
## [0.2.0] - 2025-11-20
53+
- **Breaking Changes**: None (fully backward compatible)
54+
- **SemVer Level**: MINOR (new features, no breaking changes)
55+
- **Migration Required**: No
56+
- **Dependencies**: Added `ts-semver-detector@^0.3.1` (dev dependency)
57+
58+
## [2.0.0] - 2024-11-20
7459

7560
### Added
76-
- New `findByIds()` method for batch retrieval
77-
- Support for custom request headers
78-
- Connection pooling support
61+
62+
- **AppSheet Field Type System** (SOSO-247)
63+
- Support for all 27 AppSheet-specific field types
64+
- Core types: Text, Number, Date, DateTime, Time, Duration, YesNo
65+
- Specialized text: Name, Email, URL, Phone, Address
66+
- Specialized numbers: Decimal, Percent, Price
67+
- Selection types: Enum, EnumList
68+
- Media types: Image, File, Drawing, Signature
69+
- Tracking types: ChangeCounter, ChangeTimestamp, ChangeLocation
70+
- Reference types: Ref, RefList
71+
- Special types: Color, Show
72+
73+
- **Enhanced Validation System**
74+
- Format validation for Email, URL, Phone fields
75+
- Range validation for Percent type (0.00 to 1.00)
76+
- Enum/EnumList value validation with `allowedValues`
77+
- Required field validation for add operations
78+
- Type-specific validation for all AppSheet types
79+
80+
- **Validator Architecture**
81+
- `BaseTypeValidator`: JavaScript primitive type validation
82+
- `FormatValidator`: Format-specific validation (Email, URL, Phone, Date, DateTime, Percent)
83+
- `AppSheetTypeValidator`: Main orchestrator for field type validation
84+
85+
- **SchemaInspector Enhancements**
86+
- Automatic detection of all 27 AppSheet field types from data
87+
- Smart Enum detection based on unique value ratio
88+
- Pattern detection for Email, URL, Phone, Date, DateTime, Percent
89+
- Automatic extraction of `allowedValues` for Enum/EnumList fields
7990

8091
### Changed
81-
- Improved error messages with more context
8292

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

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

89-
```markdown
90-
## [1.0.0] - 2025-12-01
103+
### Migration Guide
91104

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

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

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

103-
### Migration Guide
111+
---
112+
113+
## Links
104114

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

107-
Before (0.x.x):
108-
```typescript
109-
const rows = await client.findAll('Users');
110-
```
119+
## SemVer Policy
111120

112-
After (1.0.0):
113-
```typescript
114-
const result = await client.find({ tableName: 'Users' });
115-
const rows = result.rows;
116-
```
117-
```
121+
This project follows [Semantic Versioning](https://semver.org/):
118122

119-
[Unreleased]: https://github.com/techdivision/appsheet/compare/v0.1.0...HEAD
120-
[0.1.0]: https://github.com/techdivision/appsheet/releases/tag/v0.1.0
123+
- **MAJOR** version: Breaking changes, incompatible API changes
124+
- **MINOR** version: New features, backward-compatible additions
125+
- **PATCH** version: Bug fixes, backward-compatible fixes

CLAUDE.md

Lines changed: 100 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,13 +118,21 @@ npx appsheet inspect --help # After npm install (uses bin entry)
118118
- Central management class that:
119119
1. Validates loaded schema
120120
2. Initializes ConnectionManager with all connections
121-
3. Creates DynamicTable instances for each table
122-
4. Provides `table<T>(connection, tableName)` method
121+
3. Creates DynamicTable instances on-the-fly (no caching)
122+
4. Provides `table<T>(connection, tableName, runAsUserEmail?)` method
123+
- **Per-Request User Context**: Optional `runAsUserEmail` parameter on `table()` method
124+
- Creates user-specific table clients on-the-fly without caching
125+
- Enables multi-tenant MCP servers with per-request user context
126+
- Backward compatible: omit parameter for default behavior
123127
- Entry point for schema-based usage pattern
124128

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

130138
### CLI Tool
@@ -195,8 +203,31 @@ await client.findAll('TableName');
195203
```typescript
196204
const schema = SchemaLoader.fromYaml('./config/schema.yaml');
197205
const db = new SchemaManager(schema);
206+
207+
// Default behavior (uses global user from schema if configured)
198208
const table = db.table<Type>('connection', 'tableName');
199209
await table.findAll();
210+
211+
// Per-request user context (new in v2.1.0)
212+
const userTable = db.table<Type>('connection', 'tableName', 'user@example.com');
213+
await userTable.findAll(); // Executes as user@example.com
214+
```
215+
216+
**Pattern 3: Multi-Tenant MCP Server** (New in v2.1.0)
217+
```typescript
218+
// MCP Server with per-request user context
219+
const schema = SchemaLoader.fromYaml('./config/schema.yaml');
220+
const db = new SchemaManager(schema);
221+
222+
// Handler for MCP request with authenticated user
223+
async function handleToolCall(toolName: string, params: any, userEmail: string) {
224+
// Create user-specific table client on-the-fly
225+
const table = db.table('worklog', 'worklogs', userEmail);
226+
227+
// All operations execute with user's permissions
228+
const worklogs = await table.findAll();
229+
return worklogs;
230+
}
200231
```
201232

202233
### Validation Examples
@@ -236,6 +267,73 @@ await table.add([{ discount: 1.5 }]);
236267
// ❌ ValidationError: Field "discount" must be between 0.00 and 1.00
237268
```
238269
270+
### Per-Request User Context (v2.1.0)
271+
272+
**Feature**: Execute operations with per-request user context in multi-tenant environments.
273+
274+
**Use Cases**:
275+
- Multi-tenant MCP servers where different authenticated users make requests
276+
- Row-level security and permissions enforcement by AppSheet
277+
- User-specific audit trails and tracking
278+
- User-based data filtering and access control
279+
280+
**ConnectionManager Usage**:
281+
```typescript
282+
const manager = new ConnectionManager();
283+
manager.register({
284+
name: 'worklog',
285+
appId: 'app-id',
286+
applicationAccessKey: 'key',
287+
runAsUserEmail: 'default@example.com' // Optional global default
288+
});
289+
290+
// Get default client (uses global user or no user)
291+
const defaultClient = manager.get('worklog');
292+
293+
// Get user-specific client (creates new instance with user context)
294+
const userClient = manager.get('worklog', 'user@example.com');
295+
await userClient.findAll('worklogs'); // Executes as user@example.com
296+
```
297+
298+
**SchemaManager Usage**:
299+
```typescript
300+
const schema = SchemaLoader.fromYaml('./config/schema.yaml');
301+
const db = new SchemaManager(schema);
302+
303+
// Get default table client
304+
const table = db.table('worklog', 'worklogs');
305+
306+
// Get user-specific table client (creates new instance)
307+
const userTable = db.table('worklog', 'worklogs', 'user@example.com');
308+
await userTable.findAll(); // Executes as user@example.com
309+
```
310+
311+
**Important Notes**:
312+
- User-specific clients are created on-the-fly (not cached) - this is a lightweight operation
313+
- Parameter `runAsUserEmail` overrides any global `runAsUserEmail` from schema
314+
- Omitting the parameter returns default client (backward compatible)
315+
- Each call with `runAsUserEmail` creates a new client instance
316+
- DynamicTable and AppSheetClient are lightweight, so on-the-fly creation is efficient
317+
318+
**MCP Server Example**:
319+
```typescript
320+
// Single SchemaManager instance for entire server
321+
const db = new SchemaManager(SchemaLoader.fromYaml('./schema.yaml'));
322+
323+
// MCP tool handler with per-request user context
324+
server.tool('list_worklogs', async (params, context) => {
325+
// Extract user from MCP context (authentication handled by MCP framework)
326+
const userEmail = context.user?.email;
327+
328+
// Create user-specific table client
329+
const table = db.table('worklog', 'worklogs', userEmail);
330+
331+
// All operations execute with user's AppSheet permissions
332+
const worklogs = await table.findAll();
333+
return worklogs;
334+
});
335+
```
336+
239337
### Error Handling
240338

241339
All errors extend `AppSheetError` with specific subtypes:

0 commit comments

Comments
 (0)