Commit graph

2765 commits

Author SHA1 Message Date
Nick Misasi
0fda377dec docs(13-04): create server gRPC CLAUDE.md
Add AI development guidance for Mattermost contributors working on the
Python plugin gRPC infrastructure. Includes directory structure, key
components, proto organization, common patterns, and instructions for
adding new API methods and hooks.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 10:42:03 -05:00
Nick Misasi
f1f1ee8d95 docs(13-01): create comprehensive gRPC architecture documentation
Add ARCHITECTURE.md documenting the Python plugin gRPC system including:
- High-level overview and ASCII system architecture diagram
- Component layer documentation (Protocol, Go Infrastructure, Python SDK)
- Process lifecycle (loading, environment variables, shutdown)
- Communication flow diagrams (hooks, API calls, ServeHTTP streaming)
- Key design decisions (embedded AppError, 64KB chunks, APIServerRegistrar)
- Complete file reference table mapping functionality to source files
- Extension guide for adding new API methods and hooks

This documentation enables internal Mattermost engineers to understand,
debug, and extend the Python plugin infrastructure.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 10:38:52 -05:00
Nick Misasi
a049f81c76 feat(13-02): add Python proto generation targets to server Makefile
Add python-proto-gen and proto-gen-all targets to server/public/Makefile
for complete gRPC code generation workflow:

- python-proto-gen: Generates Python gRPC code by calling generate_protos.py
- proto-gen-all: Runs both Go and Python proto generation in sequence

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 10:37:03 -05:00
Nick Misasi
d486fb74f6 test(12-01): add tests for Python API callback server
- Add TestPythonAPIServerStartup: verify server starts and registrar called
- Add TestPythonAPIServerLifecycle: verify cleanup stops server properly
- Add TestPythonCommandWithAPIServer: verify env var not set when apiImpl is nil

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 09:58:28 -05:00
Nick Misasi
98eb666891 feat(12-01): wire API server lifecycle into supervisor shutdown
- Add apiServerCleanup call to supervisor Shutdown method
- Cleanup happens AFTER Python process terminates for graceful disconnect
- Fix test assertion to check for relative executable path

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 09:49:51 -05:00
Nick Misasi
453ab3dc74 feat(12-01): start gRPC PluginAPI server for Python plugins
- Add startAPIServer function to start gRPC server on random port
- Add APIServerRegistrar type to break import cycle with apiserver package
- Modify WithCommandFromManifest to accept apiImpl and registrar
- Update configurePythonCommand to start API server and set MATTERMOST_PLUGIN_API_TARGET env var
- Add apiServerCleanup field to supervisor struct for cleanup on shutdown
- Add SetAPIServerRegistrar method to Environment for dependency injection
- Wire up API server registrar in app/plugin.go

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 09:46:34 -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
d6a7645f2a fix(python): register missing hook names in hookNameToId map
OnActivate, ServeHTTP, MessageWillBePosted, MessageWillBeUpdated, and
ServeMetrics are handled specially and not in client_rpc_generated.go,
but they still need to be in the hookNameToId map for the Python plugin
Implemented() mechanism to work.

Without this, Python plugins could report implementing OnActivate but
the Go side wouldn't recognize the hook name and wouldn't call it.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 23:21:24 -05:00
Nick Misasi
0743aad9f0 fix(python): convert venv interpreter path to absolute
When pluginInfo.Path is relative (e.g., "plugins/com.mattermost.hello-python"),
the venv interpreter path was also relative. exec.Command needs an absolute
path to find the executable when cmd.Dir is set.

Fix: Convert the venv path to absolute using filepath.Abs before returning.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 23:06:38 -05:00
Nick Misasi
0b361c40c4 fix(python): resolve path duplication when executing Python plugins
When cmd.Dir is set, command arguments are resolved relative to that
directory. The code was passing the full path (including plugin dir)
as the script argument while also setting cmd.Dir to the plugin dir,
causing Python to look for the script at a duplicated path like:
plugins/com.mattermost.hello-python/plugins/com.mattermost.hello-python/plugin.py

Fix: Pass just the executable name (e.g., "plugin.py") to the command
since cmd.Dir is already set to the plugin directory. The full path
is still used for the existence check before command creation.

Also removes debug logging that was added for diagnosis.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 23:00:03 -05:00
Nick Misasi
6f7e1c3fa5 test(11-03): create comprehensive Python plugin integration tests
Add integration tests demonstrating complete Python plugin lifecycle:
- TestPythonPluginIntegration: Tests startup, hooks, ServeHTTP, shutdown
- TestPythonPluginServeHTTP: Tests HTTP streaming with large responses
- TestPythonPluginEnvironmentIntegration: Tests Environment activation/deactivation
- TestPythonPluginCrashRecovery: Tests crash detection and restart
- TestPythonPluginImplementsChecking: Tests hook implementation tracking

All tests use fake Python interpreters (compiled Go binaries) that implement
the PluginHooks gRPC service to validate the Go-side integration without
requiring actual Python.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 22:38:18 -05:00
Nick Misasi
c167c2e4bf test(11-02): add integration test for Python plugin hook dispatch
Add TestPythonPluginHookDispatch that verifies end-to-end hook dispatch
for Python plugins. The test:
- Creates a fake Python plugin with PluginHooks gRPC service
- Verifies hooks are properly wired through the supervisor
- Tests Implemented(), OnActivate, OnDeactivate, and MessageHasBeenPosted

Also update existing Phase 5 tests to implement PluginHooks service:
- TestPythonSupervisor_HealthCheckSuccess
- TestPythonPluginEnvironmentActivation
- TestPythonSupervisor_Restart

These tests now reflect the new architecture where Python plugins
have hooks wired via hooksGRPCClient.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 22:33:03 -05:00
Nick Misasi
8804a44de6 feat(11-02): wire hooksGRPCClient for Python plugins in supervisor
Remove Phase 5 limitation that skipped hook dispensing for Python plugins.
Now Python plugins use hooksGRPCClient to receive hook invocations through
the same infrastructure as Go plugins.

Changes:
- Extract gRPC connection from go-plugin's GRPCClient
- Create hooksGRPCClient adapter using the connection
- Wrap in hooksTimerLayer for metrics collection
- Populate implemented hooks array from gRPC client

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 22:26:26 -05:00
Nick Misasi
b7f7ee7dbf feat(11-01): implement hooksGRPCClient adapter for Python plugins
Implement the hooksGRPCClient adapter that implements the plugin.Hooks
interface by delegating to a gRPC PluginHooksClient. This enables Python
plugins to receive hook invocations through the same infrastructure as
Go plugins.

Key features:
- Task 1: Core adapter structure with constructor and lifecycle hooks
  (OnActivate, OnDeactivate, OnConfigurationChange, Implemented)
- Task 2: ServeHTTP with bidirectional streaming (64KB chunks)
- Task 3: All remaining hook methods including message, user, channel,
  team, command, WebSocket, and miscellaneous hooks

Technical details:
- Uses context.Background() with 30s timeout for gRPC calls
- Checks implemented array before making gRPC calls
- Converts between model types and protobuf types
- ServeHTTP uses bidirectional streaming for request/response body

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 22:24:14 -05:00
Nick Misasi
3397ba6a4f bench(10-03): add Go gRPC benchmark tests
Create comprehensive benchmark tests measuring gRPC server overhead
for Python plugin API calls. Benchmarks use in-memory bufconn to
isolate gRPC serialization/transport overhead from network latency.

Benchmarks included:
- BenchmarkGetServerVersion: minimal RPC baseline (~36us)
- BenchmarkGetUser: entity retrieval with model conversion (~38us)
- BenchmarkCreatePost: entity creation with request/response (~41us)
- BenchmarkGetChannel: channel retrieval pattern (~37us)
- BenchmarkKVSet/KVGet: key-value operations (~37-38us)
- BenchmarkHasPermissionTo: permission check hot-path (~37us)
- BenchmarkIsEnterpriseReady: simple boolean response (~36us)

Results establish baseline for comparing Python plugin overhead vs
native Go plugins.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 15:40:27 -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
e05bbb5f81 test(10-02): add Go integration tests for Python plugin lifecycle
Create integration tests that prove Go gRPC server and Python plugin
communicate correctly via simulated in-memory gRPC communication:

- TestPythonPluginLifecycle: Full lifecycle (activate, deactivate)
- TestPythonPluginAPICall: Round-trip plugin -> API server callback
- TestPythonPluginHookChain: MessageWillBePosted modification/rejection
- TestPythonPluginActivationFailure: Error propagation from plugin
- TestPythonPluginAPIErrorPropagation: AppError -> gRPC status mapping
- TestIntegrationConcurrentHookCalls: Concurrent hook invocations

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 15:32:04 -05:00
Nick Misasi
ae87fc7527 test(09-02): update tests for Server.Runtime detection
Replace props.runtime test cases with Server.Runtime field tests:
- python runtime from manifest field (no .py extension)
- go runtime explicit (should not be Python)
- python with full config (PythonVersion, ManifestPython)
- Keep .py extension fallback test for backward compatibility

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 14:56:36 -05:00
Nick Misasi
6917035d48 feat(09-02): update isPythonPlugin to use manifest.Server.Runtime
Replace transitional props.runtime hack with proper manifest field detection.
Now prioritizes Server.Runtime="python" over .py extension fallback.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 14:56:13 -05:00
Nick Misasi
f78a1ce5e0 test(09-01): add manifest unmarshal tests for Python plugin fields
Add TestManifestUnmarshalPython function with test cases for:
- YAML Python plugin manifest parsing with all fields
- JSON Python plugin manifest parsing with all fields
- Backward compatibility for Go plugins without runtime field
- Python plugin with minimal config (just runtime, no python section)

Verifies Runtime, PythonVersion, and Python struct fields parse correctly
in both JSON and YAML formats.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 14:53:48 -05:00
Nick Misasi
ac5dd103b4 feat(09-01): extend ManifestServer struct with Python runtime fields
Add new fields to ManifestServer for Python plugin support:
- Runtime: specifies "go" (default) or "python" runtime
- PythonVersion: minimum Python version required (e.g. "3.11")
- Python: nested ManifestPython struct for Python-specific config

Create ManifestPython struct with:
- DependencyMode: "system", "venv", or "bundled"
- VenvPath: relative path to virtual environment
- RequirementsPath: relative path to requirements.txt

Update doc comment with Python plugin example manifest.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 14:53:12 -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
49941d877a feat(08-02): implement Go streaming response with flush support
Update ServeHTTPCaller to support true streaming responses:
- Full-duplex operation: plugin can respond before request body is consumed
- Flush support: best-effort flushing when underlying writer supports http.Flusher
- Status code validation (100-999 range) to prevent server panics
- Early response handling with proper goroutine cleanup
- Proper error responses for invalid status codes and plugin failures

Key changes:
- Add logger to ServeHTTPCaller for error logging
- Add context cancellation to sendRequest for early response termination
- Add receiveFirstResponse to handle first message with body chunk + flush
- Add writeResponseHeaders with status code validation
- Add flush() method for best-effort HTTP flushing
- Update streamResponseBody to handle flush flag

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 14:36:28 -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
75bdac1b13 feat(08-01): implement Go gRPC client for ServeHTTP streaming
Add ServeHTTPCaller that streams HTTP requests to Python plugins
and receives streaming responses.

Key features:
- Bidirectional streaming for efficient large body transfer
- 64KB chunked request body streaming (no full buffering)
- Context cancellation propagation from HTTP client disconnect
- HTTPHeader conversion supporting multi-value headers
- PluginContext passed through to Python handler

Implementation details:
- sendRequest: streams body chunks with completion flag
- receiveResponseInit: gets status code and headers
- streamResponseBody: writes response chunks to ResponseWriter
- buildRequestInit: converts http.Request to protobuf init

Note: Response streaming improvements deferred to 08-02.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 14:25:07 -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
7532e8f68b test(05-03): add Python plugin crash/restart recovery test
Add end-to-end test for restart behavior:
1. Activate healthy fake Python plugin
2. Verify health check succeeds
3. Kill process via supervisor.client.Kill() to simulate crash
4. Verify health check fails after crash
5. Call RestartPlugin and verify health check succeeds again

Uses polling loops instead of arbitrary sleeps for reliability.
Proves Environment.RestartPlugin can recover crashed Python plugins.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 09:54:51 -05:00
Nick Misasi
5d9e5b309d test(05-03): add Python plugin startup failure-mode tests
Add tests for:
- Handshake timeout: fake interpreter blocks forever without printing
  handshake, verifies supervisor times out within StartTimeout
- Invalid handshake: fake interpreter prints netrpc protocol instead of
  grpc, verifies protocol mismatch error
- Malformed handshake: fake interpreter prints incomplete handshake line,
  verifies parsing error

These tests use compiled Go binaries as fake "Python interpreters" to
validate supervisor error handling without requiring real Python.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-19 09:53:37 -05:00
Nick Misasi
b2664d334b test(05-02): add Python plugin integration tests with gRPC health
- TestPythonSupervisor_HealthCheckSuccess: validates end-to-end spawning
  of Python-style plugin with gRPC health check using fake interpreter
- TestPythonPluginEnvironmentActivation: validates Environment can
  activate/deactivate Python plugins without panicking on nil hooks
- Update Environment.Activate to use WithCommandFromManifest which
  handles both Go (netrpc) and Python (gRPC) plugins

The fake interpreter is a compiled Go binary that:
1. Starts a gRPC server on random port
2. Registers health service with "plugin" status SERVING
3. Prints go-plugin handshake line
4. Blocks until killed

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 14:51:42 -05:00
Nick Misasi
ca4505c457 feat(05-02): handle nil hooks in Environment for Python plugins
- startPluginServer: skip OnActivate when Hooks() is nil
- Deactivate: guard OnDeactivate call against nil hooks
- Shutdown: guard OnDeactivate call against nil hooks
- Add informative log when Python plugin activates without hooks

This allows Phase 5 process supervision to work while hook dispatch
is deferred to Phase 7.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 14:49:01 -05:00
Nick Misasi
0e65569549 feat(05-02): add Python plugin startup path with gRPC protocol
- Detect Python plugins in newSupervisor via isPythonPlugin helper
- Configure AllowedProtocols to include ProtocolGRPC for Python plugins
- Increase StartTimeout to 10s for Python interpreter startup
- Skip Dispense("hooks") for Python plugins (Phase 5 supervision only)
- Leave sup.hooks nil - hook dispatch deferred to Phase 7

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 14:47:54 -05:00
Nick Misasi
7ed0d26531 feat(05-01): add Python plugin detection and interpreter discovery
Add helper functions for Python plugin support in the supervisor:
- isPythonPlugin: detects Python plugins via .py extension or props.runtime
- findPythonInterpreter: discovers venv-first Python with PATH fallback
- sanitizePythonScriptPath: validates script paths preventing traversal
- buildPythonCommand: creates exec.Cmd with proper working dir and WaitDelay
- WithCommandFromManifest: unified option for Go and Python plugin commands

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 14:41:02 -05:00
Nick Misasi
06407e18c6 feat(04-04): implement remaining gRPC handlers for Plugin API
Add handlers for all remaining Plugin API methods not covered by 04-02 or 04-03:
- Licensing & System: GetLicense, GetSystemInstallDate, GetDiagnosticId, GetTelemetryId
- Configuration: LoadPluginConfiguration, GetConfig, GetUnsanitizedConfig, SaveConfig, GetPluginConfig, SavePluginConfig
- Plugin Management: GetBundlePath, GetPlugins, EnablePlugin, DisablePlugin, RemovePlugin, GetPluginStatus, InstallPlugin
- Logging: LogDebug, LogInfo, LogWarn, LogError
- Commands: RegisterCommand, UnregisterCommand, ExecuteSlashCommand, CreateCommand, ListCommands, GetCommand, UpdateCommand, DeleteCommand
- Bots: CreateBot, PatchBot, GetBot, GetBots, UpdateBotActive, PermanentDeleteBot, EnsureBotUser
- Emojis: GetEmoji, GetEmojiByName, GetEmojiImage, GetEmojiList
- OAuth: CreateOAuthApp, GetOAuthApp, UpdateOAuthApp, DeleteOAuthApp
- Groups: GetGroup, GetGroupByName, GetGroupMemberUsers, GetGroupsBySource, GetGroupsForUser, CreateGroup, DeleteGroup, UpsertGroupMember, DeleteGroupMember, UpsertGroupSyncable, GetGroupSyncable, UpdateGroupSyncable, DeleteGroupSyncable
- Shared Channels: ShareChannel, UpdateSharedChannel, UnshareChannel, UpdateSharedChannelCursor, SyncSharedChannel, RegisterPluginForSharedChannels, UnregisterPluginForSharedChannels
- WebSocket: PublishWebSocketEvent
- Cluster: PublishPluginClusterEvent
- Interactive Dialog: OpenInteractiveDialog
- Upload Session: CreateUploadSession, UploadData, GetUploadSession
- Push Notification: SendPushNotification
- Mail: SendMail
- Plugin HTTP: PluginHTTP
- Trial License: RequestTrialLicense
- Permissions: RolesGrantPermission
- Cloud: GetCloudLimits
- Collection and Topic: RegisterCollectionAndTopic

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 14:34:09 -05:00
Nick Misasi
8c8093edb4 feat(04-03): implement KV Store API gRPC handlers with tests
Implement all KV Store-related gRPC RPC handlers:
- KVSet, KVGet, KVDelete, KVDeleteAll
- KVSetWithExpiry, KVSetWithOptions
- KVCompareAndSet, KVCompareAndDelete
- KVList

Add comprehensive unit tests for:
- Set/Get roundtrip
- Get non-existent key (nil value)
- Delete and DeleteAll
- CompareAndSet success and mismatch cases
- CompareAndDelete
- SetWithExpiry and SetWithOptions
- Error handling

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 14:13:03 -05:00
Nick Misasi
391f390daa feat(04-03): implement File API gRPC handlers with tests
Implement all File-related gRPC RPC handlers:
- GetFileInfo, GetFileInfos, GetFile
- GetFileLink, ReadFile, UploadFile
- CopyFileInfos, SetFileSearchableContent

Add comprehensive unit tests for:
- GetFileInfo success and not found cases
- GetFile success and error cases
- ReadFile success and not found cases
- UploadFile success and permission error
- CopyFileInfos and SetFileSearchableContent
- GetFileInfos with options

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 14:11:39 -05:00
Nick Misasi
716ec55099 feat(04-03): implement Post API gRPC handlers with tests
Implement all Post-related gRPC RPC handlers:
- CreatePost, UpdatePost, DeletePost, GetPost
- AddReaction, RemoveReaction, GetReactions
- SendEphemeralPost, UpdateEphemeralPost, DeleteEphemeralPost
- GetPostThread, GetPostsSince, GetPostsAfter, GetPostsBefore
- GetPostsForChannel, SearchPostsInTeam, SearchPostsInTeamForUser

Add comprehensive unit tests for:
- CreatePost success and error paths
- GetPost success and not found cases
- DeletePost with permission error
- UpdatePost, reactions, ephemeral posts
- PostList retrieval methods

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 14:10:08 -05:00
Nick Misasi
e4fb3f2a07 test(04-02): add unit tests for User/Team/Channel handlers
Add comprehensive unit tests for gRPC handlers using bufconn and
plugintest mocks. Tests cover:
- User handlers: GetUser, GetUserByEmail, GetUserByUsername,
  CreateUser, DeleteUser, HasPermissionTo, HasPermissionToTeam,
  HasPermissionToChannel, error handling (NotFound)
- Team handlers: GetTeam, GetTeamByName, CreateTeam, DeleteTeam,
  GetTeams, CreateTeamMember
- Channel handlers: GetChannel, GetChannelByName, CreateChannel,
  DeleteChannel, GetDirectChannel, GetGroupChannel, AddChannelMember,
  DeleteChannelMember, SearchChannels

All tests verify correct delegation to plugin.API and proper
protobuf conversion.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 14:09:46 -05:00
Nick Misasi
badb9f394d feat(04-02): implement Channel API gRPC handlers
Implement gRPC handlers for all Channel-related Plugin API methods:
- Channel CRUD: CreateChannel, DeleteChannel, GetChannel, GetChannelByName,
  GetChannelByNameForTeamName, UpdateChannel
- Channel queries: GetPublicChannelsForTeam, GetChannelsForTeamForUser,
  GetChannelStats, SearchChannels
- Direct/Group messages: GetDirectChannel, GetGroupChannel
- Channel membership: AddChannelMember, AddUserToChannel, GetChannelMember,
  GetChannelMembers, GetChannelMembersByIds, GetChannelMembersForUser,
  UpdateChannelMemberRoles, UpdateChannelMemberNotifications,
  PatchChannelMembersNotifications, DeleteChannelMember
- Sidebar categories: CreateChannelSidebarCategory,
  GetChannelSidebarCategories, UpdateChannelSidebarCategories

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 14:09:39 -05:00
Nick Misasi
d4c3d6fec1 feat(04-02): implement Team API gRPC handlers
Implement gRPC handlers for all Team-related Plugin API methods:
- Team CRUD: CreateTeam, DeleteTeam, GetTeam, GetTeamByName,
  GetTeams, UpdateTeam, SearchTeams
- Team membership: CreateTeamMember, CreateTeamMembers,
  CreateTeamMembersGracefully, DeleteTeamMember, GetTeamMember,
  GetTeamMembers, GetTeamMembersForUser, UpdateTeamMemberRoles
- Team icons: GetTeamIcon, SetTeamIcon, RemoveTeamIcon
- Team data: GetTeamsForUser, GetTeamsUnreadForUser, GetTeamStats

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 14:09:31 -05:00
Nick Misasi
3179c816df feat(04-02): implement User API gRPC handlers
Implement gRPC handlers for all User-related Plugin API methods:
- User CRUD: CreateUser, DeleteUser, GetUser, GetUserByEmail,
  GetUserByUsername, GetUsersByUsernames, GetUsersByIds, GetUsers,
  GetUsersInTeam, UpdateUser
- Status: GetUserStatus, GetUserStatusesByIds, UpdateUserStatus,
  SetUserStatusTimedDND, UpdateUserActive
- Custom status: UpdateUserCustomStatus, RemoveUserCustomStatus
- Permissions: HasPermissionTo, HasPermissionToTeam, HasPermissionToChannel
- Session: GetSession, CreateSession, ExtendSessionExpiry, RevokeSession,
  CreateUserAccessToken, RevokeUserAccessToken
- Preferences: GetPreferenceForUser, GetPreferencesForUser,
  UpdatePreferencesForUser, DeletePreferencesForUser
- Other: GetUsersInChannel, GetLDAPUserAttributes, SearchUsers,
  GetProfileImage, SetProfileImage, PublishUserTyping,
  UpdateUserAuth, UpdateUserRoles

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 14:09:25 -05:00
Nick Misasi
1850fb785e feat(04-02): add Team model to proto conversion functions
Add conversion functions between model.Team/TeamMember/TeamUnread/TeamStats
and their protobuf equivalents. Includes:
- teamToProto/teamFromProto for Team conversion
- teamMemberToProto/teamMemberFromProto for TeamMember conversion
- teamUnreadToProto for TeamUnread conversion
- teamStatsToProto for TeamStats conversion
- teamMembersWithErrorToProto for graceful member creation

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 14:09:16 -05:00
Nick Misasi
8f5aa1070d fix(04-03): fix conversion type errors in channel/user/common
Fix compilation errors in existing conversion files:

convert_channel.go:
- Add sidebarCategorySortingToProto/FromProto helper functions
- Convert SidebarCategorySorting properly (string type, not int32)

convert_user.go:
- Add time import
- Fix CustomStatus.ExpiresAt conversion (time.Time, not int64)

convert_common.go:
- Remove unused structpb import

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 14:05:37 -05:00
Nick Misasi
dc5293d19d feat(04-03): add Post/File/KV type conversion functions
Add pb <-> model conversion functions for Post, File, and KV Store types:

convert_post.go:
- Post, PostMetadata, PostEmbed, Reaction conversions
- PostList and PostSearchResults conversions
- SearchParams and SearchParameter conversions

convert_file.go:
- FileInfo conversions
- GetFileInfosOptions conversion

convert_kv.go:
- PluginKVSetOptions conversion

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 14:05:26 -05:00
Nick Misasi
296da1e5f2 docs(04-01): add extension guide for subsequent plans
Enhance package documentation to explain how subsequent plans (04-02
through 04-04) should extend the APIServer with new RPC implementations.
Includes code example and key patterns for error handling and model
conversion.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 13:57:43 -05:00
Nick Misasi
0c6a537cc0 test(04-01): add bufconn test harness and smoke tests
Add in-memory gRPC testing infrastructure using bufconn. Includes:
- testHarness struct for easy test setup/teardown
- Smoke tests for GetServerVersion and IsEnterpriseReady RPCs
- Comprehensive error conversion tests for all HTTP->gRPC mappings
- Tests use plugintest.API mock from the existing codebase

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 13:57:06 -05:00
Nick Misasi
809566ca55 feat(04-01): implement shared error conversion helpers
Add AppErrorToStatus and ErrorToStatus functions to centralize the
conversion of Mattermost errors to gRPC status codes. Maps HTTP codes
to gRPC codes following Phase 1 research recommendations:
- 400 -> InvalidArgument
- 401 -> Unauthenticated
- 403 -> PermissionDenied
- 404 -> NotFound
- 409 -> AlreadyExists
- 413/429 -> ResourceExhausted
- 501 -> Unimplemented
- 503 -> Unavailable
- default -> Internal

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 13:56:10 -05:00
Nick Misasi
aad79469cf feat(04-01): add gRPC API server skeleton
Create the APIServer struct that wraps plugin.API and embeds
UnimplementedPluginAPIServer for incremental implementation.
Includes Register helper and smoke RPCs (GetServerVersion,
IsEnterpriseReady) to validate the wiring.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 13:55:43 -05:00
Nick Misasi
20f7cdebcd feat(03-04): update Makefile and generate Go code
- Add hooks_command.proto to Makefile proto mappings
- Update hooks_command.proto to import api_remaining.proto for shared types
  (CommandArgs, CommandResponse, PluginClusterEvent)
- Define new types: WebSocketRequest, SyncMsgJson, SyncResponse, RemoteCluster
- Generated Go code compiles successfully
- Total hooks in PluginHooks service: 41 (excluding deferred ServeHTTP/ServeMetrics)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 13:42:31 -05:00
Nick Misasi
0c75338a99 feat(03-04): add remaining hook RPCs to PluginHooks service
Update hooks.proto with 11 new RPCs for command/websocket/cluster/shared channels/support hooks:

COMMAND HOOKS:
- ExecuteCommand

WEBSOCKET HOOKS:
- OnWebSocketConnect
- OnWebSocketDisconnect
- WebSocketMessageHasBeenPosted

CLUSTER HOOKS:
- OnPluginClusterEvent

SHARED CHANNELS HOOKS:
- OnSharedChannelsSyncMsg
- OnSharedChannelsPing
- OnSharedChannelsAttachmentSyncMsg
- OnSharedChannelsProfileImageSyncMsg

SUPPORT HOOKS:
- GenerateSupportData

Also added note about ServeHTTP and ServeMetrics being deferred to Phase 8.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 13:40:25 -05:00