Commit graph

47 commits

Author SHA1 Message Date
Nick Misasi
a8dffb8e34 docs(13-04): create Python SDK CLAUDE.md
Add AI development guidance for SDK maintainers. Includes directory
structure, key components, development workflow, proto code generation,
and instructions for adding new hooks and API methods.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 10:41:31 -05:00
Nick Misasi
8cc1f0b490 feat(13-02): create Python SDK Makefile with dev workflow targets
Add Makefile to python-sdk/ with targets for common development operations:

- venv: Create virtual environment and install dependencies
- proto-gen: Generate Python gRPC code from proto files
- build: Build SDK package (wheel and sdist)
- test: Run pytest test suite
- lint: Run mypy type checking
- clean: Remove build artifacts
- help: Show available targets

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 10:37:24 -05:00
Nick Misasi
a26942bde1 fix(python-sdk): accept dict responses from ExecuteCommand hook
- Add _to_command_response_proto helper to convert dict/wrapper/protobuf
  to CommandResponse protobuf
- Plugin developers can now return plain dicts with response_type, text, etc.
- Also supports CommandResponse wrapper class and raw protobufs
- Export CommandResponse from mattermost_plugin package

This fixes the "ExecuteCommand returned unexpected type: <class 'dict'>"
error when Python plugin handlers return dict responses.
2026-01-20 10:08:47 -05:00
Nick Misasi
c3938bfd77 fix(python-sdk): connect API client before hooks are called
The API client was created but never connected, causing hook handlers
like OnActivate to fail when trying to use self.api methods:
  "Client is not connected. Use 'with' statement or call connect() first."

Fix: Call api_client.connect() in serve_plugin() after creating the
client, before hooks can be invoked.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 09:20:23 -05:00
Nick Misasi
01643af641 debug: add extensive logging to trace hook registration flow
Go side:
- Log hooks returned by Implemented()
- Log each hook name -> ID mapping
- Log OnActivate implementation status
- Log OnActivate call flow

Python side:
- Log Implemented() return value
- Log OnActivate gRPC receipt and handler invocation

This is temporary debug logging to diagnose why OnActivate
isn't being called for Python plugins.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 09:12:22 -05:00
Nick Misasi
ddbcc2d164 feat(python-sdk): export Command class and register command in example
- Export Command wrapper class from main package
- Update hello_python example to register /hello command in OnActivate
- Slash commands must be registered via api.register_command() to work

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 23:13:13 -05:00
Nick Misasi
448be3350e bench(10-03): add Python SDK benchmark tests
Create comprehensive benchmark tests measuring Python SDK overhead:

API Call Benchmarks:
- GetServerVersion: baseline gRPC round-trip
- GetUser: entity retrieval with wrapper conversion
- GetChannel: another entity pattern

Wrapper Conversion Benchmarks:
- User.from_proto / User.to_proto
- Post.from_proto
- Channel.from_proto

Hook Decorator Benchmarks:
- Hook decorator application overhead
- get_hook_name lookup (attribute access)
- is_hook_handler check

Uses simple timing with time.perf_counter() for compatibility
(pytest-benchmark not required). Reports mean, median, stdev,
min, max, and ops/sec for each benchmark.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 15:43:42 -05:00
Nick Misasi
926b6f3483 chore(10-02): add CI-friendly integration test runner scripts
Create shell scripts for consistent test invocation across CI environments:

Python script (python-sdk/scripts/run_integration_tests.sh):
- Auto-detects virtual environment or uses system Python
- Installs package in editable mode if not present
- Runs test_integration_e2e.py with verbose output
- Supports passing extra pytest arguments

Go script (server/public/pluginapi/grpc/scripts/run_integration_tests.sh):
- Runs from the correct module directory
- Filters to TestPythonPlugin and TestIntegration tests
- Disables test caching for clean CI runs
- Supports passing extra go test arguments

Both scripts:
- Are executable (chmod +x)
- Print diagnostic information (working dir, version)
- Exit with appropriate error codes

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 15:36:57 -05:00
Nick Misasi
324fa65c95 test(10-02): add Python integration tests for gRPC round-trip
Create end-to-end integration tests proving Python SDK communicates
correctly with gRPC servers:

API Round-trip Tests:
- test_get_server_version_round_trip: Version API call
- test_get_system_install_date_round_trip: Install date API call
- test_get_user_round_trip: User lookup with entity serialization
- test_multiple_api_calls_in_sequence: Sequential API calls

Error Propagation Tests:
- test_not_found_error: NOT_FOUND -> NotFoundError mapping
- test_permission_denied_error: PERMISSION_DENIED mapping
- test_app_error_in_response: AppError -> PluginAPIError conversion
- test_error_recovery: Client recovery after errors

Hook Invocation Tests:
- test_implemented_returns_hooks: Implemented RPC verification
- test_lifecycle_hooks_invocation: OnActivate/OnDeactivate chain
- test_message_hook_allows_post: Normal message passthrough
- test_message_hook_rejects_spam: Message rejection
- test_message_hook_modifies_post: Message modification

Complex Scenario Tests:
- test_concurrent_api_calls: Multi-threaded API calls
- test_sequential_connect_disconnect: Connection lifecycle
- test_large_message_handling: Large payload handling

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 15:36:16 -05:00
Nick Misasi
11ddd32a8c test(08-02): add tests for response streaming, flush, and invalid status codes
Go tests:
- TestInvalidStatusCode_TooLow: verify status 42 returns 500 error
- TestInvalidStatusCode_TooHigh: verify status 1000 returns 500 error
- TestInvalidStatusCode_DoesNotPanic: ensure no panic for any invalid code
- TestFlush_WithFlusher: verify flush calls http.Flusher.Flush()
- TestFlush_WithoutFlusher: verify flush doesn't panic without Flusher
- TestFlushGracefulDegradation: mirror plugin/http_test.go behavior
- TestStreamResponseBody_WithFlush: verify flush flags processed in stream
- TestEarlyResponse_CancelsRequestSend: verify early response stops sender

Python tests:
- test_flush_method_exists: verify flush() is callable
- test_flush_applies_to_last_write: verify retroactive flush
- test_flush_applies_to_next_write_when_empty: verify pre-write flush
- test_multiple_writes_with_selective_flush: verify selective flushing
- test_streaming_response_chunks: verify multiple writes = multiple messages
- test_empty_body_response: verify no-body response handling
- test_response_headers_in_first_message: verify header placement

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 14:40:41 -05:00
Nick Misasi
00ec721e33 feat(08-02): implement Python streaming response writer with flush support
Update HTTPResponseWriter class to support streaming responses:
- Add flush() method for best-effort HTTP flush requests
- Track pending writes with flush markers for streaming mode
- Add get_pending_writes() and clear_pending_writes() methods
- Document streaming mode, header locking, and flush semantics
- Add MAX_CHUNK_SIZE constant (64KB, matching Go)

Update ServeHTTP servicer to stream responses:
- Send response chunks incrementally instead of buffering
- Include flush flag on each message when requested
- Check for cancellation between chunks
- Handle empty body case (init only)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 14:37:30 -05:00
Nick Misasi
5c7aecda49 feat(08-02): extend protobuf contract for response streaming + flush
Add flush field to ServeHTTPResponse for best-effort HTTP flush support.
Update ServeHTTPResponse and ServeHTTPResponseInit with comprehensive
documentation of streaming invariants, message ordering, and status
code validation rules.

Changes:
- Add flush boolean field (field 4) to ServeHTTPResponse
- Document response streaming invariants (init-once, header locking)
- Document status code validation (100-999 range, 0 defaults to 200)
- Regenerate Go and Python protobuf code

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 14:34:36 -05:00
Nick Misasi
503ab08cbc test(08-01): add comprehensive tests for ServeHTTP streaming
Add tests for both Go and Python ServeHTTP implementations covering:
- Body chunking behavior (small, exact, multiple, large bodies)
- Context cancellation during body streaming
- Header conversion utilities
- HTTPRequest/HTTPResponseWriter helper classes
- Request body assembly from multiple chunks
- Error handling (500 for handler errors, 404 for missing handler)

Go tests:
- TestChunking_* for body chunking scenarios
- TestConvertHTTPHeaders for header conversion
- TestBuildRequestInit for request metadata building
- TestWriteResponseHeaders for response header writing
- TestContextCancellation_DuringBodyRead for cancellation

Python tests:
- TestHTTPRequest for request wrapper class
- TestHTTPResponseWriter for response writer class
- TestHeaderConversion for proto<->dict conversion
- TestServeHTTPServicer for gRPC servicer behavior
- TestChunkingBehavior for body assembly
- TestCancellation for request cancellation

Also:
- Fixed sendRequest to properly set body_complete flag on EOF
- Added ServeHTTP to HookName enum

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 14:31:30 -05:00
Nick Misasi
147bd106a1 feat(08-01): implement Python ServeHTTP streaming handler
Add ServeHTTP bidirectional streaming implementation to the Python
hook servicer with HTTPRequest and HTTPResponseWriter helper classes.

Key features:
- Async generator for bidirectional streaming
- HTTPRequest wrapper with method, url, headers, body
- HTTPResponseWriter mimicking Go's http.ResponseWriter pattern
- Context cancellation detection during body streaming
- Header conversion utilities for proto <-> dict

Handler pattern:
- Receives: (plugin_context, response_writer, request)
- Similar to Go's http.Handler(w, r) pattern
- Response writer collects headers/status/body

Current limitations (deferred to 08-02):
- Request body fully buffered before handler invocation
- Response body fully buffered before streaming back
- No true streaming for large bodies yet

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 14:26:23 -05:00
Nick Misasi
19c392084b feat(08-01): add protobuf contract for ServeHTTP request streaming
Add bidirectional streaming RPC for ServeHTTP hook to support
efficient HTTP request/response transfer between Go and Python.

Key changes:
- New hooks_http.proto with ServeHTTPRequest/Response messages
- HTTPHeader message for multi-value header support
- ServeHTTPRequestInit with full request metadata
- ServeHTTPResponseInit for status and headers
- Body chunks with completion flag for streaming
- Updated hooks.proto with ServeHTTP streaming RPC
- Regenerated Go and Python code

Design decisions:
- 64KB default chunk size per gRPC best practices
- First message carries metadata, subsequent messages carry body
- body_complete flag signals end of stream
- Headers as repeated HTTPHeader for multi-value support

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 14:23:38 -05:00
Nick Misasi
4813f47b3f test(07-03): add comprehensive tests for remaining hooks
- Add test_hooks_command_config.py: ExecuteCommand and
  ConfigurationWillBeSaved hook tests
- Add test_hooks_user_channel.py: User lifecycle, channel/team,
  and SAML login hook tests
- Add test_hooks_notifications.py: Reaction, notification, and
  preferences hook tests
- Add test_hooks_system.py: System, WebSocket, cluster, shared
  channels, and support data hook tests
- All 65 new tests verify correct hook semantics including:
  - Fire-and-forget vs return-value hooks
  - Rejection string semantics for UserWillLogIn
  - Modify/reject semantics for notifications
  - Error handling for all hook types

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 11:41:41 -05:00
Nick Misasi
23eae41f7e feat(07-03): implement remaining hook methods in servicer
- Add ExecuteCommand hook with CommandResponse return semantics
- Add ConfigurationWillBeSaved with config modification support
- Add User lifecycle hooks (UserHasBeenCreated, UserWillLogIn,
  UserHasLoggedIn, UserHasBeenDeactivated)
- Add Channel/Team hooks (ChannelHasBeenCreated,
  UserHasJoined/LeftChannel, UserHasJoined/LeftTeam)
- Add Reaction hooks (ReactionHasBeenAdded, ReactionHasBeenRemoved)
- Add Notification hooks (NotificationWillBePushed,
  EmailNotificationWillBeSent) with modify/reject semantics
- Add PreferencesHaveChanged hook
- Add System hooks (OnInstall, OnSendDailyTelemetry,
  RunDataRetention, OnCloudLimitsUpdated)
- Add WebSocket hooks (OnWebSocketConnect, OnWebSocketDisconnect,
  WebSocketMessageHasBeenPosted)
- Add Cluster hook (OnPluginClusterEvent)
- Add Shared Channels hooks (OnSharedChannelsSyncMsg, OnSharedChannelsPing,
  OnSharedChannelsAttachmentSyncMsg, OnSharedChannelsProfileImageSyncMsg)
- Add Support hook (GenerateSupportData)
- Add SAML hook (OnSAMLLogin)
- Add implementation matrix comment documenting all hooks
- Document deferred Phase 8 hooks (ServeHTTP, ServeMetrics, FileWillBeUploaded)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 11:33:09 -05:00
Nick Misasi
ab345d7e57 test(07-02): add gRPC integration tests for hook servicer
- Add test_hooks_grpc_integration.py with in-process gRPC tests:
  - Test Implemented RPC returns correct hook list
  - Test lifecycle hooks (OnActivate, OnDeactivate, OnConfigurationChange)
  - Test MessageWillBePosted allow/reject/modify scenarios
  - Test notification hooks (MessageHasBeenPosted)
  - Test activation failure propagation via response.error
  - Test async handlers work through gRPC
- Fix lifecycle hooks to encode errors in response, not gRPC status
  - Remove context parameter from runner.invoke() for lifecycle hooks
  - Ensures activation failures are returned as AppError, not gRPC error

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 11:27:41 -05:00
Nick Misasi
6392b981fe test(07-02): add tests for lifecycle and message hook semantics
- Add test_hooks_lifecycle.py with tests for:
  - Implemented RPC returning correct hook list
  - OnActivate success/failure propagation
  - OnDeactivate best-effort semantics
  - OnConfigurationChange error handling
- Add test_hooks_messages.py with tests for:
  - MessageWillBePosted allow/reject/modify/dismiss semantics
  - MessageWillBeUpdated with old/new post handling
  - Notification hooks (Posted/Updated/Deleted)
  - MessagesWillBeConsumed list filtering/modification
- Fix @hook decorator to preserve async handlers
  - Use separate async wrapper for coroutine functions
  - Enables proper async detection in hook runner

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 11:26:03 -05:00
Nick Misasi
6d2974c821 feat(07-02): implement hook servicer with lifecycle and message hooks
- Create PluginHooksServicerImpl implementing PluginHooksServicer gRPC service
- Implement Implemented RPC returning canonical hook names from registry
- Implement lifecycle hooks: OnActivate, OnDeactivate, OnConfigurationChange
- Implement message hooks: MessageWillBePosted, MessageWillBeUpdated
- Implement notification hooks: MessageHasBeenPosted/Updated/Deleted
- Implement MessagesWillBeConsumed with post list modification support
- Add DISMISS_POST_ERROR constant for silent post dismissal
- Register hook servicer in PluginServer.start()
- Document error handling convention in module docstring

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 11:22:56 -05:00
Nick Misasi
46a931d49d test(07-01): add plugin bootstrap smoke tests
- Test handshake line format matches go-plugin protocol
- Test server starts on ephemeral port
- Test health check responds with SERVING status
- Test RuntimeConfig loading from environment
- Add integration test for full plugin lifecycle

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 11:18:43 -05:00
Nick Misasi
cf89dd1b32 test(07-01): add hook runner unit tests
- Test sync and async handler execution
- Test timeout handling with HookTimeoutError
- Test exception wrapping in HookInvocationError
- Test gRPC status code conversion
- Test HookRunner class invocation

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 11:18:38 -05:00
Nick Misasi
9cbd52e2ba test(07-01): add hook registry unit tests
- Test hook decorator with enum, string, and inferred names
- Test Plugin base class hook discovery via __init_subclass__
- Test duplicate registration error
- Test implemented_hooks() returns sorted canonical names
- Test invoke_hook() and get_hook_handler() methods

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 11:18:33 -05:00
Nick Misasi
49a7a6a121 feat(07-01): export hook system public API and add dependencies
- Export Plugin, hook, HookName, HookRegistrationError from __init__.py
- Export RuntimeConfig, load_runtime_config
- Update module docstring with plugin development example
- Add grpcio-health-checking dependency
- Add typing_extensions for Python 3.9 compatibility

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 11:18:28 -05:00
Nick Misasi
8bda0eb6ff feat(07-01): add plugin server with health service and handshake
- Create server.py with PluginServer class for gRPC server lifecycle
- Register grpc.health.v1.Health service with plugin status SERVING
- Output go-plugin handshake line (1|1|tcp|127.0.0.1:PORT|grpc)
- Bind to ephemeral port on localhost for security
- Add serve_plugin() and run_plugin() entry points

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 11:18:22 -05:00
Nick Misasi
4fb3c4ed4c feat(07-01): add runtime config loader for plugin environment
- Create runtime_config.py to load config from environment variables
- Support MATTERMOST_PLUGIN_ID, MATTERMOST_PLUGIN_API_TARGET
- Support MATTERMOST_PLUGIN_HOOK_TIMEOUT, MATTERMOST_PLUGIN_LOG_LEVEL
- Provide sensible defaults and configure_logging helper

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 11:18:16 -05:00
Nick Misasi
ef13892007 feat(07-01): add hook runner utility with timeout support
- Create hook_runner.py with run_hook_async function
- Support both sync and async handlers via asyncio.to_thread
- Enforce timeouts with asyncio.wait_for (default 30s)
- Convert exceptions to gRPC status codes
- Add HookRunner class for convenient invocation

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 11:18:11 -05:00
Nick Misasi
708e033b57 feat(07-01): add Plugin base class with hook registry
- Create plugin.py with Plugin base class for plugin authors
- Use __init_subclass__ for automatic hook discovery at class definition
- Implement implemented_hooks(), has_hook(), invoke_hook() methods
- Provide api, logger, config properties for plugin instances
- Enforce single handler per hook with clear duplicate error

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 11:18:05 -05:00
Nick Misasi
a959e8dec0 feat(07-01): add hook decorator and HookName enum
- Create hooks.py with @hook decorator for registration
- Add HookName enum with all canonical hook names from hooks.proto
- Support multiple decorator forms: @hook(HookName.X), @hook("X"), @hook
- Preserve function metadata with functools.wraps
- Validate hook names and raise HookRegistrationError for invalid names

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 11:17:59 -05:00
Nick Misasi
49fb5d8fd9 feat(06-04): implement remaining API client mixins for full RPC parity
Add comprehensive typed API client methods for all remaining Mattermost
Plugin API RPCs:

New wrapper types in wrappers.py:
- Bot, BotPatch, BotGetOptions for bot management
- Command, CommandArgs, CommandResponse for slash commands
- Preference for user preferences
- OAuthApp for OAuth applications
- Group, GroupMember, GroupSyncable for groups
- SharedChannel for shared channels
- Emoji for custom emojis
- Dialog, DialogElement, OpenDialogRequest for interactive dialogs
- AuditRecord for audit logging
- PushNotification for push notifications

New client mixins:
- BotsMixin: Bot CRUD, activation, EnsureBotUser
- CommandsMixin: Command registration/CRUD, execution, listing
- ConfigMixin: Server/plugin config, plugin management
- PreferencesMixin: User preference CRUD
- OAuthMixin: OAuth app CRUD
- GroupsMixin: Group/membership/syncable CRUD
- PropertiesMixin: Property group/field/value CRUD
- RemainingMixin: License, shared channels, dialogs, mail,
  notifications, emojis, upload sessions, post search

The audit script now reports 100% RPC coverage (236/236).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 10:17:32 -05:00
Nick Misasi
3974d55f20 test(06-03): add unit tests for Post/File/KV API methods
Add comprehensive tests (34 test cases) covering:
- Wrapper dataclass conversions (Post, Reaction, PostList, FileInfo)
- Post client methods (create, get, update, delete, ephemeral, reactions)
- File client methods (info, content, upload, link)
- KV store methods (set, get, delete, list, atomic operations)
- Error handling (NotFoundError, ValidationError, gRPC errors)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 10:06:36 -05:00
Nick Misasi
9bd8a450a5 feat(06-03): integrate Post/File/KV mixins into PluginAPIClient
Update PluginAPIClient to inherit from PostsMixin, FilesMixin,
and KVStoreMixin, providing 31 new typed methods for:
- Post CRUD, ephemeral posts, and reactions
- File metadata, content, and uploads
- Key-value store operations with atomic support

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 10:06:30 -05:00
Nick Misasi
a0149f3dc0 feat(06-03): implement KVStoreMixin with key-value store methods
Add KVStoreMixin with 9 methods:
- kv_set, kv_get, kv_delete, kv_delete_all, kv_list
- kv_set_with_expiry
- kv_compare_and_set, kv_compare_and_delete, kv_set_with_options

Provides complete KV store functionality including atomic operations
and expiring keys for plugin data persistence.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 10:06:23 -05:00
Nick Misasi
7a171a1149 feat(06-03): implement FilesMixin with file API methods
Add FilesMixin with 8 methods:
- get_file_info, get_file_infos, set_file_searchable_content
- get_file, get_file_link, read_file
- upload_file, copy_file_infos

Covers all file metadata, content retrieval, and upload operations
following the established mixin pattern.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 10:06:17 -05:00
Nick Misasi
eaad7aff18 feat(06-03): implement PostsMixin with post and reaction methods
Add PostsMixin with 15 methods:
- create_post, get_post, update_post, delete_post
- send_ephemeral_post, update_ephemeral_post, delete_ephemeral_post
- get_post_thread, get_posts_since, get_posts_after, get_posts_before
- add_reaction, remove_reaction, get_reactions

All methods follow the established mixin pattern with proper error
handling and type annotations.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 10:06:11 -05:00
Nick Misasi
31c3bd6a89 feat(06-03): add wrapper dataclasses for Post, Reaction, FileInfo, KV types
Add dataclasses for:
- Post: Mattermost message with full field support
- Reaction: User emoji reaction to a post
- PostList: Ordered list of posts with pagination
- FileInfo: File metadata with all attributes
- UploadSession: Resumable upload session
- PluginKVSetOptions: Options for KV set operations

All wrappers include from_proto() and to_proto() methods for
protobuf serialization.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 10:06:04 -05:00
Nick Misasi
529e8d9a05 test(06-02): add unit tests for User/Team/Channel API methods
Add comprehensive test suite covering:

Wrapper type tests:
- User, Team, Channel from_proto/to_proto round-trips
- UserStatus, TeamMember, ChannelMember conversions

Client method tests:
- User: get_user success, not found error, gRPC error, create_user
- Team: get_team, create_team_member, get_teams_for_user
- Channel: get_channel, add_channel_member, get_direct_channel, search

Error handling tests:
- PermissionDeniedError (HTTP 403)
- ValidationError (HTTP 400)
- AlreadyExistsError (HTTP 409)

All 21 tests use mocked stubs for fast execution.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 09:55:18 -05:00
Nick Misasi
f66cfeb78d feat(06-02): add coverage audit script for RPC/method parity
Add audit_client_coverage.py that verifies all gRPC RPCs have
corresponding Python client methods:

- Extracts RPC names from generated gRPC stub source
- Compares against PluginAPIClient public methods
- Supports --include/--exclude regex filters for scoping
- Reports coverage percentage and missing methods

Usage:
  python scripts/audit_client_coverage.py --include '(User|Team|Channel)'

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 09:55:10 -05:00
Nick Misasi
f598ab0121 fix(06-02): connect mixin classes to PluginAPIClient
Add mixin imports and inheritance to PluginAPIClient so that the
User/Team/Channel API methods are actually available on the client.

This was missing from the previous mixin implementation commit.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 09:55:01 -05:00
Nick Misasi
e1dfd1ec5f feat(06-02): implement User/Team/Channel API client mixins
Add domain-specific mixin classes for PluginAPIClient:
- UsersMixin: 33 user methods (CRUD, status, auth, sessions, permissions)
- TeamsMixin: 20 team methods (CRUD, members, icons, stats)
- ChannelsMixin: 25 channel methods (CRUD, members, sidebar categories)

Each method:
- Builds correct protobuf requests from Python inputs
- Converts responses to Pythonic wrapper types
- Maps gRPC/AppError to SDK exceptions

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 14:55:56 -05:00
Nick Misasi
4411ccbcbf feat(06-02): add wrapper dataclasses for User, Team, Channel types
Add Pythonic wrapper types for core Mattermost entities:
- User, UserStatus, CustomStatus, UserAuth, Session, UserAccessToken
- Team, TeamMember, TeamMemberWithError, TeamUnread, TeamStats
- Channel, ChannelMember, ChannelStats, SidebarCategoryWithChannels
- ViewUsersRestrictions and related enums

All wrappers implement from_proto/to_proto for protobuf conversion.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 14:55:48 -05:00
Nick Misasi
6043b7906a test(06-01): add smoke tests for codegen and client
Add comprehensive smoke tests:
- test_codegen_imports.py: verify all generated modules import correctly
- test_client_smoke.py: test client lifecycle, error mapping, and RPC calls
- Tests use in-process fake gRPC server for integration testing
- 42 tests passing covering imports, message creation, and error handling

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 14:45:01 -05:00
Nick Misasi
0c905f4d07 feat(06-01): add generated protobuf and gRPC Python code
Generate Python code from server/public/pluginapi/grpc/proto:
- Protocol buffer message definitions (*_pb2.py)
- gRPC service stubs (*_pb2_grpc.py)
- Type stubs for IDE completion (*_pb2.pyi, *_pb2_grpc.pyi)
- Imports fixed to use package-relative imports

Generated from 18 .proto files using grpcio-tools and mypy-protobuf.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 14:44:54 -05:00
Nick Misasi
5cff580fcb feat(06-01): add sync and async Plugin API clients
Add client classes for Mattermost Plugin API:
- PluginAPIClient: sync client with context manager support
- AsyncPluginAPIClient: async client with grpc.aio support
- Both implement get_server_version() as smoke test RPC
- Proper error handling converts gRPC errors to SDK exceptions

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 14:44:47 -05:00
Nick Misasi
d52baf9345 feat(06-01): add channel factory and exception hierarchy
Add core SDK runtime components:
- channel.py: gRPC channel factory with keepalive and size options
- exceptions.py: SDK exception hierarchy with gRPC error mapping
  - PluginAPIError (base), NotFoundError, PermissionDeniedError,
    ValidationError, AlreadyExistsError, UnavailableError
- convert_grpc_error() and convert_app_error() utilities

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 14:44:41 -05:00
Nick Misasi
aae96ff737 feat(06-01): add protobuf code generation script
Add generate_protos.py script that:
- Generates Python and gRPC code from .proto files
- Generates mypy type stubs via mypy-protobuf
- Fixes imports to use package-relative imports
- Outputs to src/mattermost_plugin/grpc/

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 14:44:35 -05:00
Nick Misasi
3cf6e36477 feat(06-01): add Python SDK package scaffold with pyproject.toml
Create the foundational Python SDK package structure:
- pyproject.toml with project metadata and dependencies
- src/mattermost_plugin/ package with src layout
- Configured for Python >=3.9 with grpcio and protobuf deps
- Dev dependencies include grpcio-tools, mypy-protobuf, pytest

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 14:44:29 -05:00