From 6ca3415bd3d6f620deae7b2696e790cc8bfef513 Mon Sep 17 00:00:00 2001 From: Michal <01187288@pw.edu.pl> Date: Mon, 5 Jan 2026 14:02:46 +0100 Subject: [PATCH 01/16] feat: added docstrings for client init and lib package --- client.go | 44 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/client.go b/client.go index f51ac43e..601cd755 100644 --- a/client.go +++ b/client.go @@ -1,3 +1,21 @@ +// Package lib acts as the primary entry point for the Microsoft Teams API client library. +// It adopts a Facade pattern, aggregating specialized services (Teams, Channels, Chats) +// into a single, cohesive Client. +// +// The package manages the complexity of: +// - Authentication (via MSAL and Graph Token Providers). +// - Dependency Injection (wiring APIs, Caches, and Resolvers). +// - Caching strategies (transparently wrapping operations with caching layers). +// +// Usage: +// Initialize the Client using NewClient for a standard setup. +// Alternatively, if you need only specific services, use: +// - NewTeamServiceFromGraphClient for Teams service. +// - NewChannelServiceFromGraphClient for Channels service. +// - NewChatServiceFromGraphClient for Chats service. +// +// Always ensure to call Close() upon application shutdown to flush any background +// cache operations. package lib import ( @@ -15,14 +33,21 @@ import ( "github.com/pzsp-teams/lib/teams" ) +// Client is the central hub for interacting with the Microsoft Teams ecosystem. +// It aggregates access to specific domains: Channels, Teams, and Chats, hiding +// the complexity of underlying Graph API calls and caching mechanisms. type Client struct { Channels channels.Service Teams teams.Service Chats chats.Service } +// graphClient is a package-level singleton to hold the authenticated Graph client. +// Note: This approach assumes a single identity per application instance. var graphClient *graph.GraphServiceClient +// getGraphClient ensures a singleton instance of the GraphServiceClient is created. +// It initializes the MSAL token provider using the provided authentication config. func getGraphClient(authCfg *config.AuthConfig) (*graph.GraphServiceClient, error) { if graphClient == nil { tokenProvider, err := auth.GetMSALTokenProvider(authCfg) @@ -38,7 +63,9 @@ func getGraphClient(authCfg *config.AuthConfig) (*graph.GraphServiceClient, erro return graphClient, nil } -// NewClient will be used later +// NewClient initializes a new Client instance with fully configured internal services. +// It handles the authentication handshake using the provided authCfg and sets up +// sending and caching behaviors based on senderCfg and cacheCfg. func NewClient(ctx context.Context, authCfg *config.AuthConfig, senderCfg *config.SenderConfig, cacheCfg *config.CacheConfig) (*Client, error) { cl, err := getGraphClient(authCfg) if err != nil { @@ -48,6 +75,11 @@ func NewClient(ctx context.Context, authCfg *config.AuthConfig, senderCfg *confi return NewClientFromGraphClient(cl, senderCfg, cacheCfg) } +// NewClientFromGraphClient creates a Client using an existing, pre-configured GraphServiceClient. +// This is a separated exported constructor mainly for external testing purposes (via mocking Teams API by injection of GraphServiceClient). +// +// It wires up all internal dependencies, including API clients, caching layers, and +// entity resolvers (e.g., resolving team names to IDs). func NewClientFromGraphClient(graphClient *graph.GraphServiceClient, senderCfg *config.SenderConfig, cacheCfg *config.CacheConfig) (*Client, error) { teamsAPI := api.GetTeamAPI(graphClient, senderCfg) searchAPI := api.GetSearchAPI(graphClient, senderCfg) @@ -82,6 +114,8 @@ func NewClientFromGraphClient(graphClient *graph.GraphServiceClient, senderCfg * }, nil } +// NewChannelServiceFromGraphClient creates a standalone service for Channel operations. +// Use this if you do not need the full Client wrapper and only want to interact with Channels. func NewChannelServiceFromGraphClient(ctx context.Context, authCfg *config.AuthConfig, senderCfg *config.SenderConfig, cacheCfg *config.CacheConfig) (channels.Service, error) { cl, err := getGraphClient(authCfg) if err != nil { @@ -105,6 +139,8 @@ func NewChannelServiceFromGraphClient(ctx context.Context, authCfg *config.AuthC return channelSvc, nil } +// NewTeamServiceFromGraphClient creates a standalone service for Team operations. +// Use this if you do not need the full Client wrapper and only want to interact with Teams. func NewTeamServiceFromGraphClient(ctx context.Context, authCfg *config.AuthConfig, senderCfg *config.SenderConfig, cacheCfg *config.CacheConfig) (teams.Service, error) { cl, err := getGraphClient(authCfg) if err != nil { @@ -124,6 +160,8 @@ func NewTeamServiceFromGraphClient(ctx context.Context, authCfg *config.AuthConf return teamSvc, nil } +// NewChatServiceFromGraphClient creates a standalone service for Chat operations. +// Use this if you do not need the full Client wrapper and only want to interact with Chats. func NewChatServiceFromGraphClient(ctx context.Context, authCfg *config.AuthConfig, senderCfg *config.SenderConfig, cacheCfg *config.CacheConfig) (chats.Service, error) { cl, err := getGraphClient(authCfg) if err != nil { @@ -145,7 +183,9 @@ func NewChatServiceFromGraphClient(ctx context.Context, authCfg *config.AuthConf return chatSvc, nil } -// Close waits for all background operations to complete. +// Close ensures a graceful shutdown of the library. +// It waits for any pending background operations (such as asynchronous cache updates) +// to complete before returning, preventing data loss or race conditions. func Close() { if cacher.Singleton != nil { cacher.Singleton.Runner.Wait() From 5e85e4cb6f0495184081ceb0de9586597da1a2be Mon Sep 17 00:00:00 2001 From: Michal <01187288@pw.edu.pl> Date: Tue, 6 Jan 2026 13:09:00 +0100 Subject: [PATCH 02/16] feat: prepared first version of ReadMe --- REAMDME.md | 106 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 REAMDME.md diff --git a/REAMDME.md b/REAMDME.md new file mode 100644 index 00000000..d4976e86 --- /dev/null +++ b/REAMDME.md @@ -0,0 +1,106 @@ +# Teams API wrapper Lib + +[![Go Reference](https://pkg.go.dev/badge/github.com/pzsp-teams/lib.svg)](https://pkg.go.dev/github.com/pzsp-teams/lib) + +High-level Go (Golang) library that simplifies interaction with **Microsoft Graph API**. +Provides abstraction over operations related to Teams, Channels, and Chats, adding a layer of automatic caching and name resolution. + +## ๐Ÿš€ Key Features + +- **Simplified Authentication**: Built-in MSAL token support. +- **Intelligent Cache**: Automatic mapping of team names to IDs (e.g., "DevOps Team" -> `UUID`), reducing API queries. +- **Facade Architecture**: One main `Client` providing access to all services (`Teams`, `Channels`, `Chats`). +- **Type Safety**: All operations return strongly typed models. + +## ๐Ÿ“ฆ Installation + +```bash +go get [github.com/pzsp-teams/lib](https://github.com/pzsp-teams/lib) +``` + +## ๐Ÿ› ๏ธ Architecture & Concepts +The library uses a **Facade Pattern**. The Client struct aggregates domain-specific services: + +- **client.Teams**: Manage teams lifecycles and members. +- **client.Channels**: Manage standard and private channels. +- **client.Chats**: Handle messages and chat members. + +### The "Reference" concept +Many methods accept a `_Ref` argument. This allows you to pass: +- **UUID** +- **Display Name** (**email** for UserRefs) - this provides convenient usage in interactive applications. + Library will automatically resolve refs to IDs. + +## ๐Ÿ’ป Quick Start +Full example usage is showcased [HERE](https://github.com/pzsp-teams/lib/tree/example-cmd-usage/cmd) +Here is a simple example of how to initialize the client and list the current user's teams. + +### Client init +```go +import ( + "context" + "time" + "[github.com/pzsp-teams/lib](https://github.com/pzsp-teams/lib)" + "[github.com/pzsp-teams/lib/config](https://github.com/pzsp-teams/lib/config)" +) + +func main() { + ctx := context.Background() + + // Auth config (Azure AD) + authCfg := &config.AuthConfig{ + ClientID: "your-client-id", + Tenant: "your-tenant-id", + Email: "your-email", + Scopes: []string{"[https://graph.microsoft.com/.default](https://graph.microsoft.com/.default)"}, + AuthMethod: "DEVICE_CODE", // Or "INTERACTIVE" + } + + // Cache config + cacheCfg := &config.CacheConfig{ + Mode: config.CacheAsync, + Provider: config.CacheProviderJSONFile, // Cache w pliku lokalnym + } + + // Client init + client, err := lib.NewClient(ctx, authCfg, nil, cacheCfg) + if err != nil { + panic(err) + } + + // Usage + + defer lib.Close() // Important if using cache +} +``` + +### 2. Example usage + +```go +// List joined teams +teams, _ := client.Teams.ListMyJoined(ctx) +for _, t := range teams { + fmt.Printf("Team: %s (ID: %s)\n", t.DisplayName, t.ID) +} + +// Utwรณrz nowy zespรณล‚ +newTeam, _ := client.Teams.CreateViaGroup(ctx, "Project Alpha", "project-alpha", "public") +``` + +## Authentication +The library uses `config.AuthConfig` to establish the connection.Ensure your Azure App Registration has the necessary **API Permissions** (e.g., `Team.ReadBasic.All`, `Channel.ReadBasic.All`) granted in the Azure Portal. +Complete list of scopes required by all functions is available [HERE](https://github.com/pzsp-teams/lib/blob/example-cmd-usage/.env.template) + +There are two available ways to authenticate: +- **INTERACTIVE** - log in window will automatically be opened within your browser. +- **DEVICE CODE** - library will provide you the **URL** and code, which need to be manually opened with browser of your chioce. + +## Cache +If enabled, stores non vurnerable maps, (e.g., `TeamRef` -> `UUID`) in order to provide more efficient Refs resolving. + +#### โš ๏ธ Important: +Because the cache might run background goroutines to keep data fresh, you must call lib.Close() when your application shuts down. This ensures all background operations complete and prevents memory leaks or race conditions. + +```go +defer lib.Close() +``` \ No newline at end of file From 5bd548ed1795362241eea5e1fe4158044eb5000c Mon Sep 17 00:00:00 2001 From: Michal <01187288@pw.edu.pl> Date: Tue, 6 Jan 2026 13:13:46 +0100 Subject: [PATCH 03/16] chore: typos --- REAMDME.md | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/REAMDME.md b/REAMDME.md index d4976e86..2cd9f1b9 100644 --- a/REAMDME.md +++ b/REAMDME.md @@ -15,7 +15,7 @@ Provides abstraction over operations related to Teams, Channels, and Chats, addi ## ๐Ÿ“ฆ Installation ```bash -go get [github.com/pzsp-teams/lib](https://github.com/pzsp-teams/lib) +go get (https://github.com/pzsp-teams/lib) ``` ## ๐Ÿ› ๏ธ Architecture & Concepts @@ -59,7 +59,7 @@ func main() { // Cache config cacheCfg := &config.CacheConfig{ Mode: config.CacheAsync, - Provider: config.CacheProviderJSONFile, // Cache w pliku lokalnym + Provider: config.CacheProviderJSONFile, // Local file cache } // Client init @@ -67,9 +67,6 @@ func main() { if err != nil { panic(err) } - - // Usage - defer lib.Close() // Important if using cache } ``` @@ -83,7 +80,7 @@ for _, t := range teams { fmt.Printf("Team: %s (ID: %s)\n", t.DisplayName, t.ID) } -// Utwรณrz nowy zespรณล‚ +// Create a new team newTeam, _ := client.Teams.CreateViaGroup(ctx, "Project Alpha", "project-alpha", "public") ``` @@ -93,13 +90,13 @@ Complete list of scopes required by all functions is available [HERE](https://gi There are two available ways to authenticate: - **INTERACTIVE** - log in window will automatically be opened within your browser. -- **DEVICE CODE** - library will provide you the **URL** and code, which need to be manually opened with browser of your chioce. +- **DEVICE CODE** - library will provide you the **URL** and code, which need to be manually opened with browser of your choice. ## Cache -If enabled, stores non vurnerable maps, (e.g., `TeamRef` -> `UUID`) in order to provide more efficient Refs resolving. +If enabled, stores metadata and non-sensitive mappings, (e.g., `TeamRef` -> `UUID`) to provide efficient reference resolution. #### โš ๏ธ Important: -Because the cache might run background goroutines to keep data fresh, you must call lib.Close() when your application shuts down. This ensures all background operations complete and prevents memory leaks or race conditions. +Because the cache might run background goroutines to keep data fresh, you **must** call lib.Close() when your application shuts down. This ensures all background operations complete and prevents memory leaks or race conditions. ```go defer lib.Close() From c1739a0a6b2c7a28b47028953a4ea09242a2774b Mon Sep 17 00:00:00 2001 From: Michal <01187288@pw.edu.pl> Date: Tue, 6 Jan 2026 13:17:27 +0100 Subject: [PATCH 04/16] feat: added MIT open source license --- LICENSE | 21 +++++++++++++++++++++ REAMDME.md | 18 ++++++++++++++++-- 2 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..f8bd967d --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 PZSP2 Z1 Teams + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/REAMDME.md b/REAMDME.md index 2cd9f1b9..b78513c4 100644 --- a/REAMDME.md +++ b/REAMDME.md @@ -1,6 +1,7 @@ # Teams API wrapper Lib [![Go Reference](https://pkg.go.dev/badge/github.com/pzsp-teams/lib.svg)](https://pkg.go.dev/github.com/pzsp-teams/lib) +[![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) High-level Go (Golang) library that simplifies interaction with **Microsoft Graph API**. Provides abstraction over operations related to Teams, Channels, and Chats, adding a layer of automatic caching and name resolution. @@ -19,6 +20,7 @@ go get (https://github.com/pzsp-teams/lib) ``` ## ๐Ÿ› ๏ธ Architecture & Concepts + The library uses a **Facade Pattern**. The Client struct aggregates domain-specific services: - **client.Teams**: Manage teams lifecycles and members. @@ -26,16 +28,20 @@ The library uses a **Facade Pattern**. The Client struct aggregates domain-speci - **client.Chats**: Handle messages and chat members. ### The "Reference" concept + Many methods accept a `_Ref` argument. This allows you to pass: + - **UUID** - **Display Name** (**email** for UserRefs) - this provides convenient usage in interactive applications. - Library will automatically resolve refs to IDs. + Library will automatically resolve refs to IDs. ## ๐Ÿ’ป Quick Start + Full example usage is showcased [HERE](https://github.com/pzsp-teams/lib/tree/example-cmd-usage/cmd) Here is a simple example of how to initialize the client and list the current user's teams. ### Client init + ```go import ( "context" @@ -85,19 +91,27 @@ newTeam, _ := client.Teams.CreateViaGroup(ctx, "Project Alpha", "project-alpha", ``` ## Authentication + The library uses `config.AuthConfig` to establish the connection.Ensure your Azure App Registration has the necessary **API Permissions** (e.g., `Team.ReadBasic.All`, `Channel.ReadBasic.All`) granted in the Azure Portal. Complete list of scopes required by all functions is available [HERE](https://github.com/pzsp-teams/lib/blob/example-cmd-usage/.env.template) There are two available ways to authenticate: + - **INTERACTIVE** - log in window will automatically be opened within your browser. - **DEVICE CODE** - library will provide you the **URL** and code, which need to be manually opened with browser of your choice. ## Cache + If enabled, stores metadata and non-sensitive mappings, (e.g., `TeamRef` -> `UUID`) to provide efficient reference resolution. #### โš ๏ธ Important: + Because the cache might run background goroutines to keep data fresh, you **must** call lib.Close() when your application shuts down. This ensures all background operations complete and prevents memory leaks or race conditions. ```go defer lib.Close() -``` \ No newline at end of file +``` + +## ๐Ÿ“„ License + +This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. From da7bf40845fe1f2fbb33400ae0bd3c6bc7d1d43f Mon Sep 17 00:00:00 2001 From: Michal <01187288@pw.edu.pl> Date: Tue, 6 Jan 2026 13:24:11 +0100 Subject: [PATCH 05/16] chore: formatting --- REAMDME.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/REAMDME.md b/REAMDME.md index b78513c4..15364cbb 100644 --- a/REAMDME.md +++ b/REAMDME.md @@ -3,8 +3,9 @@ [![Go Reference](https://pkg.go.dev/badge/github.com/pzsp-teams/lib.svg)](https://pkg.go.dev/github.com/pzsp-teams/lib) [![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) -High-level Go (Golang) library that simplifies interaction with **Microsoft Graph API**. -Provides abstraction over operations related to Teams, Channels, and Chats, adding a layer of automatic caching and name resolution. +
+ +High-level Go (Golang) library that simplifies interaction with **Microsoft Graph API**. Provides abstraction over operations related to Teams, Channels, and Chats, adding a layer of automatic caching and name resolution. ## ๐Ÿš€ Key Features @@ -104,7 +105,9 @@ There are two available ways to authenticate: If enabled, stores metadata and non-sensitive mappings, (e.g., `TeamRef` -> `UUID`) to provide efficient reference resolution. -#### โš ๏ธ Important: +
+ +### โš ๏ธ Important: Because the cache might run background goroutines to keep data fresh, you **must** call lib.Close() when your application shuts down. This ensures all background operations complete and prevents memory leaks or race conditions. From 295fa99ef557f891334c1d199fd808b463e5065e Mon Sep 17 00:00:00 2001 From: Michal <01187288@pw.edu.pl> Date: Thu, 8 Jan 2026 13:43:15 +0100 Subject: [PATCH 06/16] docs: generated markdown docs and used mkdocs for website view --- docs/channels.md | 112 +++++++++++++++++++ docs/chats.md | 153 ++++++++++++++++++++++++++ docs/client.md | 105 ++++++++++++++++++ docs/config.md | 129 ++++++++++++++++++++++ docs/generate_docs.sh | 6 ++ docs/index.md | 117 ++++++++++++++++++++ docs/models.md | 242 ++++++++++++++++++++++++++++++++++++++++++ docs/teams.md | 89 ++++++++++++++++ mkdocs.yml | 36 +++++++ 9 files changed, 989 insertions(+) create mode 100644 docs/channels.md create mode 100644 docs/chats.md create mode 100644 docs/client.md create mode 100644 docs/config.md create mode 100755 docs/generate_docs.sh create mode 100644 docs/index.md create mode 100644 docs/models.md create mode 100644 docs/teams.md create mode 100644 mkdocs.yml diff --git a/docs/channels.md b/docs/channels.md new file mode 100644 index 00000000..4c311dc3 --- /dev/null +++ b/docs/channels.md @@ -0,0 +1,112 @@ + + +# channels + +```go +import "github.com/pzsp-teams/lib/channels" +``` + +Package channels provides various channel\-related operations. It abstracts the underlying Microsoft Graph API calls. Package provides two services implementations \- one with cache and one without cache. Both can be instantiated and used interchangeably. If cache is enabled, the service will use a caching layer to store and retrieve channel/member references, improving performance and reducing API calls. Concepts: + +- Channels belong to teams. +- Channels can be standard or private. +- Users are identified by userID or email. +- Some operations require messageID \- these can be obtained via ListMessages. +- ChannelRef is a reference \(display name or ID\) to a channel used in method parameters. +- If teamRef or channelRef is a display and is not unique, an ambiguity error is returned. +- The authenticated user \(derived from MSAL\) is the one making the API calls \(appropriate scopes must be granted\). + +If an async cached service is used, call Wait\(\) to ensure all background cache updates are finished. + +## Index + +- [type Service](<#Service>) + - [func NewService\(ops channelOps, tr resolver.TeamResolver, cr resolver.ChannelResolver\) Service](<#NewService>) + + + +## type [Service]() + +Service defines the interface for channel\-related operations. It includes methods for managing channels, members, messages, and more. + +```go +type Service interface { + // ListChannels returns all channels in a team. + ListChannels(ctx context.Context, teamRef string) ([]*models.Channel, error) + + // Get retrieves a specific channel by its reference (ID or display name) within a team. + Get(ctx context.Context, teamRef, channelRef string) (*models.Channel, error) + + // CreateStandardChannel creates a standard channel within a team. + // Standard channels are open to all team members. + CreateStandardChannel(ctx context.Context, teamRef, name string) (*models.Channel, error) + + // CreatePrivateChannel creates a private channel within a team. + // Private channels are restricted to specific members. + // At least one owner must be specified. + CreatePrivateChannel(ctx context.Context, teamRef, name string, memberRefs, ownerRefs []string) (*models.Channel, error) + + // Delete removes a channel from a team. + Delete(ctx context.Context, teamRef, channelRef string) error + + // SendMessage sends a message to a channel. + // Body parameter is the body of the message. It includes: + // - Content: the text or html content of the message. + // - ContentType: the type of content (text or html). + // - Mentions: optional mentions to include in the message. + SendMessage(ctx context.Context, teamRef, channelRef string, body models.MessageBody) (*models.Message, error) + + // SendReply sends a reply to a specific message in a channel. + // Body parameter is the body of the reply message. It includes: + // - Content: the text or html content of the message. + // - ContentType: the type of content (text or html). + // - Mentions: optional mentions to include in the message. + SendReply(ctx context.Context, teamRef, channelRef, messageID string, body models.MessageBody) (*models.Message, error) + + // ListMessages returns one page of messages in a channel. + // + // NextLink in the returned MessageCollection can be used to retrieve the next page of messages. + ListMessages(ctx context.Context, teamRef, channelRef string, opts *models.ListMessagesOptions, includeSystem bool, nextLink *string) (*models.MessageCollection, error) + + // GetMessage retrieves a specific message from a channel by its ID. + GetMessage(ctx context.Context, teamRef, channelRef, messageID string) (*models.Message, error) + + // ListReplies returns one page of replies to a specific message in a channel. + // + // NextLink in the returned MessageCollection can be used to retrieve the next page of replies. + ListReplies(ctx context.Context, teamRef, channelRef, messageID string, top *int32, includeSystem bool, nextLink *string) (*models.MessageCollection, error) + + // GetReply retrieves a specific reply to a message in a channel by its ID. + GetReply(ctx context.Context, teamRef, channelRef, messageID, replyID string) (*models.Message, error) + + // ListMembers returns all members of a channel. + ListMembers(ctx context.Context, teamRef, channelRef string) ([]*models.Member, error) + + // AddMember adds a user to a channel. + AddMember(ctx context.Context, teamRef, channelRef, userRef string, isOwner bool) (*models.Member, error) + + // UpdateMemberRoles updates the roles of a member in a channel. + UpdateMemberRoles(ctx context.Context, teamRef, channelRef, userRef string, isOwner bool) (*models.Member, error) + + // RemoveMember removes a user from a channel. + RemoveMember(ctx context.Context, teamRef, channelRef, userRef string) error + + // GetMentions resolves raw mention strings to Mention objects in the context of a channel. Raw mentions can be: + // - Emails + // - Channel (only the same channel as channelRef can be mentioned). It can be used by specifying "channel" or channel display name as raw mention. + // - Team (only the parent team of the channel can be mentioned). It can be used by specifying "team" or team display name as raw mention. + // - User IDs + GetMentions(ctx context.Context, teamRef, channelRef string, rawMentions []string) ([]models.Mention, error) +} +``` + + +### func [NewService]() + +```go +func NewService(ops channelOps, tr resolver.TeamResolver, cr resolver.ChannelResolver) Service +``` + +NewService creates a new channels Service instance + +Generated by [gomarkdoc]() diff --git a/docs/chats.md b/docs/chats.md new file mode 100644 index 00000000..d03286b2 --- /dev/null +++ b/docs/chats.md @@ -0,0 +1,153 @@ + + +# chats + +```go +import "github.com/pzsp-teams/lib/chats" +``` + +Package chats provides various chat\-related operations. It abstracts the underlying Microsoft Graph API calls. Package provides two services implementations \- one with cache and one without cache. Both can be instantiated and used interchangeably. If cache is enabled, the service will use a caching layer to store and retrieve chat/member references, improving performance and reducing API calls. Concepts: + +- Chats are either one\-on\-one or group chats. +- Users are identified by userID or email. +- Some operations require messageID \- these can be obtained via ListMessages. +- ChatRef and GroupChatRef are references to chats used in method parameters. +- If chatRef is a topic and is not unique, an ambiguity error is returned. +- The authenticated user \(derived from MSAL\) is the one making the API calls \(appropriate scopes must be granted\). + +If an async cached service is used, call Wait\(\) to ensure all background cache updates are finished. + +## Index + +- [type ChatRef](<#ChatRef>) +- [type GroupChatRef](<#GroupChatRef>) +- [type OneOnOneChatRef](<#OneOnOneChatRef>) +- [type Service](<#Service>) + - [func NewService\(chatOps chatOps, cr resolver.ChatResolver\) Service](<#NewService>) + + + +## type [ChatRef]() + +ChatRef is an interface representing a reference to a chat, which can be either a group chat or a one\-on\-one chat. + +```go +type ChatRef interface { + // contains filtered or unexported methods +} +``` + + +## type [GroupChatRef]() + +GroupChatRef identifies a group chat. It may reference a chat by: + +- unique chatID + +- chat topic + +Note: Using chat topic may lead to ambiguities \(which must be resolved manually\) if multiple group chats share the same topic. + +```go +type GroupChatRef struct { + Ref string +} +``` + + +## type [OneOnOneChatRef]() + +OneOnOneChatRef identifies a one\-on\-one chat. It may reference a chat by: + +- unique chatID + +- recipient's reference \(userID, email\) + +Note: Chat must be established between the logged\-in user and the recipient for resolution to succeed. + +```go +type OneOnOneChatRef struct { + Ref string +} +``` + + +## type [Service]() + +Service defines the interface for chat\-related operations. It includes methods for creating chats, managing members, sending messages, and more. + +```go +type Service interface { + // CreateOneOneOne creates a one-on-one chat with the given recipient. + // The authenticated user is automatically added to the chat. + CreateOneOnOne(ctx context.Context, recipientRef string) (*models.Chat, error) + + // CreateGroup creates a group chat with the given recipients and topic. + // The authenticated user may be included by setting includeMe to true. + CreateGroup(ctx context.Context, recipientRefs []string, topic string, includeMe bool) (*models.Chat, error) + + // AddMemberToGroupChat adds a user to a group chat. + AddMemberToGroupChat(ctx context.Context, chatRef GroupChatRef, userRef string) (*models.Member, error) + + // RemoveMemberFromGroupChat removes a user from a group chat. + RemoveMemberFromGroupChat(ctx context.Context, chatRef GroupChatRef, userRef string) error + + // ListGroupChatMembers returns all members of a group chat. + ListGroupChatMembers(ctx context.Context, chatRef GroupChatRef) ([]*models.Member, error) + + // UpdateGroupChatTopic updates the topic of a group chat. + UpdateGroupChatTopic(ctx context.Context, chatRef GroupChatRef, topic string) (*models.Chat, error) + + // ListMessages returns all messages in a chat. + // + // NextLink in the returned MessageCollection can be used to retrieve the next page of messages. + ListMessages(ctx context.Context, chatRef ChatRef, includeSystem bool, nextLink *string) (*models.MessageCollection, error) + + // SendMessage sends a message to a chat. + // Body parameter is the body of the message. It includes: + // - Content: the text or html content of the message. + // - ContentType: the type of content (text or html). + // - Mentions: optional mentions to include in the message. + SendMessage(ctx context.Context, chatRef ChatRef, body models.MessageBody) (*models.Message, error) + + // DeleteMessage deletes a message from a chat. Action is reversible - soft delete is performed. + DeleteMessage(ctx context.Context, chatRef ChatRef, messageID string) error + + // GetMessage retrieves a specific message from a chat by its ID. + GetMessage(ctx context.Context, chatRef ChatRef, messageID string) (*models.Message, error) + + // ListChats returns all chats, optionally filtered by chat type. + ListChats(ctx context.Context, chatType *models.ChatType) ([]*models.Chat, error) + + // ListAllMessages returns all messages in all chats within the specified time range. Top limits the number of messages returned. + // + // Note: This operation does not work in delegated permission mode. + ListAllMessages(ctx context.Context, startTime, endTime *time.Time, top *int32) ([]*models.Message, error) + + // ListPinnedMessages returns all pinned messages in a chat. + ListPinnedMessages(ctx context.Context, chatRef ChatRef) ([]*models.Message, error) + + // PinMessage pins a message in a chat. + PinMessage(ctx context.Context, chatRef ChatRef, messageID string) error + + // UnpinMessage unpins a message in a chat. + UnpinMessage(ctx context.Context, chatRef ChatRef, pinnedMessageID string) error + + // GetMentions resolves raw mention strings to Mention objects in the context of a chat. Raw mentions can be: + // - Emails + // - Everyone (for group chats) + // - User IDs + GetMentions(ctx context.Context, chatRef ChatRef, rawMentions []string) ([]models.Mention, error) +} +``` + + +### func [NewService]() + +```go +func NewService(chatOps chatOps, cr resolver.ChatResolver) Service +``` + +NewService creates a new instance of the chat service. + +Generated by [gomarkdoc]() diff --git a/docs/client.md b/docs/client.md new file mode 100644 index 00000000..d36597b9 --- /dev/null +++ b/docs/client.md @@ -0,0 +1,105 @@ + + +# lib + +```go +import "github.com/pzsp-teams/lib" +``` + +Package lib acts as the primary entry point for the Microsoft Teams API client library. It adopts a Facade pattern, aggregating specialized services \(Teams, Channels, Chats\) into a single, cohesive Client. + +The package manages the complexity of: + +- Authentication \(via MSAL and Graph Token Providers\). +- Dependency Injection \(wiring APIs, Caches, and Resolvers\). +- Caching strategies \(transparently wrapping operations with caching layers\). + +Usage: Initialize the Client using NewClient for a standard setup. Alternatively, if you need only specific services, use: + +- NewTeamServiceFromGraphClient for Teams service. +- NewChannelServiceFromGraphClient for Channels service. +- NewChatServiceFromGraphClient for Chats service. + +Always ensure to call Close\(\) upon application shutdown to flush any background cache operations. + +## Index + +- [func Close\(\)](<#Close>) +- [func NewChannelServiceFromGraphClient\(ctx context.Context, authCfg \*config.AuthConfig, senderCfg \*config.SenderConfig, cacheCfg \*config.CacheConfig\) \(channels.Service, error\)](<#NewChannelServiceFromGraphClient>) +- [func NewChatServiceFromGraphClient\(ctx context.Context, authCfg \*config.AuthConfig, senderCfg \*config.SenderConfig, cacheCfg \*config.CacheConfig\) \(chats.Service, error\)](<#NewChatServiceFromGraphClient>) +- [func NewTeamServiceFromGraphClient\(ctx context.Context, authCfg \*config.AuthConfig, senderCfg \*config.SenderConfig, cacheCfg \*config.CacheConfig\) \(teams.Service, error\)](<#NewTeamServiceFromGraphClient>) +- [type Client](<#Client>) + - [func NewClient\(ctx context.Context, authCfg \*config.AuthConfig, senderCfg \*config.SenderConfig, cacheCfg \*config.CacheConfig\) \(\*Client, error\)](<#NewClient>) + - [func NewClientFromGraphClient\(graphClient \*graph.GraphServiceClient, senderCfg \*config.SenderConfig, cacheCfg \*config.CacheConfig\) \(\*Client, error\)](<#NewClientFromGraphClient>) + + + +## func [Close]() + +```go +func Close() +``` + +Close ensures a graceful shutdown of the library. It waits for any pending background operations \(such as asynchronous cache updates\) to complete before returning, preventing data loss or race conditions. + + +## func [NewChannelServiceFromGraphClient]() + +```go +func NewChannelServiceFromGraphClient(ctx context.Context, authCfg *config.AuthConfig, senderCfg *config.SenderConfig, cacheCfg *config.CacheConfig) (channels.Service, error) +``` + +NewChannelServiceFromGraphClient creates a standalone service for Channel operations. Use this if you do not need the full Client wrapper and only want to interact with Channels. + + +## func [NewChatServiceFromGraphClient]() + +```go +func NewChatServiceFromGraphClient(ctx context.Context, authCfg *config.AuthConfig, senderCfg *config.SenderConfig, cacheCfg *config.CacheConfig) (chats.Service, error) +``` + +NewChatServiceFromGraphClient creates a standalone service for Chat operations. Use this if you do not need the full Client wrapper and only want to interact with Chats. + + +## func [NewTeamServiceFromGraphClient]() + +```go +func NewTeamServiceFromGraphClient(ctx context.Context, authCfg *config.AuthConfig, senderCfg *config.SenderConfig, cacheCfg *config.CacheConfig) (teams.Service, error) +``` + +NewTeamServiceFromGraphClient creates a standalone service for Team operations. Use this if you do not need the full Client wrapper and only want to interact with Teams. + + +## type [Client]() + +Client is the central hub for interacting with the Microsoft Teams ecosystem. It aggregates access to specific domains: Channels, Teams, and Chats, hiding the complexity of underlying Graph API calls and caching mechanisms. + +```go +type Client struct { + Channels channels.Service + Teams teams.Service + Chats chats.Service +} +``` + + +### func [NewClient]() + +```go +func NewClient(ctx context.Context, authCfg *config.AuthConfig, senderCfg *config.SenderConfig, cacheCfg *config.CacheConfig) (*Client, error) +``` + +NewClient initializes a new Client instance with fully configured internal services. It handles the authentication handshake using the provided authCfg and sets up sending and caching behaviors based on senderCfg and cacheCfg. + + +### func [NewClientFromGraphClient]() + +```go +func NewClientFromGraphClient(graphClient *graph.GraphServiceClient, senderCfg *config.SenderConfig, cacheCfg *config.CacheConfig) (*Client, error) +``` + +NewClientFromGraphClient creates a Client using an existing, pre\-configured GraphServiceClient. This is a separated exported constructor mainly for external testing purposes \(via mocking Teams API by injection of GraphServiceClient\). + +It wires up all internal dependencies, including API clients, caching layers, and entity resolvers \(e.g., resolving team names to IDs\). + +Generated by [gomarkdoc]() diff --git a/docs/config.md b/docs/config.md new file mode 100644 index 00000000..f1331b66 --- /dev/null +++ b/docs/config.md @@ -0,0 +1,129 @@ + + +# config + +```go +import "github.com/pzsp-teams/lib/config" +``` + +Package config holds configuration structs used across the application. Defined configs: + +- AuthConfig: holds authentication configuration. +- SenderConfig: holds sender configuration. +- CacheConfig: holds caching configuration. + +## Index + +- [type AuthConfig](<#AuthConfig>) +- [type CacheConfig](<#CacheConfig>) +- [type CacheMode](<#CacheMode>) +- [type CacheProvider](<#CacheProvider>) +- [type Method](<#Method>) +- [type SenderConfig](<#SenderConfig>) + + + +## type [AuthConfig]() + +AuthConfig holds configuration for authentication. All fields are required \- they are needed to acquire tokens via MSAL. + +```go +type AuthConfig struct { + ClientID string + Tenant string + Email string + Scopes []string + AuthMethod Method +} +``` + + +## type [CacheConfig]() + +CacheConfig holds configuration for caching. + +```go +type CacheConfig struct { + Mode CacheMode + Provider CacheProvider + Path *string +} +``` + + +## type [CacheMode]() + +CacheMode defines the caching strategy used by the application. + +```go +type CacheMode string +``` + + + +```go +const ( + // CacheDisabled indicates that caching is turned off. + CacheDisabled CacheMode = "DISABLED" + + // CacheSync indicates that cache operations are performed synchronously. + CacheSync CacheMode = "SYNC" + + // CacheAsync indicates that cache operations are performed asynchronously. + CacheAsync CacheMode = "ASYNC" +) +``` + + +## type [CacheProvider]() + +CacheProvider defines the backend used for caching. + +```go +type CacheProvider string +``` + + + +```go +const ( + // CacheProviderJSONFile indicates that json-file cache is used. + CacheProviderJSONFile CacheProvider = "JSON_FILE" +) +``` + + +## type [Method]() + +Method defines the authentication flow used when acquiring tokens. + +```go +type Method string +``` + + + +```go +const ( + // Interactive opens a browser window for user authentication. + Interactive Method = "INTERACTIVE" + + // DeviceCode prints a device code to the console and prompts the user to visit a URL to authenticate. + DeviceCode Method = "DEVICE_CODE" +) +``` + + +## type [SenderConfig]() + +SenderConfig defines configuration for the request sender which connects with the Microsoft Graph API. MaxRetryDelay and Timeout are in seconds. + +```go +type SenderConfig struct { + MaxRetries int + NextRetryDelay int + Timeout int +} +``` + +Generated by [gomarkdoc]() diff --git a/docs/generate_docs.sh b/docs/generate_docs.sh new file mode 100755 index 00000000..04741f9c --- /dev/null +++ b/docs/generate_docs.sh @@ -0,0 +1,6 @@ +gomarkdoc ./teams --output docs/teams.md +gomarkdoc ./channels --output docs/channels.md +gomarkdoc ./chats --output docs/chats.md +gomarkdoc ./models --output docs/models.md +gomarkdoc ./config --output docs/config.md +gomarkdoc . --output docs/client.md \ No newline at end of file diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 00000000..cb848bf3 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,117 @@ +# Teams API wrapper Lib + +[![Go Reference](https://pkg.go.dev/badge/github.com/pzsp-teams/lib.svg)](https://pkg.go.dev/github.com/pzsp-teams/lib) +[![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) + +
+ +High-level Go (Golang) library that simplifies interaction with **Microsoft Graph API**. Provides abstraction over operations related to Teams, Channels, and Chats, adding a layer of automatic caching and name resolution. + +## ๐Ÿš€ Key Features + +- **Simplified Authentication**: Built-in MSAL token support. +- **Intelligent Cache**: Automatic mapping of team names to IDs (e.g., "DevOps Team" -> `UUID`), reducing API queries. +- **Facade Architecture**: One main `Client` providing access to all services (`Teams`, `Channels`, `Chats`). +- **Type Safety**: All operations return strongly typed models. + +## ๐Ÿ“ฆ Installation + +```bash +go get (https://github.com/pzsp-teams/lib) +``` + +## ๐Ÿ› ๏ธ Architecture & Concepts + +The library uses a **Facade Pattern**. The Client struct aggregates domain-specific services: + +- **client.Teams**: Manage teams lifecycles and members. +- **client.Channels**: Manage standard and private channels. +- **client.Chats**: Handle messages and chat members. + +### The "Reference" concept + +Many methods accept a `_Ref` argument. This allows you to pass: + +- **UUID** +- **Display Name** (**email** for UserRefs) - this provides convenient usage in interactive applications. + Library will automatically resolve refs to IDs. + +## ๐Ÿ’ป Quick Start + +Full example usage is showcased [HERE](https://github.com/pzsp-teams/lib/tree/example-cmd-usage/cmd) +Here is a simple example of how to initialize the client and list the current user's teams. + +### Client init + +```go +import ( + "context" + "time" + "[github.com/pzsp-teams/lib](https://github.com/pzsp-teams/lib)" + "[github.com/pzsp-teams/lib/config](https://github.com/pzsp-teams/lib/config)" +) + +func main() { + ctx := context.Background() + + // Auth config (Azure AD) + authCfg := &config.AuthConfig{ + ClientID: "your-client-id", + Tenant: "your-tenant-id", + Email: "your-email", + Scopes: []string{"[https://graph.microsoft.com/.default](https://graph.microsoft.com/.default)"}, + AuthMethod: "DEVICE_CODE", // Or "INTERACTIVE" + } + + // Cache config + cacheCfg := &config.CacheConfig{ + Mode: config.CacheAsync, + Provider: config.CacheProviderJSONFile, // Local file cache + } + + // Client init + client, err := lib.NewClient(ctx, authCfg, nil, cacheCfg) + if err != nil { + panic(err) + } + defer lib.Close() // Important if using cache +} +``` + +### 2. Example usage + +```go +// List joined teams +teams, _ := client.Teams.ListMyJoined(ctx) +for _, t := range teams { + fmt.Printf("Team: %s (ID: %s)\n", t.DisplayName, t.ID) +} + +// Create a new team +newTeam, _ := client.Teams.CreateViaGroup(ctx, "Project Alpha", "project-alpha", "public") +``` + +## Authentication + +The library uses `config.AuthConfig` to establish the connection.Ensure your Azure App Registration has the necessary **API Permissions** (e.g., `Team.ReadBasic.All`, `Channel.ReadBasic.All`) granted in the Azure Portal. +Complete list of scopes required by all functions is available [HERE](https://github.com/pzsp-teams/lib/blob/example-cmd-usage/.env.template) + +There are two available ways to authenticate: + +- **INTERACTIVE** - log in window will automatically be opened within your browser. +- **DEVICE CODE** - library will provide you the **URL** and code, which need to be manually opened with browser of your choice. + +## Cache + +If enabled, stores metadata and non-sensitive mappings, (e.g., `TeamRef` -> `UUID`) to provide efficient reference resolution. + +
+ +### โš ๏ธ Important: + +Because the cache might run background goroutines to keep data fresh, you **must** call lib.Close() when your application shuts down. This ensures all background operations complete and prevents memory leaks or race conditions. + +```go +defer lib.Close() +``` + diff --git a/docs/models.md b/docs/models.md new file mode 100644 index 00000000..051c620a --- /dev/null +++ b/docs/models.md @@ -0,0 +1,242 @@ + + +# models + +```go +import "github.com/pzsp-teams/lib/models" +``` + +Package models contains simplified Microsoft Teams domain types used by this library. + +## Index + +- [type Channel](<#Channel>) +- [type Chat](<#Chat>) +- [type ChatType](<#ChatType>) +- [type ListMessagesOptions](<#ListMessagesOptions>) +- [type Member](<#Member>) +- [type Mention](<#Mention>) +- [type MentionKind](<#MentionKind>) +- [type Message](<#Message>) +- [type MessageBody](<#MessageBody>) +- [type MessageCollection](<#MessageCollection>) +- [type MessageContentType](<#MessageContentType>) +- [type MessageFrom](<#MessageFrom>) +- [type Team](<#Team>) +- [type TeamUpdate](<#TeamUpdate>) + + + +## type [Channel]() + +Channel represents a Microsoft Teams channel. + +```go +type Channel struct { + ID string + Name string + IsGeneral bool +} +``` + + +## type [Chat]() + +Chat represents a chat in Microsoft Teams. + +```go +type Chat struct { + ID string + Type ChatType + IsHidden bool + Topic *string +} +``` + + +## type [ChatType]() + +ChatType represents the type of chat in Microsoft Teams. Type can be either one\-on\-one or group chat + +```go +type ChatType string +``` + + + +```go +const ( + // ChatTypeOneOnOne represents a one-on-one chat. + ChatTypeOneOnOne ChatType = "one-on-one" + // ChatTypeGroup represents a group chat. + ChatTypeGroup ChatType = "group" +) +``` + + +## type [ListMessagesOptions]() + +ListMessagesOptions contains options for listing messages. + +```go +type ListMessagesOptions struct { + Top *int32 + ExpandReplies bool +} +``` + + +## type [Member]() + +Member represents a member of a Microsoft Teams channel, direct chat or team. + +```go +type Member struct { + ID string + UserID string + DisplayName string + Role string + Email string +} +``` + + +## type [Mention]() + +Mention represents a mention in a Microsoft Teams message. + +```go +type Mention struct { + Kind MentionKind + AtID int32 + Text string + TargetID string +} +``` + + +## type [MentionKind]() + +MentionKind represents the kind of mention in a Microsoft Teams message. + +```go +type MentionKind string +``` + + + +```go +const ( + // MentionUser represents a user mention. + MentionUser MentionKind = "user" + // MentionChannel represents a channel mention. + MentionChannel MentionKind = "channel" + // MentionTeam represents a team mention. + MentionTeam MentionKind = "team" + // MentionEveryone represents an everyone mention - applicable to group chats only + MentionEveryone MentionKind = "everyone" +) +``` + + +## type [Message]() + +Message represents a Microsoft Teams chat message. It can be used in both chats and channels. + +```go +type Message struct { + ID string + Content string + ContentType MessageContentType + CreatedDateTime time.Time + From *MessageFrom + ReplyCount int +} +``` + + +## type [MessageBody]() + +MessageBody represents the body of a message in Microsoft Teams. + +```go +type MessageBody struct { + Content string + ContentType MessageContentType + Mentions []Mention +} +``` + + +## type [MessageCollection]() + +MessageCollection represents a collection of messages, potentially with a link to the next page of results. + +```go +type MessageCollection struct { + Messages []*Message + NextLink *string +} +``` + + +## type [MessageContentType]() + +MessageContentType represents the type of content in a Microsoft Teams message. It can be either text or HTML. + +```go +type MessageContentType string +``` + + + +```go +const ( + // MessageContentTypeText represents plain text content. + MessageContentTypeText MessageContentType = "text" + // MessageContentTypeHTML represents HTML content. + MessageContentTypeHTML MessageContentType = "html" +) +``` + + +## type [MessageFrom]() + +MessageFrom represents the sender of a message in Microsoft Teams. + +```go +type MessageFrom struct { + UserID string + DisplayName string +} +``` + + +## type [Team]() + +Team represents a Microsoft Teams team. + +```go +type Team struct { + ID string + DisplayName string + Description string + IsArchived bool + Visibility *string +} +``` + + +## type [TeamUpdate]() + +TeamUpdate represents the fields that can be updated for a Team. + +```go +type TeamUpdate struct { + DisplayName *string + Description *string + Visibility *string +} +``` + +Generated by [gomarkdoc]() diff --git a/docs/teams.md b/docs/teams.md new file mode 100644 index 00000000..d0838e3d --- /dev/null +++ b/docs/teams.md @@ -0,0 +1,89 @@ + + +# teams + +```go +import "github.com/pzsp-teams/lib/teams" +``` + +Package teams provides team\-related operations and abstracts the underlying Microsoft Graph API calls. + +The package exposes two interchangeable service implementations: one without cache and one with cache. When cache is enabled, the service stores and reuses team references \(e.g. display name \-\> team ID\), reducing the number of resolver/API calls. The cache may be cleared on request errors. + +Concepts: + +- teamRef is a team reference \(ID or display name\) used in method parameters. +- Operations are executed on behalf of the authenticated user \(derived from MSAL\); required scopes must be granted. +- Some operations accept a Graph patch object \(msmodels.Team\) for updates. +- Archived teams can be archived/unarchived via dedicated operations. +- Deleted teams can be restored using a deleted group ID. + +If an async cached service is used, call Wait\(\) to ensure all background cache updates are finished. + +## Index + +- [type Service](<#Service>) + - [func NewService\(teamOps teamsOps, tr resolver.TeamResolver\) Service](<#NewService>) + + + +## type [Service]() + +Service defines the interface for team\-related operations. It includes methods for retrieving, creating, updating, archiving, unarchiving, deleting, and restoring teams. + +```go +type Service interface { + // Get retrieves a specific team by its reference (ID or display name). + Get(ctx context.Context, teamRef string) (*models.Team, error) + + // ListMyJoined returns all teams the authenticated user has joined. + ListMyJoined(ctx context.Context) ([]*models.Team, error) + + // CreateViaGroup creates a new team associated with a Microsoft 365 group. + CreateViaGroup(ctx context.Context, displayName, mailNickname, visibility string) (*models.Team, error) + + // CreateFromTemplate creates a new team from a template. + CreateFromTemplate(ctx context.Context, displayName, description string, owners, members []string, visibility string, includeMe bool) (string, error) + + // Archive archives a team, optionally making SharePoint read-only for members. + Archive(ctx context.Context, teamRef string, spoReadOnlyForMembers *bool) error + + // Unarchive restores an archived team. + Unarchive(ctx context.Context, teamRef string) error + + // Delete removes a team. + Delete(ctx context.Context, teamRef string) error + + // RestoreDeleted restores a deleted team using the deleted group ID. + RestoreDeleted(ctx context.Context, deletedGroupID string) (string, error) + + // ListMembers returns all members of a team. + ListMembers(ctx context.Context, teamRef string) ([]*models.Member, error) + + // GetMember retrieves a specific member of a team by their member ID or user email. + GetMember(ctx context.Context, teamRef, userRef string) (*models.Member, error) + + // AddMember adds a new member to a team. + AddMember(ctx context.Context, teamRef string, userRef string, isOwner bool) (*models.Member, error) + + // RemoveMember removes a member from a team by their member ID or user email. + RemoveMember(ctx context.Context, teamRef, userRef string) error + + // UpdateMemberRoles updates the roles of a team member (e.g., promote to owner or demote to member). + UpdateMemberRoles(ctx context.Context, teamRef, userRef string, isOwner bool) (*models.Member, error) + + // UpdateTeam applies updates to a team using the provided TeamUpdate object. + UpdateTeam(ctx context.Context, teamRef string, update *models.TeamUpdate) (*models.Team, error) +} +``` + + +### func [NewService]() + +```go +func NewService(teamOps teamsOps, tr resolver.TeamResolver) Service +``` + +NewService creates a new Service instance. + +Generated by [gomarkdoc]() diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 00000000..51375ea4 --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,36 @@ +site_name: Teams API library +site_description: A Go library for interacting with Microsoft Teams API. +site_url: https://pzsp-teams.github.io/lib/ +repo_url: https://github.com/pzsp-teams/lib + +theme: + name: material + features: + - content.code.copy + - navigation.expand + palette: + - scheme: default + primary: teal + accent: teal + toggle: + icon: material/brightness-7 + name: Switch to dark mode + - scheme: slate + primary: teal + accent: teal + toggle: + icon: material/brightness-4 + name: Switch to light mode + +plugins: + - search + +nav: + - Home: index.md + - API: + - Client: client.md + - Teams: teams.md + - Channels: channels.md + - Chats: chats.md + - Models: models.md + - Config: config.md From 917b08e83340741ae08c2a2df9df6050a4015503 Mon Sep 17 00:00:00 2001 From: Michal <01187288@pw.edu.pl> Date: Thu, 8 Jan 2026 13:46:32 +0100 Subject: [PATCH 07/16] docs: added documentation link to main README --- REAMDME.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/REAMDME.md b/REAMDME.md index 15364cbb..f4078bf0 100644 --- a/REAMDME.md +++ b/REAMDME.md @@ -115,6 +115,12 @@ Because the cache might run background goroutines to keep data fresh, you **must defer lib.Close() ``` +## ๐Ÿ“š Documentation + +Full API reference, architecture details, and configuration guides are available here: + +๐Ÿ‘‰ [Read the Documentation](https://pzsp-teams.github.io/lib/) + ## ๐Ÿ“„ License This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. From aa1aed7874db925b6dd87efeb81e04e47068a8e2 Mon Sep 17 00:00:00 2001 From: Michal <01187288@pw.edu.pl> Date: Thu, 8 Jan 2026 13:48:40 +0100 Subject: [PATCH 08/16] fix: typo --- REAMDME.md => README.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename REAMDME.md => README.md (100%) diff --git a/REAMDME.md b/README.md similarity index 100% rename from REAMDME.md rename to README.md From 1594c52214339e0ef307bb7f580824c7d266ab5c Mon Sep 17 00:00:00 2001 From: Michal <01187288@pw.edu.pl> Date: Thu, 8 Jan 2026 13:50:34 +0100 Subject: [PATCH 09/16] fix: enumerate typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f4078bf0..d34379b4 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ Many methods accept a `_Ref` argument. This allows you to pass: Full example usage is showcased [HERE](https://github.com/pzsp-teams/lib/tree/example-cmd-usage/cmd) Here is a simple example of how to initialize the client and list the current user's teams. -### Client init +### 1. Client init ```go import ( From d1547f10366ce893b7473a2c0b6fb3b9ed5c66c9 Mon Sep 17 00:00:00 2001 From: Michal <01187288@pw.edu.pl> Date: Thu, 8 Jan 2026 13:50:53 +0100 Subject: [PATCH 10/16] fix: another enum typo --- docs/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/index.md b/docs/index.md index cb848bf3..5863398c 100644 --- a/docs/index.md +++ b/docs/index.md @@ -41,7 +41,7 @@ Many methods accept a `_Ref` argument. This allows you to pass: Full example usage is showcased [HERE](https://github.com/pzsp-teams/lib/tree/example-cmd-usage/cmd) Here is a simple example of how to initialize the client and list the current user's teams. -### Client init +### 1. Client init ```go import ( From 26d496e042f8fb6aca5fe2d513b5545a7386c339 Mon Sep 17 00:00:00 2001 From: Michal <01187288@pw.edu.pl> Date: Thu, 8 Jan 2026 13:55:00 +0100 Subject: [PATCH 11/16] docs: added python reference --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index d34379b4..e8a27bf6 100644 --- a/README.md +++ b/README.md @@ -124,3 +124,7 @@ Full API reference, architecture details, and configuration guides are available ## ๐Ÿ“„ License This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. + +## Ports + +This library is also available in [Python](https://github.com/pzsp-teams/lib-python) From ca1de1e0fe84917192c97ea8b6552ac26497a3bb Mon Sep 17 00:00:00 2001 From: Michal <01187288@pw.edu.pl> Date: Wed, 14 Jan 2026 18:13:39 +0100 Subject: [PATCH 12/16] docs: updated documentation page --- .gitignore | 1 + docs/channels.md | 12 ++- docs/chats.md | 16 +++- docs/client.md | 8 +- docs/generate_docs.sh | 13 +-- docs/search.md | 179 ++++++++++++++++++++++++++++++++++++++++++ mkdocs.yml | 1 + 7 files changed, 215 insertions(+), 15 deletions(-) create mode 100644 docs/search.md diff --git a/.gitignore b/.gitignore index 6f27dc31..1694fa8c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .vscode .env +/site \ No newline at end of file diff --git a/docs/channels.md b/docs/channels.md index 4c311dc3..53732fa5 100644 --- a/docs/channels.md +++ b/docs/channels.md @@ -25,7 +25,7 @@ If an async cached service is used, call Wait\(\) to ensure all background cache -## type [Service]() +## type [Service]() Service defines the interface for channel\-related operations. It includes methods for managing channels, members, messages, and more. @@ -97,11 +97,19 @@ type Service interface { // - Team (only the parent team of the channel can be mentioned). It can be used by specifying "team" or team display name as raw mention. // - User IDs GetMentions(ctx context.Context, teamRef, channelRef string, rawMentions []string) ([]models.Mention, error) + + // SearchMessagesInChannel searches for messages in a channel matching the specified query and options. + // + // If channelRef is nil, searches across all channels the user has access to. + // If teamRef is also nil, searches across all teams and channels the user has access to. + // + // Returns search results containing matching messages. + SearchMessages(ctx context.Context, teamRef, channelRef *string, opts *search.SearchMessagesOptions, searchConfig *search.SearchConfig) (*search.SearchResults, error) } ``` -### func [NewService]() +### func [NewService]() ```go func NewService(ops channelOps, tr resolver.TeamResolver, cr resolver.ChannelResolver) Service diff --git a/docs/chats.md b/docs/chats.md index d03286b2..ec8ccd93 100644 --- a/docs/chats.md +++ b/docs/chats.md @@ -72,13 +72,13 @@ type OneOnOneChatRef struct { ``` -## type [Service]() +## type [Service]() Service defines the interface for chat\-related operations. It includes methods for creating chats, managing members, sending messages, and more. ```go type Service interface { - // CreateOneOneOne creates a one-on-one chat with the given recipient. + // CreateOneOnOne creates a one-on-one chat with the given recipient. // The authenticated user is automatically added to the chat. CreateOneOnOne(ctx context.Context, recipientRef string) (*models.Chat, error) @@ -86,6 +86,9 @@ type Service interface { // The authenticated user may be included by setting includeMe to true. CreateGroup(ctx context.Context, recipientRefs []string, topic string, includeMe bool) (*models.Chat, error) + // GetChat retrieves a chat (one-on-one or group) by its reference. + GetChat(ctx context.Context, chatRef ChatRef) (*models.Chat, error) + // AddMemberToGroupChat adds a user to a group chat. AddMemberToGroupChat(ctx context.Context, chatRef GroupChatRef, userRef string) (*models.Member, error) @@ -138,11 +141,18 @@ type Service interface { // - Everyone (for group chats) // - User IDs GetMentions(ctx context.Context, chatRef ChatRef, rawMentions []string) ([]models.Mention, error) + + // SearchMessages searches for messages in a chat matching the specified query and options. + // + // If chatRef is nil, searches across all chats the user has access to. + // + // Returns search results containing matching messages. + SearchMessages(ctx context.Context, chatRef ChatRef, opts *search.SearchMessagesOptions, searchConfig *search.SearchConfig) (*search.SearchResults, error) } ``` -### func [NewService]() +### func [NewService]() ```go func NewService(chatOps chatOps, cr resolver.ChatResolver) Service diff --git a/docs/client.md b/docs/client.md index d36597b9..f31f27ed 100644 --- a/docs/client.md +++ b/docs/client.md @@ -34,7 +34,7 @@ Always ensure to call Close\(\) upon application shutdown to flush any backgroun -## func [Close]() +## func [Close]() ```go func Close() @@ -43,7 +43,7 @@ func Close() Close ensures a graceful shutdown of the library. It waits for any pending background operations \(such as asynchronous cache updates\) to complete before returning, preventing data loss or race conditions. -## func [NewChannelServiceFromGraphClient]() +## func [NewChannelServiceFromGraphClient]() ```go func NewChannelServiceFromGraphClient(ctx context.Context, authCfg *config.AuthConfig, senderCfg *config.SenderConfig, cacheCfg *config.CacheConfig) (channels.Service, error) @@ -52,7 +52,7 @@ func NewChannelServiceFromGraphClient(ctx context.Context, authCfg *config.AuthC NewChannelServiceFromGraphClient creates a standalone service for Channel operations. Use this if you do not need the full Client wrapper and only want to interact with Channels. -## func [NewChatServiceFromGraphClient]() +## func [NewChatServiceFromGraphClient]() ```go func NewChatServiceFromGraphClient(ctx context.Context, authCfg *config.AuthConfig, senderCfg *config.SenderConfig, cacheCfg *config.CacheConfig) (chats.Service, error) @@ -61,7 +61,7 @@ func NewChatServiceFromGraphClient(ctx context.Context, authCfg *config.AuthConf NewChatServiceFromGraphClient creates a standalone service for Chat operations. Use this if you do not need the full Client wrapper and only want to interact with Chats. -## func [NewTeamServiceFromGraphClient]() +## func [NewTeamServiceFromGraphClient]() ```go func NewTeamServiceFromGraphClient(ctx context.Context, authCfg *config.AuthConfig, senderCfg *config.SenderConfig, cacheCfg *config.CacheConfig) (teams.Service, error) diff --git a/docs/generate_docs.sh b/docs/generate_docs.sh index 04741f9c..de2e92c5 100755 --- a/docs/generate_docs.sh +++ b/docs/generate_docs.sh @@ -1,6 +1,7 @@ -gomarkdoc ./teams --output docs/teams.md -gomarkdoc ./channels --output docs/channels.md -gomarkdoc ./chats --output docs/chats.md -gomarkdoc ./models --output docs/models.md -gomarkdoc ./config --output docs/config.md -gomarkdoc . --output docs/client.md \ No newline at end of file +$(go env GOPATH)/bin/gomarkdoc ./teams --output docs/teams.md +$(go env GOPATH)/bin/gomarkdoc ./channels --output docs/channels.md +$(go env GOPATH)/bin/gomarkdoc ./chats --output docs/chats.md +$(go env GOPATH)/bin/gomarkdoc ./models --output docs/models.md +$(go env GOPATH)/bin/gomarkdoc ./config --output docs/config.md +$(go env GOPATH)/bin/gomarkdoc ./search --output docs/search.md +$(go env GOPATH)/bin/gomarkdoc . --output docs/client.md \ No newline at end of file diff --git a/docs/search.md b/docs/search.md new file mode 100644 index 00000000..35c9c4ea --- /dev/null +++ b/docs/search.md @@ -0,0 +1,179 @@ + + +# search + +```go +import "github.com/pzsp-teams/lib/search" +``` + +## Index + +- [type SearchConfig](<#SearchConfig>) + - [func DefaultSearchConfig\(\) \*SearchConfig](<#DefaultSearchConfig>) +- [type SearchMessagesOptions](<#SearchMessagesOptions>) +- [type SearchPage](<#SearchPage>) +- [type SearchResult](<#SearchResult>) +- [type SearchResults](<#SearchResults>) +- [type TimeInterval](<#TimeInterval>) + + + +## type [SearchConfig]() + +SearchConfig holds configuration for search operations. + +MaxWorkers specifies the maximum number of concurrent workers to use when fetching search results. A higher number can speed up searches but may increase resource usage. Default is 8. + +```go +type SearchConfig struct { + MaxWorkers int +} +``` + + +### func [DefaultSearchConfig]() + +```go +func DefaultSearchConfig() *SearchConfig +``` + +DefaultSearchConfig returns a SearchConfig with default settings. + + +## type [SearchMessagesOptions]() + +SearchMessagesOptions contains options for searching messages. + +Fields: + +- Query: The search query string. +- SearchPage: Pagination options. +- From: List of sender email addresses to include. +- NotFrom: List of sender email addresses to exclude. +- IsRead: Filter by read status \(true for read, false for unread\). +- IsMentioned: Filter by mention status \(true for mentioned, false for not mentioned\). +- To: List of recipient email addresses to include. +- NotTo: List of recipient email addresses to exclude. +- StartTime: Start time for the sent time range filter. +- EndTime: End time for the sent time range filter. +- Interval: Predefined time interval for the sent time filter. +- NotFromMe: Exclude messages sent by the current user. +- NotToMe: Exclude messages sent to the current user. +- FromMe: Include only messages sent by the current user. +- ToMe: Include only messages sent to the current user. + +Note: If Interval is set, it takes precedence over StartTime and EndTime. + +Note: Using \`to\` clauses works only in chats, not in team channels. + +Note: Currently, the queries for IsRead may not function as expected due to API limitations. + +```go +type SearchMessagesOptions struct { + Query *string + SearchPage *SearchPage + From []string + NotFrom []string + IsRead *bool + IsMentioned *bool + To []string + NotTo []string + StartTime *time.Time + EndTime *time.Time + Interval *TimeInterval + NotFromMe bool + NotToMe bool + FromMe bool + ToMe bool +} +``` + + +## type [SearchPage]() + +SearchPage contains pagination options for searching messages. Fields: + +- From: The starting index of the search results. +- Size: The number of results to return. + +Note: If not set, default pagination values will be used. From is zero\-based. Default Size is typically 25 + +```go +type SearchPage struct { + From *int32 + Size *int32 +} +``` + + +## type [SearchResult]() + +SearchResult represents a single search result containing a message and its context. + +Fields: + +- Message: The chat message. +- ChannelID: The ID of the channel where the message was found \(if applicable\). +- TeamID: The ID of the team where the message was found \(if applicable\). +- ChatID: The ID of the chat where the message was found \(if applicable\). + +```go +type SearchResult struct { + Message *models.Message + ChannelID *string + TeamID *string + ChatID *string +} +``` + + +## type [SearchResults]() + +SearchResults represents the results of a message search. + +Fields: + +- Messages: A list of search results. +- NextFrom: The pagination token for the next page of results \(if applicable\). + +```go +type SearchResults struct { + Messages []*SearchResult + NextFrom *int32 +} +``` + + +## type [TimeInterval]() + +TimeInterval represents a predefined time interval for message searches. + +Possible values include: + +- Today +- Yesterday +- ThisWeek +- ThisMonth +- LastMonth +- ThisYear +- LastYear + +```go +type TimeInterval string +``` + + + +```go +const ( + Today TimeInterval = "today" + Yesterday TimeInterval = "yesterday" + ThisWeek TimeInterval = "this week" + ThisMonth TimeInterval = "this month" + LastMonth TimeInterval = "last month" + ThisYear TimeInterval = "this year" + LastYear TimeInterval = "last year" +) +``` + +Generated by [gomarkdoc]() diff --git a/mkdocs.yml b/mkdocs.yml index 51375ea4..358b7846 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -34,3 +34,4 @@ nav: - Chats: chats.md - Models: models.md - Config: config.md + - Search: search.md From 8aead77e1acfd066897c2dd74649413749908c88 Mon Sep 17 00:00:00 2001 From: Michal <01187288@pw.edu.pl> Date: Wed, 14 Jan 2026 18:18:32 +0100 Subject: [PATCH 13/16] docs: typos in README --- README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index e8a27bf6..c3f1e980 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ High-level Go (Golang) library that simplifies interaction with **Microsoft Grap ## ๐Ÿ“ฆ Installation ```bash -go get (https://github.com/pzsp-teams/lib) +go get https://github.com/pzsp-teams/lib ``` ## ๐Ÿ› ๏ธ Architecture & Concepts @@ -33,22 +33,22 @@ The library uses a **Facade Pattern**. The Client struct aggregates domain-speci Many methods accept a `_Ref` argument. This allows you to pass: - **UUID** -- **Display Name** (**email** for UserRefs) - this provides convenient usage in interactive applications. +- **Display Name** (**email** address in case of UserRefs) - this provides convenient usage in interactive applications. Library will automatically resolve refs to IDs. ## ๐Ÿ’ป Quick Start Full example usage is showcased [HERE](https://github.com/pzsp-teams/lib/tree/example-cmd-usage/cmd) -Here is a simple example of how to initialize the client and list the current user's teams. +Below is a simple example showing how to initialize the client and list the current user's teams. -### 1. Client init +### 1. Client initialization ```go import ( "context" "time" - "[github.com/pzsp-teams/lib](https://github.com/pzsp-teams/lib)" - "[github.com/pzsp-teams/lib/config](https://github.com/pzsp-teams/lib/config)" + "github.com/pzsp-teams/lib" + "github.com/pzsp-teams/lib/config" ) func main() { @@ -93,7 +93,7 @@ newTeam, _ := client.Teams.CreateViaGroup(ctx, "Project Alpha", "project-alpha", ## Authentication -The library uses `config.AuthConfig` to establish the connection.Ensure your Azure App Registration has the necessary **API Permissions** (e.g., `Team.ReadBasic.All`, `Channel.ReadBasic.All`) granted in the Azure Portal. +The library uses `config.AuthConfig` to establish the connection. Ensure your Azure App Registration has the necessary **API Permissions** (e.g., `Team.ReadBasic.All`, `Channel.ReadBasic.All`) granted in the Azure Portal. Complete list of scopes required by all functions is available [HERE](https://github.com/pzsp-teams/lib/blob/example-cmd-usage/.env.template) There are two available ways to authenticate: @@ -103,7 +103,7 @@ There are two available ways to authenticate: ## Cache -If enabled, stores metadata and non-sensitive mappings, (e.g., `TeamRef` -> `UUID`) to provide efficient reference resolution. +If enabled, stores metadata and non-sensitive mappings (e.g., `TeamRef` -> `UUID`) to provide efficient reference resolution.
@@ -112,7 +112,7 @@ If enabled, stores metadata and non-sensitive mappings, (e.g., `TeamRef` -> `UUI Because the cache might run background goroutines to keep data fresh, you **must** call lib.Close() when your application shuts down. This ensures all background operations complete and prevents memory leaks or race conditions. ```go -defer lib.Close() +defer lib.Close() // Important: closes global cache/background workers ``` ## ๐Ÿ“š Documentation From 710389be6ecd863f91172bd9b0b4493a790edf1b Mon Sep 17 00:00:00 2001 From: KamilMarszalek Date: Fri, 16 Jan 2026 21:34:37 +0100 Subject: [PATCH 14/16] docs: enhance search package documentation --- search/search.go | 160 +++++++++++++++++++++++----------------- search/search_config.go | 10 +-- 2 files changed, 96 insertions(+), 74 deletions(-) diff --git a/search/search.go b/search/search.go index 8001cf73..e69c70fb 100644 --- a/search/search.go +++ b/search/search.go @@ -1,3 +1,17 @@ +// Package search provides types used to build message-search queries and to represent +// paginated search results returned from the messaging backend (e.g., Microsoft Graph). +// +// The package defines: +// - query options (SearchMessagesOptions), +// - pagination controls (SearchPage), +// - predefined time windows (TimeInterval), +// - result containers (SearchResult, SearchResults), +// - and a small concurrency configuration (SearchConfig). +// +// Notes: +// - If Interval is provided, it takes precedence over StartTime and EndTime. +// - "To" / "NotTo" filters typically work for chats, not for team channels. +// - Some providers may not support all filters (e.g., IsRead), depending on API limitations. package search import ( @@ -6,102 +20,110 @@ import ( "github.com/pzsp-teams/lib/models" ) -// SearchPage contains pagination options for searching messages. -// Fields: -// - From: The starting index of the search results. -// - Size: The number of results to return. +// SearchPage defines pagination options for message searches. // -// Note: If not set, default pagination values will be used. From is zero-based. -// Default Size is typically 25 +// From is a zero-based index of the first item to return. +// Size is the maximum number of items to return. +// +// If nil, provider defaults are used (commonly Size=25). type SearchPage struct { From *int32 Size *int32 } -// TimeInterval represents a predefined time interval for message searches. +// TimeInterval is a predefined, human-friendly time window for searching messages. // -// Possible values include: -// - Today -// - Yesterday -// - ThisWeek -// - ThisMonth -// - LastMonth -// - ThisYear -// - LastYear +// When Interval is used in SearchMessagesOptions, it overrides StartTime/EndTime. type TimeInterval string const ( - Today TimeInterval = "today" + // Today selects messages sent today. + Today TimeInterval = "today" + // Yesterday selects messages sent yesterday. Yesterday TimeInterval = "yesterday" - ThisWeek TimeInterval = "this week" + // ThisWeek selects messages sent in the current week. + ThisWeek TimeInterval = "this week" + // ThisMonth selects messages sent in the current month. ThisMonth TimeInterval = "this month" + // LastMonth selects messages sent in the previous month. LastMonth TimeInterval = "last month" - ThisYear TimeInterval = "this year" - LastYear TimeInterval = "last year" + // ThisYear selects messages sent in the current year. + ThisYear TimeInterval = "this year" + // LastYear selects messages sent in the previous year. + LastYear TimeInterval = "last year" ) -// SearchMessagesOptions contains options for searching messages. -// -// Fields: -// - Query: The search query string. -// - SearchPage: Pagination options. -// - From: List of sender email addresses to include. -// - NotFrom: List of sender email addresses to exclude. -// - IsRead: Filter by read status (true for read, false for unread). -// - IsMentioned: Filter by mention status (true for mentioned, false for not mentioned). -// - To: List of recipient email addresses to include. -// - NotTo: List of recipient email addresses to exclude. -// - StartTime: Start time for the sent time range filter. -// - EndTime: End time for the sent time range filter. -// - Interval: Predefined time interval for the sent time filter. -// - NotFromMe: Exclude messages sent by the current user. -// - NotToMe: Exclude messages sent to the current user. -// - FromMe: Include only messages sent by the current user. -// - ToMe: Include only messages sent to the current user. +// SearchMessagesOptions describes filters and parameters used to search for messages. // -// Note: If Interval is set, it takes precedence over StartTime and EndTime. +// Interval takes precedence over StartTime and EndTime. // -// Note: Using `to` clauses works only in chats, not in team channels. -// -// Note: Currently, the queries for IsRead may not function as expected due to API limitations. +// Provider notes: +// - "To" / "NotTo" filters typically work only for chats (not for team channels). +// - Some providers may not support all filters (e.g., IsRead) due to API limitations. type SearchMessagesOptions struct { - Query *string - SearchPage *SearchPage - From []string - NotFrom []string - IsRead *bool + // Query is the full-text query string (provider-dependent syntax). + Query *string + + // SearchPage controls pagination (From/Size). + SearchPage *SearchPage + + // From includes messages from these sender email addresses. + From []string + // NotFrom excludes messages from these sender email addresses. + NotFrom []string + + // IsRead filters by read status (true=read, false=unread). + // Note: may be ignored by some providers due to API limitations. + IsRead *bool + + // IsMentioned filters by whether the current user is mentioned. IsMentioned *bool - To []string - NotTo []string - StartTime *time.Time - EndTime *time.Time - Interval *TimeInterval - NotFromMe bool - NotToMe bool - FromMe bool - ToMe bool + + // To includes messages addressed to these recipients. + // Note: commonly chat-only; may not work for channel messages. + To []string + // NotTo excludes messages addressed to these recipients. + // Note: commonly chat-only; may not work for channel messages. + NotTo []string + + // StartTime is the inclusive start of the sent-time filter. + StartTime *time.Time + // EndTime is the exclusive end of the sent-time filter (provider-dependent). + EndTime *time.Time + + // Interval is a predefined time window; when set it overrides StartTime/EndTime. + Interval *TimeInterval + + // NotFromMe excludes messages sent by the current user. + NotFromMe bool + // NotToMe excludes messages sent to the current user (provider-dependent). + NotToMe bool + // FromMe includes only messages sent by the current user. + FromMe bool + // ToMe includes only messages sent to the current user (provider-dependent). + ToMe bool } -// SearchResult represents a single search result containing a message and its context. +// SearchResult is a single search hit together with its location context. // -// Fields: -// - Message: The chat message. -// - ChannelID: The ID of the channel where the message was found (if applicable). -// - TeamID: The ID of the team where the message was found (if applicable). -// - ChatID: The ID of the chat where the message was found (if applicable). +// Exactly which of ChannelID/TeamID/ChatID is set depends on where the message was found. type SearchResult struct { - Message *models.Message + // Message is the found message payload. + Message *models.Message + + // ChannelID is set when the message was found in a channel. ChannelID *string - TeamID *string - ChatID *string + // TeamID is set when the message was found in a team (typically with ChannelID). + TeamID *string + // ChatID is set when the message was found in a chat. + ChatID *string } -// SearchResults represents the results of a message search. -// -// Fields: -// - Messages: A list of search results. -// - NextFrom: The pagination token for the next page of results (if applicable). +// SearchResults is a paginated container of search hits. type SearchResults struct { + // Messages contains the list of hits for this page. Messages []*SearchResult + + // NextFrom is the pagination cursor/index to continue from (if available). NextFrom *int32 } diff --git a/search/search_config.go b/search/search_config.go index c9f1451f..749adf0d 100644 --- a/search/search_config.go +++ b/search/search_config.go @@ -1,15 +1,15 @@ package search -// SearchConfig holds configuration for search operations. +// SearchConfig configures how search operations are executed. // -// MaxWorkers specifies the maximum number of concurrent workers to use -// when fetching search results. A higher number can speed up searches but -// may increase resource usage. Default is 8. +// MaxWorkers defines the maximum number of concurrent workers used to fetch +// and enrich search results. Higher values can speed up searches but increase +// resource usage and pressure on the upstream API. type SearchConfig struct { MaxWorkers int } -// DefaultSearchConfig returns a SearchConfig with default settings. +// DefaultSearchConfig returns a SearchConfig initialized with sane defaults. func DefaultSearchConfig() *SearchConfig { return &SearchConfig{ MaxWorkers: 8, From d93724d596755852516bc54d2f89232a7d808f30 Mon Sep 17 00:00:00 2001 From: KamilMarszalek Date: Fri, 16 Jan 2026 21:52:44 +0100 Subject: [PATCH 15/16] ci: add workflow for docs build and deployment --- .github/workflows/docs.yml | 84 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 .github/workflows/docs.yml diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 00000000..c4e76254 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,84 @@ +name: Docs + +on: + push: + branches: ["main"] + pull_request: + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + build: + name: Build documentation + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + cache: true + + - name: Install gomarkdoc + run: | + go install github.com/princjef/gomarkdoc/cmd/gomarkdoc@latest + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + cache: "pip" + + - name: Install MkDocs dependencies + run: | + python -m pip install --upgrade pip + if [ -f docs/requirements.txt ]; then + pip install -r docs/requirements.txt + elif [ -f requirements.txt ]; then + pip install -r requirements.txt + else + pip install mkdocs mkdocs-material + fi + + - name: Configure GitHub Pages + if: github.event_name != 'pull_request' + uses: actions/configure-pages@v5 + + - name: Generate Go API docs (gomarkdoc -> docs/*.md) + run: | + bash docs/generate_docs.sh + + - name: Build MkDocs site + run: | + mkdocs build --strict --site-dir site + + - name: Upload Pages artifact + if: github.event_name != 'pull_request' + uses: actions/upload-pages-artifact@v3 + with: + path: site + + deploy: + name: Deploy to GitHub Pages + if: github.event_name != 'pull_request' + needs: build + runs-on: ubuntu-latest + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + + steps: + - name: Deploy + id: deployment + uses: actions/deploy-pages@v4 From 2305f0e31f0ed6620c5f90e0ec46aebf78cd7c5a Mon Sep 17 00:00:00 2001 From: KamilMarszalek Date: Fri, 16 Jan 2026 21:57:08 +0100 Subject: [PATCH 16/16] ci: remove pip cache from Python setup in workflow --- .github/workflows/docs.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index c4e76254..32b72c84 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -38,7 +38,6 @@ jobs: uses: actions/setup-python@v5 with: python-version: "3.12" - cache: "pip" - name: Install MkDocs dependencies run: |