diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
new file mode 100644
index 00000000..32b72c84
--- /dev/null
+++ b/.github/workflows/docs.yml
@@ -0,0 +1,83 @@
+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"
+
+ - 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
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/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/README.md b/README.md
new file mode 100644
index 00000000..c3f1e980
--- /dev/null
+++ b/README.md
@@ -0,0 +1,130 @@
+# Teams API wrapper Lib
+
+[](https://pkg.go.dev/github.com/pzsp-teams/lib)
+[](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** 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)
+Below is a simple example showing how to initialize the client and list the current user's teams.
+
+### 1. Client initialization
+
+```go
+import (
+ "context"
+ "time"
+ "github.com/pzsp-teams/lib"
+ "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() // Important: closes global cache/background workers
+```
+
+## 📚 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.
+
+## Ports
+
+This library is also available in [Python](https://github.com/pzsp-teams/lib-python)
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()
diff --git a/docs/channels.md b/docs/channels.md
new file mode 100644
index 00000000..53732fa5
--- /dev/null
+++ b/docs/channels.md
@@ -0,0 +1,120 @@
+
+
+# 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)
+
+ // 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]()
+
+```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..ec8ccd93
--- /dev/null
+++ b/docs/chats.md
@@ -0,0 +1,163 @@
+
+
+# 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 {
+ // 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)
+
+ // 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)
+
+ // 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)
+
+ // 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)
+
+ // 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]()
+
+```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..f31f27ed
--- /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..de2e92c5
--- /dev/null
+++ b/docs/generate_docs.sh
@@ -0,0 +1,7 @@
+$(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/index.md b/docs/index.md
new file mode 100644
index 00000000..5863398c
--- /dev/null
+++ b/docs/index.md
@@ -0,0 +1,117 @@
+# Teams API wrapper Lib
+
+[](https://pkg.go.dev/github.com/pzsp-teams/lib)
+[](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.
+
+### 1. 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/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/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..358b7846
--- /dev/null
+++ b/mkdocs.yml
@@ -0,0 +1,37 @@
+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
+ - Search: search.md
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,