From 5e44e9330fa7d551f7aabe251bde7846b4e4e2e9 Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Sun, 21 Dec 2025 12:06:45 +0100
Subject: [PATCH 01/99] feat: implemented better json decoding function
---
go/go.mod | 1 +
go/go.sum | 8 ++-
.../json-client/decoders/decode-msTeam.go | 25 +++++++++
.../json-client/decoders/decode-params.go | 27 ++++++++++
go/internal/json-client/requests-teams.go | 52 +++++++++++++++++++
5 files changed, 111 insertions(+), 2 deletions(-)
create mode 100644 go/internal/json-client/decoders/decode-msTeam.go
create mode 100644 go/internal/json-client/decoders/decode-params.go
create mode 100644 go/internal/json-client/requests-teams.go
diff --git a/go/go.mod b/go/go.mod
index 75df08b..7e695aa 100644
--- a/go/go.mod
+++ b/go/go.mod
@@ -5,6 +5,7 @@ go 1.25.5
require (
github.com/microsoft/kiota-abstractions-go v1.9.3
github.com/microsoftgraph/msgraph-sdk-go v1.90.0
+ github.com/mitchellh/mapstructure v1.5.0
github.com/pzsp-teams/lib v0.0.0-20251213104555-e41d3adf599f
)
diff --git a/go/go.sum b/go/go.sum
index e345e5c..6e98158 100644
--- a/go/go.sum
+++ b/go/go.sum
@@ -23,6 +23,8 @@ github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeD
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
+github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
+github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/keybase/go-keychain v0.0.0-20230523030712-b5615109f100 h1:rG3VnJUnAWyiv7qYmmdOdSapzz6HM+zb9/uRFr0T5EM=
@@ -51,18 +53,20 @@ github.com/microsoftgraph/msgraph-sdk-go v1.90.0 h1:ygVeWfGB8TMO4rTFxtrYueZmj1mL
github.com/microsoftgraph/msgraph-sdk-go v1.90.0/go.mod h1:UdZWxbZiFvjPug9DYayD90JNiHjXyNRA39lEpcy3Kms=
github.com/microsoftgraph/msgraph-sdk-go-core v1.4.0 h1:0SrIoFl7TQnMRrsi5TFaeNe0q8KO5lRzRp4GSCCL2So=
github.com/microsoftgraph/msgraph-sdk-go-core v1.4.0/go.mod h1:A1iXs+vjsRjzANxF6UeKv2ACExG7fqTwHHbwh1FL+EE=
+github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
+github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/pzsp-teams/lib v0.0.0-20251208073120-ca1606f2b27d h1:hFdPrkbnUngxrGdMkPVZpvY76T6QgnXo87u9wzXgNWY=
-github.com/pzsp-teams/lib v0.0.0-20251208073120-ca1606f2b27d/go.mod h1:uGrgoO+KmBc55Le/AeysA0d24HOCQHO55Uw5Z7naQo4=
github.com/pzsp-teams/lib v0.0.0-20251213104555-e41d3adf599f h1:tmXKJEWYl0f/H4n91wnfW1asgkHbftKgX5nbpafNHmU=
github.com/pzsp-teams/lib v0.0.0-20251213104555-e41d3adf599f/go.mod h1:EPtQVPz+HYZNsSpcoQ3QWLbMEz1O2qeIH5ojlYJLEic=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/std-uritemplate/std-uritemplate/go/v2 v2.0.3 h1:7hth9376EoQEd1hH4lAp3vnaLP2UMyxuMMghLKzDHyU=
github.com/std-uritemplate/std-uritemplate/go/v2 v2.0.3/go.mod h1:Z5KcoM0YLC7INlNhEezeIZ0TZNYf7WSNO0Lvah4DSeQ=
+github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
+github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/zalando/go-keyring v0.2.6 h1:r7Yc3+H+Ux0+M72zacZoItR3UDxeWfKTcabvkI8ua9s=
diff --git a/go/internal/json-client/decoders/decode-msTeam.go b/go/internal/json-client/decoders/decode-msTeam.go
new file mode 100644
index 0000000..c67986d
--- /dev/null
+++ b/go/internal/json-client/decoders/decode-msTeam.go
@@ -0,0 +1,25 @@
+package decoders
+
+import(
+ msmodels "github.com/microsoftgraph/msgraph-sdk-go/models"
+)
+
+type TeamPatchDTO struct {
+ DisplayName *string `json:"displayName"`
+ Description *string `json:"description"`
+ // Dodaj inne pola, które chcesz aktualizować...
+}
+
+func GetMSTeam(patch *TeamPatchDTO) *msmodels.Team {
+ team := msmodels.NewTeam()
+
+ if patch.DisplayName != nil {
+ team.SetDisplayName(patch.DisplayName)
+ }
+ if patch.Description != nil {
+ team.SetDescription(patch.Description)
+ }
+ // Ustaw inne pola w podobny sposób...
+
+ return team
+}
\ No newline at end of file
diff --git a/go/internal/json-client/decoders/decode-params.go b/go/internal/json-client/decoders/decode-params.go
new file mode 100644
index 0000000..6651123
--- /dev/null
+++ b/go/internal/json-client/decoders/decode-params.go
@@ -0,0 +1,27 @@
+package decoders
+
+import (
+ "github.com/mitchellh/mapstructure"
+)
+
+func DecodeParams[T any](p interface{}) (*T, error) {
+ var result T
+
+ config := &mapstructure.DecoderConfig{
+ Metadata: nil,
+ Result: &result,
+ TagName: "json",
+ WeaklyTypedInput: true,
+ }
+
+ decoder, err := mapstructure.NewDecoder(config)
+ if err != nil {
+ return nil, err
+ }
+
+ if err := decoder.Decode(p); err != nil {
+ return nil, err
+ }
+
+ return &result, nil
+}
\ No newline at end of file
diff --git a/go/internal/json-client/requests-teams.go b/go/internal/json-client/requests-teams.go
new file mode 100644
index 0000000..a6898a8
--- /dev/null
+++ b/go/internal/json-client/requests-teams.go
@@ -0,0 +1,52 @@
+package jsonclient
+
+import (
+ "context"
+
+ "github.com/pzsp-teams/lib-python/internal/decoders"
+)
+
+type getTeamParams struct {
+ TeamRef string `json:"teamRef"`
+}
+
+func (jsonclient *TeamsJSONClient) Get(p map[string]interface{}) (interface{}, error) {
+ params, err := decoders.DecodeParams[getTeamParams](p)
+ if err != nil {
+ return nil, err
+ }
+ team, err := jsonclient.client.Teams.Get(context.TODO(), params.TeamRef)
+ if err != nil {
+ return nil, err
+ } else {
+ return team, nil
+ }
+}
+
+func (jsonclient *TeamsJSONClient) ListMyJoined() (interface{}, error) {
+ teams, err := jsonclient.client.Teams.ListMyJoined(context.TODO())
+ if err != nil {
+ return nil, err
+ } else {
+ return teams, nil
+ }
+}
+
+type updateTeamParams struct {
+ TeamRef string `json:"teamRef"`
+ TeamPatch decoders.TeamPatchDTO `json:"team"`
+}
+
+func (jsonclient *TeamsJSONClient) Update(p map[string]interface{}) (interface{}, error) {
+ params, err := decoders.DecodeParams[updateTeamParams](p)
+ msTeam := decoders.GetMSTeam(¶ms.TeamPatch)
+ if err != nil {
+ return nil, err
+ }
+ updatedTeam, err := jsonclient.client.Teams.Update(context.TODO(), params.TeamRef, msTeam)
+ if err != nil {
+ return nil, err
+ } else {
+ return updatedTeam, nil
+ }
+}
From 177141e9b18402b430658933d9aab056ba502c15 Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Sun, 21 Dec 2025 12:14:08 +0100
Subject: [PATCH 02/99] feat: written Teams service in go bridge
---
go/internal/json-client/requests-teams.go | 112 ++++++++++++++++++++--
1 file changed, 105 insertions(+), 7 deletions(-)
diff --git a/go/internal/json-client/requests-teams.go b/go/internal/json-client/requests-teams.go
index a6898a8..847d52b 100644
--- a/go/internal/json-client/requests-teams.go
+++ b/go/internal/json-client/requests-teams.go
@@ -3,7 +3,7 @@ package jsonclient
import (
"context"
- "github.com/pzsp-teams/lib-python/internal/decoders"
+ "github.com/pzsp-teams/lib-python/internal/json-client/decoders"
)
type getTeamParams struct {
@@ -18,18 +18,16 @@ func (jsonclient *TeamsJSONClient) Get(p map[string]interface{}) (interface{}, e
team, err := jsonclient.client.Teams.Get(context.TODO(), params.TeamRef)
if err != nil {
return nil, err
- } else {
- return team, nil
}
+ return team, nil
}
func (jsonclient *TeamsJSONClient) ListMyJoined() (interface{}, error) {
teams, err := jsonclient.client.Teams.ListMyJoined(context.TODO())
if err != nil {
return nil, err
- } else {
- return teams, nil
}
+ return teams, nil
}
type updateTeamParams struct {
@@ -46,7 +44,107 @@ func (jsonclient *TeamsJSONClient) Update(p map[string]interface{}) (interface{}
updatedTeam, err := jsonclient.client.Teams.Update(context.TODO(), params.TeamRef, msTeam)
if err != nil {
return nil, err
- } else {
- return updatedTeam, nil
}
+ return updatedTeam, nil
+}
+
+type createViaGroupParams struct {
+ DisplayName string `json:"displayName"`
+ MailNickname string `json:"mailNickname"`
+ Visibility string `json:"visibility"`
+}
+
+func (jsonclient *TeamsJSONClient) CreateViaGroup(p map[string]interface{}) (interface{}, error) {
+ params, err := decoders.DecodeParams[createViaGroupParams](p)
+ if err != nil {
+ return nil, err
+ }
+ createdTeam, err := jsonclient.client.Teams.CreateViaGroup(context.TODO(), params.DisplayName, params.MailNickname, params.Visibility)
+ if err != nil {
+ return nil, err
+ }
+ return createdTeam, nil
+}
+
+type createFromTemplateParams struct {
+ DisplayName string `json:"displayName"`
+ Description string `json:"description"`
+ Owners []string `json:"owners"`
+}
+
+func (jsonclient *TeamsJSONClient) CreateFromTemplate(p map[string]interface{}) (interface{}, error) {
+ params, err := decoders.DecodeParams[createFromTemplateParams](p)
+ if err != nil {
+ return nil, err
+ }
+ createdTeam, err := jsonclient.client.Teams.CreateFromTemplate(context.TODO(), params.DisplayName, params.Description, params.Owners)
+ if err != nil {
+ return nil, err
+ }
+ return createdTeam, nil
+}
+
+type archiveTeamParams struct {
+ TeamRef string `json:"teamRef"`
+ SpoReadOnlyFromMembers *bool `json:"spoReadOnlyFromMembers,omitempty"`
+}
+
+func (jsonclient *TeamsJSONClient) Archive(p map[string]interface{}) (interface{}, error) {
+ params, err := decoders.DecodeParams[archiveTeamParams](p)
+ if err != nil {
+ return nil, err
+ }
+ err = jsonclient.client.Teams.Archive(context.TODO(), params.TeamRef, params.SpoReadOnlyFromMembers)
+ if err != nil {
+ return nil, err
+ }
+ return "archived", nil
+}
+
+type unarchiveTeamParams struct {
+ TeamRef string `json:"teamRef"`
+}
+
+func (jsonclient *TeamsJSONClient) Unarchive(p map[string]interface{}) (interface{}, error) {
+ params, err := decoders.DecodeParams[unarchiveTeamParams](p)
+ if err != nil {
+ return nil, err
+ }
+ err = jsonclient.client.Teams.Unarchive(context.TODO(), params.TeamRef)
+ if err != nil {
+ return nil, err
+ }
+ return "unarchived", nil
+}
+
+type deleteTeamParams struct {
+ TeamRef string `json:"teamRef"`
+}
+
+func (jsonclient *TeamsJSONClient) Delete(p map[string]interface{}) (interface{}, error) {
+ params, err := decoders.DecodeParams[deleteTeamParams](p)
+ if err != nil {
+ return nil, err
+ }
+ err = jsonclient.client.Teams.Delete(context.TODO(), params.TeamRef)
+ if err != nil {
+ return nil, err
+ }
+ return "deleted", nil
+}
+
+type restoreTeamParams struct {
+ DeletedGroupID string `json:"deletedGroupId"`
+}
+
+func (jsonclient *TeamsJSONClient) RestoreDeleted(p map[string]interface{}) (interface{}, error) {
+ params, err := decoders.DecodeParams[restoreTeamParams](p)
+ if err != nil {
+ return nil, err
+ }
+ restoredTeam, err := jsonclient.client.Teams.RestoreDeleted(context.TODO(), params.DeletedGroupID)
+ if err != nil {
+ return nil, err
+ }
+ return restoredTeam, nil
}
From 8442b0c8763f9a59a724e8c514b314d9c6a43d91 Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Sun, 21 Dec 2025 12:23:56 +0100
Subject: [PATCH 03/99] feat: adjusted update Team patch DTO
---
.../json-client/decoders/decode-msTeam.go | 117 +++++++++++++++++-
1 file changed, 113 insertions(+), 4 deletions(-)
diff --git a/go/internal/json-client/decoders/decode-msTeam.go b/go/internal/json-client/decoders/decode-msTeam.go
index c67986d..3447881 100644
--- a/go/internal/json-client/decoders/decode-msTeam.go
+++ b/go/internal/json-client/decoders/decode-msTeam.go
@@ -5,9 +5,44 @@ import(
)
type TeamPatchDTO struct {
- DisplayName *string `json:"displayName"`
- Description *string `json:"description"`
- // Dodaj inne pola, które chcesz aktualizować...
+ DisplayName *string `json:"displayName"`
+ Description *string `json:"description"`
+ Classification *string `json:"classification"`
+ Visibility *string `json:"visibility"`
+ IsArchived *bool `json:"isArchived"`
+
+ MemberSettings *TeamMemberSettingsDTO `json:"memberSettings"`
+ GuestSettings *TeamGuestSettingsDTO `json:"guestSettings"`
+ MessagingSettings *TeamMessagingSettingsDTO `json:"messagingSettings"`
+ FunSettings *TeamFunSettingsDTO `json:"funSettings"`
+}
+
+type TeamMemberSettingsDTO struct {
+ AllowCreateUpdateChannels *bool `json:"allowCreateUpdateChannels"`
+ AllowDeleteChannels *bool `json:"allowDeleteChannels"`
+ AllowAddRemoveApps *bool `json:"allowAddRemoveApps"`
+ AllowCreateUpdateRemoveTabs *bool `json:"allowCreateUpdateRemoveTabs"`
+ AllowCreateUpdateRemoveConnectors *bool `json:"allowCreateUpdateRemoveConnectors"`
+}
+
+type TeamGuestSettingsDTO struct {
+ AllowCreateUpdateChannels *bool `json:"allowCreateUpdateChannels"`
+ AllowDeleteChannels *bool `json:"allowDeleteChannels"`
+}
+
+type TeamMessagingSettingsDTO struct {
+ AllowUserEditMessages *bool `json:"allowUserEditMessages"`
+ AllowUserDeleteMessages *bool `json:"allowUserDeleteMessages"`
+ AllowOwnerDeleteMessages *bool `json:"allowOwnerDeleteMessages"`
+ AllowTeamMentions *bool `json:"allowTeamMentions"`
+ AllowChannelMentions *bool `json:"allowChannelMentions"`
+}
+
+type TeamFunSettingsDTO struct {
+ AllowGiphy *bool `json:"allowGiphy"`
+ GiphyContentRating *string `json:"giphyContentRating"`
+ AllowStickersAndMemes *bool `json:"allowStickersAndMemes"`
+ AllowCustomMemes *bool `json:"allowCustomMemes"`
}
func GetMSTeam(patch *TeamPatchDTO) *msmodels.Team {
@@ -19,7 +54,81 @@ func GetMSTeam(patch *TeamPatchDTO) *msmodels.Team {
if patch.Description != nil {
team.SetDescription(patch.Description)
}
- // Ustaw inne pola w podobny sposób...
+ if patch.Classification != nil {
+ team.SetClassification(patch.Classification)
+ }
+ if patch.IsArchived != nil {
+ team.SetIsArchived(patch.IsArchived)
+ }
+ if patch.Visibility != nil {
+ switch *patch.Visibility {
+ case "Public":
+ v := msmodels.PUBLIC_TEAMVISIBILITYTYPE
+ team.SetVisibility(&v)
+ case "Private":
+ v := msmodels.PRIVATE_TEAMVISIBILITYTYPE
+ team.SetVisibility(&v)
+ case "HiddenMembership":
+ v := msmodels.HIDDENMEMBERSHIP_TEAMVISIBILITYTYPE
+ team.SetVisibility(&v)
+ }
+ }
+
+ if patch.MemberSettings != nil {
+ msSettings := msmodels.NewTeamMemberSettings()
+ p := patch.MemberSettings
+ if p.AllowCreateUpdateChannels != nil { msSettings.SetAllowCreateUpdateChannels(p.AllowCreateUpdateChannels) }
+ if p.AllowDeleteChannels != nil { msSettings.SetAllowDeleteChannels(p.AllowDeleteChannels) }
+ if p.AllowAddRemoveApps != nil { msSettings.SetAllowAddRemoveApps(p.AllowAddRemoveApps) }
+ if p.AllowCreateUpdateRemoveTabs != nil { msSettings.SetAllowCreateUpdateRemoveTabs(p.AllowCreateUpdateRemoveTabs) }
+ if p.AllowCreateUpdateRemoveConnectors != nil { msSettings.SetAllowCreateUpdateRemoveConnectors(p.AllowCreateUpdateRemoveConnectors) }
+
+ team.SetMemberSettings(msSettings)
+ }
+
+ if patch.GuestSettings != nil {
+ msSettings := msmodels.NewTeamGuestSettings()
+ p := patch.GuestSettings
+ if p.AllowCreateUpdateChannels != nil { msSettings.SetAllowCreateUpdateChannels(p.AllowCreateUpdateChannels) }
+ if p.AllowDeleteChannels != nil { msSettings.SetAllowDeleteChannels(p.AllowDeleteChannels) }
+
+ team.SetGuestSettings(msSettings)
+ }
+
+ if patch.MessagingSettings != nil {
+ msSettings := msmodels.NewTeamMessagingSettings()
+ p := patch.MessagingSettings
+ if p.AllowUserEditMessages != nil { msSettings.SetAllowUserEditMessages(p.AllowUserEditMessages) }
+ if p.AllowUserDeleteMessages != nil { msSettings.SetAllowUserDeleteMessages(p.AllowUserDeleteMessages) }
+ if p.AllowOwnerDeleteMessages != nil { msSettings.SetAllowOwnerDeleteMessages(p.AllowOwnerDeleteMessages) }
+ if p.AllowTeamMentions != nil { msSettings.SetAllowTeamMentions(p.AllowTeamMentions) }
+ if p.AllowChannelMentions != nil { msSettings.SetAllowChannelMentions(p.AllowChannelMentions) }
+
+ team.SetMessagingSettings(msSettings)
+ }
+
+ if patch.FunSettings != nil {
+ msSettings := msmodels.NewTeamFunSettings()
+ p := patch.FunSettings
+ if p.AllowGiphy != nil { msSettings.SetAllowGiphy(p.AllowGiphy) }
+ if p.GiphyContentRating != nil {
+ switch *p.GiphyContentRating {
+ case "moderate":
+ v := msmodels.MODERATE_GIPHYRATINGTYPE
+ msSettings.SetGiphyContentRating(&v)
+ case "strict":
+ v := msmodels.STRICT_GIPHYRATINGTYPE
+ msSettings.SetGiphyContentRating(&v)
+ default:
+ v := msmodels.MODERATE_GIPHYRATINGTYPE
+ msSettings.SetGiphyContentRating(&v)
+ }
+}
+ if p.AllowStickersAndMemes != nil { msSettings.SetAllowStickersAndMemes(p.AllowStickersAndMemes) }
+ if p.AllowCustomMemes != nil { msSettings.SetAllowCustomMemes(p.AllowCustomMemes) }
+
+ team.SetFunSettings(msSettings)
+ }
return team
}
\ No newline at end of file
From b0dcf5fdad5f883d3d10b83dc78d53205a41461f Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Tue, 23 Dec 2025 11:45:20 +0100
Subject: [PATCH 04/99] refactor: improved go requests handling
---
go/bridge/main.go | 19 +++++-----
go/internal/json-client/request-map.go | 46 +++++++++++++++++++++++
go/internal/json-client/requests-teams.go | 16 ++++----
3 files changed, 63 insertions(+), 18 deletions(-)
create mode 100644 go/internal/json-client/request-map.go
diff --git a/go/bridge/main.go b/go/bridge/main.go
index ec5052b..d14276c 100644
--- a/go/bridge/main.go
+++ b/go/bridge/main.go
@@ -51,19 +51,18 @@ func main() {
continue
}
- switch req.Method {
- case "listChannels":
- channels, err := client.ListChannels(req.Params)
- if err != nil {
- respondError(writer, err)
- } else {
- respondResult(writer, channels)
- }
- default:
+ handler, exists := jsonClientLib.Handlers[req.Method]
+ if !exists {
respondError(writer, fmt.Errorf("unknown method"))
+ continue
+ }
+
+ result, err := handler(client, req.Params)
+ if err != nil {
+ respondError(writer, err)
}
+ respondResult(writer, result)
continue
}
- respondError(writer, fmt.Errorf("unknown request type"))
}
}
diff --git a/go/internal/json-client/request-map.go b/go/internal/json-client/request-map.go
new file mode 100644
index 0000000..24d0b65
--- /dev/null
+++ b/go/internal/json-client/request-map.go
@@ -0,0 +1,46 @@
+package jsonclient
+
+type HandlerFunc func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error)
+
+var Handlers = map[string]HandlerFunc{
+
+ "listChannels": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ return c.ListChannels(p)
+ },
+
+ "getTeam": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ return c.GetTeam(p)
+ },
+
+ "updateTeam": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ return c.UpdateTeam(p)
+ },
+
+ "listMyJoined": func(c *TeamsJSONClient, _ map[string]interface{}) (interface{}, error) {
+ return c.ListMyJoined()
+ },
+
+ "createTeamViaGroup": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ return c.CreateTeamViaGroup(p)
+ },
+
+ "createTeamFromTemplate": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ return c.CreateTeamFromTemplate(p)
+ },
+
+ "archiveTeam": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ return c.ArchiveTeam(p)
+ },
+
+ "unarchiveTeam": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ return c.UnarchiveTeam(p)
+ },
+
+ "deleteTeam": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ return c.DeleteTeam(p)
+ },
+
+ "restoreDeletedTeam": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ return c.RestoreDeletedTeam(p)
+ },
+}
diff --git a/go/internal/json-client/requests-teams.go b/go/internal/json-client/requests-teams.go
index 847d52b..0c8a3d4 100644
--- a/go/internal/json-client/requests-teams.go
+++ b/go/internal/json-client/requests-teams.go
@@ -10,7 +10,7 @@ type getTeamParams struct {
TeamRef string `json:"teamRef"`
}
-func (jsonclient *TeamsJSONClient) Get(p map[string]interface{}) (interface{}, error) {
+func (jsonclient *TeamsJSONClient) GetTeam(p map[string]interface{}) (interface{}, error) {
params, err := decoders.DecodeParams[getTeamParams](p)
if err != nil {
return nil, err
@@ -35,7 +35,7 @@ type updateTeamParams struct {
TeamPatch decoders.TeamPatchDTO `json:"team"`
}
-func (jsonclient *TeamsJSONClient) Update(p map[string]interface{}) (interface{}, error) {
+func (jsonclient *TeamsJSONClient) UpdateTeam(p map[string]interface{}) (interface{}, error) {
params, err := decoders.DecodeParams[updateTeamParams](p)
msTeam := decoders.GetMSTeam(¶ms.TeamPatch)
if err != nil {
@@ -54,7 +54,7 @@ type createViaGroupParams struct {
Visibility string `json:"visibility"`
}
-func (jsonclient *TeamsJSONClient) CreateViaGroup(p map[string]interface{}) (interface{}, error) {
+func (jsonclient *TeamsJSONClient) CreateTeamViaGroup(p map[string]interface{}) (interface{}, error) {
params, err := decoders.DecodeParams[createViaGroupParams](p)
if err != nil {
return nil, err
@@ -72,7 +72,7 @@ type createFromTemplateParams struct {
Owners []string `json:"owners"`
}
-func (jsonclient *TeamsJSONClient) CreateFromTemplate(p map[string]interface{}) (interface{}, error) {
+func (jsonclient *TeamsJSONClient) CreateTeamFromTemplate(p map[string]interface{}) (interface{}, error) {
params, err := decoders.DecodeParams[createFromTemplateParams](p)
if err != nil {
return nil, err
@@ -89,7 +89,7 @@ type archiveTeamParams struct {
SpoReadOnlyFromMembers *bool `json:"spoReadOnlyFromMembers,omitempty"`
}
-func (jsonclient *TeamsJSONClient) Archive(p map[string]interface{}) (interface{}, error) {
+func (jsonclient *TeamsJSONClient) ArchiveTeam(p map[string]interface{}) (interface{}, error) {
params, err := decoders.DecodeParams[archiveTeamParams](p)
if err != nil {
return nil, err
@@ -105,7 +105,7 @@ type unarchiveTeamParams struct {
TeamRef string `json:"teamRef"`
}
-func (jsonclient *TeamsJSONClient) Unarchive(p map[string]interface{}) (interface{}, error) {
+func (jsonclient *TeamsJSONClient) UnarchiveTeam(p map[string]interface{}) (interface{}, error) {
params, err := decoders.DecodeParams[unarchiveTeamParams](p)
if err != nil {
return nil, err
@@ -121,7 +121,7 @@ type deleteTeamParams struct {
TeamRef string `json:"teamRef"`
}
-func (jsonclient *TeamsJSONClient) Delete(p map[string]interface{}) (interface{}, error) {
+func (jsonclient *TeamsJSONClient) DeleteTeam(p map[string]interface{}) (interface{}, error) {
params, err := decoders.DecodeParams[deleteTeamParams](p)
if err != nil {
return nil, err
@@ -137,7 +137,7 @@ type restoreTeamParams struct {
DeletedGroupID string `json:"deletedGroupId"`
}
-func (jsonclient *TeamsJSONClient) RestoreDeleted(p map[string]interface{}) (interface{}, error) {
+func (jsonclient *TeamsJSONClient) RestoreDeletedTeam(p map[string]interface{}) (interface{}, error) {
params, err := decoders.DecodeParams[restoreTeamParams](p)
if err != nil {
return nil, err
From be98ae66684539b1d063daabf3f7d8dcc58e66ca Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Tue, 23 Dec 2025 12:03:14 +0100
Subject: [PATCH 05/99] feat: implemented most of teams service in python
---
python/teams_lib_pzsp2_z1/services/teams.py | 117 ++++++++++++++++++++
1 file changed, 117 insertions(+)
create mode 100644 python/teams_lib_pzsp2_z1/services/teams.py
diff --git a/python/teams_lib_pzsp2_z1/services/teams.py b/python/teams_lib_pzsp2_z1/services/teams.py
new file mode 100644
index 0000000..249554a
--- /dev/null
+++ b/python/teams_lib_pzsp2_z1/services/teams.py
@@ -0,0 +1,117 @@
+from teams_lib_pzsp2_z1.model.team import Team
+from teams_lib_pzsp2_z1.services.base_service import BaseService
+
+
+class TeamsService(BaseService):
+ def get(self, teamRef: str) -> Team:
+ response = self.client.execute(
+ cmd_type="request",
+ method="getTeam",
+ params={
+ "teamRef": teamRef,
+ },
+ )
+ return Team(
+ ID=response["ID"],
+ DisplayName=response["DisplayName"],
+ Description=response["Description"],
+ IsArchived=(True if response["IsArchived"] else False),
+ Visibility=response["Visibility"],
+ )
+
+ def list_my_joined(self) -> list[Team]:
+ response = self.client.execute(
+ cmd_type="request",
+ method="listMyJoined",
+ params={},
+ )
+ return [
+ Team(
+ ID=team["ID"],
+ DisplayName=team["DisplayName"],
+ Description=team["Description"],
+ IsArchived=(True if team["IsArchived"] else False),
+ Visibility=team["Visibility"],
+ )
+ for team in response
+ ]
+
+ # def update //TODO
+
+ def create_via_group(
+ self, display_name: str, mail_nickname: str, visibility: str
+ ) -> Team:
+ response = self.client.execute(
+ cmd_type="request",
+ method="createTeamViaGroup",
+ params={
+ "displayName": display_name,
+ "mailNickname": mail_nickname,
+ "visibility": visibility,
+ },
+ )
+ return Team(
+ ID=response["ID"],
+ DisplayName=response["DisplayName"],
+ Description=response["Description"],
+ IsArchived=(True if response["IsArchived"] else False),
+ Visibility=response["Visibility"],
+ )
+
+ def create_from_template(
+ self,
+ display_name: str,
+ description: str,
+ owners: list[str],
+ ) -> str:
+ response = self.client.execute(
+ cmd_type="request",
+ method="createTeamFromTemplate",
+ params={
+ "displayName": display_name,
+ "description": description,
+ "owners": owners,
+ },
+ )
+ return response["id"]
+
+ def archive(self, teamRef: str, spo_read_only_from_members: bool) -> bool:
+ response = self.client.execute(
+ cmd_type="request",
+ method="archiveTeam",
+ params={
+ "teamRef": teamRef,
+ "spoReadOnlyFromMembers": spo_read_only_from_members,
+ },
+ )
+ return response == "archived"
+
+ def unarchive(self, teamRef: str) -> bool:
+ response = self.client.execute(
+ cmd_type="request",
+ method="unarchiveTeam",
+ params={
+ "teamRef": teamRef,
+ },
+ )
+ return response == "unarchived"
+
+ def delete(self, teamRef: str) -> bool:
+ response = self.client.execute(
+ cmd_type="request",
+ method="deleteTeam",
+ params={
+ "teamRef": teamRef,
+ },
+ )
+ return response == "deleted"
+
+ def restore_deleted(self, teamRef: str) -> str:
+ response = self.client.execute(
+ cmd_type="request",
+ method="restoreDeletedTeam",
+ params={
+ "teamRef": teamRef,
+ },
+ )
+ return response["id"]
From 894abbd6109daa06e6405114c6ec3cc166a43d2f Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Tue, 23 Dec 2025 12:13:15 +0100
Subject: [PATCH 06/99] feat: implemented update team function in python
---
python/teams_lib_pzsp2_z1/model/team.py | 41 +++++++++++++++++++++
python/teams_lib_pzsp2_z1/services/teams.py | 20 +++++++++-
2 files changed, 59 insertions(+), 2 deletions(-)
diff --git a/python/teams_lib_pzsp2_z1/model/team.py b/python/teams_lib_pzsp2_z1/model/team.py
index 9de6e1b..d53ee4a 100644
--- a/python/teams_lib_pzsp2_z1/model/team.py
+++ b/python/teams_lib_pzsp2_z1/model/team.py
@@ -8,3 +8,44 @@ class Team:
Description: str
IsArchived: bool
Visibility: str
+
+
+@dataclass
+class MSTeamsUpdate:
+ DisplayName: str | None = None
+ Description: str | None = None
+ Classification: str | None = None
+ Visibility: str | None = None
+ IsArchived: bool | None = None
+ MemberSettings: "TeamMemberSettingsDTO | None" = None
+ GuestSettings: "TeamGuestSettingsDTO | None" = None
+ MessagingSettings: "TeamMessagingSettingsDTO | None" = None
+ FunSettings: "TeamFunSettingsDTO | None" = None
+
+ @dataclass
+ class TeamMemberSettingsDTO:
+ AllowCreateUpdateChannels: bool | None = None
+ AllowDeleteChannels: bool | None = None
+ AllowAddRemoveApps: bool | None = None
+ AllowCreateUpdateRemoveTabs: bool | None = None
+ AllowCreateUpdateRemoveConnectors: bool | None = None
+
+ @dataclass
+ class TeamGuestSettingsDTO:
+ AllowCreateUpdateChannels: bool | None = None
+ AllowDeleteChannels: bool | None = None
+
+ @dataclass
+ class TeamMessagingSettingsDTO:
+ AllowUserEditMessages: bool | None = None
+ AllowUserDeleteMessages: bool | None = None
+ AllowOwnerDeleteMessages: bool | None = None
+ AllowTeamMentions: bool | None = None
+ AllowChannelMentions: bool | None = None
+
+ @dataclass
+ class TeamFunSettingsDTO:
+ AllowGiphy: bool | None = None
+ GiphyContentRating: str | None = None
+ AllowStickersAndMemes: bool | None = None
+ AllowCustomMemes: bool | None = None
diff --git a/python/teams_lib_pzsp2_z1/services/teams.py b/python/teams_lib_pzsp2_z1/services/teams.py
index 249554a..599ed16 100644
--- a/python/teams_lib_pzsp2_z1/services/teams.py
+++ b/python/teams_lib_pzsp2_z1/services/teams.py
@@ -1,4 +1,4 @@
-from teams_lib_pzsp2_z1.model.team import Team
+from teams_lib_pzsp2_z1.model.team import MSTeamsUpdate, Team
from teams_lib_pzsp2_z1.services.base_service import BaseService
@@ -36,7 +36,23 @@ def list_my_joined(self) -> list[Team]:
for team in response
]
- # def update //TODO
+ def update(self, teamRef: str, update: MSTeamsUpdate) -> Team:
+ params = {"teamRef": teamRef}
+ update_dict = {k: v for k, v in update.__dict__.items() if v is not None}
+ params["team"] = update_dict
+
+ response = self.client.execute(
+ cmd_type="request",
+ method="updateTeam",
+ params=params,
+ )
+ return Team(
+ ID=response["ID"],
+ DisplayName=response["DisplayName"],
+ Description=response["Description"],
+ IsArchived=(True if response["IsArchived"] else False),
+ Visibility=response["Visibility"],
+ )
def create_via_group(
self, display_name: str, mail_nickname: str, visibility: str
From 8e48ff76351c08e5638f0e66fa1bf99b5773b655 Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Tue, 23 Dec 2025 15:25:22 +0100
Subject: [PATCH 07/99] feat: Written test to validate get teams request
---
go/internal/json-client/requests-teams.go | 8 +++
python/teams_lib_pzsp2_z1/client.py | 5 +-
python/teams_lib_pzsp2_z1/services/teams.py | 1 +
python/tests/fake_server/data.py | 25 +++++++--
python/tests/fake_server/setup.py | 14 ++++-
python/tests/test_teams.py | 60 +++++++++++++++++++++
6 files changed, 106 insertions(+), 7 deletions(-)
create mode 100644 python/tests/test_teams.py
diff --git a/go/internal/json-client/requests-teams.go b/go/internal/json-client/requests-teams.go
index 0c8a3d4..c461bfa 100644
--- a/go/internal/json-client/requests-teams.go
+++ b/go/internal/json-client/requests-teams.go
@@ -2,6 +2,8 @@ package jsonclient
import (
"context"
+ "fmt"
+ "os"
"github.com/pzsp-teams/lib-python/internal/json-client/decoders"
)
@@ -12,6 +14,9 @@ type getTeamParams struct {
func (jsonclient *TeamsJSONClient) GetTeam(p map[string]interface{}) (interface{}, error) {
params, err := decoders.DecodeParams[getTeamParams](p)
+
+ fmt.Fprintf(os.Stderr, "Decoded params: %+v\n", params)
+
if err != nil {
return nil, err
}
@@ -19,6 +24,9 @@ func (jsonclient *TeamsJSONClient) GetTeam(p map[string]interface{}) (interface{
if err != nil {
return nil, err
}
+
+ fmt.Fprintf(os.Stderr, "Received team: %+v\n", team)
+
return team, nil
}
diff --git a/python/teams_lib_pzsp2_z1/client.py b/python/teams_lib_pzsp2_z1/client.py
index 055ac45..65b994a 100644
--- a/python/teams_lib_pzsp2_z1/client.py
+++ b/python/teams_lib_pzsp2_z1/client.py
@@ -4,9 +4,11 @@
import subprocess
import threading
from typing import Any
+import sys
from teams_lib_pzsp2_z1 import config
from teams_lib_pzsp2_z1.services.channels import ChannelsService
+from teams_lib_pzsp2_z1.services.teams import TeamsService
class TeamsClient:
@@ -23,13 +25,14 @@ def __init__(
[str(self._binary())],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
- stderr=subprocess.DEVNULL,
+ stderr=sys.stderr,
text=True,
bufsize=1,
)
self.env_path = env_path
self.channels = ChannelsService(self)
+ self.teams = TeamsService(self)
if auto_init:
self.init_client(cache_enabled, cache_path)
diff --git a/python/teams_lib_pzsp2_z1/services/teams.py b/python/teams_lib_pzsp2_z1/services/teams.py
index 599ed16..508ae13 100644
--- a/python/teams_lib_pzsp2_z1/services/teams.py
+++ b/python/teams_lib_pzsp2_z1/services/teams.py
@@ -11,6 +11,7 @@ def get(self, teamRef: str) -> Team:
"teamRef": teamRef,
},
)
+
return Team(
ID=response["ID"],
DisplayName=response["DisplayName"],
diff --git a/python/tests/fake_server/data.py b/python/tests/fake_server/data.py
index d917f45..d5e7fb7 100644
--- a/python/tests/fake_server/data.py
+++ b/python/tests/fake_server/data.py
@@ -14,8 +14,15 @@ def __init__(self) -> FakeServerData:
DisplayName="Test Team",
Description="A team for testing",
IsArchived=False,
- Visibility="Private",
+ Visibility="private",
),
+ Team(
+ ID="team-456-def",
+ DisplayName="Another Team",
+ Description="Another team for testing",
+ IsArchived=False,
+ Visibility="public",
+ )
]
self.channels = {
"team-123-abc": [
@@ -34,7 +41,6 @@ def __init__(self) -> FakeServerData:
def get_myJoinedTeams_response(self) -> dict:
return {
- "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#teams",
"value": [
{
"id": team.ID,
@@ -49,7 +55,6 @@ def get_myJoinedTeams_response(self) -> dict:
def get_listChannels_response(self, team_id: str) -> dict:
return {
- "@odata.context": f"https://graph.microsoft.com/v1.0/$metadata#teams('{team_id}')/channels",
"value": [
{
"id": channel.ID,
@@ -58,4 +63,16 @@ def get_listChannels_response(self, team_id: str) -> dict:
}
for channel in self.channels.get(team_id, [])
],
- }
\ No newline at end of file
+ }
+
+ def get_team_response(self, team_id: str) -> dict | None:
+ return {
+ "id": team.ID,
+ "displayName": team.DisplayName,
+ "description": team.Description,
+ "isArchived": team.IsArchived,
+ "visibility": team.Visibility,
+ } if (team := next((t for t in self.teams if t.ID == team_id), None)) else None
+
+
+
diff --git a/python/tests/fake_server/setup.py b/python/tests/fake_server/setup.py
index aa92527..63e2b33 100644
--- a/python/tests/fake_server/setup.py
+++ b/python/tests/fake_server/setup.py
@@ -7,15 +7,25 @@ def setup_fake_server(httpserver) -> FakeServerData:
data = FakeServerData()
- # Mock response for listing joined teams
httpserver.expect_request(
"/v1.0/users/me-token-to-replace/joinedTeams", method="GET"
).respond_with_json(data.get_myJoinedTeams_response())
- # Mock response for listing channels in the fake team
httpserver.expect_request(
f"/v1.0/teams/{data.teams[0].ID}/channels", method="GET"
).respond_with_json(data.get_listChannels_response(data.teams[0].ID))
+ httpserver.expect_request(
+ f"/v1.0/teams/{data.teams[1].ID}/channels", method="GET"
+ ).respond_with_json(data.get_listChannels_response(data.teams[1].ID))
+
+ httpserver.expect_request(
+ f"/v1.0/teams/{data.teams[0].ID}", method="GET"
+ ).respond_with_json(data.get_team_response(data.teams[0].ID))
+
+ httpserver.expect_request(
+ f"/v1.0/teams/{data.teams[1].ID}", method="GET"
+ ).respond_with_json(data.get_team_response(data.teams[1].ID))
+
return data
diff --git a/python/tests/test_teams.py b/python/tests/test_teams.py
new file mode 100644
index 0000000..1dea2fb
--- /dev/null
+++ b/python/tests/test_teams.py
@@ -0,0 +1,60 @@
+from teams_lib_pzsp2_z1.client import TeamsClient
+from tests.init_fake_client import init_fake_client
+from tests.fake_server.setup import setup_fake_server
+
+
+def test_list_my_teams_integration(httpserver):
+ """
+ Integration test: Python -> Go Binary -> Fake HTTP -> Python Mock Server
+ """
+
+ data = setup_fake_server(httpserver)
+
+ # Init fake client
+ client = TeamsClient(auto_init=False)
+ try:
+ init_fake_client(client, httpserver.url_for(""))
+
+ teams = client.teams.list_my_joined()
+
+ assert len(teams) == len(data.teams)
+
+ assert teams[0].DisplayName == data.teams[0].DisplayName
+ assert teams[0].ID == data.teams[0].ID
+ assert teams[0].Description == data.teams[0].Description
+ assert teams[0].IsArchived == data.teams[0].IsArchived
+ assert teams[0].Visibility == data.teams[0].Visibility
+
+ assert teams[1].DisplayName == data.teams[1].DisplayName
+ assert teams[1].ID == data.teams[1].ID
+ assert teams[1].Description == data.teams[1].Description
+ assert teams[1].IsArchived == data.teams[1].IsArchived
+ assert teams[1].Visibility == data.teams[1].Visibility
+
+ finally:
+ client.close()
+
+def test_get_team_integration(httpserver):
+ """
+ Integration test: Python -> Go Binary -> Fake HTTP -> Python Mock Server
+ """
+
+ data = setup_fake_server(httpserver)
+
+ # Init fake client
+ client = TeamsClient(auto_init=False)
+ try:
+ init_fake_client(client, httpserver.url_for(""))
+
+ team = client.teams.get(data.teams[0].DisplayName)
+
+ print(data.get_team_response(data.teams[0].ID))
+
+ assert team.DisplayName == data.teams[0].DisplayName
+ assert team.ID == data.teams[0].ID
+ assert team.Description == data.teams[0].Description
+ assert team.IsArchived == data.teams[0].IsArchived
+ assert team.Visibility == data.teams[0].Visibility
+
+ finally:
+ client.close()
From f9fd045fd3b938464fe6e46c93edea21266f645f Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Tue, 23 Dec 2025 18:27:14 +0100
Subject: [PATCH 08/99] feat: added test to createTeamViaGroup (torture)
---
python/tests/fake_server/data.py | 56 ++++++++++++++++++++
python/tests/fake_server/setup.py | 85 +++++++++++++++++++++++++++----
python/tests/test_teams.py | 26 +++++++++-
3 files changed, 156 insertions(+), 11 deletions(-)
diff --git a/python/tests/fake_server/data.py b/python/tests/fake_server/data.py
index d5e7fb7..035ae8f 100644
--- a/python/tests/fake_server/data.py
+++ b/python/tests/fake_server/data.py
@@ -1,12 +1,19 @@
from dataclasses import dataclass
from teams_lib_pzsp2_z1.model.team import Team
from teams_lib_pzsp2_z1.model.channel import Channel
+from dataclasses import field
@dataclass
class FakeServerData:
teams: list[Team]
channels: dict[str, list[Channel]]
+ newGroupID: str
+ newTeamName: str
+ newGroupMailNickname: str
+ newTeamVisibility: str
+ potentialTeams: list[Team] = field(default_factory=list)
+
def __init__(self) -> FakeServerData:
self.teams = [
Team(
@@ -38,6 +45,12 @@ def __init__(self) -> FakeServerData:
),
],
}
+ self.newGroupID = "group-789-ghi"
+ self.newTeamName = "New Team"
+ self.newGroupMailNickname = "new-team-nickname"
+ self.newTeamVisibility = "private"
+ self.potentialTeams = []
+
def get_myJoinedTeams_response(self) -> dict:
return {
@@ -74,5 +87,48 @@ def get_team_response(self, team_id: str) -> dict | None:
"visibility": team.Visibility,
} if (team := next((t for t in self.teams if t.ID == team_id), None)) else None
+ def get_createGroup_response(self, request_json: dict) -> dict:
+ visibility = request_json.get("visibility").lower()
+ if visibility not in ("private", "public"):
+ visibility = "private"
+
+ self.potentialTeams.append(
+ Team(
+ ID=self.newGroupID,
+ DisplayName=request_json.get("displayName"),
+ Description=request_json.get("description"),
+ IsArchived=False,
+ Visibility=visibility,
+ )
+ )
+
+ return {
+ "id": self.newGroupID,
+ "displayName": request_json.get("displayName"),
+ "description": request_json.get("displayName"),
+ "visibility": visibility,
+ "groupTypes": ["Unified"],
+ "mailEnabled": True,
+ "mailNickname": request_json.get("mailNickname"),
+ "securityEnabled": False,
+ "createdDateTime": "2024-01-01T00:00:00Z",
+ }
+
+ def get_createTeamViaGroup_response(self, group_id: str) -> dict:
+ team = next((t for t in self.potentialTeams if t.ID == group_id), None)
+ if not team:
+ return {}
+
+ self.teams.append(team)
+
+ return {
+ "id": team.ID,
+ "displayName": team.DisplayName,
+ "description": team.Description,
+ "isArchived": team.IsArchived,
+ "visibility": team.Visibility,
+ }
+
+
diff --git a/python/tests/fake_server/setup.py b/python/tests/fake_server/setup.py
index 63e2b33..2b4d72b 100644
--- a/python/tests/fake_server/setup.py
+++ b/python/tests/fake_server/setup.py
@@ -1,31 +1,96 @@
from tests.fake_server.data import FakeServerData
+import json
+import re
+import sys
+from werkzeug.wrappers import Response
def setup_fake_server(httpserver) -> FakeServerData:
"""
Sets up a fake HTTP server to simulate Microsoft Graph API responses.
+ Includes stdout logging for debugging.
"""
data = FakeServerData()
+ def make_log_response(request, data_dict: dict, context_msg: str = "") -> Response:
+ print(f"\n[MOCK SERVER] 🟢 REQUEST: {request.method} {request.path}", file=sys.stdout)
+ if context_msg:
+ print(f"[MOCK SERVER] Context: {context_msg}", file=sys.stdout)
+
+ if request.content_type == "application/json":
+ try:
+ req_data = request.json
+ if req_data:
+ print(f"[MOCK SERVER] Body: {req_data}", file=sys.stdout)
+ except Exception as e:
+ print(f"[MOCK SERVER] ⚠️ Body Error: {e}", file=sys.stdout)
+
+ status_code = 200 if data_dict else 404
+ if request.method in ["POST", "PUT"] and data_dict:
+ status_code = 201
+
+ print(f"[MOCK SERVER] 👉 Response: {status_code}", file=sys.stdout)
+
+ return Response(
+ json.dumps(data_dict),
+ mimetype="application/json",
+ status=status_code
+ )
+
+ # GET /users/me/joinedTeams
httpserver.expect_request(
"/v1.0/users/me-token-to-replace/joinedTeams", method="GET"
- ).respond_with_json(data.get_myJoinedTeams_response())
+ ).respond_with_handler(lambda req: make_log_response(
+ req, data.get_myJoinedTeams_response(), "List Joined Teams"
+ ))
+ # GET /teams/{id}/channels
httpserver.expect_request(
- f"/v1.0/teams/{data.teams[0].ID}/channels", method="GET"
- ).respond_with_json(data.get_listChannels_response(data.teams[0].ID))
+ re.compile(r"^/v1.0/teams/([^/]+)/channels"),
+ method="GET"
+ ).respond_with_handler(lambda req: make_log_response(
+ req,
+ data.get_listChannels_response(re.search(r"/teams/([^/]+)/channels", req.path).group(1)),
+ "List Channels"
+ ))
+ # POST /groups
httpserver.expect_request(
- f"/v1.0/teams/{data.teams[1].ID}/channels", method="GET"
- ).respond_with_json(data.get_listChannels_response(data.teams[1].ID))
+ "/v1.0/groups",
+ method="POST"
+ ).respond_with_handler(lambda req: make_log_response(
+ req,
+ data.get_createGroup_response(req.json),
+ "Create Group"
+ ))
+ # PUT /groups/{id}/team
httpserver.expect_request(
- f"/v1.0/teams/{data.teams[0].ID}", method="GET"
- ).respond_with_json(data.get_team_response(data.teams[0].ID))
+ re.compile(r"^/v1.0/groups/([^/]+)/team"),
+ method="PUT"
+ ).respond_with_handler(lambda req: make_log_response(
+ req,
+ data.get_createTeamViaGroup_response(re.search(r"/groups/([^/]+)/team", req.path).group(1)),
+ "Teamify Group (PUT)"
+ ))
+ # GET /teams/{id}
httpserver.expect_request(
- f"/v1.0/teams/{data.teams[1].ID}", method="GET"
- ).respond_with_json(data.get_team_response(data.teams[1].ID))
+ re.compile(r"^/v1.0/teams/([^/]+)(?:$|\?)"),
+ method="GET"
+ ).respond_with_handler(lambda req: make_log_response(
+ req,
+ data.get_team_response(re.search(r"/teams/([^/?]+)", req.path).group(1)),
+ "Get Team Details"
+ ))
- return data
+ # Fallback for unmatched routes
+ httpserver.expect_request(re.compile(".*")).respond_with_handler(
+ lambda req: Response(
+ json.dumps({"error": f"Route not matched in Mock: {req.method} {req.path}"}),
+ status=404,
+ mimetype="application/json"
+ )
+ )
+ return data
\ No newline at end of file
diff --git a/python/tests/test_teams.py b/python/tests/test_teams.py
index 1dea2fb..48419e5 100644
--- a/python/tests/test_teams.py
+++ b/python/tests/test_teams.py
@@ -41,7 +41,6 @@ def test_get_team_integration(httpserver):
data = setup_fake_server(httpserver)
- # Init fake client
client = TeamsClient(auto_init=False)
try:
init_fake_client(client, httpserver.url_for(""))
@@ -58,3 +57,28 @@ def test_get_team_integration(httpserver):
finally:
client.close()
+
+
+def test_create_team_via_group_integration(httpserver):
+ """
+ Integration test: Python -> Go Binary -> Fake HTTP -> Python Mock Server
+ """
+
+ data = setup_fake_server(httpserver)
+
+ client = TeamsClient(auto_init=False)
+ try:
+ init_fake_client(client, httpserver.url_for(""))
+
+ team = client.teams.create_via_group(
+ display_name=data.newTeamName,
+ mail_nickname=data.newGroupMailNickname,
+ visibility=data.newTeamVisibility,
+ )
+
+ assert team.ID == data.newGroupID
+ assert team.DisplayName == data.newTeamName
+ assert team.Visibility == data.newTeamVisibility
+
+ finally:
+ client.close()
From 463e88e4cfbd37dc2c8c556075027c411f5462be Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Sat, 27 Dec 2025 12:15:28 +0100
Subject: [PATCH 09/99] feat: Implemented test to createTeamFromTemplate
---
python/teams_lib_pzsp2_z1/services/teams.py | 2 +-
python/tests/fake_server/data.py | 15 ++++++++++++
python/tests/fake_server/setup.py | 10 ++++++++
python/tests/test_teams.py | 26 ++++++++++++++++-----
4 files changed, 46 insertions(+), 7 deletions(-)
diff --git a/python/teams_lib_pzsp2_z1/services/teams.py b/python/teams_lib_pzsp2_z1/services/teams.py
index 508ae13..a756a2a 100644
--- a/python/teams_lib_pzsp2_z1/services/teams.py
+++ b/python/teams_lib_pzsp2_z1/services/teams.py
@@ -90,7 +90,7 @@ def create_from_template(
"owners": owners,
},
)
- return response["id"]
+ return response
def archive(self, teamRef: str, spo_read_only_from_members: bool) -> bool:
response = self.client.execute(
diff --git a/python/tests/fake_server/data.py b/python/tests/fake_server/data.py
index 035ae8f..829227f 100644
--- a/python/tests/fake_server/data.py
+++ b/python/tests/fake_server/data.py
@@ -129,6 +129,21 @@ def get_createTeamViaGroup_response(self, group_id: str) -> dict:
"visibility": team.Visibility,
}
+ def get_createTeamFromTemplate_response(self, request_json: dict) -> dict:
+ new_team_id = "team-from-template-001"
+ new_team = Team(
+ ID=new_team_id,
+ DisplayName=request_json.get("displayName"),
+ Description=request_json.get("description"),
+ IsArchived=False,
+ Visibility="private",
+ )
+ self.teams.append(new_team)
+
+ return {
+ "id": new_team_id,
+ }
+
diff --git a/python/tests/fake_server/setup.py b/python/tests/fake_server/setup.py
index 2b4d72b..d82994b 100644
--- a/python/tests/fake_server/setup.py
+++ b/python/tests/fake_server/setup.py
@@ -84,6 +84,16 @@ def make_log_response(request, data_dict: dict, context_msg: str = "") -> Respon
"Get Team Details"
))
+ # POST /teams
+ httpserver.expect_request(
+ "/v1.0/teams",
+ method="POST"
+ ).respond_with_handler(lambda req: make_log_response(
+ req,
+ data.get_createTeamFromTemplate_response(req.json),
+ "Create Team from Template (POST)"
+ ))
+
# Fallback for unmatched routes
httpserver.expect_request(re.compile(".*")).respond_with_handler(
lambda req: Response(
diff --git a/python/tests/test_teams.py b/python/tests/test_teams.py
index 48419e5..0bf28bc 100644
--- a/python/tests/test_teams.py
+++ b/python/tests/test_teams.py
@@ -35,9 +35,6 @@ def test_list_my_teams_integration(httpserver):
client.close()
def test_get_team_integration(httpserver):
- """
- Integration test: Python -> Go Binary -> Fake HTTP -> Python Mock Server
- """
data = setup_fake_server(httpserver)
@@ -60,9 +57,6 @@ def test_get_team_integration(httpserver):
def test_create_team_via_group_integration(httpserver):
- """
- Integration test: Python -> Go Binary -> Fake HTTP -> Python Mock Server
- """
data = setup_fake_server(httpserver)
@@ -82,3 +76,23 @@ def test_create_team_via_group_integration(httpserver):
finally:
client.close()
+
+
+def test_create_team_from_template_integration(httpserver):
+
+ data = setup_fake_server(httpserver)
+
+ client = TeamsClient(auto_init=False)
+ try:
+ init_fake_client(client, httpserver.url_for(""))
+
+ msg = client.teams.create_from_template(
+ display_name=data.newTeamName,
+ description="A team created from a template",
+ owners=["user-123-abc"],
+ )
+
+ assert msg == "id will be given later (async)"
+
+ finally:
+ client.close()
\ No newline at end of file
From 8689e6617fefd690a353587780ff98b9ef671bd6 Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Sat, 27 Dec 2025 12:19:09 +0100
Subject: [PATCH 10/99] feat: implemented test to ArchiveTeam
---
python/tests/fake_server/data.py | 15 +++++++++++++++
python/tests/fake_server/setup.py | 10 ++++++++++
python/tests/test_teams.py | 19 +++++++++++++++++++
3 files changed, 44 insertions(+)
diff --git a/python/tests/fake_server/data.py b/python/tests/fake_server/data.py
index 829227f..e27b20e 100644
--- a/python/tests/fake_server/data.py
+++ b/python/tests/fake_server/data.py
@@ -29,6 +29,13 @@ def __init__(self) -> FakeServerData:
Description="Another team for testing",
IsArchived=False,
Visibility="public",
+ ),
+ Team(
+ ID="archived-team-789-ghi",
+ DisplayName="Archived Team",
+ Description="An archived team for testing",
+ IsArchived=True,
+ Visibility="private",
)
]
self.channels = {
@@ -144,6 +151,14 @@ def get_createTeamFromTemplate_response(self, request_json: dict) -> dict:
"id": new_team_id,
}
+ def get_archiveTeam_response(self, team_id: str) -> dict:
+ team = next((t for t in self.teams if t.ID == team_id), None)
+ if not team:
+ return {"success": False}
+
+ team.IsArchived = True
+ return {"success": True}
+
diff --git a/python/tests/fake_server/setup.py b/python/tests/fake_server/setup.py
index d82994b..3e0d6d8 100644
--- a/python/tests/fake_server/setup.py
+++ b/python/tests/fake_server/setup.py
@@ -94,6 +94,16 @@ def make_log_response(request, data_dict: dict, context_msg: str = "") -> Respon
"Create Team from Template (POST)"
))
+ # POST /teams/{id}/archive
+ httpserver.expect_request(
+ re.compile(r"^/v1.0/teams/([^/]+)/archive"),
+ method="POST"
+ ).respond_with_handler(lambda req: make_log_response(
+ req,
+ data.get_archiveTeam_response(re.search(r"/teams/([^/]+)/archive", req.path).group(1)),
+ "Archive Team (POST)"
+ ))
+
# Fallback for unmatched routes
httpserver.expect_request(re.compile(".*")).respond_with_handler(
lambda req: Response(
diff --git a/python/tests/test_teams.py b/python/tests/test_teams.py
index 0bf28bc..325a1c4 100644
--- a/python/tests/test_teams.py
+++ b/python/tests/test_teams.py
@@ -94,5 +94,24 @@ def test_create_team_from_template_integration(httpserver):
assert msg == "id will be given later (async)"
+ finally:
+ client.close()
+
+
+def test_archive_team_integration(httpserver):
+
+ data = setup_fake_server(httpserver)
+
+ client = TeamsClient(auto_init=False)
+ try:
+ init_fake_client(client, httpserver.url_for(""))
+
+ success = client.teams.archive(
+ teamRef=data.teams[0].DisplayName,
+ spo_read_only_from_members=True,
+ )
+
+ assert success is True
+
finally:
client.close()
\ No newline at end of file
From 1166782557ea6912146a9e002d30ab1ccca984da Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Sat, 27 Dec 2025 12:20:50 +0100
Subject: [PATCH 11/99] feat: Implemented test for UnarchiveTeam integration
---
python/tests/fake_server/data.py | 8 ++++++++
python/tests/fake_server/setup.py | 10 ++++++++++
python/tests/test_teams.py | 18 ++++++++++++++++++
3 files changed, 36 insertions(+)
diff --git a/python/tests/fake_server/data.py b/python/tests/fake_server/data.py
index e27b20e..17daf30 100644
--- a/python/tests/fake_server/data.py
+++ b/python/tests/fake_server/data.py
@@ -159,6 +159,14 @@ def get_archiveTeam_response(self, team_id: str) -> dict:
team.IsArchived = True
return {"success": True}
+ def get_unarchiveTeam_response(self, team_id: str) -> dict:
+ team = next((t for t in self.teams if t.ID == team_id), None)
+ if not team:
+ return {"success": False}
+
+ team.IsArchived = False
+ return {"success": True}
+
diff --git a/python/tests/fake_server/setup.py b/python/tests/fake_server/setup.py
index 3e0d6d8..649d43a 100644
--- a/python/tests/fake_server/setup.py
+++ b/python/tests/fake_server/setup.py
@@ -104,6 +104,16 @@ def make_log_response(request, data_dict: dict, context_msg: str = "") -> Respon
"Archive Team (POST)"
))
+ # POST /teams/{id}/unarchive
+ httpserver.expect_request(
+ re.compile(r"^/v1.0/teams/([^/]+)/unarchive"),
+ method="POST"
+ ).respond_with_handler(lambda req: make_log_response(
+ req,
+ data.get_unarchiveTeam_response(re.search(r"/teams/([^/]+)/unarchive", req.path).group(1)),
+ "Unarchive Team (POST)"
+ ))
+
# Fallback for unmatched routes
httpserver.expect_request(re.compile(".*")).respond_with_handler(
lambda req: Response(
diff --git a/python/tests/test_teams.py b/python/tests/test_teams.py
index 325a1c4..5c15d2f 100644
--- a/python/tests/test_teams.py
+++ b/python/tests/test_teams.py
@@ -113,5 +113,23 @@ def test_archive_team_integration(httpserver):
assert success is True
+ finally:
+ client.close()
+
+
+def test_unarchive_team_integration(httpserver):
+
+ data = setup_fake_server(httpserver)
+
+ client = TeamsClient(auto_init=False)
+ try:
+ init_fake_client(client, httpserver.url_for(""))
+
+ success = client.teams.unarchive(
+ teamRef=data.teams[2].DisplayName,
+ )
+
+ assert success is True
+
finally:
client.close()
\ No newline at end of file
From 4daa3b590a1d8b8f50ed8af888b4fc9948392925 Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Sat, 27 Dec 2025 12:38:29 +0100
Subject: [PATCH 12/99] feat: implemented integration tests for delete and
restore Teams
---
python/teams_lib_pzsp2_z1/services/teams.py | 6 ++--
python/tests/fake_server/data.py | 25 ++++++++++++++
python/tests/fake_server/setup.py | 20 +++++++++++
python/tests/test_teams.py | 37 +++++++++++++++++++++
4 files changed, 85 insertions(+), 3 deletions(-)
diff --git a/python/teams_lib_pzsp2_z1/services/teams.py b/python/teams_lib_pzsp2_z1/services/teams.py
index a756a2a..47602a4 100644
--- a/python/teams_lib_pzsp2_z1/services/teams.py
+++ b/python/teams_lib_pzsp2_z1/services/teams.py
@@ -123,12 +123,12 @@ def delete(self, teamRef: str) -> bool:
)
return response == "deleted"
- def restore_deleted(self, teamRef: str) -> str:
+ def restore_deleted(self, deleted_team_ID: str) -> str:
response = self.client.execute(
cmd_type="request",
method="restoreDeletedTeam",
params={
- "teamRef": teamRef,
+ "deletedGroupID": deleted_team_ID,
},
)
- return response["id"]
+ return response
diff --git a/python/tests/fake_server/data.py b/python/tests/fake_server/data.py
index 17daf30..6821439 100644
--- a/python/tests/fake_server/data.py
+++ b/python/tests/fake_server/data.py
@@ -167,6 +167,31 @@ def get_unarchiveTeam_response(self, team_id: str) -> dict:
team.IsArchived = False
return {"success": True}
+ def get_deleteTeam_response(self, team_id: str) -> dict:
+ team = next((t for t in self.teams if t.ID == team_id), None)
+ if not team:
+ return {"success": False}
+
+ self.teams.remove(team)
+ return {"success": True}
+
+ def get_restoreTeam_response(self, team_id: str) -> dict:
+ archived_team = next((t for t in self.teams if t.ID == team_id and t.IsArchived), None)
+ if not archived_team:
+ return {"success": False}
+
+ archived_team.IsArchived = False
+ return {
+ "id": archived_team.ID,
+ "displayName": archived_team.DisplayName,
+ "description": archived_team.Description,
+ "isArchived": archived_team.IsArchived,
+ "visibility": archived_team.Visibility,
+ "mailNickname": self.newGroupMailNickname
+ }
+
+
+
diff --git a/python/tests/fake_server/setup.py b/python/tests/fake_server/setup.py
index 649d43a..0a4df85 100644
--- a/python/tests/fake_server/setup.py
+++ b/python/tests/fake_server/setup.py
@@ -114,6 +114,26 @@ def make_log_response(request, data_dict: dict, context_msg: str = "") -> Respon
"Unarchive Team (POST)"
))
+ # DELETE /teams/{id}/delete
+ httpserver.expect_request(
+ re.compile(r"^/v1.0/groups/([^/]+)"),
+ method="DELETE"
+ ).respond_with_handler(lambda req: make_log_response(
+ req,
+ data.get_deleteTeam_response(re.search(r"/groups/([^/]+)", req.path).group(1)),
+ "Delete Team (DELETE)"
+ ))
+
+ # POST /teams/{id}/restore
+ httpserver.expect_request(
+ re.compile(r"^/v1.0/directory/deletedItems/([^/]+)/restore"),
+ method="POST"
+ ).respond_with_handler(lambda req: make_log_response(
+ req,
+ data.get_restoreTeam_response(re.search(r"/directory/deletedItems/([^/]+)/restore", req.path).group(1)),
+ "Restore Deleted Team (POST)"
+ ))
+
# Fallback for unmatched routes
httpserver.expect_request(re.compile(".*")).respond_with_handler(
lambda req: Response(
diff --git a/python/tests/test_teams.py b/python/tests/test_teams.py
index 5c15d2f..6a4e35a 100644
--- a/python/tests/test_teams.py
+++ b/python/tests/test_teams.py
@@ -131,5 +131,42 @@ def test_unarchive_team_integration(httpserver):
assert success is True
+ finally:
+ client.close()
+
+
+def test_delete_team_integration(httpserver):
+
+ data = setup_fake_server(httpserver)
+
+ client = TeamsClient(auto_init=False)
+ try:
+ init_fake_client(client, httpserver.url_for(""))
+
+ success = client.teams.delete(
+ teamRef=data.teams[0].DisplayName,
+ )
+
+ assert success is True
+ # assert True is False
+
+ finally:
+ client.close()
+
+
+def test_restore_deleted_team_integration(httpserver):
+
+ data = setup_fake_server(httpserver)
+
+ client = TeamsClient(auto_init=False)
+ try:
+ init_fake_client(client, httpserver.url_for(""))
+
+ msg = client.teams.restore_deleted(
+ deleted_team_ID=data.teams[2].ID,
+ )
+
+ assert msg == data.teams[2].ID
+
finally:
client.close()
\ No newline at end of file
From fc4284cd348e244fe674228324b16cce7176dba9 Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Sat, 27 Dec 2025 12:44:32 +0100
Subject: [PATCH 13/99] feat: added integration test to check error handling
---
python/teams_lib_pzsp2_z1/client.py | 2 +-
python/tests/test_teams.py | 18 ++++++++++++++++++
2 files changed, 19 insertions(+), 1 deletion(-)
diff --git a/python/teams_lib_pzsp2_z1/client.py b/python/teams_lib_pzsp2_z1/client.py
index 65b994a..a5f28c0 100644
--- a/python/teams_lib_pzsp2_z1/client.py
+++ b/python/teams_lib_pzsp2_z1/client.py
@@ -2,9 +2,9 @@
import pathlib
import platform
import subprocess
+import sys
import threading
from typing import Any
-import sys
from teams_lib_pzsp2_z1 import config
from teams_lib_pzsp2_z1.services.channels import ChannelsService
diff --git a/python/tests/test_teams.py b/python/tests/test_teams.py
index 6a4e35a..3835eff 100644
--- a/python/tests/test_teams.py
+++ b/python/tests/test_teams.py
@@ -168,5 +168,23 @@ def test_restore_deleted_team_integration(httpserver):
assert msg == data.teams[2].ID
+ finally:
+ client.close()
+
+
+def test_error_handling_integration(httpserver):
+
+ data = setup_fake_server(httpserver)
+
+ client = TeamsClient(auto_init=False)
+ try:
+ init_fake_client(client, httpserver.url_for(""))
+
+ try:
+ client.teams.get("non-existent-team")
+ assert False, "Expected an exception for non-existent team"
+ except Exception as e:
+ assert "Go Error" in str(e)
+
finally:
client.close()
\ No newline at end of file
From ecfaa8724b843a5732b317b1df28fc1e04efaa4b Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Sat, 27 Dec 2025 12:47:42 +0100
Subject: [PATCH 14/99] feat: implemented integration test for UpdateTeam
---
python/tests/fake_server/data.py | 20 ++++++++++++++++++++
python/tests/fake_server/setup.py | 13 +++++++++++++
python/tests/test_teams.py | 28 +++++++++++++++++++++++++++-
3 files changed, 60 insertions(+), 1 deletion(-)
diff --git a/python/tests/fake_server/data.py b/python/tests/fake_server/data.py
index 6821439..3334bba 100644
--- a/python/tests/fake_server/data.py
+++ b/python/tests/fake_server/data.py
@@ -94,6 +94,26 @@ def get_team_response(self, team_id: str) -> dict | None:
"visibility": team.Visibility,
} if (team := next((t for t in self.teams if t.ID == team_id), None)) else None
+ def get_updateTeam_response(self, team_id: str, update_json: dict) -> dict | None:
+ team = next((t for t in self.teams if t.ID == team_id), None)
+ if not team:
+ return None
+
+ if "displayName" in update_json:
+ team.DisplayName = update_json["displayName"]
+ if "description" in update_json:
+ team.Description = update_json["description"]
+ if "visibility" in update_json:
+ team.Visibility = update_json["visibility"]
+
+ return {
+ "id": team.ID,
+ "displayName": team.DisplayName,
+ "description": team.Description,
+ "isArchived": team.IsArchived,
+ "visibility": team.Visibility,
+ }
+
def get_createGroup_response(self, request_json: dict) -> dict:
visibility = request_json.get("visibility").lower()
if visibility not in ("private", "public"):
diff --git a/python/tests/fake_server/setup.py b/python/tests/fake_server/setup.py
index 0a4df85..3a996f6 100644
--- a/python/tests/fake_server/setup.py
+++ b/python/tests/fake_server/setup.py
@@ -84,6 +84,19 @@ def make_log_response(request, data_dict: dict, context_msg: str = "") -> Respon
"Get Team Details"
))
+ # PATCH /teams/{id}
+ httpserver.expect_request(
+ re.compile(r"^/v1.0/teams/([^/]+)"),
+ method="PATCH"
+ ).respond_with_handler(lambda req: make_log_response(
+ req,
+ data.get_updateTeam_response(
+ re.search(r"/teams/([^/]+)", req.path).group(1),
+ req.json
+ ),
+ "Update Team (PATCH)"
+ ))
+
# POST /teams
httpserver.expect_request(
"/v1.0/teams",
diff --git a/python/tests/test_teams.py b/python/tests/test_teams.py
index 3835eff..6b1873a 100644
--- a/python/tests/test_teams.py
+++ b/python/tests/test_teams.py
@@ -1,3 +1,4 @@
+from python.teams_lib_pzsp2_z1.model.team import MSTeamsUpdate
from teams_lib_pzsp2_z1.client import TeamsClient
from tests.init_fake_client import init_fake_client
from tests.fake_server.setup import setup_fake_server
@@ -56,6 +57,32 @@ def test_get_team_integration(httpserver):
client.close()
+def test_update_team_integration(httpserver):
+
+ data = setup_fake_server(httpserver)
+
+ client = TeamsClient(auto_init=False)
+ try:
+ init_fake_client(client, httpserver.url_for(""))
+
+ updated_description = "Updated team description"
+ team = client.teams.update(
+ teamRef=data.teams[0].DisplayName,
+ update=MSTeamsUpdate(
+ Description=updated_description,
+ ),
+ )
+
+ assert team.DisplayName == data.teams[0].DisplayName
+ assert team.ID == data.teams[0].ID
+ assert team.Description == updated_description
+ assert team.IsArchived == data.teams[0].IsArchived
+ assert team.Visibility == data.teams[0].Visibility
+
+ finally:
+ client.close()
+
+
def test_create_team_via_group_integration(httpserver):
data = setup_fake_server(httpserver)
@@ -148,7 +175,6 @@ def test_delete_team_integration(httpserver):
)
assert success is True
- # assert True is False
finally:
client.close()
From 2b0fdd112757f7873f857ff1dc3fa957f08236c3 Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Sat, 27 Dec 2025 15:00:44 +0100
Subject: [PATCH 15/99] feat: implemented part of channels endpoints
---
.../decoders/decode-messageBody.go | 33 +++++
go/internal/json-client/requests-channels.go | 113 ++++++++++++++++--
go/internal/json-client/requests-teams.go | 6 -
3 files changed, 137 insertions(+), 15 deletions(-)
create mode 100644 go/internal/json-client/decoders/decode-messageBody.go
diff --git a/go/internal/json-client/decoders/decode-messageBody.go b/go/internal/json-client/decoders/decode-messageBody.go
new file mode 100644
index 0000000..a08afd6
--- /dev/null
+++ b/go/internal/json-client/decoders/decode-messageBody.go
@@ -0,0 +1,33 @@
+package decoders
+
+import (
+ "github.com/mitchellh/mapstructure"
+ "github.com/pzsp-teams/lib/models"
+)
+
+type messageBodyDTO struct {
+ ContentType string `json:"contentType"`
+ Content string `json:"content"`
+}
+
+func DecodeMessageBody(body *messageBodyDTO) (*models.MessageBody, error) {
+ var result models.MessageBody
+
+ config := &mapstructure.DecoderConfig{
+ Metadata: nil,
+ Result: &result,
+ TagName: "json",
+ WeaklyTypedInput: true,
+ }
+
+ decoder, err := mapstructure.NewDecoder(config)
+ if err != nil {
+ return nil, err
+ }
+
+ if err := decoder.Decode(body); err != nil {
+ return nil, err
+ }
+
+ return &result, nil
+}
diff --git a/go/internal/json-client/requests-channels.go b/go/internal/json-client/requests-channels.go
index cdfb18a..241073f 100644
--- a/go/internal/json-client/requests-channels.go
+++ b/go/internal/json-client/requests-channels.go
@@ -2,22 +2,117 @@ package jsonclient
import (
"context"
- "fmt"
+
+ "github.com/pzsp-teams/lib-python/internal/json-client/decoders"
+ "github.com/pzsp-teams/lib/models"
)
-type ListChannelsParams struct {
+type listChannelsParams struct {
+ TeamRef string `json:"teamRef"`
+}
+
+func (jsonclient *TeamsJSONClient) ListChannels(p map[string]interface{}) (interface{}, error) {
+ params, err := decoders.DecodeParams[listChannelsParams](p)
+ if err != nil {
+ return nil, err
+ }
+ channels, err := jsonclient.client.Channels.ListChannels(context.TODO(), params.TeamRef)
+ if err != nil {
+ return nil, err
+ }
+ return channels, nil
+}
+
+type baseChannelParams struct {
+ TeamRef string `json:"teamRef"`
+ ChannelRef string `json:"channelRef"`
+}
+
+func (jsonclient *TeamsJSONClient) GetChannel(p map[string]interface{}) (interface{}, error) {
+ params, err := decoders.DecodeParams[baseChannelParams](p)
+ if err != nil {
+ return nil, err
+ }
+ channel, err := jsonclient.client.Channels.Get(context.TODO(), params.TeamRef, params.ChannelRef)
+ if err != nil {
+ return nil, err
+ }
+ return channel, nil
+}
+
+type createChannelParams struct {
TeamRef string `json:"teamRef"`
+ Name string `json:"name"`
}
-func (jsonclient *TeamsJSONClient) ListChannels(params map[string]interface{}) (interface{}, error) {
- teamRef, ok := params["teamRef"].(string)
- if !ok || teamRef == "" {
- return nil, fmt.Errorf("invalid teamRef parameter")
+func (jsonclient *TeamsJSONClient) CreateStandardChannel(p map[string]interface{}) (interface{}, error) {
+ params, err := decoders.DecodeParams[createChannelParams](p)
+ if err != nil {
+ return nil, err
}
- channels, err := jsonclient.client.Channels.ListChannels(context.TODO(), teamRef)
+ channel, err := jsonclient.client.Channels.CreateStandardChannel(context.TODO(), params.TeamRef, params.Name)
if err != nil {
return nil, err
- } else {
- return channels, nil
}
+ return channel, nil
+}
+
+type createPrivateChannelParams struct {
+ TeamRef string `json:"teamRef"`
+ Name string `json:"name"`
+ MemberRefs []string `json:"memberRefs"`
+ OwnerRefs []string `json:"ownerRefs"`
}
+
+func (jsonclient *TeamsJSONClient) CreatePrivateChannel(p map[string]interface{}) (interface{}, error) {
+ params, err := decoders.DecodeParams[createPrivateChannelParams](p)
+ if err != nil {
+ return nil, err
+ }
+ channel, err := jsonclient.client.Channels.CreatePrivateChannel(context.TODO(), params.TeamRef, params.Name, params.MemberRefs, params.OwnerRefs)
+ if err != nil {
+ return nil, err
+ }
+ return channel, nil
+}
+
+func (jsonclient *TeamsJSONClient) DeleteChannel(p map[string]interface{}) (interface{}, error) {
+ params, err := decoders.DecodeParams[baseChannelParams](p)
+ if err != nil {
+ return nil, err
+ }
+ err = jsonclient.client.Channels.Delete(context.TODO(), params.TeamRef, params.ChannelRef)
+ if err != nil {
+ return nil, err
+ }
+ return "deleted", nil
+}
+
+type messageBodyDTO struct {
+ ContentType string `json:"contentType"`
+ Content string `json:"content"`
+}
+
+type sendMessageToChannelParams struct {
+ TeamRef string `json:"teamRef"`
+ ChannelRef string `json:"channelRef"`
+ Body messageBodyDTO `json:"body"`
+}
+
+func (jsonclient *TeamsJSONClient) SendMessageToChannel(p map[string]interface{}) (interface{}, error) {
+ params, err := decoders.DecodeParams[sendMessageToChannelParams](p)
+ if err != nil {
+ return nil, err
+ }
+ body, err := decoders.DecodeParams[models.MessageBody](¶ms.Body)
+ if err != nil {
+ return nil, err
+ }
+ sentMessage, err := jsonclient.client.Channels.SendMessage(context.TODO(), params.TeamRef, params.ChannelRef, *body)
+ if err != nil {
+ return nil, err
+ }
+ return sentMessage, nil
+}
+
+
diff --git a/go/internal/json-client/requests-teams.go b/go/internal/json-client/requests-teams.go
index c461bfa..52b77a3 100644
--- a/go/internal/json-client/requests-teams.go
+++ b/go/internal/json-client/requests-teams.go
@@ -14,9 +14,6 @@ type getTeamParams struct {
func (jsonclient *TeamsJSONClient) GetTeam(p map[string]interface{}) (interface{}, error) {
params, err := decoders.DecodeParams[getTeamParams](p)
-
- fmt.Fprintf(os.Stderr, "Decoded params: %+v\n", params)
-
if err != nil {
return nil, err
}
@@ -24,9 +21,6 @@ func (jsonclient *TeamsJSONClient) GetTeam(p map[string]interface{}) (interface{
if err != nil {
return nil, err
}
-
- fmt.Fprintf(os.Stderr, "Received team: %+v\n", team)
-
return team, nil
}
From 41e5dff60b108ab77ef43436428bc7e74dab0723 Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Sat, 27 Dec 2025 15:11:07 +0100
Subject: [PATCH 16/99] feat: implemented rest of channel endpoints
---
go/internal/json-client/requests-channels.go | 143 +++++++++++++++++++
1 file changed, 143 insertions(+)
diff --git a/go/internal/json-client/requests-channels.go b/go/internal/json-client/requests-channels.go
index 241073f..56b72b2 100644
--- a/go/internal/json-client/requests-channels.go
+++ b/go/internal/json-client/requests-channels.go
@@ -115,4 +115,147 @@ func (jsonclient *TeamsJSONClient) SendMessageToChannel(p map[string]interface{}
return sentMessage, nil
}
+type listMessagesOptionsDTO struct {
+ Top *int32 `json:"top"`
+ ExpandReplies bool `json:"expandReplies"`
+}
+
+type listMessagesParams struct {
+ TeamRef string `json:"teamRef"`
+ ChannelRef string `json:"channelRef"`
+ Options listMessagesOptionsDTO `json:"options"`
+}
+
+func (jsonclient *TeamsJSONClient) ListMessagesInChannel(p map[string]interface{}) (interface{}, error) {
+ params, err := decoders.DecodeParams[listMessagesParams](p)
+ if err != nil {
+ return nil, err
+ }
+ options, err := decoders.DecodeParams[models.ListMessagesOptions](params.Options)
+ if err != nil {
+ return nil, err
+ }
+ messages, err := jsonclient.client.Channels.ListMessages(context.TODO(), params.TeamRef, params.ChannelRef, options)
+ if err != nil {
+ return nil, err
+ }
+ return messages, nil
+}
+
+type getMessageParams struct {
+ TeamRef string `json:"teamRef"`
+ ChannelRef string `json:"channelRef"`
+ MessageID string `json:"messageId"`
+}
+
+func (jsonclient *TeamsJSONClient) GetMessageInChannel(p map[string]interface{}) (interface{}, error) {
+ params, err := decoders.DecodeParams[getMessageParams](p)
+ if err != nil {
+ return nil, err
+ }
+ message, err := jsonclient.client.Channels.GetMessage(context.TODO(), params.TeamRef, params.ChannelRef, params.MessageID)
+ if err != nil {
+ return nil, err
+ }
+ return message, nil
+}
+
+type listRepliesParams struct {
+ TeamRef string `json:"teamRef"`
+ ChannelRef string `json:"channelRef"`
+ MessageID string `json:"messageId"`
+ Top *int32 `json:"top"`
+}
+
+func (jsonclient *TeamsJSONClient) ListMessageRepliesInChannel(p map[string]interface{}) (interface{}, error) {
+ params, err := decoders.DecodeParams[listRepliesParams](p)
+ if err != nil {
+ return nil, err
+ }
+ messages, err := jsonclient.client.Channels.ListReplies(context.TODO(), params.TeamRef, params.ChannelRef, params.MessageID, params.Top)
+ if err != nil {
+ return nil, err
+ }
+ return messages, nil
+}
+
+type getReplyParams struct {
+ TeamRef string `json:"teamRef"`
+ ChannelRef string `json:"channelRef"`
+ MessageID string `json:"messageId"`
+ ReplyID string `json:"replyId"`
+}
+
+func (jsonclient *TeamsJSONClient) GetMessageReplyInChannel(p map[string]interface{}) (interface{}, error) {
+ params, err := decoders.DecodeParams[getReplyParams](p)
+ if err != nil {
+ return nil, err
+ }
+ message, err := jsonclient.client.Channels.GetReply(context.TODO(), params.TeamRef, params.ChannelRef, params.MessageID, params.ReplyID)
+ if err != nil {
+ return nil, err
+ }
+ return message, nil
+}
+
+func (jsonclient *TeamsJSONClient) ListChannelMembers(p map[string]interface{}) (interface{}, error) {
+ params, err := decoders.DecodeParams[baseChannelParams](p)
+ if err != nil {
+ return nil, err
+ }
+ members, err := jsonclient.client.Channels.ListMembers(context.TODO(), params.TeamRef, params.ChannelRef)
+ if err != nil {
+ return nil, err
+ }
+ return members, nil
+}
+
+type addOrUpdateMemberToChannelParams struct {
+ TeamRef string `json:"teamRef"`
+ ChannelRef string `json:"channelRef"`
+ UserRef string `json:"userRef"`
+ IsOwner bool `json:"isOwner"`
+}
+
+func (jsonclient *TeamsJSONClient) AddMemberToChannel(p map[string]interface{}) (interface{}, error) {
+ params, err := decoders.DecodeParams[addOrUpdateMemberToChannelParams](p)
+ if err != nil {
+ return nil, err
+ }
+ member, err := jsonclient.client.Channels.AddMember(context.TODO(), params.TeamRef, params.ChannelRef, params.UserRef, params.IsOwner)
+ if err != nil {
+ return nil, err
+ }
+ return member, nil
+}
+
+func (jsonclient *TeamsJSONClient) UpdateMemberInChannel(p map[string]interface{}) (interface{}, error) {
+ params, err := decoders.DecodeParams[addOrUpdateMemberToChannelParams](p)
+ if err != nil {
+ return nil, err
+ }
+ member, err := jsonclient.client.Channels.UpdateMemberRole(context.TODO(), params.TeamRef, params.ChannelRef, params.UserRef, params.IsOwner)
+ if err != nil {
+ return nil, err
+ }
+ return member, nil
+}
+
+type removeMemberFromChannelParams struct {
+ TeamRef string `json:"teamRef"`
+ ChannelRef string `json:"channelRef"`
+ UserRef string `json:"userRef"`
+}
+
+func (jsonclient *TeamsJSONClient) RemoveMemberFromChannel(p map[string]interface{}) (interface{}, error) {
+ params, err := decoders.DecodeParams[removeMemberFromChannelParams](p)
+ if err != nil {
+ return nil, err
+ }
+ err = jsonclient.client.Channels.RemoveMember(context.TODO(), params.TeamRef, params.ChannelRef, params.UserRef)
+ if err != nil {
+ return nil, err
+ }
+ return "removed", nil
+}
From 52e89aca73d8feaabaff69ed4b2c253d9de3ff75 Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Sat, 27 Dec 2025 15:14:55 +0100
Subject: [PATCH 17/99] feat: added channels endpoints to map
---
go/internal/json-client/request-map.go | 62 +++++++++++++++++++++--
go/internal/json-client/requests-teams.go | 2 -
2 files changed, 58 insertions(+), 6 deletions(-)
diff --git a/go/internal/json-client/request-map.go b/go/internal/json-client/request-map.go
index 24d0b65..b0c9099 100644
--- a/go/internal/json-client/request-map.go
+++ b/go/internal/json-client/request-map.go
@@ -4,10 +4,7 @@ type HandlerFunc func(c *TeamsJSONClient, p map[string]interface{}) (interface{}
var Handlers = map[string]HandlerFunc{
- "listChannels": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
- return c.ListChannels(p)
- },
-
+ // TEAMS
"getTeam": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
return c.GetTeam(p)
},
@@ -43,4 +40,61 @@ var Handlers = map[string]HandlerFunc{
"restoreDeletedTeam": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
return c.RestoreDeletedTeam(p)
},
+
+ // CHANNELS
+ "listChannels": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ return c.ListChannels(p)
+ },
+
+ "getChannel": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ return c.GetChannel(p)
+ },
+
+ "createStandardChannel": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ return c.CreateStandardChannel(p)
+ },
+
+ "createPrivateChannel": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ return c.CreatePrivateChannel(p)
+ },
+
+ "deleteChannel": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ return c.DeleteChannel(p)
+ },
+
+ "listChannelMembers": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ return c.ListChannelMembers(p)
+ },
+
+ "addMemberToChannel": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ return c.AddMemberToChannel(p)
+ },
+
+ "removeMemberFromChannel": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ return c.RemoveMemberFromChannel(p)
+ },
+
+ "updateMemberRoleInChannel": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ return c.UpdateMemberInChannel(p)
+ },
+
+ "sendMessageToChannel": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ return c.SendMessageToChannel(p)
+ },
+
+ "listMessagesInChannel": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ return c.ListMessagesInChannel(p)
+ },
+
+ "getMessageInChannel": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ return c.GetMessageInChannel(p)
+ },
+
+ "listMessageRepliesInChannel": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ return c.ListMessageRepliesInChannel(p)
+ },
+
+ "getMessageReplyInChannel": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ return c.GetMessageReplyInChannel(p)
+ },
}
diff --git a/go/internal/json-client/requests-teams.go b/go/internal/json-client/requests-teams.go
index 52b77a3..0c8a3d4 100644
--- a/go/internal/json-client/requests-teams.go
+++ b/go/internal/json-client/requests-teams.go
@@ -2,8 +2,6 @@ package jsonclient
import (
"context"
- "fmt"
- "os"
"github.com/pzsp-teams/lib-python/internal/json-client/decoders"
)
From 76eaec011f7dbad9307ee93c9694fe881db475b0 Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Sat, 27 Dec 2025 15:16:26 +0100
Subject: [PATCH 18/99] chore: deleted unnecessary file
---
.../decoders/decode-messageBody.go | 33 -------------------
1 file changed, 33 deletions(-)
delete mode 100644 go/internal/json-client/decoders/decode-messageBody.go
diff --git a/go/internal/json-client/decoders/decode-messageBody.go b/go/internal/json-client/decoders/decode-messageBody.go
deleted file mode 100644
index a08afd6..0000000
--- a/go/internal/json-client/decoders/decode-messageBody.go
+++ /dev/null
@@ -1,33 +0,0 @@
-package decoders
-
-import (
- "github.com/mitchellh/mapstructure"
- "github.com/pzsp-teams/lib/models"
-)
-
-type messageBodyDTO struct {
- ContentType string `json:"contentType"`
- Content string `json:"content"`
-}
-
-func DecodeMessageBody(body *messageBodyDTO) (*models.MessageBody, error) {
- var result models.MessageBody
-
- config := &mapstructure.DecoderConfig{
- Metadata: nil,
- Result: &result,
- TagName: "json",
- WeaklyTypedInput: true,
- }
-
- decoder, err := mapstructure.NewDecoder(config)
- if err != nil {
- return nil, err
- }
-
- if err := decoder.Decode(body); err != nil {
- return nil, err
- }
-
- return &result, nil
-}
From 53de9fad330c95167fe8755bf8c52c27de307f19 Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Sat, 27 Dec 2025 15:25:48 +0100
Subject: [PATCH 19/99] feat: added models of member and message in python
---
python/teams_lib_pzsp2_z1/model/member.py | 10 ++++++++++
python/teams_lib_pzsp2_z1/model/message.py | 23 ++++++++++++++++++++++
2 files changed, 33 insertions(+)
create mode 100644 python/teams_lib_pzsp2_z1/model/member.py
create mode 100644 python/teams_lib_pzsp2_z1/model/message.py
diff --git a/python/teams_lib_pzsp2_z1/model/member.py b/python/teams_lib_pzsp2_z1/model/member.py
new file mode 100644
index 0000000..4d8b88e
--- /dev/null
+++ b/python/teams_lib_pzsp2_z1/model/member.py
@@ -0,0 +1,10 @@
+from dataclasses import dataclass
+
+
+@dataclass
+class Member:
+ ID: str
+ UserID: str
+ DisplayName: str
+ Role: str
+ Email: str
diff --git a/python/teams_lib_pzsp2_z1/model/message.py b/python/teams_lib_pzsp2_z1/model/message.py
new file mode 100644
index 0000000..970cf00
--- /dev/null
+++ b/python/teams_lib_pzsp2_z1/model/message.py
@@ -0,0 +1,23 @@
+from dataclasses import dataclass
+from enum import Enum
+from time import datetime
+
+
+class MessageContentType(Enum):
+ TEXT = "text"
+ HTML = "html"
+
+@dataclass
+class MessageFrom:
+ UserID: str
+ UserDisplayName: str
+
+@dataclass
+class Message:
+ ID: str
+ Content: str
+ ContentType: MessageContentType
+ CreateDateTime: datetime.datetime
+ From: MessageFrom
+ ReplyCount: int
+
From 9ce9d6311403f426583b9719e2cd8e605cda8f20 Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Sun, 28 Dec 2025 18:26:38 +0100
Subject: [PATCH 20/99] feat: Wrapped functions for channels and messages
---
python/teams_lib_pzsp2_z1/model/message.py | 22 +-
.../teams_lib_pzsp2_z1/services/channels.py | 219 ++++++++++++++++++
2 files changed, 238 insertions(+), 3 deletions(-)
diff --git a/python/teams_lib_pzsp2_z1/model/message.py b/python/teams_lib_pzsp2_z1/model/message.py
index 970cf00..2e62ae0 100644
--- a/python/teams_lib_pzsp2_z1/model/message.py
+++ b/python/teams_lib_pzsp2_z1/model/message.py
@@ -1,6 +1,6 @@
from dataclasses import dataclass
+from datetime import datetime
from enum import Enum
-from time import datetime
class MessageContentType(Enum):
@@ -10,14 +10,30 @@ class MessageContentType(Enum):
@dataclass
class MessageFrom:
UserID: str
- UserDisplayName: str
+ DisplayName: str
@dataclass
class Message:
ID: str
Content: str
ContentType: MessageContentType
- CreateDateTime: datetime.datetime
+ CreateDateTime: datetime
From: MessageFrom
ReplyCount: int
+@dataclass
+class MessageBody:
+ ContentType: MessageContentType
+ Content: str
+
+ def __dict__(self):
+ return {
+ "ContentType": self.ContentType.value,
+ "Content": self.Content,
+ }
+
+@dataclass
+class ListMessagesOptions:
+ Top: int | None = None
+ ExpandReplies: bool = False
+
diff --git a/python/teams_lib_pzsp2_z1/services/channels.py b/python/teams_lib_pzsp2_z1/services/channels.py
index 303e74e..62fb4b2 100644
--- a/python/teams_lib_pzsp2_z1/services/channels.py
+++ b/python/teams_lib_pzsp2_z1/services/channels.py
@@ -1,4 +1,10 @@
from teams_lib_pzsp2_z1.model.channel import Channel
+from teams_lib_pzsp2_z1.model.message import (
+ Message,
+ MessageBody,
+ MessageContentType,
+ MessageFrom,
+)
from teams_lib_pzsp2_z1.services.base_service import BaseService
@@ -19,3 +25,216 @@ def list_channels(self, teamRef: str) -> list[Channel]:
)
for channel in response
]
+
+ def get(self, teamRef: str, channelRef: str) -> Channel:
+ response = self.client.execute(
+ cmd_type="request",
+ method="getChannel",
+ params={
+ "teamRef": teamRef,
+ "channelRef": channelRef,
+ },
+ )
+
+ return Channel(
+ ID=response["ID"],
+ Name=response["Name"],
+ IsGeneral=(True if response["IsGeneral"] else False),
+ )
+
+ def create_standard(self, teamRef: str, display_name: str) -> Channel:
+ response = self.client.execute(
+ cmd_type="request",
+ method="createStandardChannel",
+ params={
+ "teamRef": teamRef,
+ "name": display_name,
+ },
+ )
+
+ return Channel(
+ ID=response["ID"],
+ Name=response["Name"],
+ IsGeneral=(True if response["IsGeneral"] else False),
+ )
+
+ def create_private(
+ self,
+ teamRef: str,
+ display_name: str,
+ member_refs: list[str],
+ owner_refs: list[str]
+ ) -> Channel:
+ response = self.client.execute(
+ cmd_type="request",
+ method="createPrivateChannel",
+ params={
+ "teamRef": teamRef,
+ "name": display_name,
+ "memberRefs": member_refs,
+ "ownerRefs": owner_refs,
+ },
+ )
+
+ return Channel(
+ ID=response["ID"],
+ Name=response["Name"],
+ IsGeneral=(True if response["IsGeneral"] else False),
+ )
+
+ def delete(self, teamRef: str, channelRef: str) -> bool:
+ response = self.client.execute(
+ cmd_type="request",
+ method="deleteChannel",
+ params={
+ "teamRef": teamRef,
+ "channelRef": channelRef,
+ },
+ )
+ return response == "deleted"
+
+ def send_message(self, teamRef: str, channelRef: str, body: MessageBody) -> Message:
+ response = self.client.execute(
+ cmd_type="request",
+ method="sendChannelMessage",
+ params={
+ "teamRef": teamRef,
+ "channelRef": channelRef,
+ "body": dict(body),
+ },
+ )
+
+ return Message(
+ ID=response["ID"],
+ Content=response["Content"],
+ ContentType=MessageContentType(response["ContentType"]),
+ CreateDateTime=response["CreateDateTime"],
+ From=MessageFrom(
+ UserID=response["From"]["UserID"],
+ DisplayName=response["From"]["DisplayName"],
+ ),
+ ReplyCount=response["ReplyCount"],
+ )
+
+ def list_messages_in_channel(
+ self,
+ teamRef: str,
+ channelRef: str,
+ top: int | None = 10,
+ expand_replies: bool = False,
+ ):
+ response = self.client.execute(
+ cmd_type="request",
+ method="listChannelMessages",
+ params={
+ "teamRef": teamRef,
+ "channelRef": channelRef,
+ "options": {
+ "top": top,
+ "expandReplies": expand_replies,
+ },
+ },
+ )
+
+ return [
+ Message(
+ ID=message["ID"],
+ Content=message["Content"],
+ ContentType=MessageContentType(message["ContentType"]),
+ CreateDateTime=message["CreateDateTime"],
+ From=MessageFrom(
+ UserID=message["From"]["UserID"],
+ DisplayName=message["From"]["DisplayName"],
+ ),
+ ReplyCount=message["ReplyCount"],
+ )
+ for message in response
+ ]
+
+ def get_message(self, teamRef: str, channelRef: str, messageID: str) -> Message:
+ response = self.client.execute(
+ cmd_type="request",
+ method="getChannelMessage",
+ params={
+ "teamRef": teamRef,
+ "channelRef": channelRef,
+ "messageID": messageID,
+ },
+ )
+
+ return Message(
+ ID=response["ID"],
+ Content=response["Content"],
+ ContentType=MessageContentType(response["ContentType"]),
+ CreateDateTime=response["CreateDateTime"],
+ From=MessageFrom(
+ UserID=response["From"]["UserID"],
+ DisplayName=response["From"]["DisplayName"],
+ ),
+ ReplyCount=response["ReplyCount"],
+ )
+
+ def list_message_replies(
+ self,
+ teamRef: str,
+ channelRef: str,
+ messageID: str,
+ top: int | None = 10,
+ ):
+ response = self.client.execute(
+ cmd_type="request",
+ method="listChannelMessageReplies",
+ params={
+ "teamRef": teamRef,
+ "channelRef": channelRef,
+ "messageID": messageID,
+ "top": top,
+ },
+ )
+
+ return [
+ Message(
+ ID=message["ID"],
+ Content=message["Content"],
+ ContentType=MessageContentType(message["ContentType"]),
+ CreateDateTime=message["CreateDateTime"],
+ From=MessageFrom(
+ UserID=message["From"]["UserID"],
+ DisplayName=message["From"]["DisplayName"],
+ ),
+ ReplyCount=message["ReplyCount"],
+ )
+ for message in response
+ ]
+
+ def get_message_reply(
+ self,
+ teamRef: str,
+ channelRef: str,
+ messageID: str,
+ replyID: str,
+ ) -> Message:
+ response = self.client.execute(
+ cmd_type="request",
+ method="getChannelMessageReply",
+ params={
+ "teamRef": teamRef,
+ "channelRef": channelRef,
+ "messageID": messageID,
+ "replyID": replyID,
+ },
+ )
+
+ return Message(
+ ID=response["ID"],
+ Content=response["Content"],
+ ContentType=MessageContentType(response["ContentType"]),
+ CreateDateTime=response["CreateDateTime"],
+ From=MessageFrom(
+ UserID=response["From"]["UserID"],
+ DisplayName=response["From"]["DisplayName"],
+ ),
+ ReplyCount=response["ReplyCount"],
+ )
+
+
From 83bc2b9a4219730984fab9a72ee17924b7c5191b Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Sun, 28 Dec 2025 18:32:00 +0100
Subject: [PATCH 21/99] feat: wrapped members management functions
---
.../teams_lib_pzsp2_z1/services/channels.py | 54 ++++++++++++++++++-
1 file changed, 53 insertions(+), 1 deletion(-)
diff --git a/python/teams_lib_pzsp2_z1/services/channels.py b/python/teams_lib_pzsp2_z1/services/channels.py
index 62fb4b2..a5d2302 100644
--- a/python/teams_lib_pzsp2_z1/services/channels.py
+++ b/python/teams_lib_pzsp2_z1/services/channels.py
@@ -1,4 +1,5 @@
from teams_lib_pzsp2_z1.model.channel import Channel
+from teams_lib_pzsp2_z1.model.member import Member
from teams_lib_pzsp2_z1.model.message import (
Message,
MessageBody,
@@ -237,4 +238,55 @@ def get_message_reply(
ReplyCount=response["ReplyCount"],
)
-
+ def list_members(self, teamRef: str, channelRef: str) -> list[Member]:
+ response = self.client.execute(
+ cmd_type="request",
+ method="listChannelMembers",
+ params={
+ "teamRef": teamRef,
+ "channelRef": channelRef,
+ },
+ )
+ return [Member(**member) for member in response]
+
+ def add_member(
+ self, teamRef: str, channelRef: str, userRef: str, isOwner: bool
+ ) -> Member:
+ response = self.client.execute(
+ cmd_type="request",
+ method="addChannelMember",
+ params={
+ "teamRef": teamRef,
+ "channelRef": channelRef,
+ "userRef": userRef,
+ "isOwner": isOwner,
+ },
+ )
+ return Member(**response)
+
+ def update_member_role(
+ self, teamRef: str, channelRef: str, userRef: str, isOwner: bool
+ ) -> Member:
+ response = self.client.execute(
+ cmd_type="request",
+ method="updateChannelMemberRole",
+ params={
+ "teamRef": teamRef,
+ "channelRef": channelRef,
+ "userRef": userRef,
+ "isOwner": isOwner,
+ },
+ )
+ return Member(**response)
+
+ def remove_member(self, teamRef: str, channelRef: str, userRef: str) -> bool:
+ response = self.client.execute(
+ cmd_type="request",
+ method="removeChannelMember",
+ params={
+ "teamRef": teamRef,
+ "channelRef": channelRef,
+ "userRef": userRef,
+ },
+ )
+ return response == "removed"
From 2844dec05f4fc224bb0bab929aad43b360ec5638 Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Sun, 28 Dec 2025 18:47:23 +0100
Subject: [PATCH 22/99] feat: Added integration test for get_channel
---
python/tests/fake_server/data.py | 11 +++++++++++
python/tests/fake_server/setup.py | 13 +++++++++++++
python/tests/test_channels.py | 21 +++++++++++++++++++++
3 files changed, 45 insertions(+)
diff --git a/python/tests/fake_server/data.py b/python/tests/fake_server/data.py
index 3334bba..e79d8e8 100644
--- a/python/tests/fake_server/data.py
+++ b/python/tests/fake_server/data.py
@@ -210,6 +210,17 @@ def get_restoreTeam_response(self, team_id: str) -> dict:
"mailNickname": self.newGroupMailNickname
}
+ def get_channel_response(self, team_id: str, channel_id: str) -> dict | None:
+ channel = next((c for c in self.channels.get(team_id, []) if c.ID == channel_id), None)
+ if not channel:
+ return None
+
+ return {
+ "id": channel.ID,
+ "displayName": channel.Name,
+ "isGeneral": channel.IsGeneral,
+ }
+
diff --git a/python/tests/fake_server/setup.py b/python/tests/fake_server/setup.py
index 3a996f6..e599d1a 100644
--- a/python/tests/fake_server/setup.py
+++ b/python/tests/fake_server/setup.py
@@ -44,6 +44,19 @@ def make_log_response(request, data_dict: dict, context_msg: str = "") -> Respon
req, data.get_myJoinedTeams_response(), "List Joined Teams"
))
+ # GET /teams/{team_id}/channels/{channel_id}
+ httpserver.expect_request(
+ re.compile(r"^/v1.0/teams/([^/]+)/channels/([^/]+)"),
+ method="GET"
+ ).respond_with_handler(lambda req: make_log_response(
+ req,
+ data.get_channel_response(
+ re.search(r"/teams/([^/]+)/channels/([^/]+)", req.path).group(1),
+ re.search(r"/teams/([^/]+)/channels/([^/]+)", req.path).group(2)
+ ),
+ "Get Channel Details"
+ ))
+
# GET /teams/{id}/channels
httpserver.expect_request(
re.compile(r"^/v1.0/teams/([^/]+)/channels"),
diff --git a/python/tests/test_channels.py b/python/tests/test_channels.py
index 121f315..f700c7c 100644
--- a/python/tests/test_channels.py
+++ b/python/tests/test_channels.py
@@ -29,3 +29,24 @@ def test_list_channels_integration(httpserver):
finally:
client.close()
+
+
+def test_get_channel_integration(httpserver):
+
+ data = setup_fake_server(httpserver)
+
+ client = TeamsClient(auto_init=False)
+ try:
+ init_fake_client(client, httpserver.url_for(""))
+
+ channel = client.channels.get(
+ teamRef=data.teams[0].DisplayName,
+ channelRef=data.channels[data.teams[0].ID][1].Name,
+ )
+
+ assert channel.Name == data.channels[data.teams[0].ID][1].Name
+ assert channel.ID == data.channels[data.teams[0].ID][1].ID
+ assert channel.IsGeneral == data.channels[data.teams[0].ID][1].IsGeneral
+
+ finally:
+ client.close()
From ad8335288287693e7488d029e9c4fb2ed3c1fa95 Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Sun, 28 Dec 2025 19:00:49 +0100
Subject: [PATCH 23/99] feat: added test for creating standard channel
---
python/tests/fake_server/data.py | 26 ++++++++++++++++++++++++++
python/tests/fake_server/setup.py | 13 +++++++++++++
python/tests/test_channels.py | 21 +++++++++++++++++++++
3 files changed, 60 insertions(+)
diff --git a/python/tests/fake_server/data.py b/python/tests/fake_server/data.py
index e79d8e8..946a77c 100644
--- a/python/tests/fake_server/data.py
+++ b/python/tests/fake_server/data.py
@@ -57,6 +57,8 @@ def __init__(self) -> FakeServerData:
self.newGroupMailNickname = "new-team-nickname"
self.newTeamVisibility = "private"
self.potentialTeams = []
+ self.newChannelName = "New Channel"
+ self.newChannelID = "19:newchannelid@thread.tacv2"
def get_myJoinedTeams_response(self) -> dict:
@@ -221,6 +223,30 @@ def get_channel_response(self, team_id: str, channel_id: str) -> dict | None:
"isGeneral": channel.IsGeneral,
}
+ def get_create_channel_response(self, team_id: str, request_json: dict) -> dict:
+ display_name = request_json.get("displayName")
+ description = request_json.get("description", display_name)
+
+ new_channel = Channel(
+ ID=self.newChannelID,
+ Name=display_name,
+ IsGeneral=False,
+ )
+
+ if team_id not in self.channels:
+ self.channels[team_id] = []
+
+ self.channels[team_id].append(new_channel)
+
+ return {
+ "id": new_channel.ID,
+ "displayName": new_channel.Name,
+ "description": description,
+ "isGeneral": new_channel.IsGeneral,
+ "membershipType": "standard"
+ }
+
+
diff --git a/python/tests/fake_server/setup.py b/python/tests/fake_server/setup.py
index e599d1a..0cb0f29 100644
--- a/python/tests/fake_server/setup.py
+++ b/python/tests/fake_server/setup.py
@@ -160,6 +160,19 @@ def make_log_response(request, data_dict: dict, context_msg: str = "") -> Respon
"Restore Deleted Team (POST)"
))
+ # POST /teams/{id}/channels
+ httpserver.expect_request(
+ re.compile(r"^/v1.0/teams/([^/]+)/channels"),
+ method="POST"
+ ).respond_with_handler(lambda req: make_log_response(
+ req,
+ data.get_create_channel_response(
+ re.search(r"/teams/([^/]+)/channels", req.path).group(1),
+ req.json
+ ),
+ "Create Channel (POST)"
+ ))
+
# Fallback for unmatched routes
httpserver.expect_request(re.compile(".*")).respond_with_handler(
lambda req: Response(
diff --git a/python/tests/test_channels.py b/python/tests/test_channels.py
index f700c7c..e9f46f2 100644
--- a/python/tests/test_channels.py
+++ b/python/tests/test_channels.py
@@ -50,3 +50,24 @@ def test_get_channel_integration(httpserver):
finally:
client.close()
+
+
+def test_create_standard_channel_integration(httpserver):
+
+ data = setup_fake_server(httpserver)
+
+ client = TeamsClient(auto_init=False)
+ try:
+ init_fake_client(client, httpserver.url_for(""))
+
+ channel = client.channels.create_standard(
+ teamRef=data.teams[0].DisplayName,
+ display_name=data.newChannelName,
+ )
+
+ assert channel.Name == data.newChannelName
+ assert channel.ID == data.newChannelID
+ assert channel.IsGeneral is False
+
+ finally:
+ client.close()
\ No newline at end of file
From 31b2e3dc36521eb42a002ff959d9763f0b31833a Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Sun, 28 Dec 2025 19:36:02 +0100
Subject: [PATCH 24/99] feat: added tests to getting messages
---
python/teams_lib_pzsp2_z1/model/message.py | 7 +-
.../teams_lib_pzsp2_z1/services/channels.py | 64 +++++------
python/tests/fake_server/data.py | 104 +++++++++++++++++
python/tests/fake_server/setup.py | 42 +++++++
python/tests/test_channels.py | 107 +++++++++++++++++-
5 files changed, 289 insertions(+), 35 deletions(-)
diff --git a/python/teams_lib_pzsp2_z1/model/message.py b/python/teams_lib_pzsp2_z1/model/message.py
index 2e62ae0..6bee67d 100644
--- a/python/teams_lib_pzsp2_z1/model/message.py
+++ b/python/teams_lib_pzsp2_z1/model/message.py
@@ -7,20 +7,23 @@ class MessageContentType(Enum):
TEXT = "text"
HTML = "html"
+
@dataclass
class MessageFrom:
UserID: str
DisplayName: str
+
@dataclass
class Message:
ID: str
Content: str
ContentType: MessageContentType
- CreateDateTime: datetime
+ CreatedDateTime: datetime
From: MessageFrom
ReplyCount: int
+
@dataclass
class MessageBody:
ContentType: MessageContentType
@@ -32,8 +35,8 @@ def __dict__(self):
"Content": self.Content,
}
+
@dataclass
class ListMessagesOptions:
Top: int | None = None
ExpandReplies: bool = False
-
diff --git a/python/teams_lib_pzsp2_z1/services/channels.py b/python/teams_lib_pzsp2_z1/services/channels.py
index a5d2302..11a22ce 100644
--- a/python/teams_lib_pzsp2_z1/services/channels.py
+++ b/python/teams_lib_pzsp2_z1/services/channels.py
@@ -60,11 +60,11 @@ def create_standard(self, teamRef: str, display_name: str) -> Channel:
)
def create_private(
- self,
- teamRef: str,
- display_name: str,
- member_refs: list[str],
- owner_refs: list[str]
+ self,
+ teamRef: str,
+ display_name: str,
+ member_refs: list[str],
+ owner_refs: list[str],
) -> Channel:
response = self.client.execute(
cmd_type="request",
@@ -109,7 +109,7 @@ def send_message(self, teamRef: str, channelRef: str, body: MessageBody) -> Mess
ID=response["ID"],
Content=response["Content"],
ContentType=MessageContentType(response["ContentType"]),
- CreateDateTime=response["CreateDateTime"],
+ CreatedDateTime=response["CreatedDateTime"],
From=MessageFrom(
UserID=response["From"]["UserID"],
DisplayName=response["From"]["DisplayName"],
@@ -117,16 +117,16 @@ def send_message(self, teamRef: str, channelRef: str, body: MessageBody) -> Mess
ReplyCount=response["ReplyCount"],
)
- def list_messages_in_channel(
- self,
- teamRef: str,
- channelRef: str,
- top: int | None = 10,
- expand_replies: bool = False,
+ def list_messages(
+ self,
+ teamRef: str,
+ channelRef: str,
+ top: int | None = 10,
+ expand_replies: bool = False,
):
response = self.client.execute(
cmd_type="request",
- method="listChannelMessages",
+ method="listMessagesInChannel",
params={
"teamRef": teamRef,
"channelRef": channelRef,
@@ -142,7 +142,7 @@ def list_messages_in_channel(
ID=message["ID"],
Content=message["Content"],
ContentType=MessageContentType(message["ContentType"]),
- CreateDateTime=message["CreateDateTime"],
+ CreatedDateTime=message["CreatedDateTime"],
From=MessageFrom(
UserID=message["From"]["UserID"],
DisplayName=message["From"]["DisplayName"],
@@ -155,7 +155,7 @@ def list_messages_in_channel(
def get_message(self, teamRef: str, channelRef: str, messageID: str) -> Message:
response = self.client.execute(
cmd_type="request",
- method="getChannelMessage",
+ method="getMessageInChannel",
params={
"teamRef": teamRef,
"channelRef": channelRef,
@@ -167,7 +167,7 @@ def get_message(self, teamRef: str, channelRef: str, messageID: str) -> Message:
ID=response["ID"],
Content=response["Content"],
ContentType=MessageContentType(response["ContentType"]),
- CreateDateTime=response["CreateDateTime"],
+ CreatedDateTime=response["CreatedDateTime"],
From=MessageFrom(
UserID=response["From"]["UserID"],
DisplayName=response["From"]["DisplayName"],
@@ -176,15 +176,15 @@ def get_message(self, teamRef: str, channelRef: str, messageID: str) -> Message:
)
def list_message_replies(
- self,
- teamRef: str,
- channelRef: str,
- messageID: str,
- top: int | None = 10,
+ self,
+ teamRef: str,
+ channelRef: str,
+ messageID: str,
+ top: int | None = 10,
):
response = self.client.execute(
cmd_type="request",
- method="listChannelMessageReplies",
+ method="listMessageRepliesInChannel",
params={
"teamRef": teamRef,
"channelRef": channelRef,
@@ -198,7 +198,7 @@ def list_message_replies(
ID=message["ID"],
Content=message["Content"],
ContentType=MessageContentType(message["ContentType"]),
- CreateDateTime=message["CreateDateTime"],
+ CreatedDateTime=message["CreatedDateTime"],
From=MessageFrom(
UserID=message["From"]["UserID"],
DisplayName=message["From"]["DisplayName"],
@@ -209,15 +209,15 @@ def list_message_replies(
]
def get_message_reply(
- self,
- teamRef: str,
- channelRef: str,
- messageID: str,
- replyID: str,
+ self,
+ teamRef: str,
+ channelRef: str,
+ messageID: str,
+ replyID: str,
) -> Message:
response = self.client.execute(
cmd_type="request",
- method="getChannelMessageReply",
+ method="getMessageReplyInChannel",
params={
"teamRef": teamRef,
"channelRef": channelRef,
@@ -230,7 +230,7 @@ def get_message_reply(
ID=response["ID"],
Content=response["Content"],
ContentType=MessageContentType(response["ContentType"]),
- CreateDateTime=response["CreateDateTime"],
+ CreatedDateTime=response["CreatedDateTime"],
From=MessageFrom(
UserID=response["From"]["UserID"],
DisplayName=response["From"]["DisplayName"],
@@ -250,7 +250,7 @@ def list_members(self, teamRef: str, channelRef: str) -> list[Member]:
return [Member(**member) for member in response]
def add_member(
- self, teamRef: str, channelRef: str, userRef: str, isOwner: bool
+ self, teamRef: str, channelRef: str, userRef: str, isOwner: bool
) -> Member:
response = self.client.execute(
cmd_type="request",
@@ -265,7 +265,7 @@ def add_member(
return Member(**response)
def update_member_role(
- self, teamRef: str, channelRef: str, userRef: str, isOwner: bool
+ self, teamRef: str, channelRef: str, userRef: str, isOwner: bool
) -> Member:
response = self.client.execute(
cmd_type="request",
diff --git a/python/tests/fake_server/data.py b/python/tests/fake_server/data.py
index 946a77c..3d3f990 100644
--- a/python/tests/fake_server/data.py
+++ b/python/tests/fake_server/data.py
@@ -1,6 +1,7 @@
from dataclasses import dataclass
from teams_lib_pzsp2_z1.model.team import Team
from teams_lib_pzsp2_z1.model.channel import Channel
+from teams_lib_pzsp2_z1.model.message import Message, MessageFrom
from dataclasses import field
@dataclass
@@ -52,6 +53,58 @@ def __init__(self) -> FakeServerData:
),
],
}
+ self.messages = {
+ "19:123123@thread.tacv2": [
+ Message(
+ ID="msg-001",
+ Content="Hello, team!",
+ ContentType="text",
+ From=MessageFrom(
+ UserID="user-123-abc",
+ DisplayName="Alice"
+ ),
+ CreatedDateTime="2024-01-01T10:00:00Z",
+ ReplyCount=0,
+ ),
+ Message(
+ ID="msg-002",
+ Content="Don't forget the meeting at 3 PM.",
+ ContentType="text",
+ From=MessageFrom(
+ UserID="user-456-def",
+ DisplayName="Bob"
+ ),
+ CreatedDateTime="2024-01-01T11:00:00Z",
+ ReplyCount=2,
+ ),
+ ],
+ }
+ self.replies = {
+ "msg-002": [
+ Message(
+ ID="msg-002-reply-001",
+ Content="Thanks for the reminder!",
+ ContentType="text",
+ From=MessageFrom(
+ UserID="user-789-ghi",
+ DisplayName="Charlie"
+ ),
+ CreatedDateTime="2024-01-01T12:00:00Z",
+ ReplyCount=0,
+ ),
+ Message(
+ ID="msg-002-reply-002",
+ Content="I'll be there.",
+ ContentType="text",
+ From=MessageFrom(
+ UserID="user-123-abc",
+ DisplayName="Alice"
+ ),
+ CreatedDateTime="2024-01-01T12:30:00Z",
+ ReplyCount=0,
+ ),
+ ],
+ }
self.newGroupID = "group-789-ghi"
self.newTeamName = "New Team"
self.newGroupMailNickname = "new-team-nickname"
@@ -246,6 +299,57 @@ def get_create_channel_response(self, team_id: str, request_json: dict) -> dict:
"membershipType": "standard"
}
+ def get_delete_channel_response(self, team_id: str, channel_id: str) -> dict:
+ channel = next((c for c in self.channels.get(team_id, []) if c.ID == channel_id), None)
+ if not channel:
+ return {"success": False}
+
+ self.channels[team_id].remove(channel)
+ return {"success": True}
+
+ def get_list_messages_response(self, team_id: str, channel_id: str) -> dict:
+ return {
+ "value": [
+ {
+ "id": message.ID,
+ "body": {
+ "content": message.Content,
+ "contentType": message.ContentType,
+ },
+ "from": {
+ "user": {
+ "id": message.From.UserID,
+ "displayName": message.From.DisplayName,
+ }
+ },
+ "createdDateTime": message.CreatedDateTime,
+ "replies": [{"id": f"dummy-reply-{i}"} for i in range(message.ReplyCount)]
+ }
+ for message in self.messages.get(channel_id, [])
+ ],
+ }
+
+ def get_message_response(self, team_id: str, channel_id: str, message_id: str) -> dict | None:
+ message = next((m for m in self.messages.get(channel_id, []) if m.ID == message_id), None)
+ if not message:
+ return None
+
+ return {
+ "id": message.ID,
+ "body": {
+ "content": message.Content,
+ "contentType": message.ContentType,
+ },
+ "from": {
+ "user": {
+ "id": message.From.UserID,
+ "displayName": message.From.DisplayName,
+ }
+ },
+ "createdDateTime": message.CreatedDateTime,
+ "replies": [{"id": f"dummy-reply-{i}"} for i in range(message.ReplyCount)]
+ }
+
diff --git a/python/tests/fake_server/setup.py b/python/tests/fake_server/setup.py
index 0cb0f29..fa218ce 100644
--- a/python/tests/fake_server/setup.py
+++ b/python/tests/fake_server/setup.py
@@ -44,6 +44,33 @@ def make_log_response(request, data_dict: dict, context_msg: str = "") -> Respon
req, data.get_myJoinedTeams_response(), "List Joined Teams"
))
+ # GET /teams/{team_id}/channels/{channel_id}/messages/{message_id}
+ httpserver.expect_request(
+ re.compile(r"^/v1.0/teams/([^/]+)/channels/([^/]+)/messages/([^/]+)"),
+ method="GET"
+ ).respond_with_handler(lambda req: make_log_response(
+ req,
+ data.get_message_response(
+ re.search(r"/teams/([^/]+)/channels/([^/]+)/messages/([^/]+)", req.path).group(1),
+ re.search(r"/teams/([^/]+)/channels/([^/]+)/messages/([^/]+)", req.path).group(2),
+ re.search(r"/teams/([^/]+)/channels/([^/]+)/messages/([^/]+)", req.path).group(3)
+ ),
+ "Get Channel Message"
+ ))
+
+ # GET /teams/{team_id}/channels/{channel_id}/messages
+ httpserver.expect_request(
+ re.compile(r"^/v1.0/teams/([^/]+)/channels/([^/]+)/messages"),
+ method="GET"
+ ).respond_with_handler(lambda req: make_log_response(
+ req,
+ data.get_list_messages_response(
+ re.search(r"/teams/([^/]+)/channels/([^/]+)/messages", req.path).group(1),
+ re.search(r"/teams/([^/]+)/channels/([^/]+)/messages", req.path).group(2)
+ ),
+ "List Channel Messages"
+ ))
+
# GET /teams/{team_id}/channels/{channel_id}
httpserver.expect_request(
re.compile(r"^/v1.0/teams/([^/]+)/channels/([^/]+)"),
@@ -173,6 +200,21 @@ def make_log_response(request, data_dict: dict, context_msg: str = "") -> Respon
"Create Channel (POST)"
))
+ # DELETE /teams/{team_id}/channels/{channel_id}
+ httpserver.expect_request(
+ re.compile(r"^/v1.0/teams/([^/]+)/channels/([^/]+)"),
+ method="DELETE"
+ ).respond_with_handler(lambda req: make_log_response(
+ req,
+ data.get_delete_channel_response(
+ re.search(r"/teams/([^/]+)/channels/([^/]+)", req.path).group(1),
+ re.search(r"/teams/([^/]+)/channels/([^/]+)", req.path).group(2)
+ ),
+ "Delete Channel (DELETE)"
+ ))
+
+
+
# Fallback for unmatched routes
httpserver.expect_request(re.compile(".*")).respond_with_handler(
lambda req: Response(
diff --git a/python/tests/test_channels.py b/python/tests/test_channels.py
index e9f46f2..06524dc 100644
--- a/python/tests/test_channels.py
+++ b/python/tests/test_channels.py
@@ -1,6 +1,7 @@
from teams_lib_pzsp2_z1.client import TeamsClient
from tests.init_fake_client import init_fake_client
from tests.fake_server.setup import setup_fake_server
+from teams_lib_pzsp2_z1.model.message import MessageContentType
def test_list_channels_integration(httpserver):
@@ -70,4 +71,108 @@ def test_create_standard_channel_integration(httpserver):
assert channel.IsGeneral is False
finally:
- client.close()
\ No newline at end of file
+ client.close()
+
+
+def test_create_private_channel_integration(httpserver):
+
+ data = setup_fake_server(httpserver)
+
+ client = TeamsClient(auto_init=False)
+ try:
+ init_fake_client(client, httpserver.url_for(""))
+
+ channel = client.channels.create_private(
+ teamRef=data.teams[0].DisplayName,
+ display_name=data.newChannelName,
+ member_refs=[],
+ owner_refs=[],
+ )
+
+ assert channel.Name == data.newChannelName
+ assert channel.ID == data.newChannelID
+ assert channel.IsGeneral is False
+
+ finally:
+ client.close()
+
+
+def test_delete_channel_integration(httpserver):
+
+ data = setup_fake_server(httpserver)
+
+ client = TeamsClient(auto_init=False)
+ try:
+ init_fake_client(client, httpserver.url_for(""))
+
+ success = client.channels.delete(
+ teamRef=data.teams[0].DisplayName,
+ channelRef=data.channels[data.teams[0].ID][1].Name,
+ )
+
+ assert success is True
+
+ finally:
+ client.close()
+
+
+def test_list_messages_integration(httpserver):
+
+ data = setup_fake_server(httpserver)
+
+ client = TeamsClient(auto_init=False)
+ try:
+ init_fake_client(client, httpserver.url_for(""))
+
+ messages = client.channels.list_messages(
+ teamRef=data.teams[0].DisplayName,
+ channelRef=data.channels[data.teams[0].ID][0].Name,
+ )
+
+ assert len(messages) == len(data.messages[data.channels[data.teams[0].ID][0].ID])
+ assert messages[0].ID == data.messages[data.channels[data.teams[0].ID][0].ID][0].ID
+ assert messages[0].Content == data.messages[data.channels[data.teams[0].ID][0].ID][0].Content
+ assert messages[0].ContentType == MessageContentType(data.messages[data.channels[data.teams[0].ID][0].ID][0].ContentType)
+ assert messages[0].From.UserID == data.messages[data.channels[data.teams[0].ID][0].ID][0].From.UserID
+ assert messages[0].From.DisplayName == data.messages[data.channels[data.teams[0].ID][0].ID][0].From.DisplayName
+ assert messages[0].ReplyCount == data.messages[data.channels[data.teams[0].ID][0].ID][0].ReplyCount
+ assert messages[0].CreatedDateTime == data.messages[data.channels[data.teams[0].ID][0].ID][0].CreatedDateTime
+
+ assert messages[1].ID == data.messages[data.channels[data.teams[0].ID][0].ID][1].ID
+ assert messages[1].Content == data.messages[data.channels[data.teams[0].ID][0].ID][1].Content
+ assert messages[1].ContentType == MessageContentType(data.messages[data.channels[data.teams[0].ID][0].ID][1].ContentType)
+ assert messages[1].From.UserID == data.messages[data.channels[data.teams[0].ID][0].ID][1].From.UserID
+ assert messages[1].From.DisplayName == data.messages[data.channels[data.teams[0].ID][0].ID][1].From.DisplayName
+ assert messages[1].ReplyCount == data.messages[data.channels[data.teams[0].ID][0].ID][1].ReplyCount
+ assert messages[1].CreatedDateTime == data.messages[data.channels[data.teams[0].ID][0].ID][1].CreatedDateTime
+
+ finally:
+ client.close()
+
+
+def test_get_message_integration(httpserver):
+
+ data = setup_fake_server(httpserver)
+
+ client = TeamsClient(auto_init=False)
+ try:
+ init_fake_client(client, httpserver.url_for(""))
+
+ message = client.channels.get_message(
+ teamRef=data.teams[0].DisplayName,
+ channelRef=data.channels[data.teams[0].ID][0].Name,
+ messageID=data.messages[data.channels[data.teams[0].ID][0].ID][0].ID,
+ )
+
+ assert message.ID == data.messages[data.channels[data.teams[0].ID][0].ID][0].ID
+ assert message.Content == data.messages[data.channels[data.teams[0].ID][0].ID][0].Content
+ assert message.ContentType == MessageContentType(data.messages[data.channels[data.teams[0].ID][0].ID][0].ContentType)
+ assert message.From.UserID == data.messages[data.channels[data.teams[0].ID][0].ID][0].From.UserID
+ assert message.From.DisplayName == data.messages[data.channels[data.teams[0].ID][0].ID][0].From.DisplayName
+ assert message.ReplyCount == data.messages[data.channels[data.teams[0].ID][0].ID][0].ReplyCount
+ assert message.CreatedDateTime == data.messages[data.channels[data.teams[0].ID][0].ID][0].CreatedDateTime
+
+ finally:
+ client.close()
+
+
From 221ad3881c464ffbd62bb397486132542547dbb6 Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Mon, 29 Dec 2025 10:55:43 +0100
Subject: [PATCH 25/99] feat: implemented tests for replies
---
python/tests/fake_server/data.py | 41 ++++++++++++++++++++
python/tests/fake_server/setup.py | 30 +++++++++++++++
python/tests/test_channels.py | 62 +++++++++++++++++++++++++++++++
3 files changed, 133 insertions(+)
diff --git a/python/tests/fake_server/data.py b/python/tests/fake_server/data.py
index 3d3f990..872c5a5 100644
--- a/python/tests/fake_server/data.py
+++ b/python/tests/fake_server/data.py
@@ -350,6 +350,47 @@ def get_message_response(self, team_id: str, channel_id: str, message_id: str) -
"replies": [{"id": f"dummy-reply-{i}"} for i in range(message.ReplyCount)]
}
+ def get_list_replies_response(self, team_id: str, channel_id: str, message_id: str) -> dict:
+ return {
+ "value": [
+ {
+ "id": reply.ID,
+ "body": {
+ "content": reply.Content,
+ "contentType": reply.ContentType,
+ },
+ "from": {
+ "user": {
+ "id": reply.From.UserID,
+ "displayName": reply.From.DisplayName,
+ }
+ },
+ "createdDateTime": reply.CreatedDateTime,
+ }
+ for reply in self.replies.get(message_id, [])
+ ],
+ }
+
+ def get_reply_response(self, team_id: str, channel_id: str, message_id: str, reply_id: str) -> dict | None:
+ reply = next((r for r in self.replies.get(message_id, []) if r.ID == reply_id), None)
+ if not reply:
+ return None
+
+ return {
+ "id": reply.ID,
+ "body": {
+ "content": reply.Content,
+ "contentType": reply.ContentType,
+ },
+ "from": {
+ "user": {
+ "id": reply.From.UserID,
+ "displayName": reply.From.DisplayName,
+ }
+ },
+ "createdDateTime": reply.CreatedDateTime,
+ }
+
diff --git a/python/tests/fake_server/setup.py b/python/tests/fake_server/setup.py
index fa218ce..2c25da0 100644
--- a/python/tests/fake_server/setup.py
+++ b/python/tests/fake_server/setup.py
@@ -44,6 +44,36 @@ def make_log_response(request, data_dict: dict, context_msg: str = "") -> Respon
req, data.get_myJoinedTeams_response(), "List Joined Teams"
))
+
+ # GET /teams/{team_id}/channels/{channel_id}/messages/{message_id}/replies/{reply_id}
+ httpserver.expect_request(
+ re.compile(r"^/v1.0/teams/([^/]+)/channels/([^/]+)/messages/([^/]+)/replies/([^/]+)"),
+ method="GET"
+ ).respond_with_handler(lambda req: make_log_response(
+ req,
+ data.get_reply_response(
+ re.search(r"/teams/([^/]+)/channels/([^/]+)/messages/([^/]+)/replies/([^/]+)", req.path).group(1),
+ re.search(r"/teams/([^/]+)/channels/([^/]+)/messages/([^/]+)/replies/([^/]+)", req.path).group(2),
+ re.search(r"/teams/([^/]+)/channels/([^/]+)/messages/([^/]+)/replies/([^/]+)", req.path).group(3),
+ re.search(r"/teams/([^/]+)/channels/([^/]+)/messages/([^/]+)/replies/([^/]+)", req.path).group(4)
+ ),
+ "Get Message Reply"
+ ))
+
+ # GET /teams/{team_id}/channels/{channel_id}/messages/{message_id}/replies
+ httpserver.expect_request(
+ re.compile(r"^/v1.0/teams/([^/]+)/channels/([^/]+)/messages/([^/]+)/replies"),
+ method="GET"
+ ).respond_with_handler(lambda req: make_log_response(
+ req,
+ data.get_list_replies_response(
+ re.search(r"/teams/([^/]+)/channels/([^/]+)/messages/([^/]+)/replies", req.path).group(1),
+ re.search(r"/teams/([^/]+)/channels/([^/]+)/messages/([^/]+)/replies", req.path).group(2),
+ re.search(r"/teams/([^/]+)/channels/([^/]+)/messages/([^/]+)/replies", req.path).group(3)
+ ),
+ "List Message Replies"
+ ))
+
# GET /teams/{team_id}/channels/{channel_id}/messages/{message_id}
httpserver.expect_request(
re.compile(r"^/v1.0/teams/([^/]+)/channels/([^/]+)/messages/([^/]+)"),
diff --git a/python/tests/test_channels.py b/python/tests/test_channels.py
index 06524dc..dcf8196 100644
--- a/python/tests/test_channels.py
+++ b/python/tests/test_channels.py
@@ -176,3 +176,65 @@ def test_get_message_integration(httpserver):
client.close()
+def test_list_replies_integration(httpserver):
+
+ data = setup_fake_server(httpserver)
+
+ client = TeamsClient(auto_init=False)
+ try:
+ init_fake_client(client, httpserver.url_for(""))
+
+ replies = client.channels.list_message_replies(
+ teamRef=data.teams[0].DisplayName,
+ channelRef=data.channels[data.teams[0].ID][0].Name,
+ messageID=data.messages[data.channels[data.teams[0].ID][0].ID][1].ID,
+ )
+
+ assert len(replies) == len(data.replies[data.messages[data.channels[data.teams[0].ID][0].ID][1].ID])
+ assert replies[0].ID == data.replies[data.messages[data.channels[data.teams[0].ID][0].ID][1].ID][0].ID
+ assert replies[0].Content == data.replies[data.messages[data.channels[data.teams[0].ID][0].ID][1].ID][0].Content
+ assert replies[0].ContentType == MessageContentType(data.replies[data.messages[data.channels[data.teams[0].ID][0].ID][1].ID][0].ContentType)
+ assert replies[0].From.UserID == data.replies[data.messages[data.channels[data.teams[0].ID][0].ID][1].ID][0].From.UserID
+ assert replies[0].From.DisplayName == data.replies[data.messages[data.channels[data.teams[0].ID][0].ID][1].ID][0].From.DisplayName
+ assert replies[0].CreatedDateTime == data.replies[data.messages[data.channels[data.teams[0].ID][0].ID][1].ID][0].CreatedDateTime
+
+ assert replies[1].ID == data.replies[data.messages[data.channels[data.teams[0].ID][0].ID][1].ID][1].ID
+ assert replies[1].Content == data.replies[data.messages[data.channels[data.teams[0].ID][0].ID][1].ID][1].Content
+ assert replies[1].ContentType == MessageContentType(data.replies[data.messages[data.channels[data.teams[0].ID][0].ID][1].ID][1].ContentType)
+ assert replies[1].From.UserID == data.replies[data.messages[data.channels[data.teams[0].ID][0].ID][1].ID][1].From.UserID
+ assert replies[1].From.DisplayName == data.replies[data.messages[data.channels[data.teams[0].ID][0].ID][1].ID][1].From.DisplayName
+ assert replies[1].CreatedDateTime == data.replies[data.messages[data.channels[data.teams[0].ID][0].ID][1].ID][1].CreatedDateTime
+
+ finally:
+ client.close()
+
+
+def test_get_reply_integration(httpserver):
+
+ data = setup_fake_server(httpserver)
+
+ client = TeamsClient(auto_init=False)
+ try:
+ init_fake_client(client, httpserver.url_for(""))
+
+ reply = client.channels.get_message_reply(
+ teamRef=data.teams[0].DisplayName,
+ channelRef=data.channels[data.teams[0].ID][0].Name,
+ messageID=data.messages[data.channels[data.teams[0].ID][0].ID][1].ID,
+ replyID=data.replies[data.messages[data.channels[data.teams[0].ID][0].ID][1].ID][0].ID,
+ )
+
+ assert reply.ID == data.replies[data.messages[data.channels[data.teams[0].ID][0].ID][1].ID][0].ID
+ assert reply.Content == data.replies[data.messages[data.channels[data.teams[0].ID][0].ID][1].ID][0].Content
+ assert reply.ContentType == MessageContentType(data.replies[data.messages[data.channels[data.teams[0].ID][0].ID][1].ID][0].ContentType)
+ assert reply.From.UserID == data.replies[data.messages[data.channels[data.teams[0].ID][0].ID][1].ID][0].From.UserID
+ assert reply.From.DisplayName == data.replies[data.messages[data.channels[data.teams[0].ID][0].ID][1].ID][0].From.DisplayName
+ assert reply.CreatedDateTime == data.replies[data.messages[data.channels[data.teams[0].ID][0].ID][1].ID][0].CreatedDateTime
+
+ finally:
+ client.close()
+
+
+
+
+
From e168fc6bd6207b1b747bb1e535dc037bc9664e84 Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Mon, 29 Dec 2025 11:04:13 +0100
Subject: [PATCH 26/99] feat: added send message in channel test
---
python/teams_lib_pzsp2_z1/model/message.py | 4 +++
.../teams_lib_pzsp2_z1/services/channels.py | 2 +-
python/tests/fake_server/data.py | 29 +++++++++++++++++
python/tests/fake_server/setup.py | 14 +++++++++
python/tests/test_channels.py | 31 ++++++++++++++++++-
5 files changed, 78 insertions(+), 2 deletions(-)
diff --git a/python/teams_lib_pzsp2_z1/model/message.py b/python/teams_lib_pzsp2_z1/model/message.py
index 6bee67d..5ff9a4d 100644
--- a/python/teams_lib_pzsp2_z1/model/message.py
+++ b/python/teams_lib_pzsp2_z1/model/message.py
@@ -35,6 +35,10 @@ def __dict__(self):
"Content": self.Content,
}
+ def __iter__(self):
+ yield "ContentType", self.ContentType.value
+ yield "Content", self.Content
+
@dataclass
class ListMessagesOptions:
diff --git a/python/teams_lib_pzsp2_z1/services/channels.py b/python/teams_lib_pzsp2_z1/services/channels.py
index 11a22ce..0ec951a 100644
--- a/python/teams_lib_pzsp2_z1/services/channels.py
+++ b/python/teams_lib_pzsp2_z1/services/channels.py
@@ -97,7 +97,7 @@ def delete(self, teamRef: str, channelRef: str) -> bool:
def send_message(self, teamRef: str, channelRef: str, body: MessageBody) -> Message:
response = self.client.execute(
cmd_type="request",
- method="sendChannelMessage",
+ method="sendMessageToChannel",
params={
"teamRef": teamRef,
"channelRef": channelRef,
diff --git a/python/tests/fake_server/data.py b/python/tests/fake_server/data.py
index 872c5a5..c14d454 100644
--- a/python/tests/fake_server/data.py
+++ b/python/tests/fake_server/data.py
@@ -112,6 +112,17 @@ def __init__(self) -> FakeServerData:
self.potentialTeams = []
self.newChannelName = "New Channel"
self.newChannelID = "19:newchannelid@thread.tacv2"
+ self.newMessageTemplate = Message(
+ ID="new-message-id",
+ Content="This is a new message.",
+ ContentType="text",
+ From=MessageFrom(
+ UserID="user-new-001",
+ DisplayName="New User"
+ ),
+ CreatedDateTime="2024-01-02T10:00:00Z",
+ ReplyCount=0,
+ )
def get_myJoinedTeams_response(self) -> dict:
@@ -307,6 +318,22 @@ def get_delete_channel_response(self, team_id: str, channel_id: str) -> dict:
self.channels[team_id].remove(channel)
return {"success": True}
+ def get_send_message_response(self, team_id: str, channel_id: str, request_json: dict) -> dict:
+ return {
+ "id": self.newMessageTemplate.ID,
+ "body": {
+ "content": request_json.get("body", {}).get("content"),
+ "contentType": request_json.get("body", {}).get("contentType"),
+ },
+ "from": {
+ "user": {
+ "id": self.newMessageTemplate.From.UserID,
+ "displayName": self.newMessageTemplate.From.DisplayName,
+ }
+ },
+ "createdDateTime": self.newMessageTemplate.CreatedDateTime,
+ }
+
def get_list_messages_response(self, team_id: str, channel_id: str) -> dict:
return {
"value": [
@@ -397,3 +424,5 @@ def get_reply_response(self, team_id: str, channel_id: str, message_id: str, rep
+
+
diff --git a/python/tests/fake_server/setup.py b/python/tests/fake_server/setup.py
index 2c25da0..e3a0453 100644
--- a/python/tests/fake_server/setup.py
+++ b/python/tests/fake_server/setup.py
@@ -44,6 +44,20 @@ def make_log_response(request, data_dict: dict, context_msg: str = "") -> Respon
req, data.get_myJoinedTeams_response(), "List Joined Teams"
))
+ #POST /teams/{team_id}/channels/{channel_id}/messages
+ httpserver.expect_request(
+ re.compile(r"^/v1.0/teams/([^/]+)/channels/([^/]+)/messages"),
+ method="POST"
+ ).respond_with_handler(lambda req: make_log_response(
+ req,
+ data.get_send_message_response(
+ re.search(r"/teams/([^/]+)/channels/([^/]+)/messages", req.path).group(1),
+ re.search(r"/teams/([^/]+)/channels/([^/]+)/messages", req.path).group(2),
+ req.json
+ ),
+ "Create Channel Message"
+ ))
+
# GET /teams/{team_id}/channels/{channel_id}/messages/{message_id}/replies/{reply_id}
httpserver.expect_request(
diff --git a/python/tests/test_channels.py b/python/tests/test_channels.py
index dcf8196..8a6c687 100644
--- a/python/tests/test_channels.py
+++ b/python/tests/test_channels.py
@@ -1,7 +1,7 @@
from teams_lib_pzsp2_z1.client import TeamsClient
from tests.init_fake_client import init_fake_client
from tests.fake_server.setup import setup_fake_server
-from teams_lib_pzsp2_z1.model.message import MessageContentType
+from teams_lib_pzsp2_z1.model.message import MessageContentType, MessageBody
def test_list_channels_integration(httpserver):
@@ -116,6 +116,35 @@ def test_delete_channel_integration(httpserver):
client.close()
+def test_send_message_integration(httpserver):
+
+ data = setup_fake_server(httpserver)
+
+ client = TeamsClient(auto_init=False)
+ try:
+ init_fake_client(client, httpserver.url_for(""))
+
+ message = client.channels.send_message(
+ teamRef=data.teams[0].DisplayName,
+ channelRef=data.channels[data.teams[0].ID][0].Name,
+ body=MessageBody(
+ Content=data.newMessageTemplate.Content,
+ ContentType=MessageContentType.TEXT
+ )
+ )
+
+ assert message.ID == data.newMessageTemplate.ID
+ assert message.Content == data.newMessageTemplate.Content
+ assert message.ContentType == MessageContentType(data.newMessageTemplate.ContentType)
+ assert message.From.UserID == data.newMessageTemplate.From.UserID
+ assert message.From.DisplayName == data.newMessageTemplate.From.DisplayName
+ assert message.ReplyCount == data.newMessageTemplate.ReplyCount
+ assert message.CreatedDateTime == data.newMessageTemplate.CreatedDateTime
+
+ finally:
+ client.close()
+
+
def test_list_messages_integration(httpserver):
data = setup_fake_server(httpserver)
From 7f20b764d28d8c2b74eb51cf77c1c5c07a97d532 Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Mon, 29 Dec 2025 11:45:01 +0100
Subject: [PATCH 27/99] feat: added tests to members management
---
.../teams_lib_pzsp2_z1/services/channels.py | 6 +-
python/tests/fake_server/data.py | 95 ++++++++++++++++
python/tests/fake_server/setup.py | 57 ++++++++++
python/tests/test_channels.py | 107 ++++++++++++++++++
4 files changed, 262 insertions(+), 3 deletions(-)
diff --git a/python/teams_lib_pzsp2_z1/services/channels.py b/python/teams_lib_pzsp2_z1/services/channels.py
index 0ec951a..c4c5172 100644
--- a/python/teams_lib_pzsp2_z1/services/channels.py
+++ b/python/teams_lib_pzsp2_z1/services/channels.py
@@ -254,7 +254,7 @@ def add_member(
) -> Member:
response = self.client.execute(
cmd_type="request",
- method="addChannelMember",
+ method="addMemberToChannel",
params={
"teamRef": teamRef,
"channelRef": channelRef,
@@ -269,7 +269,7 @@ def update_member_role(
) -> Member:
response = self.client.execute(
cmd_type="request",
- method="updateChannelMemberRole",
+ method="updateMemberRoleInChannel",
params={
"teamRef": teamRef,
"channelRef": channelRef,
@@ -282,7 +282,7 @@ def update_member_role(
def remove_member(self, teamRef: str, channelRef: str, userRef: str) -> bool:
response = self.client.execute(
cmd_type="request",
- method="removeChannelMember",
+ method="removeMemberFromChannel",
params={
"teamRef": teamRef,
"channelRef": channelRef,
diff --git a/python/tests/fake_server/data.py b/python/tests/fake_server/data.py
index c14d454..27e22ca 100644
--- a/python/tests/fake_server/data.py
+++ b/python/tests/fake_server/data.py
@@ -2,6 +2,7 @@
from teams_lib_pzsp2_z1.model.team import Team
from teams_lib_pzsp2_z1.model.channel import Channel
from teams_lib_pzsp2_z1.model.message import Message, MessageFrom
+from teams_lib_pzsp2_z1.model.member import Member
from dataclasses import field
@dataclass
@@ -105,6 +106,26 @@ def __init__(self) -> FakeServerData:
),
],
}
+ self.members = {
+ "team-123-abc": {
+ "19:123123@thread.tacv2": [
+ Member(
+ ID="user-123-abc",
+ UserID="user-123-abc",
+ DisplayName="Alice",
+ Role="owner",
+ Email="alice@example.com"
+ ),
+ Member(
+ ID="user-456-def",
+ UserID="user-456-def",
+ DisplayName="Bob",
+ Role="member",
+ Email="bob@example.com"
+ ),
+ ],
+ }
+ }
self.newGroupID = "group-789-ghi"
self.newTeamName = "New Team"
self.newGroupMailNickname = "new-team-nickname"
@@ -123,6 +144,15 @@ def __init__(self) -> FakeServerData:
CreatedDateTime="2024-01-02T10:00:00Z",
ReplyCount=0,
)
+ self.newMemberTemplate = Member(
+ ID="user-new-002",
+ UserID="new-user-002",
+ DisplayName="New Member",
+ Role="owner",
+ Email="newmember@example.com"
+ )
+
+
def get_myJoinedTeams_response(self) -> dict:
@@ -418,6 +448,71 @@ def get_reply_response(self, team_id: str, channel_id: str, message_id: str, rep
"createdDateTime": reply.CreatedDateTime,
}
+ def get_add_member_response(self, team_id: str, request_json: dict) -> dict:
+ roles = []
+ if self.newMemberTemplate.Role == "owner":
+ roles = ["owner"]
+
+ return {
+ "@odata.context": f"https://graph.microsoft.com/v1.0/$metadata#teams('{team_id}')/members/$entity",
+ "@odata.type": "#microsoft.graph.aadUserConversationMember",
+
+ "id": self.newMemberTemplate.ID,
+ "roles": roles,
+ "displayName": self.newMemberTemplate.DisplayName,
+ "userId": self.newMemberTemplate.UserID,
+ "email": self.newMemberTemplate.Email,
+ }
+
+ def get_update_member_role_response(self, team_id: str, channel_id: str, member_id: str, request_json: dict) -> dict | None:
+ channel_members = self.members.get(team_id, {}).get(channel_id, [])
+
+ member = next((m for m in channel_members if m.ID == member_id), None)
+ if not member:
+ return None
+
+ if "roles" in request_json:
+ member.Role = "owner" if "owner" in request_json["roles"] else "member"
+
+ return {
+ "@odata.context": f"https://graph.microsoft.com/v1.0/$metadata#teams('{team_id}')/channels('{channel_id}')/members/$entity",
+ "@odata.type": "#microsoft.graph.aadUserConversationMember",
+ "id": member.ID,
+ "userId": member.UserID,
+ "displayName": member.DisplayName,
+ "roles": [member.Role] if member.Role == "owner" else [],
+ "email": member.Email,
+ }
+
+ def get_list_members_response(self, team_id: str, channel_id: str) -> dict:
+ return {
+ "@odata.context": f"https://graph.microsoft.com/v1.0/$metadata#teams('{team_id}')/channels('{channel_id}')/members",
+ "value": [
+ {
+ "@odata.type": "#microsoft.graph.aadUserConversationMember",
+ "id": member.ID,
+ "userId": member.UserID,
+ "displayName": member.DisplayName,
+ "roles": [member.Role] if member.Role == "owner" else [],
+ "email": member.Email,
+ }
+ for member in self.members.get(team_id, {}).get(channel_id, [])
+ ],
+ }
+
+ def get_remove_member_response(self, team_id: str, channel_id: str, member_id: str) -> dict:
+ channel_members = self.members.get(team_id, {}).get(channel_id, [])
+
+ member = next((m for m in channel_members if m.ID == member_id), None)
+ if not member:
+ return {"success": False}
+
+ channel_members.remove(member)
+ return {"success": True}
+
+
+
+
diff --git a/python/tests/fake_server/setup.py b/python/tests/fake_server/setup.py
index e3a0453..93e1bb1 100644
--- a/python/tests/fake_server/setup.py
+++ b/python/tests/fake_server/setup.py
@@ -44,6 +44,63 @@ def make_log_response(request, data_dict: dict, context_msg: str = "") -> Respon
req, data.get_myJoinedTeams_response(), "List Joined Teams"
))
+ #POST /teams/{team_id}/channels/{channel_id}/members
+ httpserver.expect_request(
+ re.compile(r"^/v1.0/teams/([^/]+)/channels/([^/]+)/members"),
+ method="POST"
+ ).respond_with_handler(lambda req: make_log_response(
+ req,
+ data.get_add_member_response(
+ re.search(r"/teams/([^/]+)/channels/([^/]+)/members", req.path).group(1),
+ req.json
+ ),
+ "Add member to channel"
+ ))
+
+
+ # PATCH /teams/{tid}/channels/{cid}/members/{mid}
+ httpserver.expect_request(
+ re.compile(r"^/v1.0/teams/([^/]+)/channels/([^/]+)/members/([^/]+)"),
+ method="PATCH"
+ ).respond_with_handler(lambda req: make_log_response(
+ req,
+ data.get_update_member_role_response(
+ re.search(r"/teams/([^/]+)/channels/([^/]+)/members/([^/]+)", req.path).group(1), # team_id
+ re.search(r"/teams/([^/]+)/channels/([^/]+)/members/([^/]+)", req.path).group(2), # channel_id
+ re.search(r"/teams/([^/]+)/channels/([^/]+)/members/([^/]+)", req.path).group(3), # member_id
+ req.json
+ ),
+ "Update Member Role"
+ ))
+
+ # DELETE /teams/{team_id}/channels/{channel_id}/members/{member_id}
+ httpserver.expect_request(
+ re.compile(r"^/v1.0/teams/([^/]+)/channels/([^/]+)/members/([^/]+)"),
+ method="DELETE"
+ ).respond_with_handler(lambda req: make_log_response(
+ req,
+ data.get_remove_member_response(
+ re.search(r"/teams/([^/]+)/channels/([^/]+)/members/([^/]+)", req.path).group(1), # team_id
+ re.search(r"/teams/([^/]+)/channels/([^/]+)/members/([^/]+)", req.path).group(2), # channel_id
+ re.search(r"/teams/([^/]+)/channels/([^/]+)/members/([^/]+)", req.path).group(3) # member_id
+ ),
+ "Remove Member from Channel"
+ ))
+
+ #GET /teams/{team_id}/channels/{channel_id}/members
+ httpserver.expect_request(
+ re.compile(r"^/v1.0/teams/([^/]+)/channels/([^/]+)/members"),
+ method="GET"
+ ).respond_with_handler(lambda req: make_log_response(
+ req,
+ data.get_list_members_response(
+ re.search(r"/teams/([^/]+)/channels/([^/]+)/members", req.path).group(1),
+ re.search(r"/teams/([^/]+)/channels/([^/]+)/members", req.path).group(2)
+ ),
+ "List channel members"
+ ))
+
+
#POST /teams/{team_id}/channels/{channel_id}/messages
httpserver.expect_request(
re.compile(r"^/v1.0/teams/([^/]+)/channels/([^/]+)/messages"),
diff --git a/python/tests/test_channels.py b/python/tests/test_channels.py
index 8a6c687..fe8e161 100644
--- a/python/tests/test_channels.py
+++ b/python/tests/test_channels.py
@@ -264,6 +264,113 @@ def test_get_reply_integration(httpserver):
client.close()
+def test_list_members_integration(httpserver):
+ data = setup_fake_server(httpserver)
+
+ client = TeamsClient(auto_init=False)
+ try:
+ init_fake_client(client, httpserver.url_for(""))
+
+ team_id = data.teams[0].ID
+ channel = data.channels[team_id][0]
+ channel_id = channel.ID
+
+ members = client.channels.list_members(
+ teamRef=data.teams[0].DisplayName,
+ channelRef=channel.Name,
+ )
+
+ expected_members = data.members[team_id][channel_id]
+
+ assert len(members) == len(expected_members)
+
+ assert members[0].UserID == expected_members[0].UserID
+ assert members[0].DisplayName == expected_members[0].DisplayName
+ expected_role_0 = "owner" if expected_members[0].Role == "owner" else ""
+ assert members[0].Role == expected_role_0
+ assert members[0].Email == expected_members[0].Email
+ assert members[0].ID == expected_members[0].ID
+
+ assert members[1].UserID == expected_members[1].UserID
+ assert members[1].DisplayName == expected_members[1].DisplayName
+ expected_role_1 = "owner" if expected_members[1].Role == "owner" else ""
+ assert members[1].Role == expected_role_1
+ assert members[1].Email == expected_members[1].Email
+ assert members[1].ID == expected_members[1].ID
+
+ finally:
+ client.close()
+
+
+def test_add_member_integration(httpserver):
+
+ data = setup_fake_server(httpserver)
+
+ client = TeamsClient(auto_init=False)
+ try:
+ init_fake_client(client, httpserver.url_for(""))
+
+ member = client.channels.add_member(
+ teamRef=data.teams[0].DisplayName,
+ channelRef=data.channels[data.teams[0].ID][1].Name,
+ userRef=data.newMemberTemplate.DisplayName,
+ isOwner=True if data.newMemberTemplate.Role == "owner" else False,
+ )
+
+ assert member.UserID == data.newMemberTemplate.UserID
+ assert member.DisplayName == data.newMemberTemplate.DisplayName
+ assert member.Role == data.newMemberTemplate.Role
+ assert member.Email == data.newMemberTemplate.Email
+ assert member.ID == data.newMemberTemplate.ID
+
+ finally:
+ client.close()
+
+
+def test_update_member_role_integration(httpserver):
+
+ data = setup_fake_server(httpserver)
+
+ client = TeamsClient(auto_init=False)
+ try:
+ init_fake_client(client, httpserver.url_for(""))
+
+ member = client.channels.update_member_role(
+ teamRef=data.teams[0].DisplayName,
+ channelRef=data.channels[data.teams[0].ID][0].Name,
+ userRef=data.members[data.teams[0].ID][data.channels[data.teams[0].ID][0].ID][1].DisplayName,
+ isOwner=True
+ )
+
+ assert member.UserID == data.members[data.teams[0].ID][data.channels[data.teams[0].ID][0].ID][1].UserID
+ assert member.DisplayName == data.members[data.teams[0].ID][data.channels[data.teams[0].ID][0].ID][1].DisplayName
+ assert member.Role == "owner"
+ assert member.Email == data.members[data.teams[0].ID][data.channels[data.teams[0].ID][0].ID][1].Email
+ assert member.ID == data.members[data.teams[0].ID][data.channels[data.teams[0].ID][0].ID][1].ID
+ finally:
+ client.close()
+
+
+def test_remove_member_integration(httpserver):
+
+ data = setup_fake_server(httpserver)
+
+ client = TeamsClient(auto_init=False)
+ try:
+ init_fake_client(client, httpserver.url_for(""))
+
+ success = client.channels.remove_member(
+ teamRef=data.teams[0].DisplayName,
+ channelRef=data.channels[data.teams[0].ID][0].Name,
+ userRef=data.members[data.teams[0].ID][data.channels[data.teams[0].ID][0].ID][1].DisplayName,
+ )
+
+ assert success is True
+
+ finally:
+ client.close()
+
+
From 8267c4e5d2f24ef917e855059d09cee7ef56f311 Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Mon, 29 Dec 2025 11:58:23 +0100
Subject: [PATCH 28/99] feat: added chats endpoints to bridge
---
go/internal/json-client/requests-chats.go | 100 ++++++++++++++++++++++
1 file changed, 100 insertions(+)
create mode 100644 go/internal/json-client/requests-chats.go
diff --git a/go/internal/json-client/requests-chats.go b/go/internal/json-client/requests-chats.go
new file mode 100644
index 0000000..59c60d7
--- /dev/null
+++ b/go/internal/json-client/requests-chats.go
@@ -0,0 +1,100 @@
+package jsonclient
+
+import (
+ "context"
+
+ "github.com/pzsp-teams/lib-python/internal/json-client/decoders"
+)
+
+type createOneToOneChatParams struct {
+ OwnerEmail string `json:"ownerEmail"`
+ RecipientEmail string `json:"recipientEmail"`
+}
+
+func (jsonclient *TeamsJSONClient) CreateOneToOneChat(p map[string]interface{}) (interface{}, error) {
+ params, err := decoders.DecodeParams[createOneToOneChatParams](p)
+ if err != nil {
+ return nil, err
+ }
+ chat, err := jsonclient.client.Chats.CreateOneToOne(context.TODO(), params.OwnerEmail, params.RecipientEmail)
+ if err != nil {
+ return nil, err
+ }
+ return chat, nil
+}
+
+type createGroupChatParams struct {
+ OwnerEmail string `json:"ownerEmail"`
+ MemberEmails []string `json:"memberEmails"`
+ Topic string `json:"topic"`
+}
+
+func (jsonclient *TeamsJSONClient) CreateGroupChat(p map[string]interface{}) (interface{}, error) {
+ params, err := decoders.DecodeParams[createGroupChatParams](p)
+ if err != nil {
+ return nil, err
+ }
+ chat, err := jsonclient.client.Chats.CreateGroup(context.TODO(), params.OwnerEmail, params.MemberEmails, params.Topic)
+ if err != nil {
+ return nil, err
+ }
+ return chat, nil
+}
+
+type sendMessageInChatParams struct {
+ ChatID string `json:"chatID"`
+ Content string `json:"content"`
+}
+
+func (jsonclient *TeamsJSONClient) SendMessageInChat(p map[string]interface{}) (interface{}, error) {
+ params, err := decoders.DecodeParams[sendMessageInChatParams](p)
+ if err != nil {
+ return nil, err
+ }
+ message, err := jsonclient.client.Chats.SendMessage(context.TODO(), params.ChatID, params.Content)
+ if err != nil {
+ return nil, err
+ }
+ return message, nil
+}
+
+func (jsonclient *TeamsJSONClient) ListMyChats(_ map[string]interface{}) (interface{}, error) {
+ chats, err := jsonclient.client.Chats.ListMyJoined(context.TODO())
+ if err != nil {
+ return nil, err
+ }
+ return chats, nil
+}
+
+type listMembersInChatParams struct {
+ ChatID string `json:"chatID"`
+}
+
+func (jsonclient *TeamsJSONClient) ListMembersInChat(p map[string]interface{}) (interface{}, error) {
+ params, err := decoders.DecodeParams[listMembersInChatParams](p)
+ if err != nil {
+ return nil, err
+ }
+ members, err := jsonclient.client.Chats.ListMembers(context.TODO(), params.ChatID)
+ if err != nil {
+ return nil, err
+ }
+ return members, nil
+}
+
+type addMemberToChatParams struct {
+ ChatID string `json:"chatID"`
+ Email string `json:"email"`
+}
+
+func (jsonclient *TeamsJSONClient) AddMemberToChat(p map[string]interface{}) (interface{}, error) {
+ params, err := decoders.DecodeParams[addMemberToChatParams](p)
+ if err != nil {
+ return nil, err
+ }
+ member, err := jsonclient.client.Chats.AddMember(context.TODO(), params.ChatID, params.Email)
+ if err != nil {
+ return nil, err
+ }
+ return member, nil
+}
From be56e37bae93bb7a39b566273a5e57d92d775068 Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Mon, 29 Dec 2025 11:59:54 +0100
Subject: [PATCH 29/99] feat: added chat endpoints to request map
---
go/internal/json-client/request-map.go | 25 +++++++++++++++++++++++++
1 file changed, 25 insertions(+)
diff --git a/go/internal/json-client/request-map.go b/go/internal/json-client/request-map.go
index b0c9099..b91027f 100644
--- a/go/internal/json-client/request-map.go
+++ b/go/internal/json-client/request-map.go
@@ -97,4 +97,29 @@ var Handlers = map[string]HandlerFunc{
"getMessageReplyInChannel": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
return c.GetMessageReplyInChannel(p)
},
+
+ // CHATS
+ "createOneOnOneChat": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ return c.CreateOneToOneChat(p)
+ },
+
+ "createGroupChat": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ return c.CreateGroupChat(p)
+ },
+
+ "sendMessageInChat": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ return c.SendMessageInChat(p)
+ },
+
+ "listMyChats": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ return c.ListMyChats(p)
+ },
+
+ "listMembersInChat": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ return c.ListMembersInChat(p)
+ },
+
+ "addMemberToChat": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ return c.AddMemberToChat(p)
+ },
}
From de296a62b493f935bda018dcf3dce6caf986cd6f Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Mon, 29 Dec 2025 12:06:49 +0100
Subject: [PATCH 30/99] feat: prepared structure for chat service in python
---
python/teams_lib_pzsp2_z1/client.py | 2 ++
python/teams_lib_pzsp2_z1/model/chat.py | 15 +++++++++++++++
python/teams_lib_pzsp2_z1/services/chats.py | 12 ++++++++++++
3 files changed, 29 insertions(+)
create mode 100644 python/teams_lib_pzsp2_z1/model/chat.py
create mode 100644 python/teams_lib_pzsp2_z1/services/chats.py
diff --git a/python/teams_lib_pzsp2_z1/client.py b/python/teams_lib_pzsp2_z1/client.py
index a5f28c0..7a0e76c 100644
--- a/python/teams_lib_pzsp2_z1/client.py
+++ b/python/teams_lib_pzsp2_z1/client.py
@@ -8,6 +8,7 @@
from teams_lib_pzsp2_z1 import config
from teams_lib_pzsp2_z1.services.channels import ChannelsService
+from teams_lib_pzsp2_z1.services.chats import ChatsService
from teams_lib_pzsp2_z1.services.teams import TeamsService
@@ -33,6 +34,7 @@ def __init__(
self.env_path = env_path
self.channels = ChannelsService(self)
self.teams = TeamsService(self)
+ self.chats = ChatsService(self)
if auto_init:
self.init_client(cache_enabled, cache_path)
diff --git a/python/teams_lib_pzsp2_z1/model/chat.py b/python/teams_lib_pzsp2_z1/model/chat.py
new file mode 100644
index 0000000..7224aed
--- /dev/null
+++ b/python/teams_lib_pzsp2_z1/model/chat.py
@@ -0,0 +1,15 @@
+from dataclasses import dataclass
+from enum import Enum
+
+
+class ChatType(Enum):
+ ONEONONE = "one-on-one"
+ GROUP = "group"
+
+
+@dataclass
+class Chat:
+ ID: str
+ Type: ChatType
+ IsHidden: bool
+ Topic: str
diff --git a/python/teams_lib_pzsp2_z1/services/chats.py b/python/teams_lib_pzsp2_z1/services/chats.py
new file mode 100644
index 0000000..ae55f9e
--- /dev/null
+++ b/python/teams_lib_pzsp2_z1/services/chats.py
@@ -0,0 +1,12 @@
+from teams_lib_pzsp2_z1.model.chat import Chat, ChatType
+from teams_lib_pzsp2_z1.model.member import Member
+from teams_lib_pzsp2_z1.model.message import (
+ Message,
+ MessageBody,
+ MessageContentType,
+ MessageFrom,
+)
+from teams_lib_pzsp2_z1.services.base_service import BaseService
+
+
+class ChatsService(BaseService):
From b64f8829710c34bf63ec694f56251ffde50e0219 Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Wed, 31 Dec 2025 13:13:47 +0100
Subject: [PATCH 31/99] feat: resolved chatRef
---
go/go.mod | 33 +++---
go/go.sum | 66 ++++++-----
.../json-client/decoders/decode-chatRef.go | 28 +++++
.../decoders/decode-messageBody.go | 6 +
go/internal/json-client/requests-channels.go | 7 +-
go/internal/json-client/requests-chats.go | 112 +++++++++++++-----
python/teams_lib_pzsp2_z1/services/chats.py | 31 +++++
7 files changed, 202 insertions(+), 81 deletions(-)
create mode 100644 go/internal/json-client/decoders/decode-chatRef.go
create mode 100644 go/internal/json-client/decoders/decode-messageBody.go
diff --git a/go/go.mod b/go/go.mod
index 7e695aa..a0e5afd 100644
--- a/go/go.mod
+++ b/go/go.mod
@@ -4,24 +4,25 @@ go 1.25.5
require (
github.com/microsoft/kiota-abstractions-go v1.9.3
- github.com/microsoftgraph/msgraph-sdk-go v1.90.0
+ github.com/microsoftgraph/msgraph-sdk-go v1.92.0
github.com/mitchellh/mapstructure v1.5.0
- github.com/pzsp-teams/lib v0.0.0-20251213104555-e41d3adf599f
+ github.com/pzsp-teams/lib v0.0.0-20251231113700-9da62b291ebb
)
require (
- al.essio.dev/pkg/shellescape v1.5.1 // indirect
- github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.1 // indirect
+ al.essio.dev/pkg/shellescape v1.6.0 // indirect
+ github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 // indirect
github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 // indirect
github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 // indirect
- github.com/danieljoos/wincred v1.2.2 // indirect
+ github.com/cespare/xxhash/v2 v2.3.0 // indirect
+ github.com/danieljoos/wincred v1.2.3 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
- github.com/godbus/dbus/v5 v5.1.0 // indirect
- github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
+ github.com/godbus/dbus/v5 v5.2.2 // indirect
+ github.com/golang-jwt/jwt/v5 v5.3.0 // indirect
github.com/google/uuid v1.6.0 // indirect
- github.com/keybase/go-keychain v0.0.0-20230523030712-b5615109f100 // indirect
+ github.com/keybase/go-keychain v0.0.1 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/microsoft/kiota-authentication-azure-go v1.3.1 // indirect
github.com/microsoft/kiota-http-go v1.5.4 // indirect
@@ -31,13 +32,13 @@ require (
github.com/microsoft/kiota-serialization-text-go v1.1.3 // indirect
github.com/microsoftgraph/msgraph-sdk-go-core v1.4.0 // indirect
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
- github.com/std-uritemplate/std-uritemplate/go/v2 v2.0.3 // indirect
+ github.com/std-uritemplate/std-uritemplate/go/v2 v2.0.8 // indirect
github.com/zalando/go-keyring v0.2.6 // indirect
- go.opentelemetry.io/auto/sdk v1.1.0 // indirect
- go.opentelemetry.io/otel v1.37.0 // indirect
- go.opentelemetry.io/otel/metric v1.37.0 // indirect
- go.opentelemetry.io/otel/trace v1.37.0 // indirect
- golang.org/x/net v0.43.0 // indirect
- golang.org/x/sys v0.35.0 // indirect
- golang.org/x/text v0.28.0 // indirect
+ go.opentelemetry.io/auto/sdk v1.2.1 // indirect
+ go.opentelemetry.io/otel v1.39.0 // indirect
+ go.opentelemetry.io/otel/metric v1.39.0 // indirect
+ go.opentelemetry.io/otel/trace v1.39.0 // indirect
+ golang.org/x/net v0.48.0 // indirect
+ golang.org/x/sys v0.39.0 // indirect
+ golang.org/x/text v0.32.0 // indirect
)
diff --git a/go/go.sum b/go/go.sum
index 6e98158..bff219a 100644
--- a/go/go.sum
+++ b/go/go.sum
@@ -1,15 +1,17 @@
-al.essio.dev/pkg/shellescape v1.5.1 h1:86HrALUujYS/h+GtqoB26SBEdkWfmMI6FubjXlsXyho=
-al.essio.dev/pkg/shellescape v1.5.1/go.mod h1:6sIqp7X2P6mThCQ7twERpZTuigpr6KbZWtls1U8I890=
-github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.1 h1:5YTBM8QDVIBN3sxBil89WfdAAqDZbyJTgh688DSxX5w=
-github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.1/go.mod h1:YD5h/ldMsG0XiIw7PdyNhLxaM317eFh5yNLccNfGdyw=
+al.essio.dev/pkg/shellescape v1.6.0 h1:NxFcEqzFSEVCGN2yq7Huv/9hyCEGVa/TncnOOBBeXHA=
+al.essio.dev/pkg/shellescape v1.6.0/go.mod h1:6sIqp7X2P6mThCQ7twERpZTuigpr6KbZWtls1U8I890=
+github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0 h1:JXg2dwJUmPB9JmtVmdEB16APJ7jurfbY5jnfXpJoRMc=
+github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0/go.mod h1:YD5h/ldMsG0XiIw7PdyNhLxaM317eFh5yNLccNfGdyw=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 h1:9iefClla7iYpfYWdzPCRDozdmndjTm8DXdpCzPajMgA=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2/go.mod h1:XtLgD3ZD34DAaVIIAyG3objl5DynM3CQ/vMcbBNJZGI=
github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM=
github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE=
github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 h1:XRzhVemXdgvJqCH0sFfrBUTnUJSBrBf7++ypk+twtRs=
github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0/go.mod h1:HKpQxkWaGLJ+D/5H8QRpyQXA1eKjxkFlOMwck5+33Jk=
-github.com/danieljoos/wincred v1.2.2 h1:774zMFJrqaeYCK2W57BgAem/MLi6mtSE47MB6BOJ0i0=
-github.com/danieljoos/wincred v1.2.2/go.mod h1:w7w4Utbrz8lqeMbDAK0lkNJUv5sAOkFi7nd/ogr0Uh8=
+github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
+github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/danieljoos/wincred v1.2.3 h1:v7dZC2x32Ut3nEfRH+vhoZGvN72+dQ/snVXo/vMFLdQ=
+github.com/danieljoos/wincred v1.2.3/go.mod h1:6qqX0WNrS4RzPZ1tnroDzq9kY3fu1KwE7MRLQK4X0bs=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
@@ -17,18 +19,18 @@ github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
-github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
-github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
-github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
-github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
+github.com/godbus/dbus/v5 v5.2.2 h1:TUR3TgtSVDmjiXOgAAyaZbYmIeP3DPkld3jgKGV8mXQ=
+github.com/godbus/dbus/v5 v5.2.2/go.mod h1:3AAv2+hPq5rdnr5txxxRwiGjPXamgoIHgz9FPBfOp3c=
+github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
+github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/keybase/go-keychain v0.0.0-20230523030712-b5615109f100 h1:rG3VnJUnAWyiv7qYmmdOdSapzz6HM+zb9/uRFr0T5EM=
-github.com/keybase/go-keychain v0.0.0-20230523030712-b5615109f100/go.mod h1:qDHUvIjGZJUtdPtuP4WMu5/U4aVWbFw1MhlkJqCGmCQ=
+github.com/keybase/go-keychain v0.0.1 h1:way+bWYa6lDppZoZcgMbYsvC7GxljxrskdNInRtuthU=
+github.com/keybase/go-keychain v0.0.1/go.mod h1:PdEILRW3i9D8JcdM+FmY6RwkHGnhHxXwkPPMeUgOK1k=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
@@ -49,8 +51,8 @@ github.com/microsoft/kiota-serialization-multipart-go v1.1.2 h1:1pUyA1QgIeKslQwb
github.com/microsoft/kiota-serialization-multipart-go v1.1.2/go.mod h1:j2K7ZyYErloDu7Kuuk993DsvfoP7LPWvAo7rfDpdPio=
github.com/microsoft/kiota-serialization-text-go v1.1.3 h1:8z7Cebn0YAAr++xswVgfdxZjnAZ4GOB9O7XP4+r5r/M=
github.com/microsoft/kiota-serialization-text-go v1.1.3/go.mod h1:NDSvz4A3QalGMjNboKKQI9wR+8k+ih8UuagNmzIRgTQ=
-github.com/microsoftgraph/msgraph-sdk-go v1.90.0 h1:ygVeWfGB8TMO4rTFxtrYueZmj1mLqtDKW5UZ4iJwczU=
-github.com/microsoftgraph/msgraph-sdk-go v1.90.0/go.mod h1:UdZWxbZiFvjPug9DYayD90JNiHjXyNRA39lEpcy3Kms=
+github.com/microsoftgraph/msgraph-sdk-go v1.92.0 h1:LOTZ5un7tsSki0S072D+TIHsiOIpmGJnDkNps9Em6cw=
+github.com/microsoftgraph/msgraph-sdk-go v1.92.0/go.mod h1:sue5+4Z9FCOon6pHgvC1djjybs9ZYB3LZaAGYI1Qcfo=
github.com/microsoftgraph/msgraph-sdk-go-core v1.4.0 h1:0SrIoFl7TQnMRrsi5TFaeNe0q8KO5lRzRp4GSCCL2So=
github.com/microsoftgraph/msgraph-sdk-go-core v1.4.0/go.mod h1:A1iXs+vjsRjzANxF6UeKv2ACExG7fqTwHHbwh1FL+EE=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
@@ -59,33 +61,33 @@ github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmd
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/pzsp-teams/lib v0.0.0-20251213104555-e41d3adf599f h1:tmXKJEWYl0f/H4n91wnfW1asgkHbftKgX5nbpafNHmU=
-github.com/pzsp-teams/lib v0.0.0-20251213104555-e41d3adf599f/go.mod h1:EPtQVPz+HYZNsSpcoQ3QWLbMEz1O2qeIH5ojlYJLEic=
+github.com/pzsp-teams/lib v0.0.0-20251231113700-9da62b291ebb h1:Bzr1vl4wA1X0g+FIKEn5vBexVJGhP1CztwTCyy7TPlo=
+github.com/pzsp-teams/lib v0.0.0-20251231113700-9da62b291ebb/go.mod h1:EPtQVPz+HYZNsSpcoQ3QWLbMEz1O2qeIH5ojlYJLEic=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
-github.com/std-uritemplate/std-uritemplate/go/v2 v2.0.3 h1:7hth9376EoQEd1hH4lAp3vnaLP2UMyxuMMghLKzDHyU=
-github.com/std-uritemplate/std-uritemplate/go/v2 v2.0.3/go.mod h1:Z5KcoM0YLC7INlNhEezeIZ0TZNYf7WSNO0Lvah4DSeQ=
+github.com/std-uritemplate/std-uritemplate/go/v2 v2.0.8 h1:gMBdYMTHt2mmTdXW8YfvRjRUZ0GhyGV+IqSH9H15bGw=
+github.com/std-uritemplate/std-uritemplate/go/v2 v2.0.8/go.mod h1:Z5KcoM0YLC7INlNhEezeIZ0TZNYf7WSNO0Lvah4DSeQ=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/zalando/go-keyring v0.2.6 h1:r7Yc3+H+Ux0+M72zacZoItR3UDxeWfKTcabvkI8ua9s=
github.com/zalando/go-keyring v0.2.6/go.mod h1:2TCrxYrbUNYfNS/Kgy/LSrkSQzZ5UPVH85RwfczwvcI=
-go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
-go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
-go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ=
-go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I=
-go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE=
-go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E=
-go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4=
-go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=
-golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
-golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
+go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
+go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
+go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48=
+go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8=
+go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0=
+go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs=
+go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI=
+go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA=
+golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
+golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
-golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
-golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
-golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
+golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
+golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
+golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
+golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
diff --git a/go/internal/json-client/decoders/decode-chatRef.go b/go/internal/json-client/decoders/decode-chatRef.go
new file mode 100644
index 0000000..bd46c4d
--- /dev/null
+++ b/go/internal/json-client/decoders/decode-chatRef.go
@@ -0,0 +1,28 @@
+package decoders
+
+import (
+ "fmt"
+
+ "github.com/pzsp-teams/lib/chats"
+)
+
+type ChatRefDTO struct {
+ Ref string `json:"ref"`
+ Type string `json:"type"`
+}
+
+const (
+ ChatTypeGroup = "group"
+ ChatTypeOneOnOne = "oneOnOne"
+)
+
+func GetChatRef(dto ChatRefDTO) (chats.ChatRef, error) {
+ switch dto.Type {
+ case ChatTypeGroup:
+ return chats.GroupChatRef{Ref: dto.Ref}, nil
+ case ChatTypeOneOnOne:
+ return chats.OneOnOneChatRef{Ref: dto.Ref}, nil
+ default:
+ return nil, fmt.Errorf("unknown chat type: %s", dto.Type)
+ }
+}
diff --git a/go/internal/json-client/decoders/decode-messageBody.go b/go/internal/json-client/decoders/decode-messageBody.go
new file mode 100644
index 0000000..d9fc39b
--- /dev/null
+++ b/go/internal/json-client/decoders/decode-messageBody.go
@@ -0,0 +1,6 @@
+package decoders
+
+type MessageBodyDTO struct {
+ ContentType string `json:"contentType"`
+ Content string `json:"content"`
+}
\ No newline at end of file
diff --git a/go/internal/json-client/requests-channels.go b/go/internal/json-client/requests-channels.go
index 56b72b2..d0f9659 100644
--- a/go/internal/json-client/requests-channels.go
+++ b/go/internal/json-client/requests-channels.go
@@ -88,15 +88,10 @@ func (jsonclient *TeamsJSONClient) DeleteChannel(p map[string]interface{}) (inte
return "deleted", nil
}
-type messageBodyDTO struct {
- ContentType string `json:"contentType"`
- Content string `json:"content"`
-}
-
type sendMessageToChannelParams struct {
TeamRef string `json:"teamRef"`
ChannelRef string `json:"channelRef"`
- Body messageBodyDTO `json:"body"`
+ Body decoders.MessageBodyDTO `json:"body"`
}
func (jsonclient *TeamsJSONClient) SendMessageToChannel(p map[string]interface{}) (interface{}, error) {
diff --git a/go/internal/json-client/requests-chats.go b/go/internal/json-client/requests-chats.go
index 59c60d7..00c2c8d 100644
--- a/go/internal/json-client/requests-chats.go
+++ b/go/internal/json-client/requests-chats.go
@@ -4,11 +4,12 @@ import (
"context"
"github.com/pzsp-teams/lib-python/internal/json-client/decoders"
+ "github.com/pzsp-teams/lib/chats"
+ "github.com/pzsp-teams/lib/models"
)
type createOneToOneChatParams struct {
- OwnerEmail string `json:"ownerEmail"`
- RecipientEmail string `json:"recipientEmail"`
+ RecipientRef string `json:"recipientRef"`
}
func (jsonclient *TeamsJSONClient) CreateOneToOneChat(p map[string]interface{}) (interface{}, error) {
@@ -16,7 +17,7 @@ func (jsonclient *TeamsJSONClient) CreateOneToOneChat(p map[string]interface{})
if err != nil {
return nil, err
}
- chat, err := jsonclient.client.Chats.CreateOneToOne(context.TODO(), params.OwnerEmail, params.RecipientEmail)
+ chat, err := jsonclient.client.Chats.CreateOneOneOne(context.TODO(), params.RecipientRef)
if err != nil {
return nil, err
}
@@ -24,9 +25,9 @@ func (jsonclient *TeamsJSONClient) CreateOneToOneChat(p map[string]interface{})
}
type createGroupChatParams struct {
- OwnerEmail string `json:"ownerEmail"`
- MemberEmails []string `json:"memberEmails"`
- Topic string `json:"topic"`
+ RecipientRefs []string `json:"recipientRefs"`
+ Topic string `json:"topic"`
+ IncludeMe bool `json:"includeMe"`
}
func (jsonclient *TeamsJSONClient) CreateGroupChat(p map[string]interface{}) (interface{}, error) {
@@ -34,67 +35,124 @@ func (jsonclient *TeamsJSONClient) CreateGroupChat(p map[string]interface{}) (in
if err != nil {
return nil, err
}
- chat, err := jsonclient.client.Chats.CreateGroup(context.TODO(), params.OwnerEmail, params.MemberEmails, params.Topic)
+ chat, err := jsonclient.client.Chats.CreateGroup(context.TODO(), params.RecipientRefs, params.Topic, params.IncludeMe)
if err != nil {
return nil, err
}
return chat, nil
}
-type sendMessageInChatParams struct {
- ChatID string `json:"chatID"`
- Content string `json:"content"`
+type groupChatMemberParams struct {
+ GroupChatRef string `json:"groupChatRef"`
+ UserRef string `json:"userRef"`
}
-func (jsonclient *TeamsJSONClient) SendMessageInChat(p map[string]interface{}) (interface{}, error) {
- params, err := decoders.DecodeParams[sendMessageInChatParams](p)
+func (jsonclient *TeamsJSONClient) AddMemberGroupToChat(p map[string]interface{}) (interface{}, error) {
+ params, err := decoders.DecodeParams[groupChatMemberParams](p)
if err != nil {
return nil, err
}
- message, err := jsonclient.client.Chats.SendMessage(context.TODO(), params.ChatID, params.Content)
+ member, err := jsonclient.client.Chats.AddMemberToGroupChat(context.TODO(), chats.GroupChatRef{Ref: params.GroupChatRef}, params.UserRef)
if err != nil {
return nil, err
}
- return message, nil
+ return member, nil
}
-func (jsonclient *TeamsJSONClient) ListMyChats(_ map[string]interface{}) (interface{}, error) {
- chats, err := jsonclient.client.Chats.ListMyJoined(context.TODO())
+func (jsonclient *TeamsJSONClient) RemoveMemberToGroupChat(p map[string]interface{}) (interface{}, error) {
+ params, err := decoders.DecodeParams[groupChatMemberParams](p)
if err != nil {
return nil, err
}
- return chats, nil
+ err = jsonclient.client.Chats.RemoveMemberFromGroupChat(context.TODO(), chats.GroupChatRef{Ref: params.GroupChatRef}, params.UserRef)
+ if err != nil {
+ return nil, err
+ }
+ return "removed", nil
}
type listMembersInChatParams struct {
- ChatID string `json:"chatID"`
+ GroupChatRef string `json:"groupChatRef"`
}
-func (jsonclient *TeamsJSONClient) ListMembersInChat(p map[string]interface{}) (interface{}, error) {
+func (jsonclient *TeamsJSONClient) ListGroupChatMembers(p map[string]interface{}) (interface{}, error) {
params, err := decoders.DecodeParams[listMembersInChatParams](p)
if err != nil {
return nil, err
}
- members, err := jsonclient.client.Chats.ListMembers(context.TODO(), params.ChatID)
+ members, err := jsonclient.client.Chats.ListGroupChatMembers(context.TODO(), chats.GroupChatRef{Ref: params.GroupChatRef})
if err != nil {
return nil, err
}
return members, nil
}
-type addMemberToChatParams struct {
- ChatID string `json:"chatID"`
- Email string `json:"email"`
+type updateGroupChatTopicParams struct {
+ GroupChatRef string `json:"groupChatRef"`
+ Topic string `json:"topic"`
}
-func (jsonclient *TeamsJSONClient) AddMemberToChat(p map[string]interface{}) (interface{}, error) {
- params, err := decoders.DecodeParams[addMemberToChatParams](p)
+func (jsonclient *TeamsJSONClient) UpdateGroupChatTopic(p map[string]interface{}) (interface{}, error) {
+ params, err := decoders.DecodeParams[updateGroupChatTopicParams](p)
if err != nil {
return nil, err
}
- member, err := jsonclient.client.Chats.AddMember(context.TODO(), params.ChatID, params.Email)
+ chat, err := jsonclient.client.Chats.UpdateGroupChatTopic(context.TODO(), chats.GroupChatRef{Ref: params.GroupChatRef}, params.Topic)
if err != nil {
return nil, err
}
- return member, nil
+ return chat, nil
+}
+
+type baseChatParams struct {
+ ChatRef decoders.ChatRefDTO `json:"chatRef"`
+}
+
+func (jsonclient *TeamsJSONClient) ListMessagesInChat(p map[string]interface{}) (interface{}, error) {
+ params, err := decoders.DecodeParams[baseChatParams](p)
+ if err != nil {
+ return nil, err
+ }
+ chatRef, err := decoders.GetChatRef(params.ChatRef)
+ if err != nil {
+ return nil, err
+ }
+ messages, err := jsonclient.client.Chats.ListMessages(context.TODO(), chatRef)
+ if err != nil {
+ return nil, err
+ }
+ return messages, nil
+}
+
+type sendMessageInChatParams struct {
+ ChatRef decoders.ChatRefDTO `json:"chatRef"`
+ Body decoders.MessageBodyDTO `json:"body"`
+}
+
+func (jsonclient *TeamsJSONClient) SendMessageInChat(p map[string]interface{}) (interface{}, error) {
+ params, err := decoders.DecodeParams[sendMessageInChatParams](p)
+ if err != nil {
+ return nil, err
+ }
+ chatRef, err := decoders.GetChatRef(params.ChatRef)
+ if err != nil {
+ return nil, err
+ }
+ body, err := decoders.DecodeParams[models.MessageBody](¶ms.Body)
+ if err != nil {
+ return nil, err
+ }
+ message, err := jsonclient.client.Chats.SendMessage(context.TODO(), chatRef, *body)
+ if err != nil {
+ return nil, err
+ }
+ return message, nil
+}
+
+func (jsonclient *TeamsJSONClient) ListMyChats(_ map[string]interface{}) (interface{}, error) {
+ chats, err := jsonclient.client.Chats.ListMyJoined(context.TODO())
+ if err != nil {
+ return nil, err
+ }
+ return chats, nil
}
diff --git a/python/teams_lib_pzsp2_z1/services/chats.py b/python/teams_lib_pzsp2_z1/services/chats.py
index ae55f9e..baec592 100644
--- a/python/teams_lib_pzsp2_z1/services/chats.py
+++ b/python/teams_lib_pzsp2_z1/services/chats.py
@@ -10,3 +10,34 @@
class ChatsService(BaseService):
+ def list_my_joined(self) -> list[Chat]:
+ response = self.client.execute(
+ cmd_type="request",
+ method="listMyJoinedChats",
+ params={},
+ )
+ return [
+ Chat(
+ ID=chat["ID"],
+ Type=ChatType(chat["Type"]),
+ IsHidden=(True if chat["IsHidden"] else False),
+ Topic=chat["Topic"],
+ )
+ for chat in response
+ ]
+
+ def create_one_on_one(self, user_id: str) -> Chat:
+ response = self.client.execute(
+ cmd_type="request",
+ method="createOneOnOneChat",
+ params={
+ "userID": user_id,
+ },
+ )
+
+ return Chat(
+ ID=response["ID"],
+ Type=ChatType(response["Type"]),
+ IsHidden=(True if response["IsHidden"] else False),
+ Topic=response["Topic"],
+ )
\ No newline at end of file
From 83c5824f4151220af96ccd606e299cf9873b8011 Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Wed, 31 Dec 2025 13:39:14 +0100
Subject: [PATCH 32/99] feat: added chat endpoints to bridge
---
.../json-client/decoders/decode-chatRef.go | 2 +
go/internal/json-client/requests-chats.go | 143 +++++++++++++++++-
2 files changed, 142 insertions(+), 3 deletions(-)
diff --git a/go/internal/json-client/decoders/decode-chatRef.go b/go/internal/json-client/decoders/decode-chatRef.go
index bd46c4d..c9c9c0c 100644
--- a/go/internal/json-client/decoders/decode-chatRef.go
+++ b/go/internal/json-client/decoders/decode-chatRef.go
@@ -26,3 +26,5 @@ func GetChatRef(dto ChatRefDTO) (chats.ChatRef, error) {
return nil, fmt.Errorf("unknown chat type: %s", dto.Type)
}
}
+
+
diff --git a/go/internal/json-client/requests-chats.go b/go/internal/json-client/requests-chats.go
index 00c2c8d..1356d7a 100644
--- a/go/internal/json-client/requests-chats.go
+++ b/go/internal/json-client/requests-chats.go
@@ -2,6 +2,7 @@ package jsonclient
import (
"context"
+ "time"
"github.com/pzsp-teams/lib-python/internal/json-client/decoders"
"github.com/pzsp-teams/lib/chats"
@@ -149,10 +150,146 @@ func (jsonclient *TeamsJSONClient) SendMessageInChat(p map[string]interface{}) (
return message, nil
}
-func (jsonclient *TeamsJSONClient) ListMyChats(_ map[string]interface{}) (interface{}, error) {
- chats, err := jsonclient.client.Chats.ListMyJoined(context.TODO())
+type messageInChatParams struct {
+ ChatRef decoders.ChatRefDTO `json:"chatRef"`
+ MessageID string `json:"messageId"`
+}
+
+func (jsonclient *TeamsJSONClient) DeleteMessageInChat(p map[string]interface{}) (interface{}, error) {
+ params, err := decoders.DecodeParams[messageInChatParams](p)
+ if err != nil {
+ return nil, err
+ }
+ chatRef, err := decoders.GetChatRef(params.ChatRef)
+ if err != nil {
+ return nil, err
+ }
+ err = jsonclient.client.Chats.DeleteMessage(context.TODO(), chatRef, params.MessageID)
+ if err != nil {
+ return nil, err
+ }
+ return "deleted", nil
+}
+
+func (jsonclient *TeamsJSONClient) GetMessageInChat(p map[string]interface{}) (interface{}, error) {
+ params, err := decoders.DecodeParams[messageInChatParams](p)
+ if err != nil {
+ return nil, err
+ }
+ chatRef, err := decoders.GetChatRef(params.ChatRef)
+ if err != nil {
+ return nil, err
+ }
+ message, err := jsonclient.client.Chats.GetMessage(context.TODO(), chatRef, params.MessageID)
+ if err != nil {
+ return nil, err
+ }
+ return message, nil
+}
+
+type ListMyChatsParams struct {
+ ChatType string `json:"chatType"`
+}
+
+func (jsonclient *TeamsJSONClient) ListMyChats(p map[string]interface{}) (interface{}, error) {
+ params, err := decoders.DecodeParams[ListMyChatsParams](p)
+ if err != nil {
+ return nil, err
+ }
+ var chatType models.ChatType
+ if params.ChatType != "" {
+ chatType = models.ChatType(params.ChatType)
+ }
+ chatsList, err := jsonclient.client.Chats.ListChats(context.TODO(), &chatType)
+ if err != nil {
+ return nil, err
+ }
+ return chatsList, nil
+}
+
+type listChatMessagesParams struct {
+ StartTime time.Time `json:"startTime"`
+ EndTime time.Time `json:"endTime"`
+ Top *int32 `json:"top"`
+}
+
+func (jsonclient *TeamsJSONClient) ListMyChatMessages(p map[string]interface{}) (interface{}, error) {
+ params, err := decoders.DecodeParams[listChatMessagesParams](p)
+ if err != nil {
+ return nil, err
+ }
+ messages, err := jsonclient.client.Chats.ListAllMessages(context.TODO(), ¶ms.StartTime, ¶ms.EndTime, params.Top)
+ if err != nil {
+ return nil, err
+ }
+ return messages, nil
+}
+
+func (jsonclient *TeamsJSONClient) ListPinnedMessagesInChat(p map[string]interface{}) (interface{}, error) {
+ params, err := decoders.DecodeParams[baseChatParams](p)
+ if err != nil {
+ return nil, err
+ }
+ chatRef, err := decoders.GetChatRef(params.ChatRef)
+ if err != nil {
+ return nil, err
+ }
+ messages, err := jsonclient.client.Chats.ListPinnedMessages(context.TODO(), chatRef)
+ if err != nil {
+ return nil, err
+ }
+ return messages, nil
+}
+
+func (jsonclient *TeamsJSONClient) PinMessageInChat(p map[string]interface{}) (interface{}, error) {
+ params, err := decoders.DecodeParams[messageInChatParams](p)
+ if err != nil {
+ return nil, err
+ }
+ chatRef, err := decoders.GetChatRef(params.ChatRef)
+ if err != nil {
+ return nil, err
+ }
+ err = jsonclient.client.Chats.PinMessage(context.TODO(), chatRef, params.MessageID)
+ if err != nil {
+ return nil, err
+ }
+ return "pinned", nil
+}
+
+func (jsonclient *TeamsJSONClient) UnpinMessageInChat(p map[string]interface{}) (interface{}, error) {
+ params, err := decoders.DecodeParams[messageInChatParams](p)
+ if err != nil {
+ return nil, err
+ }
+ chatRef, err := decoders.GetChatRef(params.ChatRef)
+ if err != nil {
+ return nil, err
+ }
+ err = jsonclient.client.Chats.UnpinMessage(context.TODO(), chatRef, params.MessageID)
+ if err != nil {
+ return nil, err
+ }
+ return "unpinned", nil
+}
+
+type mentionInChatParams struct {
+ ChatRef decoders.ChatRefDTO `json:"chatRef"`
+ RawMentions []string `json:"rawMentions"`
+}
+
+func (jsonclient *TeamsJSONClient) GetMentionsInChat(p map[string]interface{}) (interface{}, error) {
+ params, err := decoders.DecodeParams[mentionInChatParams](p)
+ if err != nil {
+ return nil, err
+ }
+ chatRef, err := decoders.GetChatRef(params.ChatRef)
+ if err != nil {
+ return nil, err
+ }
+ mentions, err := jsonclient.client.Chats.GetMentions(context.TODO(), chatRef, params.RawMentions)
if err != nil {
return nil, err
}
- return chats, nil
+ return mentions, nil
}
From a3ce10bdb858801cf40050f02bc281192f4877bc Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Wed, 31 Dec 2025 13:42:10 +0100
Subject: [PATCH 33/99] feat: adjusted channels endpoints to lib changes
---
go/internal/json-client/requests-channels.go | 44 +++++++++++++++++++-
1 file changed, 42 insertions(+), 2 deletions(-)
diff --git a/go/internal/json-client/requests-channels.go b/go/internal/json-client/requests-channels.go
index d0f9659..1f522ec 100644
--- a/go/internal/json-client/requests-channels.go
+++ b/go/internal/json-client/requests-channels.go
@@ -89,8 +89,8 @@ func (jsonclient *TeamsJSONClient) DeleteChannel(p map[string]interface{}) (inte
}
type sendMessageToChannelParams struct {
- TeamRef string `json:"teamRef"`
- ChannelRef string `json:"channelRef"`
+ TeamRef string `json:"teamRef"`
+ ChannelRef string `json:"channelRef"`
Body decoders.MessageBodyDTO `json:"body"`
}
@@ -110,6 +110,29 @@ func (jsonclient *TeamsJSONClient) SendMessageToChannel(p map[string]interface{}
return sentMessage, nil
}
+type sendReplyToChannelParams struct {
+ TeamRef string `json:"teamRef"`
+ ChannelRef string `json:"channelRef"`
+ MessageID string `json:"messageId"`
+ Body decoders.MessageBodyDTO `json:"body"`
+}
+
+func (jsonclient *TeamsJSONClient) SendReplyToChannel(p map[string]interface{}) (interface{}, error) {
+ params, err := decoders.DecodeParams[sendReplyToChannelParams](p)
+ if err != nil {
+ return nil, err
+ }
+ body, err := decoders.DecodeParams[models.MessageBody](¶ms.Body)
+ if err != nil {
+ return nil, err
+ }
+ sentMessage, err := jsonclient.client.Channels.SendReply(context.TODO(), params.TeamRef, params.ChannelRef, params.MessageID, *body)
+ if err != nil {
+ return nil, err
+ }
+ return sentMessage, nil
+}
+
type listMessagesOptionsDTO struct {
Top *int32 `json:"top"`
ExpandReplies bool `json:"expandReplies"`
@@ -254,3 +277,20 @@ func (jsonclient *TeamsJSONClient) RemoveMemberFromChannel(p map[string]interfac
return "removed", nil
}
+type getMentionsInChannelParams struct {
+ TeamRef string `json:"teamRef"`
+ ChannelRef string `json:"channelRef"`
+ RawMentions []string `json:"rawMentions"`
+}
+
+func (jsonclient *TeamsJSONClient) GetMentionsInChannel(p map[string]interface{}) (interface{}, error) {
+ params, err := decoders.DecodeParams[getMentionsInChannelParams](p)
+ if err != nil {
+ return nil, err
+ }
+ mentions, err := jsonclient.client.Channels.GetMentions(context.TODO(), params.TeamRef, params.ChannelRef, params.RawMentions)
+ if err != nil {
+ return nil, err
+ }
+ return mentions, nil
+}
From e3da1b674a3d716abe098ac87979dd6561ed96d8 Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Wed, 31 Dec 2025 13:46:57 +0100
Subject: [PATCH 34/99] feat: adjusted requests map
---
go/internal/json-client/request-map.go | 86 ++++++++++++++++++-----
go/internal/json-client/requests-chats.go | 2 +-
2 files changed, 68 insertions(+), 20 deletions(-)
diff --git a/go/internal/json-client/request-map.go b/go/internal/json-client/request-map.go
index b91027f..8bdcf99 100644
--- a/go/internal/json-client/request-map.go
+++ b/go/internal/json-client/request-map.go
@@ -62,6 +62,30 @@ var Handlers = map[string]HandlerFunc{
return c.DeleteChannel(p)
},
+ "sendMessageToChannel": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ return c.SendMessageToChannel(p)
+ },
+
+ "sendReplyToChannel": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ return c.SendReplyToChannel(p)
+ },
+
+ "listMessagesInChannel": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ return c.ListMessagesInChannel(p)
+ },
+
+ "getMessageInChannel": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ return c.GetMessageInChannel(p)
+ },
+
+ "listMessageRepliesInChannel": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ return c.ListMessageRepliesInChannel(p)
+ },
+
+ "getMessageReplyInChannel": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ return c.GetMessageReplyInChannel(p)
+ },
+
"listChannelMembers": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
return c.ListChannelMembers(p)
},
@@ -78,48 +102,72 @@ var Handlers = map[string]HandlerFunc{
return c.UpdateMemberInChannel(p)
},
- "sendMessageToChannel": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
- return c.SendMessageToChannel(p)
+ "getMentionsInChannel": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ return c.GetMentionsInChannel(p)
},
- "listMessagesInChannel": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
- return c.ListMessagesInChannel(p)
+ // CHATS
+ "createOneOnOneChat": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ return c.CreateOneToOneChat(p)
},
- "getMessageInChannel": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
- return c.GetMessageInChannel(p)
+ "createGroupChat": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ return c.CreateGroupChat(p)
},
- "listMessageRepliesInChannel": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
- return c.ListMessageRepliesInChannel(p)
+ "addMemberGroupToChat": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ return c.AddMemberGroupToChat(p)
},
- "getMessageReplyInChannel": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
- return c.GetMessageReplyInChannel(p)
+ "removeMemberFromGroupChat": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ return c.RemoveMemberFromGroupChat(p)
},
- // CHATS
- "createOneOnOneChat": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
- return c.CreateOneToOneChat(p)
+ "listMembersInGroupChat": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ return c.ListGroupChatMembers(p)
},
- "createGroupChat": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
- return c.CreateGroupChat(p)
+ "updateGroupChatTopic": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ return c.UpdateGroupChatTopic(p)
+ },
+
+ "listMessagesInChat": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ return c.ListMessagesInChat(p)
},
"sendMessageInChat": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
return c.SendMessageInChat(p)
},
+ "deleteMessageInChat": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ return c.DeleteMessageInChat(p)
+ },
+
+ "getMessageInChat": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ return c.GetMessageInChat(p)
+ },
+
"listMyChats": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
return c.ListMyChats(p)
},
- "listMembersInChat": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
- return c.ListMembersInChat(p)
+ "listMyChatMessages": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ return c.ListMyChatMessages(p)
+ },
+
+ "listPinnedMessagesInChat": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ return c.ListPinnedMessagesInChat(p)
+ },
+
+ "pinMessageInChat": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ return c.PinMessageInChat(p)
+ },
+
+ "unpinMessageInChat": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ return c.UnpinMessageInChat(p)
},
- "addMemberToChat": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
- return c.AddMemberToChat(p)
+ "getMentionsInChat": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ return c.GetMentionsInChat(p)
},
}
diff --git a/go/internal/json-client/requests-chats.go b/go/internal/json-client/requests-chats.go
index 1356d7a..448314d 100644
--- a/go/internal/json-client/requests-chats.go
+++ b/go/internal/json-client/requests-chats.go
@@ -60,7 +60,7 @@ func (jsonclient *TeamsJSONClient) AddMemberGroupToChat(p map[string]interface{}
return member, nil
}
-func (jsonclient *TeamsJSONClient) RemoveMemberToGroupChat(p map[string]interface{}) (interface{}, error) {
+func (jsonclient *TeamsJSONClient) RemoveMemberFromGroupChat(p map[string]interface{}) (interface{}, error) {
params, err := decoders.DecodeParams[groupChatMemberParams](p)
if err != nil {
return nil, err
From b83ea849fa50d812f7430a28efeb35aa799161a4 Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Wed, 31 Dec 2025 13:55:41 +0100
Subject: [PATCH 35/99] refactor: requests schema
---
go/internal/json-client/requests-channels.go | 252 +++++----------
go/internal/json-client/requests-chats.go | 316 +++++++------------
go/internal/json-client/requests-teams.go | 133 +++-----
go/internal/json-client/teams-json-client.go | 12 +-
4 files changed, 244 insertions(+), 469 deletions(-)
diff --git a/go/internal/json-client/requests-channels.go b/go/internal/json-client/requests-channels.go
index 1f522ec..e9d390c 100644
--- a/go/internal/json-client/requests-channels.go
+++ b/go/internal/json-client/requests-channels.go
@@ -11,16 +11,10 @@ type listChannelsParams struct {
TeamRef string `json:"teamRef"`
}
-func (jsonclient *TeamsJSONClient) ListChannels(p map[string]interface{}) (interface{}, error) {
- params, err := decoders.DecodeParams[listChannelsParams](p)
- if err != nil {
- return nil, err
- }
- channels, err := jsonclient.client.Channels.ListChannels(context.TODO(), params.TeamRef)
- if err != nil {
- return nil, err
- }
- return channels, nil
+func (c *TeamsJSONClient) ListChannels(p map[string]interface{}) (interface{}, error) {
+ return execute(p, func(params listChannelsParams) (interface{}, error) {
+ return c.client.Channels.ListChannels(context.TODO(), params.TeamRef)
+ })
}
type baseChannelParams struct {
@@ -28,16 +22,10 @@ type baseChannelParams struct {
ChannelRef string `json:"channelRef"`
}
-func (jsonclient *TeamsJSONClient) GetChannel(p map[string]interface{}) (interface{}, error) {
- params, err := decoders.DecodeParams[baseChannelParams](p)
- if err != nil {
- return nil, err
- }
- channel, err := jsonclient.client.Channels.Get(context.TODO(), params.TeamRef, params.ChannelRef)
- if err != nil {
- return nil, err
- }
- return channel, nil
+func (c *TeamsJSONClient) GetChannel(p map[string]interface{}) (interface{}, error) {
+ return execute(p, func(params baseChannelParams) (interface{}, error) {
+ return c.client.Channels.Get(context.TODO(), params.TeamRef, params.ChannelRef)
+ })
}
type createChannelParams struct {
@@ -45,16 +33,10 @@ type createChannelParams struct {
Name string `json:"name"`
}
-func (jsonclient *TeamsJSONClient) CreateStandardChannel(p map[string]interface{}) (interface{}, error) {
- params, err := decoders.DecodeParams[createChannelParams](p)
- if err != nil {
- return nil, err
- }
- channel, err := jsonclient.client.Channels.CreateStandardChannel(context.TODO(), params.TeamRef, params.Name)
- if err != nil {
- return nil, err
- }
- return channel, nil
+func (c *TeamsJSONClient) CreateStandardChannel(p map[string]interface{}) (interface{}, error) {
+ return execute(p, func(params createChannelParams) (interface{}, error) {
+ return c.client.Channels.CreateStandardChannel(context.TODO(), params.TeamRef, params.Name)
+ })
}
type createPrivateChannelParams struct {
@@ -64,28 +46,17 @@ type createPrivateChannelParams struct {
OwnerRefs []string `json:"ownerRefs"`
}
-func (jsonclient *TeamsJSONClient) CreatePrivateChannel(p map[string]interface{}) (interface{}, error) {
- params, err := decoders.DecodeParams[createPrivateChannelParams](p)
- if err != nil {
- return nil, err
- }
- channel, err := jsonclient.client.Channels.CreatePrivateChannel(context.TODO(), params.TeamRef, params.Name, params.MemberRefs, params.OwnerRefs)
- if err != nil {
- return nil, err
- }
- return channel, nil
+func (c *TeamsJSONClient) CreatePrivateChannel(p map[string]interface{}) (interface{}, error) {
+ return execute(p, func(params createPrivateChannelParams) (interface{}, error) {
+ return c.client.Channels.CreatePrivateChannel(context.TODO(), params.TeamRef, params.Name, params.MemberRefs, params.OwnerRefs)
+ })
}
-func (jsonclient *TeamsJSONClient) DeleteChannel(p map[string]interface{}) (interface{}, error) {
- params, err := decoders.DecodeParams[baseChannelParams](p)
- if err != nil {
- return nil, err
- }
- err = jsonclient.client.Channels.Delete(context.TODO(), params.TeamRef, params.ChannelRef)
- if err != nil {
- return nil, err
- }
- return "deleted", nil
+func (c *TeamsJSONClient) DeleteChannel(p map[string]interface{}) (interface{}, error) {
+ return execute(p, func(params baseChannelParams) (interface{}, error) {
+ err := c.client.Channels.Delete(context.TODO(), params.TeamRef, params.ChannelRef)
+ return "deleted", err
+ })
}
type sendMessageToChannelParams struct {
@@ -94,20 +65,14 @@ type sendMessageToChannelParams struct {
Body decoders.MessageBodyDTO `json:"body"`
}
-func (jsonclient *TeamsJSONClient) SendMessageToChannel(p map[string]interface{}) (interface{}, error) {
- params, err := decoders.DecodeParams[sendMessageToChannelParams](p)
- if err != nil {
- return nil, err
- }
- body, err := decoders.DecodeParams[models.MessageBody](¶ms.Body)
- if err != nil {
- return nil, err
- }
- sentMessage, err := jsonclient.client.Channels.SendMessage(context.TODO(), params.TeamRef, params.ChannelRef, *body)
- if err != nil {
- return nil, err
- }
- return sentMessage, nil
+func (c *TeamsJSONClient) SendMessageToChannel(p map[string]interface{}) (interface{}, error) {
+ return execute(p, func(params sendMessageToChannelParams) (interface{}, error) {
+ body, err := decoders.DecodeParams[models.MessageBody](¶ms.Body)
+ if err != nil {
+ return nil, err
+ }
+ return c.client.Channels.SendMessage(context.TODO(), params.TeamRef, params.ChannelRef, *body)
+ })
}
type sendReplyToChannelParams struct {
@@ -117,20 +82,14 @@ type sendReplyToChannelParams struct {
Body decoders.MessageBodyDTO `json:"body"`
}
-func (jsonclient *TeamsJSONClient) SendReplyToChannel(p map[string]interface{}) (interface{}, error) {
- params, err := decoders.DecodeParams[sendReplyToChannelParams](p)
- if err != nil {
- return nil, err
- }
- body, err := decoders.DecodeParams[models.MessageBody](¶ms.Body)
- if err != nil {
- return nil, err
- }
- sentMessage, err := jsonclient.client.Channels.SendReply(context.TODO(), params.TeamRef, params.ChannelRef, params.MessageID, *body)
- if err != nil {
- return nil, err
- }
- return sentMessage, nil
+func (c *TeamsJSONClient) SendReplyToChannel(p map[string]interface{}) (interface{}, error) {
+ return execute(p, func(params sendReplyToChannelParams) (interface{}, error) {
+ body, err := decoders.DecodeParams[models.MessageBody](¶ms.Body)
+ if err != nil {
+ return nil, err
+ }
+ return c.client.Channels.SendReply(context.TODO(), params.TeamRef, params.ChannelRef, params.MessageID, *body)
+ })
}
type listMessagesOptionsDTO struct {
@@ -144,20 +103,14 @@ type listMessagesParams struct {
Options listMessagesOptionsDTO `json:"options"`
}
-func (jsonclient *TeamsJSONClient) ListMessagesInChannel(p map[string]interface{}) (interface{}, error) {
- params, err := decoders.DecodeParams[listMessagesParams](p)
- if err != nil {
- return nil, err
- }
- options, err := decoders.DecodeParams[models.ListMessagesOptions](params.Options)
- if err != nil {
- return nil, err
- }
- messages, err := jsonclient.client.Channels.ListMessages(context.TODO(), params.TeamRef, params.ChannelRef, options)
- if err != nil {
- return nil, err
- }
- return messages, nil
+func (c *TeamsJSONClient) ListMessagesInChannel(p map[string]interface{}) (interface{}, error) {
+ return execute(p, func(params listMessagesParams) (interface{}, error) {
+ options, err := decoders.DecodeParams[models.ListMessagesOptions](params.Options)
+ if err != nil {
+ return nil, err
+ }
+ return c.client.Channels.ListMessages(context.TODO(), params.TeamRef, params.ChannelRef, options)
+ })
}
type getMessageParams struct {
@@ -166,16 +119,10 @@ type getMessageParams struct {
MessageID string `json:"messageId"`
}
-func (jsonclient *TeamsJSONClient) GetMessageInChannel(p map[string]interface{}) (interface{}, error) {
- params, err := decoders.DecodeParams[getMessageParams](p)
- if err != nil {
- return nil, err
- }
- message, err := jsonclient.client.Channels.GetMessage(context.TODO(), params.TeamRef, params.ChannelRef, params.MessageID)
- if err != nil {
- return nil, err
- }
- return message, nil
+func (c *TeamsJSONClient) GetMessageInChannel(p map[string]interface{}) (interface{}, error) {
+ return execute(p, func(params getMessageParams) (interface{}, error) {
+ return c.client.Channels.GetMessage(context.TODO(), params.TeamRef, params.ChannelRef, params.MessageID)
+ })
}
type listRepliesParams struct {
@@ -185,16 +132,10 @@ type listRepliesParams struct {
Top *int32 `json:"top"`
}
-func (jsonclient *TeamsJSONClient) ListMessageRepliesInChannel(p map[string]interface{}) (interface{}, error) {
- params, err := decoders.DecodeParams[listRepliesParams](p)
- if err != nil {
- return nil, err
- }
- messages, err := jsonclient.client.Channels.ListReplies(context.TODO(), params.TeamRef, params.ChannelRef, params.MessageID, params.Top)
- if err != nil {
- return nil, err
- }
- return messages, nil
+func (c *TeamsJSONClient) ListMessageRepliesInChannel(p map[string]interface{}) (interface{}, error) {
+ return execute(p, func(params listRepliesParams) (interface{}, error) {
+ return c.client.Channels.ListReplies(context.TODO(), params.TeamRef, params.ChannelRef, params.MessageID, params.Top)
+ })
}
type getReplyParams struct {
@@ -204,28 +145,16 @@ type getReplyParams struct {
ReplyID string `json:"replyId"`
}
-func (jsonclient *TeamsJSONClient) GetMessageReplyInChannel(p map[string]interface{}) (interface{}, error) {
- params, err := decoders.DecodeParams[getReplyParams](p)
- if err != nil {
- return nil, err
- }
- message, err := jsonclient.client.Channels.GetReply(context.TODO(), params.TeamRef, params.ChannelRef, params.MessageID, params.ReplyID)
- if err != nil {
- return nil, err
- }
- return message, nil
+func (c *TeamsJSONClient) GetMessageReplyInChannel(p map[string]interface{}) (interface{}, error) {
+ return execute(p, func(params getReplyParams) (interface{}, error) {
+ return c.client.Channels.GetReply(context.TODO(), params.TeamRef, params.ChannelRef, params.MessageID, params.ReplyID)
+ })
}
-func (jsonclient *TeamsJSONClient) ListChannelMembers(p map[string]interface{}) (interface{}, error) {
- params, err := decoders.DecodeParams[baseChannelParams](p)
- if err != nil {
- return nil, err
- }
- members, err := jsonclient.client.Channels.ListMembers(context.TODO(), params.TeamRef, params.ChannelRef)
- if err != nil {
- return nil, err
- }
- return members, nil
+func (c *TeamsJSONClient) ListChannelMembers(p map[string]interface{}) (interface{}, error) {
+ return execute(p, func(params baseChannelParams) (interface{}, error) {
+ return c.client.Channels.ListMembers(context.TODO(), params.TeamRef, params.ChannelRef)
+ })
}
type addOrUpdateMemberToChannelParams struct {
@@ -235,28 +164,16 @@ type addOrUpdateMemberToChannelParams struct {
IsOwner bool `json:"isOwner"`
}
-func (jsonclient *TeamsJSONClient) AddMemberToChannel(p map[string]interface{}) (interface{}, error) {
- params, err := decoders.DecodeParams[addOrUpdateMemberToChannelParams](p)
- if err != nil {
- return nil, err
- }
- member, err := jsonclient.client.Channels.AddMember(context.TODO(), params.TeamRef, params.ChannelRef, params.UserRef, params.IsOwner)
- if err != nil {
- return nil, err
- }
- return member, nil
+func (c *TeamsJSONClient) AddMemberToChannel(p map[string]interface{}) (interface{}, error) {
+ return execute(p, func(params addOrUpdateMemberToChannelParams) (interface{}, error) {
+ return c.client.Channels.AddMember(context.TODO(), params.TeamRef, params.ChannelRef, params.UserRef, params.IsOwner)
+ })
}
-func (jsonclient *TeamsJSONClient) UpdateMemberInChannel(p map[string]interface{}) (interface{}, error) {
- params, err := decoders.DecodeParams[addOrUpdateMemberToChannelParams](p)
- if err != nil {
- return nil, err
- }
- member, err := jsonclient.client.Channels.UpdateMemberRole(context.TODO(), params.TeamRef, params.ChannelRef, params.UserRef, params.IsOwner)
- if err != nil {
- return nil, err
- }
- return member, nil
+func (c *TeamsJSONClient) UpdateMemberInChannel(p map[string]interface{}) (interface{}, error) {
+ return execute(p, func(params addOrUpdateMemberToChannelParams) (interface{}, error) {
+ return c.client.Channels.UpdateMemberRole(context.TODO(), params.TeamRef, params.ChannelRef, params.UserRef, params.IsOwner)
+ })
}
type removeMemberFromChannelParams struct {
@@ -265,16 +182,11 @@ type removeMemberFromChannelParams struct {
UserRef string `json:"userRef"`
}
-func (jsonclient *TeamsJSONClient) RemoveMemberFromChannel(p map[string]interface{}) (interface{}, error) {
- params, err := decoders.DecodeParams[removeMemberFromChannelParams](p)
- if err != nil {
- return nil, err
- }
- err = jsonclient.client.Channels.RemoveMember(context.TODO(), params.TeamRef, params.ChannelRef, params.UserRef)
- if err != nil {
- return nil, err
- }
- return "removed", nil
+func (c *TeamsJSONClient) RemoveMemberFromChannel(p map[string]interface{}) (interface{}, error) {
+ return execute(p, func(params removeMemberFromChannelParams) (interface{}, error) {
+ err := c.client.Channels.RemoveMember(context.TODO(), params.TeamRef, params.ChannelRef, params.UserRef)
+ return "removed", err
+ })
}
type getMentionsInChannelParams struct {
@@ -283,14 +195,8 @@ type getMentionsInChannelParams struct {
RawMentions []string `json:"rawMentions"`
}
-func (jsonclient *TeamsJSONClient) GetMentionsInChannel(p map[string]interface{}) (interface{}, error) {
- params, err := decoders.DecodeParams[getMentionsInChannelParams](p)
- if err != nil {
- return nil, err
- }
- mentions, err := jsonclient.client.Channels.GetMentions(context.TODO(), params.TeamRef, params.ChannelRef, params.RawMentions)
- if err != nil {
- return nil, err
- }
- return mentions, nil
-}
+func (c *TeamsJSONClient) GetMentionsInChannel(p map[string]interface{}) (interface{}, error) {
+ return execute(p, func(params getMentionsInChannelParams) (interface{}, error) {
+ return c.client.Channels.GetMentions(context.TODO(), params.TeamRef, params.ChannelRef, params.RawMentions)
+ })
+}
\ No newline at end of file
diff --git a/go/internal/json-client/requests-chats.go b/go/internal/json-client/requests-chats.go
index 448314d..39cb588 100644
--- a/go/internal/json-client/requests-chats.go
+++ b/go/internal/json-client/requests-chats.go
@@ -13,16 +13,10 @@ type createOneToOneChatParams struct {
RecipientRef string `json:"recipientRef"`
}
-func (jsonclient *TeamsJSONClient) CreateOneToOneChat(p map[string]interface{}) (interface{}, error) {
- params, err := decoders.DecodeParams[createOneToOneChatParams](p)
- if err != nil {
- return nil, err
- }
- chat, err := jsonclient.client.Chats.CreateOneOneOne(context.TODO(), params.RecipientRef)
- if err != nil {
- return nil, err
- }
- return chat, nil
+func (c *TeamsJSONClient) CreateOneToOneChat(p map[string]interface{}) (interface{}, error) {
+ return execute(p, func(params createOneToOneChatParams) (interface{}, error) {
+ return c.client.Chats.CreateOneOneOne(context.TODO(), params.RecipientRef)
+ })
}
type createGroupChatParams struct {
@@ -31,16 +25,10 @@ type createGroupChatParams struct {
IncludeMe bool `json:"includeMe"`
}
-func (jsonclient *TeamsJSONClient) CreateGroupChat(p map[string]interface{}) (interface{}, error) {
- params, err := decoders.DecodeParams[createGroupChatParams](p)
- if err != nil {
- return nil, err
- }
- chat, err := jsonclient.client.Chats.CreateGroup(context.TODO(), params.RecipientRefs, params.Topic, params.IncludeMe)
- if err != nil {
- return nil, err
- }
- return chat, nil
+func (c *TeamsJSONClient) CreateGroupChat(p map[string]interface{}) (interface{}, error) {
+ return execute(p, func(params createGroupChatParams) (interface{}, error) {
+ return c.client.Chats.CreateGroup(context.TODO(), params.RecipientRefs, params.Topic, params.IncludeMe)
+ })
}
type groupChatMemberParams struct {
@@ -48,44 +36,27 @@ type groupChatMemberParams struct {
UserRef string `json:"userRef"`
}
-func (jsonclient *TeamsJSONClient) AddMemberGroupToChat(p map[string]interface{}) (interface{}, error) {
- params, err := decoders.DecodeParams[groupChatMemberParams](p)
- if err != nil {
- return nil, err
- }
- member, err := jsonclient.client.Chats.AddMemberToGroupChat(context.TODO(), chats.GroupChatRef{Ref: params.GroupChatRef}, params.UserRef)
- if err != nil {
- return nil, err
- }
- return member, nil
+func (c *TeamsJSONClient) AddMemberGroupToChat(p map[string]interface{}) (interface{}, error) {
+ return execute(p, func(params groupChatMemberParams) (interface{}, error) {
+ return c.client.Chats.AddMemberToGroupChat(context.TODO(), chats.GroupChatRef{Ref: params.GroupChatRef}, params.UserRef)
+ })
}
-func (jsonclient *TeamsJSONClient) RemoveMemberFromGroupChat(p map[string]interface{}) (interface{}, error) {
- params, err := decoders.DecodeParams[groupChatMemberParams](p)
- if err != nil {
- return nil, err
- }
- err = jsonclient.client.Chats.RemoveMemberFromGroupChat(context.TODO(), chats.GroupChatRef{Ref: params.GroupChatRef}, params.UserRef)
- if err != nil {
- return nil, err
- }
- return "removed", nil
+func (c *TeamsJSONClient) RemoveMemberFromGroupChat(p map[string]interface{}) (interface{}, error) {
+ return execute(p, func(params groupChatMemberParams) (interface{}, error) {
+ err := c.client.Chats.RemoveMemberFromGroupChat(context.TODO(), chats.GroupChatRef{Ref: params.GroupChatRef}, params.UserRef)
+ return "removed", err
+ })
}
type listMembersInChatParams struct {
GroupChatRef string `json:"groupChatRef"`
}
-func (jsonclient *TeamsJSONClient) ListGroupChatMembers(p map[string]interface{}) (interface{}, error) {
- params, err := decoders.DecodeParams[listMembersInChatParams](p)
- if err != nil {
- return nil, err
- }
- members, err := jsonclient.client.Chats.ListGroupChatMembers(context.TODO(), chats.GroupChatRef{Ref: params.GroupChatRef})
- if err != nil {
- return nil, err
- }
- return members, nil
+func (c *TeamsJSONClient) ListGroupChatMembers(p map[string]interface{}) (interface{}, error) {
+ return execute(p, func(params listMembersInChatParams) (interface{}, error) {
+ return c.client.Chats.ListGroupChatMembers(context.TODO(), chats.GroupChatRef{Ref: params.GroupChatRef})
+ })
}
type updateGroupChatTopicParams struct {
@@ -93,36 +64,24 @@ type updateGroupChatTopicParams struct {
Topic string `json:"topic"`
}
-func (jsonclient *TeamsJSONClient) UpdateGroupChatTopic(p map[string]interface{}) (interface{}, error) {
- params, err := decoders.DecodeParams[updateGroupChatTopicParams](p)
- if err != nil {
- return nil, err
- }
- chat, err := jsonclient.client.Chats.UpdateGroupChatTopic(context.TODO(), chats.GroupChatRef{Ref: params.GroupChatRef}, params.Topic)
- if err != nil {
- return nil, err
- }
- return chat, nil
+func (c *TeamsJSONClient) UpdateGroupChatTopic(p map[string]interface{}) (interface{}, error) {
+ return execute(p, func(params updateGroupChatTopicParams) (interface{}, error) {
+ return c.client.Chats.UpdateGroupChatTopic(context.TODO(), chats.GroupChatRef{Ref: params.GroupChatRef}, params.Topic)
+ })
}
type baseChatParams struct {
ChatRef decoders.ChatRefDTO `json:"chatRef"`
}
-func (jsonclient *TeamsJSONClient) ListMessagesInChat(p map[string]interface{}) (interface{}, error) {
- params, err := decoders.DecodeParams[baseChatParams](p)
- if err != nil {
- return nil, err
- }
- chatRef, err := decoders.GetChatRef(params.ChatRef)
- if err != nil {
- return nil, err
- }
- messages, err := jsonclient.client.Chats.ListMessages(context.TODO(), chatRef)
- if err != nil {
- return nil, err
- }
- return messages, nil
+func (c *TeamsJSONClient) ListMessagesInChat(p map[string]interface{}) (interface{}, error) {
+ return execute(p, func(params baseChatParams) (interface{}, error) {
+ chatRef, err := decoders.GetChatRef(params.ChatRef)
+ if err != nil {
+ return nil, err
+ }
+ return c.client.Chats.ListMessages(context.TODO(), chatRef)
+ })
}
type sendMessageInChatParams struct {
@@ -130,24 +89,18 @@ type sendMessageInChatParams struct {
Body decoders.MessageBodyDTO `json:"body"`
}
-func (jsonclient *TeamsJSONClient) SendMessageInChat(p map[string]interface{}) (interface{}, error) {
- params, err := decoders.DecodeParams[sendMessageInChatParams](p)
- if err != nil {
- return nil, err
- }
- chatRef, err := decoders.GetChatRef(params.ChatRef)
- if err != nil {
- return nil, err
- }
- body, err := decoders.DecodeParams[models.MessageBody](¶ms.Body)
- if err != nil {
- return nil, err
- }
- message, err := jsonclient.client.Chats.SendMessage(context.TODO(), chatRef, *body)
- if err != nil {
- return nil, err
- }
- return message, nil
+func (c *TeamsJSONClient) SendMessageInChat(p map[string]interface{}) (interface{}, error) {
+ return execute(p, func(params sendMessageInChatParams) (interface{}, error) {
+ chatRef, err := decoders.GetChatRef(params.ChatRef)
+ if err != nil {
+ return nil, err
+ }
+ body, err := decoders.DecodeParams[models.MessageBody](params.Body)
+ if err != nil {
+ return nil, err
+ }
+ return c.client.Chats.SendMessage(context.TODO(), chatRef, *body)
+ })
}
type messageInChatParams struct {
@@ -155,56 +108,39 @@ type messageInChatParams struct {
MessageID string `json:"messageId"`
}
-func (jsonclient *TeamsJSONClient) DeleteMessageInChat(p map[string]interface{}) (interface{}, error) {
- params, err := decoders.DecodeParams[messageInChatParams](p)
- if err != nil {
- return nil, err
- }
- chatRef, err := decoders.GetChatRef(params.ChatRef)
- if err != nil {
- return nil, err
- }
- err = jsonclient.client.Chats.DeleteMessage(context.TODO(), chatRef, params.MessageID)
- if err != nil {
- return nil, err
- }
- return "deleted", nil
+func (c *TeamsJSONClient) DeleteMessageInChat(p map[string]interface{}) (interface{}, error) {
+ return execute(p, func(params messageInChatParams) (interface{}, error) {
+ chatRef, err := decoders.GetChatRef(params.ChatRef)
+ if err != nil {
+ return nil, err
+ }
+ err = c.client.Chats.DeleteMessage(context.TODO(), chatRef, params.MessageID)
+ return "deleted", err
+ })
}
-func (jsonclient *TeamsJSONClient) GetMessageInChat(p map[string]interface{}) (interface{}, error) {
- params, err := decoders.DecodeParams[messageInChatParams](p)
- if err != nil {
- return nil, err
- }
- chatRef, err := decoders.GetChatRef(params.ChatRef)
- if err != nil {
- return nil, err
- }
- message, err := jsonclient.client.Chats.GetMessage(context.TODO(), chatRef, params.MessageID)
- if err != nil {
- return nil, err
- }
- return message, nil
+func (c *TeamsJSONClient) GetMessageInChat(p map[string]interface{}) (interface{}, error) {
+ return execute(p, func(params messageInChatParams) (interface{}, error) {
+ chatRef, err := decoders.GetChatRef(params.ChatRef)
+ if err != nil {
+ return nil, err
+ }
+ return c.client.Chats.GetMessage(context.TODO(), chatRef, params.MessageID)
+ })
}
type ListMyChatsParams struct {
ChatType string `json:"chatType"`
}
-func (jsonclient *TeamsJSONClient) ListMyChats(p map[string]interface{}) (interface{}, error) {
- params, err := decoders.DecodeParams[ListMyChatsParams](p)
- if err != nil {
- return nil, err
- }
- var chatType models.ChatType
- if params.ChatType != "" {
- chatType = models.ChatType(params.ChatType)
- }
- chatsList, err := jsonclient.client.Chats.ListChats(context.TODO(), &chatType)
- if err != nil {
- return nil, err
- }
- return chatsList, nil
+func (c *TeamsJSONClient) ListMyChats(p map[string]interface{}) (interface{}, error) {
+ return execute(p, func(params ListMyChatsParams) (interface{}, error) {
+ var chatType models.ChatType
+ if params.ChatType != "" {
+ chatType = models.ChatType(params.ChatType)
+ }
+ return c.client.Chats.ListChats(context.TODO(), &chatType)
+ })
}
type listChatMessagesParams struct {
@@ -213,83 +149,55 @@ type listChatMessagesParams struct {
Top *int32 `json:"top"`
}
-func (jsonclient *TeamsJSONClient) ListMyChatMessages(p map[string]interface{}) (interface{}, error) {
- params, err := decoders.DecodeParams[listChatMessagesParams](p)
- if err != nil {
- return nil, err
- }
- messages, err := jsonclient.client.Chats.ListAllMessages(context.TODO(), ¶ms.StartTime, ¶ms.EndTime, params.Top)
- if err != nil {
- return nil, err
- }
- return messages, nil
+func (c *TeamsJSONClient) ListMyChatMessages(p map[string]interface{}) (interface{}, error) {
+ return execute(p, func(params listChatMessagesParams) (interface{}, error) {
+ return c.client.Chats.ListAllMessages(context.TODO(), ¶ms.StartTime, ¶ms.EndTime, params.Top)
+ })
}
-func (jsonclient *TeamsJSONClient) ListPinnedMessagesInChat(p map[string]interface{}) (interface{}, error) {
- params, err := decoders.DecodeParams[baseChatParams](p)
- if err != nil {
- return nil, err
- }
- chatRef, err := decoders.GetChatRef(params.ChatRef)
- if err != nil {
- return nil, err
- }
- messages, err := jsonclient.client.Chats.ListPinnedMessages(context.TODO(), chatRef)
- if err != nil {
- return nil, err
- }
- return messages, nil
+func (c *TeamsJSONClient) ListPinnedMessagesInChat(p map[string]interface{}) (interface{}, error) {
+ return execute(p, func(params baseChatParams) (interface{}, error) {
+ chatRef, err := decoders.GetChatRef(params.ChatRef)
+ if err != nil {
+ return nil, err
+ }
+ return c.client.Chats.ListPinnedMessages(context.TODO(), chatRef)
+ })
}
-func (jsonclient *TeamsJSONClient) PinMessageInChat(p map[string]interface{}) (interface{}, error) {
- params, err := decoders.DecodeParams[messageInChatParams](p)
- if err != nil {
- return nil, err
- }
- chatRef, err := decoders.GetChatRef(params.ChatRef)
- if err != nil {
- return nil, err
- }
- err = jsonclient.client.Chats.PinMessage(context.TODO(), chatRef, params.MessageID)
- if err != nil {
- return nil, err
- }
- return "pinned", nil
+func (c *TeamsJSONClient) PinMessageInChat(p map[string]interface{}) (interface{}, error) {
+ return execute(p, func(params messageInChatParams) (interface{}, error) {
+ chatRef, err := decoders.GetChatRef(params.ChatRef)
+ if err != nil {
+ return nil, err
+ }
+ err = c.client.Chats.PinMessage(context.TODO(), chatRef, params.MessageID)
+ return "pinned", err
+ })
}
-func (jsonclient *TeamsJSONClient) UnpinMessageInChat(p map[string]interface{}) (interface{}, error) {
- params, err := decoders.DecodeParams[messageInChatParams](p)
- if err != nil {
- return nil, err
- }
- chatRef, err := decoders.GetChatRef(params.ChatRef)
- if err != nil {
- return nil, err
- }
- err = jsonclient.client.Chats.UnpinMessage(context.TODO(), chatRef, params.MessageID)
- if err != nil {
- return nil, err
- }
- return "unpinned", nil
+func (c *TeamsJSONClient) UnpinMessageInChat(p map[string]interface{}) (interface{}, error) {
+ return execute(p, func(params messageInChatParams) (interface{}, error) {
+ chatRef, err := decoders.GetChatRef(params.ChatRef)
+ if err != nil {
+ return nil, err
+ }
+ err = c.client.Chats.UnpinMessage(context.TODO(), chatRef, params.MessageID)
+ return "unpinned", err
+ })
}
type mentionInChatParams struct {
- ChatRef decoders.ChatRefDTO `json:"chatRef"`
- RawMentions []string `json:"rawMentions"`
-}
-
-func (jsonclient *TeamsJSONClient) GetMentionsInChat(p map[string]interface{}) (interface{}, error) {
- params, err := decoders.DecodeParams[mentionInChatParams](p)
- if err != nil {
- return nil, err
- }
- chatRef, err := decoders.GetChatRef(params.ChatRef)
- if err != nil {
- return nil, err
- }
- mentions, err := jsonclient.client.Chats.GetMentions(context.TODO(), chatRef, params.RawMentions)
- if err != nil {
- return nil, err
- }
- return mentions, nil
+ ChatRef decoders.ChatRefDTO `json:"chatRef"`
+ RawMentions []string `json:"rawMentions"`
+}
+
+func (c *TeamsJSONClient) GetMentionsInChat(p map[string]interface{}) (interface{}, error) {
+ return execute(p, func(params mentionInChatParams) (interface{}, error) {
+ chatRef, err := decoders.GetChatRef(params.ChatRef)
+ if err != nil {
+ return nil, err
+ }
+ return c.client.Chats.GetMentions(context.TODO(), chatRef, params.RawMentions)
+ })
}
diff --git a/go/internal/json-client/requests-teams.go b/go/internal/json-client/requests-teams.go
index 0c8a3d4..8cbc140 100644
--- a/go/internal/json-client/requests-teams.go
+++ b/go/internal/json-client/requests-teams.go
@@ -10,24 +10,14 @@ type getTeamParams struct {
TeamRef string `json:"teamRef"`
}
-func (jsonclient *TeamsJSONClient) GetTeam(p map[string]interface{}) (interface{}, error) {
- params, err := decoders.DecodeParams[getTeamParams](p)
- if err != nil {
- return nil, err
- }
- team, err := jsonclient.client.Teams.Get(context.TODO(), params.TeamRef)
- if err != nil {
- return nil, err
- }
- return team, nil
-}
-
-func (jsonclient *TeamsJSONClient) ListMyJoined() (interface{}, error) {
- teams, err := jsonclient.client.Teams.ListMyJoined(context.TODO())
- if err != nil {
- return nil, err
- }
- return teams, nil
+func (c *TeamsJSONClient) GetTeam(p map[string]interface{}) (interface{}, error) {
+ return execute(p, func(params getTeamParams) (interface{}, error) {
+ return c.client.Teams.Get(context.TODO(), params.TeamRef)
+ })
+}
+
+func (c *TeamsJSONClient) ListMyJoined() (interface{}, error) {
+ return c.client.Teams.ListMyJoined(context.TODO())
}
type updateTeamParams struct {
@@ -35,17 +25,11 @@ type updateTeamParams struct {
TeamPatch decoders.TeamPatchDTO `json:"team"`
}
-func (jsonclient *TeamsJSONClient) UpdateTeam(p map[string]interface{}) (interface{}, error) {
- params, err := decoders.DecodeParams[updateTeamParams](p)
- msTeam := decoders.GetMSTeam(¶ms.TeamPatch)
- if err != nil {
- return nil, err
- }
- updatedTeam, err := jsonclient.client.Teams.Update(context.TODO(), params.TeamRef, msTeam)
- if err != nil {
- return nil, err
- }
- return updatedTeam, nil
+func (c *TeamsJSONClient) UpdateTeam(p map[string]interface{}) (interface{}, error) {
+ return execute(p, func(params updateTeamParams) (interface{}, error) {
+ msTeam := decoders.GetMSTeam(¶ms.TeamPatch)
+ return c.client.Teams.Update(context.TODO(), params.TeamRef, msTeam)
+ })
}
type createViaGroupParams struct {
@@ -54,16 +38,10 @@ type createViaGroupParams struct {
Visibility string `json:"visibility"`
}
-func (jsonclient *TeamsJSONClient) CreateTeamViaGroup(p map[string]interface{}) (interface{}, error) {
- params, err := decoders.DecodeParams[createViaGroupParams](p)
- if err != nil {
- return nil, err
- }
- createdTeam, err := jsonclient.client.Teams.CreateViaGroup(context.TODO(), params.DisplayName, params.MailNickname, params.Visibility)
- if err != nil {
- return nil, err
- }
- return createdTeam, nil
+func (c *TeamsJSONClient) CreateTeamViaGroup(p map[string]interface{}) (interface{}, error) {
+ return execute(p, func(params createViaGroupParams) (interface{}, error) {
+ return c.client.Teams.CreateViaGroup(context.TODO(), params.DisplayName, params.MailNickname, params.Visibility)
+ })
}
type createFromTemplateParams struct {
@@ -72,79 +50,52 @@ type createFromTemplateParams struct {
Owners []string `json:"owners"`
}
-func (jsonclient *TeamsJSONClient) CreateTeamFromTemplate(p map[string]interface{}) (interface{}, error) {
- params, err := decoders.DecodeParams[createFromTemplateParams](p)
- if err != nil {
- return nil, err
- }
- createdTeam, err := jsonclient.client.Teams.CreateFromTemplate(context.TODO(), params.DisplayName, params.Description, params.Owners)
- if err != nil {
- return nil, err
- }
- return createdTeam, nil
+func (c *TeamsJSONClient) CreateTeamFromTemplate(p map[string]interface{}) (interface{}, error) {
+ return execute(p, func(params createFromTemplateParams) (interface{}, error) {
+ return c.client.Teams.CreateFromTemplate(context.TODO(), params.DisplayName, params.Description, params.Owners)
+ })
}
type archiveTeamParams struct {
- TeamRef string `json:"teamRef"`
+ TeamRef string `json:"teamRef"`
SpoReadOnlyFromMembers *bool `json:"spoReadOnlyFromMembers,omitempty"`
}
-func (jsonclient *TeamsJSONClient) ArchiveTeam(p map[string]interface{}) (interface{}, error) {
- params, err := decoders.DecodeParams[archiveTeamParams](p)
- if err != nil {
- return nil, err
- }
- err = jsonclient.client.Teams.Archive(context.TODO(), params.TeamRef, params.SpoReadOnlyFromMembers)
- if err != nil {
- return nil, err
- }
- return "archived", nil
+func (c *TeamsJSONClient) ArchiveTeam(p map[string]interface{}) (interface{}, error) {
+ return execute(p, func(params archiveTeamParams) (interface{}, error) {
+ err := c.client.Teams.Archive(context.TODO(), params.TeamRef, params.SpoReadOnlyFromMembers)
+ return "archived", err
+ })
}
type unarchiveTeamParams struct {
TeamRef string `json:"teamRef"`
}
-func (jsonclient *TeamsJSONClient) UnarchiveTeam(p map[string]interface{}) (interface{}, error) {
- params, err := decoders.DecodeParams[unarchiveTeamParams](p)
- if err != nil {
- return nil, err
- }
- err = jsonclient.client.Teams.Unarchive(context.TODO(), params.TeamRef)
- if err != nil {
- return nil, err
- }
- return "unarchived", nil
+func (c *TeamsJSONClient) UnarchiveTeam(p map[string]interface{}) (interface{}, error) {
+ return execute(p, func(params unarchiveTeamParams) (interface{}, error) {
+ err := c.client.Teams.Unarchive(context.TODO(), params.TeamRef)
+ return "unarchived", err
+ })
}
type deleteTeamParams struct {
TeamRef string `json:"teamRef"`
}
-func (jsonclient *TeamsJSONClient) DeleteTeam(p map[string]interface{}) (interface{}, error) {
- params, err := decoders.DecodeParams[deleteTeamParams](p)
- if err != nil {
- return nil, err
- }
- err = jsonclient.client.Teams.Delete(context.TODO(), params.TeamRef)
- if err != nil {
- return nil, err
- }
- return "deleted", nil
+func (c *TeamsJSONClient) DeleteTeam(p map[string]interface{}) (interface{}, error) {
+ return execute(p, func(params deleteTeamParams) (interface{}, error) {
+ err := c.client.Teams.Delete(context.TODO(), params.TeamRef)
+ return "deleted", err
+ })
}
type restoreTeamParams struct {
DeletedGroupID string `json:"deletedGroupId"`
}
-func (jsonclient *TeamsJSONClient) RestoreDeletedTeam(p map[string]interface{}) (interface{}, error) {
- params, err := decoders.DecodeParams[restoreTeamParams](p)
- if err != nil {
- return nil, err
- }
- restoredTeam, err := jsonclient.client.Teams.RestoreDeleted(context.TODO(), params.DeletedGroupID)
- if err != nil {
- return nil, err
- }
- return restoredTeam, nil
-}
+func (c *TeamsJSONClient) RestoreDeletedTeam(p map[string]interface{}) (interface{}, error) {
+ return execute(p, func(params restoreTeamParams) (interface{}, error) {
+ return c.client.Teams.RestoreDeleted(context.TODO(), params.DeletedGroupID)
+ })
+}
\ No newline at end of file
diff --git a/go/internal/json-client/teams-json-client.go b/go/internal/json-client/teams-json-client.go
index afe9430..20b87a8 100644
--- a/go/internal/json-client/teams-json-client.go
+++ b/go/internal/json-client/teams-json-client.go
@@ -1,9 +1,19 @@
package jsonclient
import (
- lib "github.com/pzsp-teams/lib"
+ "github.com/pzsp-teams/lib"
+
+ "github.com/pzsp-teams/lib-python/internal/json-client/decoders"
)
type TeamsJSONClient struct {
client *lib.Client
}
+
+func execute[T any](p map[string]interface{}, fn func(params T) (interface{}, error)) (interface{}, error) {
+ params, err := decoders.DecodeParams[T](p)
+ if err != nil {
+ return nil, err
+ }
+ return fn(*params)
+}
From 2a0944bff1b681b94f7160a3adf0b811bfb1f560 Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Thu, 1 Jan 2026 18:51:32 +0100
Subject: [PATCH 36/99] chore: tried to fix resolving user bug (failed)
---
go/internal/json-client/requests-channels.go | 14 +++++++++++++-
python/teams_lib_pzsp2_z1/client.py | 2 ++
python/tests/fake_server/data.py | 20 ++++++--------------
python/tests/test_teams.py | 2 +-
4 files changed, 22 insertions(+), 16 deletions(-)
diff --git a/go/internal/json-client/requests-channels.go b/go/internal/json-client/requests-channels.go
index e9d390c..f249de1 100644
--- a/go/internal/json-client/requests-channels.go
+++ b/go/internal/json-client/requests-channels.go
@@ -2,6 +2,8 @@ package jsonclient
import (
"context"
+ "fmt"
+ "os"
"github.com/pzsp-teams/lib-python/internal/json-client/decoders"
"github.com/pzsp-teams/lib/models"
@@ -172,6 +174,11 @@ func (c *TeamsJSONClient) AddMemberToChannel(p map[string]interface{}) (interfac
func (c *TeamsJSONClient) UpdateMemberInChannel(p map[string]interface{}) (interface{}, error) {
return execute(p, func(params addOrUpdateMemberToChannelParams) (interface{}, error) {
+
+ // --- LOGOWANIE NA STDERR ---
+ fmt.Fprintf(os.Stderr, "\n[GO DEBUG] UpdateMemberInChannel PARAMS:\n -> TeamRef: '%s'\n -> ChannelRef: '%s'\n -> UserRef: '%s'\n -> IsOwner: %v\n",
+ params.TeamRef, params.ChannelRef, params.UserRef, params.IsOwner)
+ // ---------------------------
return c.client.Channels.UpdateMemberRole(context.TODO(), params.TeamRef, params.ChannelRef, params.UserRef, params.IsOwner)
})
}
@@ -184,6 +191,11 @@ type removeMemberFromChannelParams struct {
func (c *TeamsJSONClient) RemoveMemberFromChannel(p map[string]interface{}) (interface{}, error) {
return execute(p, func(params removeMemberFromChannelParams) (interface{}, error) {
+
+ // --- LOGOWANIE NA STDERR ---
+ fmt.Fprintf(os.Stderr, "\n[GO DEBUG] RemoveMemberFromChannel PARAMS:\n -> TeamRef: '%s'\n -> ChannelRef: '%s'\n -> UserRef: '%s'\n",
+ params.TeamRef, params.ChannelRef, params.UserRef)
+ // ---------------------------
err := c.client.Channels.RemoveMember(context.TODO(), params.TeamRef, params.ChannelRef, params.UserRef)
return "removed", err
})
@@ -199,4 +211,4 @@ func (c *TeamsJSONClient) GetMentionsInChannel(p map[string]interface{}) (interf
return execute(p, func(params getMentionsInChannelParams) (interface{}, error) {
return c.client.Channels.GetMentions(context.TODO(), params.TeamRef, params.ChannelRef, params.RawMentions)
})
-}
\ No newline at end of file
+}
diff --git a/python/teams_lib_pzsp2_z1/client.py b/python/teams_lib_pzsp2_z1/client.py
index 7a0e76c..b64c63a 100644
--- a/python/teams_lib_pzsp2_z1/client.py
+++ b/python/teams_lib_pzsp2_z1/client.py
@@ -106,6 +106,8 @@ def execute(
self.proc.stdin.write(json_payload + "\n")
self.proc.stdin.flush()
+ print("Sent to Go process:", json_payload) # Debug print
+
raw_response = self.proc.stdout.readline()
except BrokenPipeError:
raise RuntimeError("Go process crashed or closed connection") # noqa: B904
diff --git a/python/tests/fake_server/data.py b/python/tests/fake_server/data.py
index 27e22ca..bcc22a2 100644
--- a/python/tests/fake_server/data.py
+++ b/python/tests/fake_server/data.py
@@ -44,8 +44,8 @@ def __init__(self) -> FakeServerData:
"team-123-abc": [
Channel(
ID="19:123123@thread.tacv2",
- Name="General",
- IsGeneral=True,
+ Name="Something",
+ IsGeneral=False,
),
Channel(
ID="19:999999@thread.tacv2",
@@ -171,11 +171,15 @@ def get_myJoinedTeams_response(self) -> dict:
def get_listChannels_response(self, team_id: str) -> dict:
return {
+ "@odata.context": f"https://graph.microsoft.com/v1.0/$metadata#teams('{team_id}')/channels",
"value": [
{
+ "@odata.type": "#microsoft.graph.channel",
"id": channel.ID,
"displayName": channel.Name,
"isGeneral": channel.IsGeneral,
+ "membershipType": "standard",
+ "email": ""
}
for channel in self.channels.get(team_id, [])
],
@@ -509,15 +513,3 @@ def get_remove_member_response(self, team_id: str, channel_id: str, member_id: s
channel_members.remove(member)
return {"success": True}
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/python/tests/test_teams.py b/python/tests/test_teams.py
index 6b1873a..a234c9f 100644
--- a/python/tests/test_teams.py
+++ b/python/tests/test_teams.py
@@ -1,4 +1,4 @@
-from python.teams_lib_pzsp2_z1.model.team import MSTeamsUpdate
+from teams_lib_pzsp2_z1.model.team import MSTeamsUpdate
from teams_lib_pzsp2_z1.client import TeamsClient
from tests.init_fake_client import init_fake_client
from tests.fake_server.setup import setup_fake_server
From 8f306804364b937b757e222563e82c46c7834ba2 Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Thu, 1 Jan 2026 19:08:08 +0100
Subject: [PATCH 37/99] feat: added majority of chats request in python client
---
go/internal/json-client/request-map.go | 4 +-
go/internal/json-client/requests-chats.go | 2 +-
python/teams_lib_pzsp2_z1/model/chat.py | 5 +
python/teams_lib_pzsp2_z1/services/chats.py | 309 +++++++++++++++++++-
4 files changed, 303 insertions(+), 17 deletions(-)
diff --git a/go/internal/json-client/request-map.go b/go/internal/json-client/request-map.go
index 8bdcf99..68bd08f 100644
--- a/go/internal/json-client/request-map.go
+++ b/go/internal/json-client/request-map.go
@@ -115,8 +115,8 @@ var Handlers = map[string]HandlerFunc{
return c.CreateGroupChat(p)
},
- "addMemberGroupToChat": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
- return c.AddMemberGroupToChat(p)
+ "addMemberToGroupChat": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ return c.AddMemberToGroupChat(p)
},
"removeMemberFromGroupChat": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
diff --git a/go/internal/json-client/requests-chats.go b/go/internal/json-client/requests-chats.go
index 39cb588..f843158 100644
--- a/go/internal/json-client/requests-chats.go
+++ b/go/internal/json-client/requests-chats.go
@@ -36,7 +36,7 @@ type groupChatMemberParams struct {
UserRef string `json:"userRef"`
}
-func (c *TeamsJSONClient) AddMemberGroupToChat(p map[string]interface{}) (interface{}, error) {
+func (c *TeamsJSONClient) AddMemberToGroupChat(p map[string]interface{}) (interface{}, error) {
return execute(p, func(params groupChatMemberParams) (interface{}, error) {
return c.client.Chats.AddMemberToGroupChat(context.TODO(), chats.GroupChatRef{Ref: params.GroupChatRef}, params.UserRef)
})
diff --git a/python/teams_lib_pzsp2_z1/model/chat.py b/python/teams_lib_pzsp2_z1/model/chat.py
index 7224aed..206bc83 100644
--- a/python/teams_lib_pzsp2_z1/model/chat.py
+++ b/python/teams_lib_pzsp2_z1/model/chat.py
@@ -13,3 +13,8 @@ class Chat:
Type: ChatType
IsHidden: bool
Topic: str
+
+@dataclass
+class ChatRef:
+ Ref: str
+ type: ChatType
diff --git a/python/teams_lib_pzsp2_z1/services/chats.py b/python/teams_lib_pzsp2_z1/services/chats.py
index baec592..4042559 100644
--- a/python/teams_lib_pzsp2_z1/services/chats.py
+++ b/python/teams_lib_pzsp2_z1/services/chats.py
@@ -1,4 +1,4 @@
-from teams_lib_pzsp2_z1.model.chat import Chat, ChatType
+from teams_lib_pzsp2_z1.model.chat import Chat, ChatType, ChatRef
from teams_lib_pzsp2_z1.model.member import Member
from teams_lib_pzsp2_z1.model.message import (
Message,
@@ -8,30 +8,105 @@
)
from teams_lib_pzsp2_z1.services.base_service import BaseService
+from datetime import datetime
+
class ChatsService(BaseService):
- def list_my_joined(self) -> list[Chat]:
+ def create_one_on_one(self, recipient_ref: str) -> Chat:
response = self.client.execute(
cmd_type="request",
- method="listMyJoinedChats",
- params={},
+ method="createOneOnOneChat",
+ params={
+ "recipientRef": recipient_ref,
+ },
+ )
+
+ return Chat(
+ ID=response["ID"],
+ Type=ChatType(response["Type"]),
+ IsHidden=(True if response["IsHidden"] else False),
+ Topic=response["Topic"],
+ )
+
+ def create_group_chat(
+ self, recipient_refs: list[str], topic: str, include_me: bool
+ ) -> Chat:
+ response = self.client.execute(
+ cmd_type="request",
+ method="createGroupChat",
+ params={
+ "recipientRefs": recipient_refs,
+ "topic": topic,
+ "includeMe": include_me,
+ },
+ )
+
+ return Chat(
+ ID=response["ID"],
+ Type=ChatType(response["Type"]),
+ IsHidden=(True if response["IsHidden"] else False),
+ Topic=response["Topic"],
+ )
+
+ def add_member_to_group_chat(self, group_chat_ref: str, user_ref: str) -> Member:
+ member = self.client.execute(
+ cmd_type="request",
+ method="addMemberToGroupChat",
+ params={
+ "groupChatRef": group_chat_ref,
+ "userRef": user_ref,
+ },
+ )
+
+ return Member(
+ ID=member["ID"],
+ DisplayName=member["DisplayName"],
+ UserRef=member["UserRef"],
+ Roles=member["Roles"],
+ Email=member["Email"],
)
+
+ def remove_member_from_group_chat(
+ self, group_chat_ref: str, member_ref: str
+ ) -> bool:
+ result = self.client.execute(
+ cmd_type="request",
+ method="removeMemberFromGroupChat",
+ params={
+ "groupChatRef": group_chat_ref,
+ "userRef": member_ref,
+ },
+ )
+
+ return result == "removed"
+
+ def list_group_chat_members(self, group_chat_ref: str) -> list[Member]:
+ members = self.client.execute(
+ cmd_type="request",
+ method="listGroupChatMembers",
+ params={
+ "groupChatRef": group_chat_ref,
+ },
+ )
+
return [
- Chat(
- ID=chat["ID"],
- Type=ChatType(chat["Type"]),
- IsHidden=(True if chat["IsHidden"] else False),
- Topic=chat["Topic"],
+ Member(
+ ID=member["ID"],
+ DisplayName=member["DisplayName"],
+ UserRef=member["UserRef"],
+ Roles=member["Roles"],
+ Email=member["Email"],
)
- for chat in response
+ for member in members
]
- def create_one_on_one(self, user_id: str) -> Chat:
+ def update_group_chat_topic(self, group_chat_ref: str, new_topic: str) -> Chat:
response = self.client.execute(
cmd_type="request",
- method="createOneOnOneChat",
+ method="updateGroupChatTopic",
params={
- "userID": user_id,
+ "groupChatRef": group_chat_ref,
+ "topic": new_topic,
},
)
@@ -40,4 +115,210 @@ def create_one_on_one(self, user_id: str) -> Chat:
Type=ChatType(response["Type"]),
IsHidden=(True if response["IsHidden"] else False),
Topic=response["Topic"],
- )
\ No newline at end of file
+ )
+
+ def list_messages(self, chat_ref: str) -> list[Message]:
+ messages = self.client.execute(
+ cmd_type="request",
+ method="listChatMessages",
+ params={
+ "chatRef": chat_ref,
+ },
+ )
+
+ return [
+ Message(
+ ID=message["ID"],
+ Body=MessageBody(
+ Content=message["Body"]["Content"],
+ ContentType=MessageContentType(message["Body"]["ContentType"]),
+ ),
+ From=MessageFrom(
+ UserRef=message["From"]["UserRef"],
+ DisplayName=message["From"]["DisplayName"],
+ ),
+ CreatedDateTime=message["CreatedDateTime"],
+ )
+ for message in messages
+ ]
+
+ def send_message(self, chat_ref: ChatRef, body: MessageBody) -> Message:
+ message = self.client.execute(
+ cmd_type="request",
+ method="sendMessageToChat",
+ params={
+ "chatRef": {
+ "ref": chat_ref.Ref,
+ "type": chat_ref.type.value,
+ },
+ "body": {
+ "content": body.Content,
+ "contentType": body.ContentType.value,
+ },
+ },
+ )
+
+ return Message(
+ ID=message["ID"],
+ Body=MessageBody(
+ Content=message["Body"]["Content"],
+ ContentType=MessageContentType(message["Body"]["ContentType"]),
+ ),
+ From=MessageFrom(
+ UserRef=message["From"]["UserRef"],
+ DisplayName=message["From"]["DisplayName"],
+ ),
+ CreatedDateTime=message["CreatedDateTime"],
+ )
+
+ def delete_message(self, chat_ref: ChatRef, message_id: str) -> bool:
+ result = self.client.execute(
+ cmd_type="request",
+ method="deleteMessageFromChat",
+ params={
+ "chatRef": {
+ "ref": chat_ref.Ref,
+ "type": chat_ref.type.value,
+ },
+ "messageID": message_id,
+ },
+ )
+
+ return result == "deleted"
+
+ def get_message(self, chat_ref: ChatRef, message_id: str) -> Message:
+ message = self.client.execute(
+ cmd_type="request",
+ method="getMessageFromChat",
+ params={
+ "chatRef": {
+ "ref": chat_ref.Ref,
+ "type": chat_ref.type.value,
+ },
+ "messageID": message_id,
+ },
+ )
+
+ return Message(
+ ID=message["ID"],
+ Body=MessageBody(
+ Content=message["Body"]["Content"],
+ ContentType=MessageContentType(message["Body"]["ContentType"]),
+ ),
+ From=MessageFrom(
+ UserRef=message["From"]["UserRef"],
+ DisplayName=message["From"]["DisplayName"],
+ ),
+ CreatedDateTime=message["CreatedDateTime"],
+ )
+
+ def list_my_joined(self, chat_type: ChatType | None = None) -> list[Chat]:
+ params = {}
+ if chat_type:
+ params["chatType"] = chat_type.value
+
+ chats = self.client.execute(
+ cmd_type="request",
+ method="listMyJoinedChats",
+ params=params,
+ )
+
+ return [
+ Chat(
+ ID=chat["ID"],
+ Type=ChatType(chat["Type"]),
+ IsHidden=(True if chat["IsHidden"] else False),
+ Topic=chat["Topic"],
+ )
+ for chat in chats
+ ]
+
+ def list_all_messages(
+ self, start_time: datetime, end_time: datetime, top: int
+ ) -> list[Message]:
+ messages = self.client.execute(
+ cmd_type="request",
+ method="listAllMessages",
+ params={
+ "startTime": start_time.isoformat(),
+ "endTime": end_time.isoformat(),
+ "top": top,
+ },
+ )
+
+ return [
+ Message(
+ ID=message["ID"],
+ Body=MessageBody(
+ Content=message["Body"]["Content"],
+ ContentType=MessageContentType(message["Body"]["ContentType"]),
+ ),
+ From=MessageFrom(
+ UserRef=message["From"]["UserRef"],
+ DisplayName=message["From"]["DisplayName"],
+ ),
+ CreatedDateTime=message["CreatedDateTime"],
+ )
+ for message in messages
+ ]
+
+ def list_pinned_messages(self, chat_ref: ChatRef) -> list[Message]:
+ messages = self.client.execute(
+ cmd_type="request",
+ method="listPinnedMessages",
+ params={
+ "chatRef": {
+ "ref": chat_ref.Ref,
+ "type": chat_ref.type.value,
+ },
+ },
+ )
+
+ return [
+ Message(
+ ID=message["ID"],
+ Body=MessageBody(
+ Content=message["Body"]["Content"],
+ ContentType=MessageContentType(message["Body"]["ContentType"]),
+ ),
+ From=MessageFrom(
+ UserRef=message["From"]["UserRef"],
+ DisplayName=message["From"]["DisplayName"],
+ ),
+ CreatedDateTime=message["CreatedDateTime"],
+ )
+ for message in messages
+ ]
+
+ def pin_message(self, chat_ref: ChatRef, message_id: str) -> bool:
+ result = self.client.execute(
+ cmd_type="request",
+ method="pinMessageInChat",
+ params={
+ "chatRef": {
+ "ref": chat_ref.Ref,
+ "type": chat_ref.type.value,
+ },
+ "messageID": message_id,
+ },
+ )
+
+ return result == "pinned"
+
+
+ def unpin_message(self, chat_ref: ChatRef, message_id: str) -> bool:
+ result = self.client.execute(
+ cmd_type="request",
+ method="unpinMessageInChat",
+ params={
+ "chatRef": {
+ "ref": chat_ref.Ref,
+ "type": chat_ref.type.value,
+ },
+ "messageID": message_id,
+ },
+ )
+
+ return result == "unpinned"
+
+ # def get_mentions(self, chat_ref: ChatRef, raw_mentions: list[str])
From e1202fd8de9dd6c056061291f361a1a1db3950f1 Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Thu, 1 Jan 2026 19:16:09 +0100
Subject: [PATCH 38/99] feat: implemented get mentions handling
---
python/teams_lib_pzsp2_z1/model/mention.py | 16 +++++++
python/teams_lib_pzsp2_z1/model/message.py | 3 ++
.../teams_lib_pzsp2_z1/services/channels.py | 15 +++++++
python/teams_lib_pzsp2_z1/services/chats.py | 45 ++++++++++++++-----
4 files changed, 67 insertions(+), 12 deletions(-)
create mode 100644 python/teams_lib_pzsp2_z1/model/mention.py
diff --git a/python/teams_lib_pzsp2_z1/model/mention.py b/python/teams_lib_pzsp2_z1/model/mention.py
new file mode 100644
index 0000000..361df1b
--- /dev/null
+++ b/python/teams_lib_pzsp2_z1/model/mention.py
@@ -0,0 +1,16 @@
+from dataclasses import dataclass
+from enum import Enum
+
+
+class MentionKind(Enum):
+ USER = "user"
+ CHANNEL = "channel"
+ TEAM = "team"
+ EVERYONE = "everyone"
+
+@dataclass
+class Mention:
+ Kind: MentionKind
+ AtID: int
+ Text: str
+ TargetID: str
diff --git a/python/teams_lib_pzsp2_z1/model/message.py b/python/teams_lib_pzsp2_z1/model/message.py
index 5ff9a4d..52f8139 100644
--- a/python/teams_lib_pzsp2_z1/model/message.py
+++ b/python/teams_lib_pzsp2_z1/model/message.py
@@ -2,6 +2,8 @@
from datetime import datetime
from enum import Enum
+from teams_lib_pzsp2_z1.model.mention import Mention
+
class MessageContentType(Enum):
TEXT = "text"
@@ -28,6 +30,7 @@ class Message:
class MessageBody:
ContentType: MessageContentType
Content: str
+ Mentions: list[Mention]
def __dict__(self):
return {
diff --git a/python/teams_lib_pzsp2_z1/services/channels.py b/python/teams_lib_pzsp2_z1/services/channels.py
index c4c5172..084e529 100644
--- a/python/teams_lib_pzsp2_z1/services/channels.py
+++ b/python/teams_lib_pzsp2_z1/services/channels.py
@@ -1,5 +1,6 @@
from teams_lib_pzsp2_z1.model.channel import Channel
from teams_lib_pzsp2_z1.model.member import Member
+from teams_lib_pzsp2_z1.model.mention import Mention
from teams_lib_pzsp2_z1.model.message import (
Message,
MessageBody,
@@ -290,3 +291,17 @@ def remove_member(self, teamRef: str, channelRef: str, userRef: str) -> bool:
},
)
return response == "removed"
+
+ def get_mentions(
+ self, teamRef: str, channelRef: str, rawMentions: list[str]
+ ) -> list[Mention]:
+ response = self.client.execute(
+ cmd_type="request",
+ method="getChannelMentions",
+ params={
+ "teamRef": teamRef,
+ "channelRef": channelRef,
+ "rawMentions": rawMentions,
+ },
+ )
+ return [Mention(**mention) for mention in response]
diff --git a/python/teams_lib_pzsp2_z1/services/chats.py b/python/teams_lib_pzsp2_z1/services/chats.py
index 4042559..5838203 100644
--- a/python/teams_lib_pzsp2_z1/services/chats.py
+++ b/python/teams_lib_pzsp2_z1/services/chats.py
@@ -1,5 +1,8 @@
-from teams_lib_pzsp2_z1.model.chat import Chat, ChatType, ChatRef
+from datetime import datetime
+
+from teams_lib_pzsp2_z1.model.chat import Chat, ChatRef, ChatType
from teams_lib_pzsp2_z1.model.member import Member
+from teams_lib_pzsp2_z1.model.mention import Mention
from teams_lib_pzsp2_z1.model.message import (
Message,
MessageBody,
@@ -8,8 +11,6 @@
)
from teams_lib_pzsp2_z1.services.base_service import BaseService
-from datetime import datetime
-
class ChatsService(BaseService):
def create_one_on_one(self, recipient_ref: str) -> Chat:
@@ -83,7 +84,7 @@ def remove_member_from_group_chat(
def list_group_chat_members(self, group_chat_ref: str) -> list[Member]:
members = self.client.execute(
cmd_type="request",
- method="listGroupChatMembers",
+ method="listMembersInGroupChat",
params={
"groupChatRef": group_chat_ref,
},
@@ -120,7 +121,7 @@ def update_group_chat_topic(self, group_chat_ref: str, new_topic: str) -> Chat:
def list_messages(self, chat_ref: str) -> list[Message]:
messages = self.client.execute(
cmd_type="request",
- method="listChatMessages",
+ method="listMessagesInChat",
params={
"chatRef": chat_ref,
},
@@ -145,7 +146,7 @@ def list_messages(self, chat_ref: str) -> list[Message]:
def send_message(self, chat_ref: ChatRef, body: MessageBody) -> Message:
message = self.client.execute(
cmd_type="request",
- method="sendMessageToChat",
+ method="sendMessageInChat",
params={
"chatRef": {
"ref": chat_ref.Ref,
@@ -174,7 +175,7 @@ def send_message(self, chat_ref: ChatRef, body: MessageBody) -> Message:
def delete_message(self, chat_ref: ChatRef, message_id: str) -> bool:
result = self.client.execute(
cmd_type="request",
- method="deleteMessageFromChat",
+ method="deleteMessageInChat",
params={
"chatRef": {
"ref": chat_ref.Ref,
@@ -189,7 +190,7 @@ def delete_message(self, chat_ref: ChatRef, message_id: str) -> bool:
def get_message(self, chat_ref: ChatRef, message_id: str) -> Message:
message = self.client.execute(
cmd_type="request",
- method="getMessageFromChat",
+ method="getMessageInChat",
params={
"chatRef": {
"ref": chat_ref.Ref,
@@ -219,7 +220,7 @@ def list_my_joined(self, chat_type: ChatType | None = None) -> list[Chat]:
chats = self.client.execute(
cmd_type="request",
- method="listMyJoinedChats",
+ method="listMyChats",
params=params,
)
@@ -238,7 +239,7 @@ def list_all_messages(
) -> list[Message]:
messages = self.client.execute(
cmd_type="request",
- method="listAllMessages",
+ method="listMyChatMessages",
params={
"startTime": start_time.isoformat(),
"endTime": end_time.isoformat(),
@@ -265,7 +266,7 @@ def list_all_messages(
def list_pinned_messages(self, chat_ref: ChatRef) -> list[Message]:
messages = self.client.execute(
cmd_type="request",
- method="listPinnedMessages",
+ method="listPinnedMessagesInChat",
params={
"chatRef": {
"ref": chat_ref.Ref,
@@ -321,4 +322,24 @@ def unpin_message(self, chat_ref: ChatRef, message_id: str) -> bool:
return result == "unpinned"
- # def get_mentions(self, chat_ref: ChatRef, raw_mentions: list[str])
+ def get_mentions(self, chat_ref: ChatRef, raw_mentions: list[str]) -> list[Mention]:
+ mentions = self.client.execute(
+ cmd_type="request",
+ method="getMentionsInChat",
+ params={
+ "chatRef": {
+ "ref": chat_ref.Ref,
+ "type": chat_ref.type.value,
+ },
+ "rawMentions": raw_mentions,
+ },
+ )
+
+ return [
+ Mention(
+ ID=mention["ID"],
+ Mentioned=mention["Mentioned"],
+ MentionText=mention["MentionText"],
+ )
+ for mention in mentions
+ ]
From 3935f7f919382e39a63731b5df369f0c8b345e9f Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Thu, 1 Jan 2026 19:51:22 +0100
Subject: [PATCH 39/99] feat: adjusted client creation in bridge to new lib
version
---
go/go.mod | 2 +-
go/go.sum | 2 ++
go/internal/json-client/fake-client.go | 4 +--
go/internal/json-client/real-client.go | 23 +++++++------
go/internal/json-model/json-model.go | 45 +++++++++++++++-----------
python/tests/test_channels.py | 3 +-
6 files changed, 48 insertions(+), 31 deletions(-)
diff --git a/go/go.mod b/go/go.mod
index a0e5afd..25ad986 100644
--- a/go/go.mod
+++ b/go/go.mod
@@ -6,7 +6,7 @@ require (
github.com/microsoft/kiota-abstractions-go v1.9.3
github.com/microsoftgraph/msgraph-sdk-go v1.92.0
github.com/mitchellh/mapstructure v1.5.0
- github.com/pzsp-teams/lib v0.0.0-20251231113700-9da62b291ebb
+ github.com/pzsp-teams/lib v0.0.0-20260101113908-bafa8ca37eae
)
require (
diff --git a/go/go.sum b/go/go.sum
index bff219a..e99e6f0 100644
--- a/go/go.sum
+++ b/go/go.sum
@@ -63,6 +63,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pzsp-teams/lib v0.0.0-20251231113700-9da62b291ebb h1:Bzr1vl4wA1X0g+FIKEn5vBexVJGhP1CztwTCyy7TPlo=
github.com/pzsp-teams/lib v0.0.0-20251231113700-9da62b291ebb/go.mod h1:EPtQVPz+HYZNsSpcoQ3QWLbMEz1O2qeIH5ojlYJLEic=
+github.com/pzsp-teams/lib v0.0.0-20260101113908-bafa8ca37eae h1:Q0NnGhGtyUbPzDHxLsBc39cHMjZBCG4yPCzU3ocNY+s=
+github.com/pzsp-teams/lib v0.0.0-20260101113908-bafa8ca37eae/go.mod h1:OUTZq+jo2AAzjV5p0w7c9VVuOKiHVWxuZhdzOhjOq5s=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/std-uritemplate/std-uritemplate/go/v2 v2.0.8 h1:gMBdYMTHt2mmTdXW8YfvRjRUZ0GhyGV+IqSH9H15bGw=
diff --git a/go/internal/json-client/fake-client.go b/go/internal/json-client/fake-client.go
index 2d731ca..eb11a74 100644
--- a/go/internal/json-client/fake-client.go
+++ b/go/internal/json-client/fake-client.go
@@ -12,7 +12,7 @@ import (
azcore "github.com/microsoft/kiota-abstractions-go"
graph "github.com/microsoftgraph/msgraph-sdk-go"
- lib "github.com/pzsp-teams/lib"
+ "github.com/pzsp-teams/lib/config"
jsonModel "github.com/pzsp-teams/lib-python/internal/json-model"
)
@@ -54,7 +54,7 @@ func NewJSONClient(req jsonModel.Request) (*TeamsJSONClient, error) {
return nil, fmt.Errorf("invalid mockServerUrl parameter")
}
- senderConfig := lib.SenderConfig{
+ senderConfig := config.SenderConfig{
MaxRetries: 3,
NextRetryDelay: 2,
Timeout: 5,
diff --git a/go/internal/json-client/real-client.go b/go/internal/json-client/real-client.go
index a00178c..628347f 100644
--- a/go/internal/json-client/real-client.go
+++ b/go/internal/json-client/real-client.go
@@ -5,7 +5,8 @@ package jsonclient
import (
"context"
- lib "github.com/pzsp-teams/lib"
+ "github.com/pzsp-teams/lib"
+ "github.com/pzsp-teams/lib/config"
jsonModel "github.com/pzsp-teams/lib-python/internal/json-model"
)
@@ -20,22 +21,26 @@ func NewJSONClient(req jsonModel.Request) (*TeamsJSONClient, error) {
return nil, err
}
- cacheEnabled, err := jsonModel.ParseCacheEnabled(req.Params["cacheEnabled"])
+ cacheMode, err := jsonModel.ParseCacheMode(req.Params["cacheMode"])
if err != nil {
return nil, err
}
-
- var cachePath *string
- if cacheEnabled {
- path, err := jsonModel.ParseCachePath(req.Params["cachePath"])
- if err != nil {
+ cachePath, err := jsonModel.ParseCachePath(req.Params["cachePath"])
+ if err != nil {
+ if cacheMode == config.CacheDisabled {
+ cachePath = nil
+ } else {
return nil, err
}
- cachePath = path
+ }
+ cacheConfig := config.CacheConfig{
+ Mode: config.CacheDisabled,
+ Provider: config.CacheProviderJSONFile,
+ Path: cachePath,
}
- client, err := lib.NewClient(context.TODO(), authConfig, senderConfig, cacheEnabled, cachePath)
+ client, err := lib.NewClient(context.TODO(), authConfig, senderConfig, &cacheConfig)
if err != nil {
return nil, err
}
diff --git a/go/internal/json-model/json-model.go b/go/internal/json-model/json-model.go
index 3d76018..39abf5f 100644
--- a/go/internal/json-model/json-model.go
+++ b/go/internal/json-model/json-model.go
@@ -3,7 +3,7 @@ package jsonmodel
import (
"fmt"
- "github.com/pzsp-teams/lib"
+ "github.com/pzsp-teams/lib/config"
)
type Request struct {
@@ -26,7 +26,7 @@ type SenderConfigMap struct {
Timeout int `json:"timeout"`
}
-func (scm SenderConfigMap) ToSenderConfig() (*lib.SenderConfig, error) {
+func (scm SenderConfigMap) ToSenderConfig() (*config.SenderConfig, error) {
if scm.MaxRetries < 0 {
return nil, fmt.Errorf("maxRetries cannot be negative")
}
@@ -37,7 +37,7 @@ func (scm SenderConfigMap) ToSenderConfig() (*lib.SenderConfig, error) {
return nil, fmt.Errorf("timeout must be > 0")
}
- return &lib.SenderConfig{
+ return &config.SenderConfig{
MaxRetries: scm.MaxRetries,
NextRetryDelay: scm.NextRetryDelay,
Timeout: scm.Timeout,
@@ -52,7 +52,7 @@ type AuthConfigMap struct {
AuthMethod string `json:"authMethod"`
}
-func (acm AuthConfigMap) ToAuthConfig() (*lib.AuthConfig, error) {
+func (acm AuthConfigMap) ToAuthConfig() (*config.AuthConfig, error) {
if acm.ClientID == "" {
return nil, fmt.Errorf("clientId is required")
}
@@ -74,27 +74,15 @@ func (acm AuthConfigMap) ToAuthConfig() (*lib.AuthConfig, error) {
return nil, err
}
- return &lib.AuthConfig{
+ return &config.AuthConfig{
ClientID: acm.ClientID,
Tenant: acm.Tenant,
Email: acm.Email,
Scopes: acm.Scopes,
- AuthMethod: authMethod,
+ AuthMethod: config.Method(authMethod),
}, nil
}
-func ParseCacheEnabled(value interface{}) (bool, error) {
- if value == nil {
- return false, nil // default
- }
-
- enabled, ok := value.(bool)
- if !ok {
- return false, fmt.Errorf("cacheEnabled must be boolean")
- }
- return enabled, nil
-}
-
func ParseCachePath(value interface{}) (*string, error) {
path, ok := value.(string)
if !ok || path == "" {
@@ -103,6 +91,27 @@ func ParseCachePath(value interface{}) (*string, error) {
return &path, nil
}
+func ParseCacheMode(value interface{}) (config.CacheMode, error) {
+ switch v := value.(type) {
+ case string:
+ switch v {
+ case "DISABLED":
+ return config.CacheDisabled, nil
+ case "SYNC":
+ return config.CacheSync, nil
+ case "ASYNC":
+ return config.CacheAsync, nil
+ default:
+ return config.CacheDisabled, fmt.Errorf("invalid cacheMode: %s", v)
+ }
+ case nil:
+ return config.CacheDisabled, fmt.Errorf("invalid cacheMode: %s", v)
+ default:
+ return config.CacheDisabled, fmt.Errorf("cacheMode must be a string")
+ }
+}
+
+
func validateAuthMethod(method string) (string, error) {
if method == "DEVICE_CODE" || method == "INTERACTIVE" {
return method, nil
diff --git a/python/tests/test_channels.py b/python/tests/test_channels.py
index fe8e161..9200b27 100644
--- a/python/tests/test_channels.py
+++ b/python/tests/test_channels.py
@@ -129,7 +129,8 @@ def test_send_message_integration(httpserver):
channelRef=data.channels[data.teams[0].ID][0].Name,
body=MessageBody(
Content=data.newMessageTemplate.Content,
- ContentType=MessageContentType.TEXT
+ ContentType=MessageContentType.TEXT,
+ Mentions=[],
)
)
From 39c9c88a334b0bdc939f22c9c0d3446ae0b8a78c Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Thu, 1 Jan 2026 19:54:15 +0100
Subject: [PATCH 40/99] chore: adjusted python client creation to new lib
version
---
python/teams_lib_pzsp2_z1/client.py | 6 ++++--
python/teams_lib_pzsp2_z1/config.py | 11 +++++++++++
2 files changed, 15 insertions(+), 2 deletions(-)
diff --git a/python/teams_lib_pzsp2_z1/client.py b/python/teams_lib_pzsp2_z1/client.py
index b64c63a..b865ee2 100644
--- a/python/teams_lib_pzsp2_z1/client.py
+++ b/python/teams_lib_pzsp2_z1/client.py
@@ -51,7 +51,9 @@ def _binary(self):
raise RuntimeError("Unsupported OS")
def init_client(
- self, cache_enabled: bool = False, cache_path: str | None = None
+ self,
+ cache_mode: config.CacheMode = config.CacheMode.DISABLED,
+ cache_path: str | None = None,
) -> Any:
sender_config = config.SenderConfig()
auth_config = config.load_auth_config(self.env_path)
@@ -70,7 +72,7 @@ def init_client(
"scopes": auth_config.scopes,
"authMethod": auth_config.auth_method,
},
- "cacheEnabled": cache_enabled,
+ "cacheMode": cache_mode.value,
"cachePath": cache_path,
},
)
diff --git a/python/teams_lib_pzsp2_z1/config.py b/python/teams_lib_pzsp2_z1/config.py
index 286aa89..f785c0a 100644
--- a/python/teams_lib_pzsp2_z1/config.py
+++ b/python/teams_lib_pzsp2_z1/config.py
@@ -1,5 +1,6 @@
import os
from dataclasses import dataclass
+from enum import Enum
from dotenv import load_dotenv
@@ -24,6 +25,16 @@ class AuthConfig:
auth_method: str
+class CacheMode(Enum):
+ DISABLED = "DISABLED"
+ SYNC = "SYNC"
+ ASYNC = "ASYNC"
+
+@dataclass
+class CacheConfig:
+ mode: CacheMode
+ path: str | None = None
+
def load_auth_config(env_path: str | None = None) -> AuthConfig:
load_dotenv(env_path)
From 2e1c804b6d1aa5d6871b777898a5b8af90778422 Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Thu, 1 Jan 2026 20:24:10 +0100
Subject: [PATCH 41/99] chore: adjusted fake client creating
---
go/go.mod | 2 +-
go/go.sum | 6 ++++--
go/internal/json-client/fake-client.go | 10 +++++++---
3 files changed, 12 insertions(+), 6 deletions(-)
diff --git a/go/go.mod b/go/go.mod
index 25ad986..fee5462 100644
--- a/go/go.mod
+++ b/go/go.mod
@@ -6,7 +6,7 @@ require (
github.com/microsoft/kiota-abstractions-go v1.9.3
github.com/microsoftgraph/msgraph-sdk-go v1.92.0
github.com/mitchellh/mapstructure v1.5.0
- github.com/pzsp-teams/lib v0.0.0-20260101113908-bafa8ca37eae
+ github.com/pzsp-teams/lib v0.0.0-20260101185821-fa4110d82a33
)
require (
diff --git a/go/go.sum b/go/go.sum
index e99e6f0..7327d45 100644
--- a/go/go.sum
+++ b/go/go.sum
@@ -61,10 +61,10 @@ github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmd
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/pzsp-teams/lib v0.0.0-20251231113700-9da62b291ebb h1:Bzr1vl4wA1X0g+FIKEn5vBexVJGhP1CztwTCyy7TPlo=
-github.com/pzsp-teams/lib v0.0.0-20251231113700-9da62b291ebb/go.mod h1:EPtQVPz+HYZNsSpcoQ3QWLbMEz1O2qeIH5ojlYJLEic=
github.com/pzsp-teams/lib v0.0.0-20260101113908-bafa8ca37eae h1:Q0NnGhGtyUbPzDHxLsBc39cHMjZBCG4yPCzU3ocNY+s=
github.com/pzsp-teams/lib v0.0.0-20260101113908-bafa8ca37eae/go.mod h1:OUTZq+jo2AAzjV5p0w7c9VVuOKiHVWxuZhdzOhjOq5s=
+github.com/pzsp-teams/lib v0.0.0-20260101185821-fa4110d82a33 h1:snmBQwIaJV1MBynhm8OKzvaKtpUva4wem3VKmyaVU0Q=
+github.com/pzsp-teams/lib v0.0.0-20260101185821-fa4110d82a33/go.mod h1:OUTZq+jo2AAzjV5p0w7c9VVuOKiHVWxuZhdzOhjOq5s=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/std-uritemplate/std-uritemplate/go/v2 v2.0.8 h1:gMBdYMTHt2mmTdXW8YfvRjRUZ0GhyGV+IqSH9H15bGw=
@@ -83,6 +83,8 @@ go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF
go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs=
go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI=
go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA=
+go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
+go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
diff --git a/go/internal/json-client/fake-client.go b/go/internal/json-client/fake-client.go
index eb11a74..2f131b6 100644
--- a/go/internal/json-client/fake-client.go
+++ b/go/internal/json-client/fake-client.go
@@ -13,6 +13,7 @@ import (
graph "github.com/microsoftgraph/msgraph-sdk-go"
"github.com/pzsp-teams/lib/config"
+ "github.com/pzsp-teams/lib"
jsonModel "github.com/pzsp-teams/lib-python/internal/json-model"
)
@@ -60,8 +61,11 @@ func NewJSONClient(req jsonModel.Request) (*TeamsJSONClient, error) {
Timeout: 5,
}
- cacheEnabled := false
- var cachePath *string = nil
+ cacheConfig := config.CacheConfig{
+ Mode: config.CacheDisabled,
+ Provider: config.CacheProviderJSONFile,
+ Path: nil,
+ }
hijackedHttpClient := &http.Client{
Transport: &HijackTransport{
@@ -85,7 +89,7 @@ func NewJSONClient(req jsonModel.Request) (*TeamsJSONClient, error) {
graphClient := graph.NewGraphServiceClient(adapter)
- client, err := lib.NewClientFromGraphClient(graphClient, &senderConfig, cacheEnabled, cachePath)
+ client, err := lib.NewClientFromGraphClient(graphClient, &senderConfig, &cacheConfig)
if err != nil {
return nil, err
}
From a15217883d4a88455215fce338068ab39287ca07 Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Thu, 1 Jan 2026 20:40:14 +0100
Subject: [PATCH 42/99] fix: real client init
---
go/internal/json-client/real-client.go | 4 +--
go/internal/json-model/json-model.go | 36 ++++++++++----------------
python/teams_lib_pzsp2_z1/client.py | 4 +--
3 files changed, 18 insertions(+), 26 deletions(-)
diff --git a/go/internal/json-client/real-client.go b/go/internal/json-client/real-client.go
index 628347f..1711f7a 100644
--- a/go/internal/json-client/real-client.go
+++ b/go/internal/json-client/real-client.go
@@ -21,11 +21,11 @@ func NewJSONClient(req jsonModel.Request) (*TeamsJSONClient, error) {
return nil, err
}
- cacheMode, err := jsonModel.ParseCacheMode(req.Params["cacheMode"])
+ cacheMode, err := jsonModel.ParseCacheMode(req.Config.CacheMode)
if err != nil {
return nil, err
}
- cachePath, err := jsonModel.ParseCachePath(req.Params["cachePath"])
+ cachePath, err := jsonModel.ParseCachePath(req.Config.CachePath)
if err != nil {
if cacheMode == config.CacheDisabled {
cachePath = nil
diff --git a/go/internal/json-model/json-model.go b/go/internal/json-model/json-model.go
index 39abf5f..750604a 100644
--- a/go/internal/json-model/json-model.go
+++ b/go/internal/json-model/json-model.go
@@ -16,7 +16,7 @@ type Request struct {
type Config struct {
SenderConfigMap SenderConfigMap `json:"senderConfig"`
AuthConfigMap AuthConfigMap `json:"authConfig"`
- CacheEnabled bool `json:"cacheEnabled"`
+ CacheMode string `json:"cacheMode,omitempty"`
CachePath string `json:"cachePath,omitempty"`
}
@@ -84,34 +84,26 @@ func (acm AuthConfigMap) ToAuthConfig() (*config.AuthConfig, error) {
}
func ParseCachePath(value interface{}) (*string, error) {
- path, ok := value.(string)
- if !ok || path == "" {
- return nil, fmt.Errorf("cachePath must be a non-empty string")
- }
- return &path, nil
+ path, ok := value.(string)
+ if !ok || path == "" {
+ return nil, fmt.Errorf("cachePath must be a non-empty string")
+ }
+ return &path, nil
}
func ParseCacheMode(value interface{}) (config.CacheMode, error) {
- switch v := value.(type) {
- case string:
- switch v {
- case "DISABLED":
- return config.CacheDisabled, nil
- case "SYNC":
- return config.CacheSync, nil
- case "ASYNC":
- return config.CacheAsync, nil
- default:
- return config.CacheDisabled, fmt.Errorf("invalid cacheMode: %s", v)
- }
- case nil:
- return config.CacheDisabled, fmt.Errorf("invalid cacheMode: %s", v)
+ switch value {
+ case "DISABLED":
+ return config.CacheDisabled, nil
+ case "SYNC":
+ return config.CacheSync, nil
+ case "ASYNC":
+ return config.CacheAsync, nil
default:
- return config.CacheDisabled, fmt.Errorf("cacheMode must be a string")
+ return config.CacheDisabled, fmt.Errorf("invalid cacheMode: %s", value)
}
}
-
func validateAuthMethod(method string) (string, error) {
if method == "DEVICE_CODE" || method == "INTERACTIVE" {
return method, nil
diff --git a/python/teams_lib_pzsp2_z1/client.py b/python/teams_lib_pzsp2_z1/client.py
index b865ee2..dc321dd 100644
--- a/python/teams_lib_pzsp2_z1/client.py
+++ b/python/teams_lib_pzsp2_z1/client.py
@@ -17,7 +17,7 @@ def __init__(
self,
auto_init: bool = True,
env_path: str | None = None,
- cache_enabled: bool = False,
+ cache_mode: config.CacheMode = config.CacheMode.DISABLED,
cache_path: str | None = None,
):
self._lock = threading.Lock()
@@ -37,7 +37,7 @@ def __init__(
self.chats = ChatsService(self)
if auto_init:
- self.init_client(cache_enabled, cache_path)
+ self.init_client(cache_mode, cache_path)
def _binary(self):
base = pathlib.Path(__file__).parent / "bin"
From f7f57599ab1d2cedfb71b41cbae3a5d8c078f303 Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Fri, 2 Jan 2026 12:28:17 +0100
Subject: [PATCH 43/99] feat: implemented test for list chats
---
python/teams_lib_pzsp2_z1/client.py | 2 ++
python/tests/fake_server/data.py | 48 +++++++++++++++++++++++++
python/tests/fake_server/setup.py | 28 +++++++++++++++
python/tests/test_channels.py | 4 +--
python/tests/test_chats.py | 56 +++++++++++++++++++++++++++++
5 files changed, 136 insertions(+), 2 deletions(-)
create mode 100644 python/tests/test_chats.py
diff --git a/python/teams_lib_pzsp2_z1/client.py b/python/teams_lib_pzsp2_z1/client.py
index dc321dd..bf00843 100644
--- a/python/teams_lib_pzsp2_z1/client.py
+++ b/python/teams_lib_pzsp2_z1/client.py
@@ -111,6 +111,8 @@ def execute(
print("Sent to Go process:", json_payload) # Debug print
raw_response = self.proc.stdout.readline()
+
+ print("Received from Go process:", raw_response.strip()) # Debug print
except BrokenPipeError:
raise RuntimeError("Go process crashed or closed connection") # noqa: B904
diff --git a/python/tests/fake_server/data.py b/python/tests/fake_server/data.py
index bcc22a2..95eb59c 100644
--- a/python/tests/fake_server/data.py
+++ b/python/tests/fake_server/data.py
@@ -1,5 +1,6 @@
from dataclasses import dataclass
from teams_lib_pzsp2_z1.model.team import Team
+from teams_lib_pzsp2_z1.model.chat import Chat, ChatType
from teams_lib_pzsp2_z1.model.channel import Channel
from teams_lib_pzsp2_z1.model.message import Message, MessageFrom
from teams_lib_pzsp2_z1.model.member import Member
@@ -126,6 +127,28 @@ def __init__(self) -> FakeServerData:
],
}
}
+ self.group_chats = [
+ Chat(
+ ID="chat-123-abc",
+ Type=ChatType.GROUP,
+ IsHidden=False,
+ Topic="Project Discussion",
+ ),
+ Chat(
+ ID="chat-456-def",
+ Type=ChatType.GROUP,
+ IsHidden=True,
+ Topic="Secret Plans",
+ ),
+ ]
+ self.oneonone_chats = [
+ Chat(
+ ID="chat-789-ghi",
+ Type=ChatType.ONEONONE,
+ IsHidden=False,
+ Topic="",
+ ),
+ ]
self.newGroupID = "group-789-ghi"
self.newTeamName = "New Team"
self.newGroupMailNickname = "new-team-nickname"
@@ -513,3 +536,28 @@ def get_remove_member_response(self, team_id: str, channel_id: str, member_id: s
channel_members.remove(member)
return {"success": True}
+
+ def get_list_chats_response(self, chat_type: ChatType) -> dict:
+ if chat_type == ChatType.ONEONONE:
+ chats = self.oneonone_chats
+ else:
+ chats = self.group_chats
+
+ print(chat_type)
+ print(chat_type == ChatType.ONEONONE)
+
+ return {
+ "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#chats",
+ "@odata.count": len(chats),
+ "value": [
+ {
+ "id": chat.ID,
+ "chatType": "oneOnOne" if chat.Type == ChatType.ONEONONE else "group",
+ "isHiddenForAllMembers": chat.IsHidden,
+ "topic": chat.Topic,
+ }
+ for chat in chats
+ ],
+ }
+
+
diff --git a/python/tests/fake_server/setup.py b/python/tests/fake_server/setup.py
index 93e1bb1..a979b9f 100644
--- a/python/tests/fake_server/setup.py
+++ b/python/tests/fake_server/setup.py
@@ -3,6 +3,7 @@
import re
import sys
from werkzeug.wrappers import Response
+from teams_lib_pzsp2_z1.model.chat import ChatType
def setup_fake_server(httpserver) -> FakeServerData:
"""
@@ -44,6 +45,33 @@ def make_log_response(request, data_dict: dict, context_msg: str = "") -> Respon
req, data.get_myJoinedTeams_response(), "List Joined Teams"
))
+ def handle_chats_request(req):
+ filter_param = req.args.get('$filter', '')
+ chat_type = None
+ match = re.search(r"chatType\s+eq\s+'([^']+)'", filter_param)
+
+ if match:
+ chat_type = match.group(1)
+
+ print(f"DEBUG: Parsowanie filtra: '{filter_param}' -> Typ: '{chat_type}'")
+
+ if chat_type == "oneOnOne":
+ chat_type = ChatType.ONEONONE
+ elif chat_type == "group":
+ chat_type = ChatType.GROUP
+
+ return make_log_response(
+ req,
+ data.get_list_chats_response(chat_type),
+ f"List Chats ({chat_type})"
+ )
+
+ # GET /users/me/chats
+ httpserver.expect_request(
+ "/v1.0/users/me-token-to-replace/chats",
+ method="GET"
+ ).respond_with_handler(handle_chats_request)
+
#POST /teams/{team_id}/channels/{channel_id}/members
httpserver.expect_request(
re.compile(r"^/v1.0/teams/([^/]+)/channels/([^/]+)/members"),
diff --git a/python/tests/test_channels.py b/python/tests/test_channels.py
index 9200b27..e0695d0 100644
--- a/python/tests/test_channels.py
+++ b/python/tests/test_channels.py
@@ -339,7 +339,7 @@ def test_update_member_role_integration(httpserver):
member = client.channels.update_member_role(
teamRef=data.teams[0].DisplayName,
channelRef=data.channels[data.teams[0].ID][0].Name,
- userRef=data.members[data.teams[0].ID][data.channels[data.teams[0].ID][0].ID][1].DisplayName,
+ userRef=data.members[data.teams[0].ID][data.channels[data.teams[0].ID][0].ID][1].Email,
isOwner=True
)
@@ -363,7 +363,7 @@ def test_remove_member_integration(httpserver):
success = client.channels.remove_member(
teamRef=data.teams[0].DisplayName,
channelRef=data.channels[data.teams[0].ID][0].Name,
- userRef=data.members[data.teams[0].ID][data.channels[data.teams[0].ID][0].ID][1].DisplayName,
+ userRef=data.members[data.teams[0].ID][data.channels[data.teams[0].ID][0].ID][1].Email,
)
assert success is True
diff --git a/python/tests/test_chats.py b/python/tests/test_chats.py
new file mode 100644
index 0000000..5393f7e
--- /dev/null
+++ b/python/tests/test_chats.py
@@ -0,0 +1,56 @@
+from teams_lib_pzsp2_z1.client import TeamsClient
+from tests.init_fake_client import init_fake_client
+from tests.fake_server.setup import setup_fake_server
+from teams_lib_pzsp2_z1.model.chat import ChatType
+
+
+def test_list_my_group_chats_integration(httpserver):
+ """
+ Integration test: Python -> Go Binary -> Fake HTTP -> Python Mock Server
+ """
+
+ data = setup_fake_server(httpserver)
+
+ # Init fake client
+ client = TeamsClient(auto_init=False)
+ try:
+ init_fake_client(client, httpserver.url_for(""))
+
+ chats = client.chats.list_my_joined(ChatType.GROUP)
+
+ assert len(chats) == len(data.group_chats)
+ assert chats[0].ID == data.group_chats[0].ID
+ assert chats[0].Type == data.group_chats[0].Type
+ assert chats[0].Topic == data.group_chats[0].Topic
+ assert chats[0].IsHidden == data.group_chats[0].IsHidden
+
+ assert chats[1].ID == data.group_chats[1].ID
+ assert chats[1].Type == data.group_chats[1].Type
+ assert chats[1].Topic == data.group_chats[1].Topic
+ assert chats[1].IsHidden == data.group_chats[1].IsHidden
+
+ finally:
+ client.close()
+
+def test_list_my_one_on_one_chats_integration(httpserver):
+ """
+ Integration test: Python -> Go Binary -> Fake HTTP -> Python Mock Server
+ """
+
+ data = setup_fake_server(httpserver)
+
+ # Init fake client
+ client = TeamsClient(auto_init=False)
+ try:
+ init_fake_client(client, httpserver.url_for(""))
+
+ chats = client.chats.list_my_joined(ChatType.ONEONONE)
+
+ assert len(chats) == len(data.oneonone_chats)
+ assert chats[0].ID == data.oneonone_chats[0].ID
+ assert chats[0].Type == data.oneonone_chats[0].Type
+ assert chats[0].Topic == data.oneonone_chats[0].Topic
+ assert chats[0].IsHidden == data.oneonone_chats[0].IsHidden
+
+ finally:
+ client.close()
\ No newline at end of file
From 33da7572d84470b8d828a37ebdf598aba7869c25 Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Fri, 2 Jan 2026 12:41:29 +0100
Subject: [PATCH 44/99] feat: implemented test for create group chat endpoint
---
python/tests/fake_server/data.py | 54 +++++++++++++++++++++++++++++++
python/tests/fake_server/setup.py | 20 ++++++++++++
python/tests/test_chats.py | 30 ++++++++++++++---
3 files changed, 100 insertions(+), 4 deletions(-)
diff --git a/python/tests/fake_server/data.py b/python/tests/fake_server/data.py
index 95eb59c..dcff1d1 100644
--- a/python/tests/fake_server/data.py
+++ b/python/tests/fake_server/data.py
@@ -127,6 +127,29 @@ def __init__(self) -> FakeServerData:
],
}
}
+ self.users = [
+ Member(
+ ID="user-123-abc",
+ UserID="user-123-abc",
+ DisplayName="Alice",
+ Role="owner",
+ Email="alice@example.com"
+ ),
+ Member(
+ ID="user-456-def",
+ UserID="user-456-def",
+ DisplayName="Bob",
+ Role="member",
+ Email="bob@example.com"
+ ),
+ ]
+ self.me = Member(
+ ID="user-me-001",
+ UserID="user-me-001",
+ DisplayName="Current User",
+ Role="member",
+ Email="me@example.com"
+ )
self.group_chats = [
Chat(
ID="chat-123-abc",
@@ -174,6 +197,12 @@ def __init__(self) -> FakeServerData:
Role="owner",
Email="newmember@example.com"
)
+ self.newChatTemplate = Chat(
+ ID="new-chat-001",
+ Type=ChatType.GROUP,
+ IsHidden=False,
+ Topic="New Chat Topic"
+ )
@@ -560,4 +589,29 @@ def get_list_chats_response(self, chat_type: ChatType) -> dict:
],
}
+ def get_me_response(self) -> dict:
+ return {
+ "id": self.me.ID,
+ "displayName": self.me.DisplayName,
+ "email": self.me.Email,
+ }
+
+ def get_create_chat_response(self, request_json: dict) -> dict:
+ if request_json["chatType"] == "oneOnOne":
+ return {
+ "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#chats/$entity",
+ "id": self.newChatTemplate.ID,
+ "chatType": "oneOnOne",
+ "isHiddenForAllMembers": self.newChatTemplate.IsHidden,
+ "topic": None,
+ }
+ else:
+ return {
+ "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#chats/$entity",
+ "id": self.newChatTemplate.ID,
+ "chatType": "group",
+ "isHiddenForAllMembers": self.newChatTemplate.IsHidden,
+ "topic": self.newChatTemplate.Topic,
+ }
+
diff --git a/python/tests/fake_server/setup.py b/python/tests/fake_server/setup.py
index a979b9f..00a6f35 100644
--- a/python/tests/fake_server/setup.py
+++ b/python/tests/fake_server/setup.py
@@ -72,6 +72,26 @@ def handle_chats_request(req):
method="GET"
).respond_with_handler(handle_chats_request)
+ # GET /users/me
+ httpserver.expect_request(
+ "/v1.0/users/me-token-to-replace",
+ method="GET"
+ ).respond_with_handler(lambda req: make_log_response(
+ req,
+ data.get_me_response(),
+ "Get Users"
+ ))
+
+ #POST /chats
+ httpserver.expect_request(
+ "/v1.0/chats",
+ method="POST"
+ ).respond_with_handler(lambda req: make_log_response(
+ req,
+ data.get_create_chat_response(req.json),
+ "Create Chat"
+ ))
+
#POST /teams/{team_id}/channels/{channel_id}/members
httpserver.expect_request(
re.compile(r"^/v1.0/teams/([^/]+)/channels/([^/]+)/members"),
diff --git a/python/tests/test_chats.py b/python/tests/test_chats.py
index 5393f7e..0f16307 100644
--- a/python/tests/test_chats.py
+++ b/python/tests/test_chats.py
@@ -33,13 +33,9 @@ def test_list_my_group_chats_integration(httpserver):
client.close()
def test_list_my_one_on_one_chats_integration(httpserver):
- """
- Integration test: Python -> Go Binary -> Fake HTTP -> Python Mock Server
- """
data = setup_fake_server(httpserver)
- # Init fake client
client = TeamsClient(auto_init=False)
try:
init_fake_client(client, httpserver.url_for(""))
@@ -52,5 +48,31 @@ def test_list_my_one_on_one_chats_integration(httpserver):
assert chats[0].Topic == data.oneonone_chats[0].Topic
assert chats[0].IsHidden == data.oneonone_chats[0].IsHidden
+ finally:
+ client.close()
+
+
+def test_create_group_chat_integration(httpserver):
+
+ data = setup_fake_server(httpserver)
+
+ client = TeamsClient(auto_init=False)
+ try:
+ init_fake_client(client, httpserver.url_for(""))
+
+ chat = client.chats.create_group_chat(
+ topic=data.newChatTemplate.Topic,
+ recipient_refs=[
+ data.users[0].Email,
+ data.users[1].Email,
+ ],
+ include_me=True,
+ )
+
+ assert chat.ID == data.newChatTemplate.ID
+ assert chat.Type == ChatType.GROUP
+ assert chat.Topic == data.newChatTemplate.Topic
+ assert chat.IsHidden == data.newChatTemplate.IsHidden
+
finally:
client.close()
\ No newline at end of file
From c36f6195a77c3c540c09039626ff472bd83ab6b9 Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Fri, 2 Jan 2026 13:13:04 +0100
Subject: [PATCH 45/99] feat: implemented test for list_members in chat
---
python/teams_lib_pzsp2_z1/services/chats.py | 4 +-
python/tests/fake_server/data.py | 50 +++++++++++++++++++++
python/tests/fake_server/setup.py | 12 +++++
python/tests/test_chats.py | 50 +++++++++++++++++++++
4 files changed, 114 insertions(+), 2 deletions(-)
diff --git a/python/teams_lib_pzsp2_z1/services/chats.py b/python/teams_lib_pzsp2_z1/services/chats.py
index 5838203..ad59b1b 100644
--- a/python/teams_lib_pzsp2_z1/services/chats.py
+++ b/python/teams_lib_pzsp2_z1/services/chats.py
@@ -94,8 +94,8 @@ def list_group_chat_members(self, group_chat_ref: str) -> list[Member]:
Member(
ID=member["ID"],
DisplayName=member["DisplayName"],
- UserRef=member["UserRef"],
- Roles=member["Roles"],
+ UserID=member["UserID"],
+ Role=member["Role"],
Email=member["Email"],
)
for member in members
diff --git a/python/tests/fake_server/data.py b/python/tests/fake_server/data.py
index dcff1d1..fbe78e9 100644
--- a/python/tests/fake_server/data.py
+++ b/python/tests/fake_server/data.py
@@ -127,6 +127,24 @@ def __init__(self) -> FakeServerData:
],
}
}
+ self.group_chat_members = {
+ "chat-123-abc": [
+ Member(
+ ID="user-123-abc",
+ UserID="user-123-abc",
+ DisplayName="Alice",
+ Role="owner",
+ Email="alice@example.com"
+ ),
+ Member(
+ ID="user-456-def",
+ UserID="user-456-def",
+ DisplayName="Bob",
+ Role="member",
+ Email="bob@example.com"
+ ),
+ ],
+ }
self.users = [
Member(
ID="user-123-abc",
@@ -615,3 +633,35 @@ def get_create_chat_response(self, request_json: dict) -> dict:
}
+ def get_add_member_to_group_chat_response(self, chat_id: str, request_json: dict) -> dict:
+ return {
+ "@odata.context": f"https://graph.microsoft.com/v1.0/$metadata#chats('{chat_id}')/members/$entity",
+ "@odata.type": "#microsoft.graph.aadUserConversationMember",
+ "id": self.newMemberTemplate.ID,
+ "roles": [self.newMemberTemplate.Role] if self.newMemberTemplate.Role == "owner" else [],
+ "displayName": self.newMemberTemplate.DisplayName,
+ "userId": self.newMemberTemplate.UserID,
+ "email": self.newMemberTemplate.Email,
+ }
+
+ def get_remove_member_from_group_chat_response(self, chat_id: str, member_id: str) -> dict:
+ return {
+ "success": True
+ }
+
+ def get_list_group_chat_members_response(self, chat_id: str) -> dict:
+ return {
+ "@odata.context": f"https://graph.microsoft.com/v1.0/$metadata#chats('{chat_id}')/members",
+ "value": [
+ {
+ "@odata.type": "#microsoft.graph.aadUserConversationMember",
+ "id": member.ID,
+ "userId": member.UserID,
+ "displayName": member.DisplayName,
+ "roles": [member.Role] if member.Role == "owner" else [],
+ "email": member.Email,
+ }
+ for member in self.group_chat_members.get(chat_id, [])
+ ],
+ }
+
diff --git a/python/tests/fake_server/setup.py b/python/tests/fake_server/setup.py
index 00a6f35..0d6c455 100644
--- a/python/tests/fake_server/setup.py
+++ b/python/tests/fake_server/setup.py
@@ -92,6 +92,18 @@ def handle_chats_request(req):
"Create Chat"
))
+ #GET /chats/{chat_id}/members
+ httpserver.expect_request(
+ re.compile(r"^/v1.0/chats/([^/]+)/members"),
+ method="GET"
+ ).respond_with_handler(lambda req: make_log_response(
+ req,
+ data.get_list_group_chat_members_response(
+ re.search(r"/chats/([^/]+)/members", req.path).group(1)
+ ),
+ "List Chat Members"
+ ))
+
#POST /teams/{team_id}/channels/{channel_id}/members
httpserver.expect_request(
re.compile(r"^/v1.0/teams/([^/]+)/channels/([^/]+)/members"),
diff --git a/python/tests/test_chats.py b/python/tests/test_chats.py
index 0f16307..4549b4d 100644
--- a/python/tests/test_chats.py
+++ b/python/tests/test_chats.py
@@ -74,5 +74,55 @@ def test_create_group_chat_integration(httpserver):
assert chat.Topic == data.newChatTemplate.Topic
assert chat.IsHidden == data.newChatTemplate.IsHidden
+ finally:
+ client.close()
+
+def test_create_one_on_one_chat_integration(httpserver):
+
+ data = setup_fake_server(httpserver)
+
+ client = TeamsClient(auto_init=False)
+ try:
+ init_fake_client(client, httpserver.url_for(""))
+
+ chat = client.chats.create_one_on_one(
+ recipient_ref=data.users[0].Email,
+ )
+
+ assert chat.ID == data.newChatTemplate.ID
+ assert chat.Type == ChatType.ONEONONE
+ assert chat.Topic == None
+ assert chat.IsHidden == data.newChatTemplate.IsHidden
+
+ finally:
+ client.close()
+
+
+def test_list_group_chat_members_integration(httpserver):
+
+ data = setup_fake_server(httpserver)
+
+ client = TeamsClient(auto_init=False)
+ try:
+ init_fake_client(client, httpserver.url_for(""))
+
+ members = client.chats.list_group_chat_members(data.group_chats[0].Topic)
+
+ assert len(members) == len(data.group_chat_members[data.group_chats[0].ID])
+
+ assert members[0].ID == data.group_chat_members[data.group_chats[0].ID][0].ID
+ assert members[0].UserID == data.group_chat_members[data.group_chats[0].ID][0].UserID
+ assert members[0].DisplayName == data.group_chat_members[data.group_chats[0].ID][0].DisplayName
+ if data.group_chat_members[data.group_chats[0].ID][0].Role == "owner":
+ assert members[0].Role == "owner"
+ assert members[0].Email == data.group_chat_members[data.group_chats[0].ID][0].Email
+
+ assert members[1].ID == data.group_chat_members[data.group_chats[0].ID][1].ID
+ assert members[1].UserID == data.group_chat_members[data.group_chats[0].ID][1].UserID
+ assert members[1].DisplayName == data.group_chat_members[data.group_chats[0].ID][1].DisplayName
+ if data.group_chat_members[data.group_chats[0].ID][1].Role == "owner":
+ assert members[1].Role == "owner"
+ assert members[1].Email == data.group_chat_members[data.group_chats[0].ID][1].Email
+
finally:
client.close()
\ No newline at end of file
From b8549ad21f7186f2ea1bd469408e1af5b7dea210 Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Fri, 2 Jan 2026 13:20:49 +0100
Subject: [PATCH 46/99] feat: implemented membership management tests
---
python/teams_lib_pzsp2_z1/services/chats.py | 14 ++---
python/tests/fake_server/data.py | 15 +++++
python/tests/fake_server/setup.py | 39 +++++++++++++
python/tests/test_chats.py | 63 +++++++++++++++++++++
4 files changed, 124 insertions(+), 7 deletions(-)
diff --git a/python/teams_lib_pzsp2_z1/services/chats.py b/python/teams_lib_pzsp2_z1/services/chats.py
index ad59b1b..ab6bef9 100644
--- a/python/teams_lib_pzsp2_z1/services/chats.py
+++ b/python/teams_lib_pzsp2_z1/services/chats.py
@@ -62,8 +62,8 @@ def add_member_to_group_chat(self, group_chat_ref: str, user_ref: str) -> Member
return Member(
ID=member["ID"],
DisplayName=member["DisplayName"],
- UserRef=member["UserRef"],
- Roles=member["Roles"],
+ UserID=member["UserID"],
+ Role=member["Role"],
Email=member["Email"],
)
@@ -135,7 +135,7 @@ def list_messages(self, chat_ref: str) -> list[Message]:
ContentType=MessageContentType(message["Body"]["ContentType"]),
),
From=MessageFrom(
- UserRef=message["From"]["UserRef"],
+ UserID=message["From"]["UserID"],
DisplayName=message["From"]["DisplayName"],
),
CreatedDateTime=message["CreatedDateTime"],
@@ -166,7 +166,7 @@ def send_message(self, chat_ref: ChatRef, body: MessageBody) -> Message:
ContentType=MessageContentType(message["Body"]["ContentType"]),
),
From=MessageFrom(
- UserRef=message["From"]["UserRef"],
+ UserID=message["From"]["UserID"],
DisplayName=message["From"]["DisplayName"],
),
CreatedDateTime=message["CreatedDateTime"],
@@ -207,7 +207,7 @@ def get_message(self, chat_ref: ChatRef, message_id: str) -> Message:
ContentType=MessageContentType(message["Body"]["ContentType"]),
),
From=MessageFrom(
- UserRef=message["From"]["UserRef"],
+ UserID=message["From"]["UserID"],
DisplayName=message["From"]["DisplayName"],
),
CreatedDateTime=message["CreatedDateTime"],
@@ -255,7 +255,7 @@ def list_all_messages(
ContentType=MessageContentType(message["Body"]["ContentType"]),
),
From=MessageFrom(
- UserRef=message["From"]["UserRef"],
+ UserID=message["From"]["UserID"],
DisplayName=message["From"]["DisplayName"],
),
CreatedDateTime=message["CreatedDateTime"],
@@ -283,7 +283,7 @@ def list_pinned_messages(self, chat_ref: ChatRef) -> list[Message]:
ContentType=MessageContentType(message["Body"]["ContentType"]),
),
From=MessageFrom(
- UserRef=message["From"]["UserRef"],
+ UserID=message["From"]["UserID"],
DisplayName=message["From"]["DisplayName"],
),
CreatedDateTime=message["CreatedDateTime"],
diff --git a/python/tests/fake_server/data.py b/python/tests/fake_server/data.py
index fbe78e9..e2dc2a2 100644
--- a/python/tests/fake_server/data.py
+++ b/python/tests/fake_server/data.py
@@ -221,6 +221,7 @@ def __init__(self) -> FakeServerData:
IsHidden=False,
Topic="New Chat Topic"
)
+ self.updatedGroupChatTopic = "Updated Chat Topic"
@@ -665,3 +666,17 @@ def get_list_group_chat_members_response(self, chat_id: str) -> dict:
],
}
+ def get_update_group_chat_topic_response(self, chat_id: str, request_json: dict) -> dict:
+ chat = next((c for c in self.group_chats if c.ID == chat_id), None)
+ if not chat:
+ return {}
+
+ chat.Topic = request_json.get("topic", chat.Topic)
+
+ return {
+ "@odata.context": f"https://graph.microsoft.com/v1.0/$metadata#chats('{chat_id}')",
+ "id": chat.ID,
+ "chatType": "group",
+ "isHiddenForAllMembers": chat.IsHidden,
+ "topic": chat.Topic,
+ }
diff --git a/python/tests/fake_server/setup.py b/python/tests/fake_server/setup.py
index 0d6c455..b386812 100644
--- a/python/tests/fake_server/setup.py
+++ b/python/tests/fake_server/setup.py
@@ -104,6 +104,45 @@ def handle_chats_request(req):
"List Chat Members"
))
+ #POST /chats/{chat_id}/members
+ httpserver.expect_request(
+ re.compile(r"^/v1.0/chats/([^/]+)/members"),
+ method="POST"
+ ).respond_with_handler(lambda req: make_log_response(
+ req,
+ data.get_add_member_to_group_chat_response(
+ re.search(r"/chats/([^/]+)/members", req.path).group(1),
+ req.json
+ ),
+ "Add Chat Member"
+ ))
+
+ #DELETE /chats/{chat_id}/members/{member_id}
+ httpserver.expect_request(
+ re.compile(r"^/v1.0/chats/([^/]+)/members/([^/]+)"),
+ method="DELETE"
+ ).respond_with_handler(lambda req: make_log_response(
+ req,
+ data.get_remove_member_from_group_chat_response(
+ re.search(r"/chats/([^/]+)/members/([^/]+)", req.path).group(1),
+ re.search(r"/chats/([^/]+)/members/([^/]+)", req.path).group(2)
+ ),
+ "Remove Chat Member"
+ ))
+
+ #PATCH /chats/{chat_id}
+ httpserver.expect_request(
+ re.compile(r"^/v1.0/chats/([^/]+)"),
+ method="PATCH"
+ ).respond_with_handler(lambda req: make_log_response(
+ req,
+ data.get_update_group_chat_topic_response(
+ re.search(r"/chats/([^/]+)", req.path).group(1),
+ req.json
+ ),
+ "Update Chat"
+ ))
+
#POST /teams/{team_id}/channels/{channel_id}/members
httpserver.expect_request(
re.compile(r"^/v1.0/teams/([^/]+)/channels/([^/]+)/members"),
diff --git a/python/tests/test_chats.py b/python/tests/test_chats.py
index 4549b4d..d688f6d 100644
--- a/python/tests/test_chats.py
+++ b/python/tests/test_chats.py
@@ -124,5 +124,68 @@ def test_list_group_chat_members_integration(httpserver):
assert members[1].Role == "owner"
assert members[1].Email == data.group_chat_members[data.group_chats[0].ID][1].Email
+ finally:
+ client.close()
+
+
+def test_add_group_chat_member_integration(httpserver):
+
+ data = setup_fake_server(httpserver)
+
+ client = TeamsClient(auto_init=False)
+ try:
+ init_fake_client(client, httpserver.url_for(""))
+
+ member = client.chats.add_member_to_group_chat(
+ group_chat_ref=data.group_chats[0].Topic,
+ user_ref=data.newMemberTemplate.Email,
+ )
+
+ assert member.ID == data.newMemberTemplate.ID
+ assert member.UserID == data.newMemberTemplate.UserID
+ assert member.DisplayName == data.newMemberTemplate.DisplayName
+ assert member.Role == data.newMemberTemplate.Role
+ assert member.Email == data.newMemberTemplate.Email
+ finally:
+ client.close()
+
+
+def test_remove_group_chat_member_integration(httpserver):
+
+ data = setup_fake_server(httpserver)
+
+ client = TeamsClient(auto_init=False)
+ try:
+ init_fake_client(client, httpserver.url_for(""))
+
+ result = client.chats.remove_member_from_group_chat(
+ group_chat_ref=data.group_chats[0].Topic,
+ member_ref=data.group_chat_members[data.group_chats[0].ID][0].Email,
+ )
+
+ assert result is True
+
+ finally:
+ client.close()
+
+
+def test_update_group_chat_topic_integration(httpserver):
+
+ data = setup_fake_server(httpserver)
+
+ client = TeamsClient(auto_init=False)
+ try:
+ init_fake_client(client, httpserver.url_for(""))
+
+ chat = client.chats.update_group_chat_topic(
+ group_chat_ref=data.group_chats[0].Topic,
+ new_topic=data.updatedGroupChatTopic,
+ )
+
+ assert chat.ID == data.group_chats[0].ID
+ assert chat.Type == data.group_chats[0].Type
+ assert chat.Topic == data.updatedGroupChatTopic
+ assert chat.IsHidden == data.group_chats[0].IsHidden
+
finally:
client.close()
\ No newline at end of file
From 8e5491e61f12ed7ef64e4e7469e570fd71669c16 Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Fri, 2 Jan 2026 14:57:06 +0100
Subject: [PATCH 47/99] feat: added list chat messages test
---
python/teams_lib_pzsp2_z1/model/chat.py | 3 +-
python/teams_lib_pzsp2_z1/services/chats.py | 57 ++++++++--------
python/tests/fake_server/data.py | 74 +++++++++++++++++++++
python/tests/fake_server/setup.py | 38 +++++++++++
python/tests/test_chats.py | 40 ++++++++++-
5 files changed, 180 insertions(+), 32 deletions(-)
diff --git a/python/teams_lib_pzsp2_z1/model/chat.py b/python/teams_lib_pzsp2_z1/model/chat.py
index 206bc83..63be741 100644
--- a/python/teams_lib_pzsp2_z1/model/chat.py
+++ b/python/teams_lib_pzsp2_z1/model/chat.py
@@ -14,7 +14,8 @@ class Chat:
IsHidden: bool
Topic: str
+
@dataclass
class ChatRef:
Ref: str
- type: ChatType
+ Type: ChatType
diff --git a/python/teams_lib_pzsp2_z1/services/chats.py b/python/teams_lib_pzsp2_z1/services/chats.py
index ab6bef9..2e705b3 100644
--- a/python/teams_lib_pzsp2_z1/services/chats.py
+++ b/python/teams_lib_pzsp2_z1/services/chats.py
@@ -118,27 +118,29 @@ def update_group_chat_topic(self, group_chat_ref: str, new_topic: str) -> Chat:
Topic=response["Topic"],
)
- def list_messages(self, chat_ref: str) -> list[Message]:
+ def list_messages(self, chat_ref: ChatRef) -> list[Message]:
messages = self.client.execute(
cmd_type="request",
method="listMessagesInChat",
params={
- "chatRef": chat_ref,
+ "chatRef": {
+ "ref": chat_ref.Ref,
+ "type": chat_ref.Type.value,
+ },
},
)
return [
Message(
ID=message["ID"],
- Body=MessageBody(
- Content=message["Body"]["Content"],
- ContentType=MessageContentType(message["Body"]["ContentType"]),
- ),
+ Content=message["Content"],
+ ContentType=MessageContentType(message["ContentType"]),
From=MessageFrom(
UserID=message["From"]["UserID"],
DisplayName=message["From"]["DisplayName"],
),
CreatedDateTime=message["CreatedDateTime"],
+ ReplyCount=message["ReplyCount"],
)
for message in messages
]
@@ -150,7 +152,7 @@ def send_message(self, chat_ref: ChatRef, body: MessageBody) -> Message:
params={
"chatRef": {
"ref": chat_ref.Ref,
- "type": chat_ref.type.value,
+ "type": chat_ref.Type.value,
},
"body": {
"content": body.Content,
@@ -161,15 +163,14 @@ def send_message(self, chat_ref: ChatRef, body: MessageBody) -> Message:
return Message(
ID=message["ID"],
- Body=MessageBody(
- Content=message["Body"]["Content"],
- ContentType=MessageContentType(message["Body"]["ContentType"]),
- ),
+ Content=message["Content"],
+ ContentType=MessageContentType(message["ContentType"]),
From=MessageFrom(
UserID=message["From"]["UserID"],
DisplayName=message["From"]["DisplayName"],
),
CreatedDateTime=message["CreatedDateTime"],
+ ReplyCount=message["ReplyCount"],
)
def delete_message(self, chat_ref: ChatRef, message_id: str) -> bool:
@@ -179,7 +180,7 @@ def delete_message(self, chat_ref: ChatRef, message_id: str) -> bool:
params={
"chatRef": {
"ref": chat_ref.Ref,
- "type": chat_ref.type.value,
+ "type": chat_ref.Type.value,
},
"messageID": message_id,
},
@@ -194,7 +195,7 @@ def get_message(self, chat_ref: ChatRef, message_id: str) -> Message:
params={
"chatRef": {
"ref": chat_ref.Ref,
- "type": chat_ref.type.value,
+ "type": chat_ref.Type.value,
},
"messageID": message_id,
},
@@ -202,15 +203,14 @@ def get_message(self, chat_ref: ChatRef, message_id: str) -> Message:
return Message(
ID=message["ID"],
- Body=MessageBody(
- Content=message["Body"]["Content"],
- ContentType=MessageContentType(message["Body"]["ContentType"]),
- ),
+ Content=message["Content"],
+ ContentType=MessageContentType(message["ContentType"]),
From=MessageFrom(
UserID=message["From"]["UserID"],
DisplayName=message["From"]["DisplayName"],
),
CreatedDateTime=message["CreatedDateTime"],
+ ReplyCount=message["ReplyCount"],
)
def list_my_joined(self, chat_type: ChatType | None = None) -> list[Chat]:
@@ -250,15 +250,14 @@ def list_all_messages(
return [
Message(
ID=message["ID"],
- Body=MessageBody(
- Content=message["Body"]["Content"],
- ContentType=MessageContentType(message["Body"]["ContentType"]),
- ),
+ Content=message["Content"],
+ ContentType=MessageContentType(message["ContentType"]),
From=MessageFrom(
UserID=message["From"]["UserID"],
DisplayName=message["From"]["DisplayName"],
),
CreatedDateTime=message["CreatedDateTime"],
+ ReplyCount=message["ReplyCount"],
)
for message in messages
]
@@ -270,7 +269,7 @@ def list_pinned_messages(self, chat_ref: ChatRef) -> list[Message]:
params={
"chatRef": {
"ref": chat_ref.Ref,
- "type": chat_ref.type.value,
+ "type": chat_ref.Type.value,
},
},
)
@@ -278,15 +277,14 @@ def list_pinned_messages(self, chat_ref: ChatRef) -> list[Message]:
return [
Message(
ID=message["ID"],
- Body=MessageBody(
- Content=message["Body"]["Content"],
- ContentType=MessageContentType(message["Body"]["ContentType"]),
- ),
+ Content=message["Content"],
+ ContentType=MessageContentType(message["ContentType"]),
From=MessageFrom(
UserID=message["From"]["UserID"],
DisplayName=message["From"]["DisplayName"],
),
CreatedDateTime=message["CreatedDateTime"],
+ ReplyCount=message["ReplyCount"],
)
for message in messages
]
@@ -298,7 +296,7 @@ def pin_message(self, chat_ref: ChatRef, message_id: str) -> bool:
params={
"chatRef": {
"ref": chat_ref.Ref,
- "type": chat_ref.type.value,
+ "type": chat_ref.Type.value,
},
"messageID": message_id,
},
@@ -306,7 +304,6 @@ def pin_message(self, chat_ref: ChatRef, message_id: str) -> bool:
return result == "pinned"
-
def unpin_message(self, chat_ref: ChatRef, message_id: str) -> bool:
result = self.client.execute(
cmd_type="request",
@@ -314,7 +311,7 @@ def unpin_message(self, chat_ref: ChatRef, message_id: str) -> bool:
params={
"chatRef": {
"ref": chat_ref.Ref,
- "type": chat_ref.type.value,
+ "type": chat_ref.Type.value,
},
"messageID": message_id,
},
@@ -329,7 +326,7 @@ def get_mentions(self, chat_ref: ChatRef, raw_mentions: list[str]) -> list[Menti
params={
"chatRef": {
"ref": chat_ref.Ref,
- "type": chat_ref.type.value,
+ "type": chat_ref.Type.value,
},
"rawMentions": raw_mentions,
},
diff --git a/python/tests/fake_server/data.py b/python/tests/fake_server/data.py
index e2dc2a2..75c42f6 100644
--- a/python/tests/fake_server/data.py
+++ b/python/tests/fake_server/data.py
@@ -145,6 +145,32 @@ def __init__(self) -> FakeServerData:
),
],
}
+ self.chat_messages = {
+ "chat-123-abc": [
+ Message(
+ ID="msg-001",
+ Content="Hello, team!",
+ ContentType="text",
+ From=MessageFrom(
+ UserID="user-123-abc",
+ DisplayName="Alice"
+ ),
+ CreatedDateTime="2024-01-01T10:00:00Z",
+ ReplyCount=0,
+ ),
+ Message(
+ ID="msg-002",
+ Content="Don't forget the meeting at 3 PM.",
+ ContentType="text",
+ From=MessageFrom(
+ UserID="user-456-def",
+ DisplayName="Bob"
+ ),
+ CreatedDateTime="2024-01-01T11:00:00Z",
+ ReplyCount=2,
+ ),
+ ],
+ }
self.users = [
Member(
ID="user-123-abc",
@@ -680,3 +706,51 @@ def get_update_group_chat_topic_response(self, chat_id: str, request_json: dict)
"isHiddenForAllMembers": chat.IsHidden,
"topic": chat.Topic,
}
+
+ def get_list_messages_in_chat_response(self, chat_id: str) -> dict:
+ return {
+ "value": [
+ {
+ "id": message.ID,
+ "body": {
+ "content": message.Content,
+ "contentType": message.ContentType,
+ },
+ "from": {
+ "user": {
+ "id": message.From.UserID,
+ "displayName": message.From.DisplayName,
+ }
+ },
+ "createdDateTime": message.CreatedDateTime,
+ "replies": [{"id": f"dummy-reply-{i}"} for i in range(message.ReplyCount)]
+ }
+ for message in self.chat_messages.get(chat_id, [])
+ ],
+ }
+
+ def get_send_message_in_chat_response(self, chat_id: str, request_json: dict) -> dict:
+ return {
+ "id": self.newMessageTemplate.ID,
+ "body": {
+ "content": request_json.get("body", {}).get("content"),
+ "contentType": request_json.get("body", {}).get("contentType"),
+ },
+ "from": {
+ "user": {
+ "id": self.newMessageTemplate.From.UserID,
+ "displayName": self.newMessageTemplate.From.DisplayName,
+ }
+ },
+ "createdDateTime": self.newMessageTemplate.CreatedDateTime,
+ }
+
+ def get_delete_message_in_chat_response(self, chat_id: str, message_id: str) -> dict:
+ chat_messages = self.chat_messages.get(chat_id, [])
+
+ message = next((m for m in chat_messages if m.ID == message_id), None)
+ if not message:
+ return {"success": False}
+
+ chat_messages.remove(message)
+ return {"success": True}
diff --git a/python/tests/fake_server/setup.py b/python/tests/fake_server/setup.py
index b386812..d4d2c25 100644
--- a/python/tests/fake_server/setup.py
+++ b/python/tests/fake_server/setup.py
@@ -143,6 +143,44 @@ def handle_chats_request(req):
"Update Chat"
))
+ #GET /chats/{chat_id}/messages
+ httpserver.expect_request(
+ re.compile(r"^/v1.0/chats/([^/]+)/messages"),
+ method="GET"
+ ).respond_with_handler(lambda req: make_log_response(
+ req,
+ data.get_list_messages_in_chat_response(
+ re.search(r"/chats/([^/]+)/messages", req.path).group(1)
+ ),
+ "List Chat Messages"
+ ))
+
+ #POST /chats/{chat_id}/messages
+ httpserver.expect_request(
+ re.compile(r"^/v1.0/chats/([^/]+)/messages"),
+ method="POST"
+ ).respond_with_handler(lambda req: make_log_response(
+ req,
+ data.get_send_message_in_chat_response(
+ re.search(r"/chats/([^/]+)/messages", req.path).group(1),
+ req.json
+ ),
+ "Send Chat Message"
+ ))
+
+ #DELETE /chats/{chat_id}/messages/{message_id}
+ httpserver.expect_request(
+ re.compile(r"^/v1.0/chats/([^/]+)/messages/([^/]+)"),
+ method="DELETE"
+ ).respond_with_handler(lambda req: make_log_response(
+ req,
+ data.get_delete_message_in_chat_response(
+ re.search(r"/chats/([^/]+)/messages/([^/]+)", req.path).group(1),
+ re.search(r"/chats/([^/]+)/messages/([^/]+)", req.path).group(2)
+ ),
+ "Delete Chat Message"
+ ))
+
#POST /teams/{team_id}/channels/{channel_id}/members
httpserver.expect_request(
re.compile(r"^/v1.0/teams/([^/]+)/channels/([^/]+)/members"),
diff --git a/python/tests/test_chats.py b/python/tests/test_chats.py
index d688f6d..89e2773 100644
--- a/python/tests/test_chats.py
+++ b/python/tests/test_chats.py
@@ -1,7 +1,8 @@
from teams_lib_pzsp2_z1.client import TeamsClient
from tests.init_fake_client import init_fake_client
from tests.fake_server.setup import setup_fake_server
-from teams_lib_pzsp2_z1.model.chat import ChatType
+from teams_lib_pzsp2_z1.model.chat import ChatType, ChatRef
+from teams_lib_pzsp2_z1.model.message import MessageContentType
def test_list_my_group_chats_integration(httpserver):
@@ -187,5 +188,42 @@ def test_update_group_chat_topic_integration(httpserver):
assert chat.Topic == data.updatedGroupChatTopic
assert chat.IsHidden == data.group_chats[0].IsHidden
+ finally:
+ client.close()
+
+
+def test_list_messeges_in_chat_integration(httpserver):
+
+ data = setup_fake_server(httpserver)
+
+ client = TeamsClient(auto_init=False)
+ try:
+ init_fake_client(client, httpserver.url_for(""))
+
+ messages = client.chats.list_messages(
+ chat_ref=ChatRef(
+ Ref=data.group_chats[0].Topic,
+ Type=ChatType.GROUP,
+ )
+ )
+
+ assert len(messages) == len(data.chat_messages[data.group_chats[0].ID])
+
+ assert messages[0].ID == data.chat_messages[data.group_chats[0].ID][0].ID
+ assert messages[0].Content == data.chat_messages[data.group_chats[0].ID][0].Content
+ assert messages[0].ContentType == MessageContentType(data.chat_messages[data.group_chats[0].ID][0].ContentType)
+ assert messages[0].From.UserID == data.chat_messages[data.group_chats[0].ID][0].From.UserID
+ assert messages[0].From.DisplayName == data.chat_messages[data.group_chats[0].ID][0].From.DisplayName
+ assert messages[0].ReplyCount == data.chat_messages[data.group_chats[0].ID][0].ReplyCount
+ assert messages[0].CreatedDateTime == data.chat_messages[data.group_chats[0].ID][0].CreatedDateTime
+
+ assert messages[1].ID == data.chat_messages[data.group_chats[0].ID][1].ID
+ assert messages[1].Content == data.chat_messages[data.group_chats[0].ID][1].Content
+ assert messages[1].ContentType == MessageContentType(data.chat_messages[data.group_chats[0].ID][1].ContentType)
+ assert messages[1].From.UserID == data.chat_messages[data.group_chats[0].ID][1].From.UserID
+ assert messages[1].From.DisplayName == data.chat_messages[data.group_chats[0].ID][1].From.DisplayName
+ assert messages[1].ReplyCount == data.chat_messages[data.group_chats[0].ID][1].ReplyCount
+ assert messages[1].CreatedDateTime == data.chat_messages[data.group_chats[0].ID][1].CreatedDateTime
+
finally:
client.close()
\ No newline at end of file
From 76801abe517780847332cfa2ff1297a3110f31dc Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Fri, 2 Jan 2026 15:05:41 +0100
Subject: [PATCH 48/99] feat: added message management tests
---
python/tests/fake_server/data.py | 9 ------
python/tests/fake_server/setup.py | 53 +++++++++++++++++-------------
python/tests/test_chats.py | 54 ++++++++++++++++++++++++++++++-
3 files changed, 84 insertions(+), 32 deletions(-)
diff --git a/python/tests/fake_server/data.py b/python/tests/fake_server/data.py
index 75c42f6..80d4287 100644
--- a/python/tests/fake_server/data.py
+++ b/python/tests/fake_server/data.py
@@ -745,12 +745,3 @@ def get_send_message_in_chat_response(self, chat_id: str, request_json: dict) ->
"createdDateTime": self.newMessageTemplate.CreatedDateTime,
}
- def get_delete_message_in_chat_response(self, chat_id: str, message_id: str) -> dict:
- chat_messages = self.chat_messages.get(chat_id, [])
-
- message = next((m for m in chat_messages if m.ID == message_id), None)
- if not message:
- return {"success": False}
-
- chat_messages.remove(message)
- return {"success": True}
diff --git a/python/tests/fake_server/setup.py b/python/tests/fake_server/setup.py
index d4d2c25..58b450e 100644
--- a/python/tests/fake_server/setup.py
+++ b/python/tests/fake_server/setup.py
@@ -66,6 +66,30 @@ def handle_chats_request(req):
f"List Chats ({chat_type})"
)
+ #GET /chats/{chat_id}/messages
+ httpserver.expect_request(
+ re.compile(r"^/v1.0/chats/([^/]+)/messages"),
+ method="GET"
+ ).respond_with_handler(lambda req: make_log_response(
+ req,
+ data.get_list_messages_in_chat_response(
+ re.search(r"/chats/([^/]+)/messages", req.path).group(1)
+ ),
+ "List Chat Messages"
+ ))
+
+ #GET users/me/chats/{chat_id}/messages
+ httpserver.expect_request(
+ re.compile(r"^/v1.0/users/me-token-to-replace/chats/([^/]+)/messages"),
+ method="GET"
+ ).respond_with_handler(lambda req: make_log_response(
+ req,
+ data.get_list_messages_in_chat_response(
+ re.search(r"/users/me-token-to-replace/chats/([^/]+)/messages", req.path).group(1)
+ ),
+ "List Chat Messages (me)"
+ ))
+
# GET /users/me/chats
httpserver.expect_request(
"/v1.0/users/me-token-to-replace/chats",
@@ -143,17 +167,7 @@ def handle_chats_request(req):
"Update Chat"
))
- #GET /chats/{chat_id}/messages
- httpserver.expect_request(
- re.compile(r"^/v1.0/chats/([^/]+)/messages"),
- method="GET"
- ).respond_with_handler(lambda req: make_log_response(
- req,
- data.get_list_messages_in_chat_response(
- re.search(r"/chats/([^/]+)/messages", req.path).group(1)
- ),
- "List Chat Messages"
- ))
+
#POST /chats/{chat_id}/messages
httpserver.expect_request(
@@ -168,18 +182,13 @@ def handle_chats_request(req):
"Send Chat Message"
))
- #DELETE /chats/{chat_id}/messages/{message_id}
+ #DELETE users/me/chats/{chat_id}/messages/{message_id}/softDelete
httpserver.expect_request(
- re.compile(r"^/v1.0/chats/([^/]+)/messages/([^/]+)"),
- method="DELETE"
- ).respond_with_handler(lambda req: make_log_response(
- req,
- data.get_delete_message_in_chat_response(
- re.search(r"/chats/([^/]+)/messages/([^/]+)", req.path).group(1),
- re.search(r"/chats/([^/]+)/messages/([^/]+)", req.path).group(2)
- ),
- "Delete Chat Message"
- ))
+ re.compile(r"/v1.0/users/[^/]+/chats/[^/]+/messages/[^/]+/softDelete"),
+ method="POST"
+ ).respond_with_response(
+ Response(status=204)
+ )
#POST /teams/{team_id}/channels/{channel_id}/members
httpserver.expect_request(
diff --git a/python/tests/test_chats.py b/python/tests/test_chats.py
index 89e2773..3602482 100644
--- a/python/tests/test_chats.py
+++ b/python/tests/test_chats.py
@@ -2,7 +2,7 @@
from tests.init_fake_client import init_fake_client
from tests.fake_server.setup import setup_fake_server
from teams_lib_pzsp2_z1.model.chat import ChatType, ChatRef
-from teams_lib_pzsp2_z1.model.message import MessageContentType
+from teams_lib_pzsp2_z1.model.message import MessageContentType, MessageBody
def test_list_my_group_chats_integration(httpserver):
@@ -225,5 +225,57 @@ def test_list_messeges_in_chat_integration(httpserver):
assert messages[1].ReplyCount == data.chat_messages[data.group_chats[0].ID][1].ReplyCount
assert messages[1].CreatedDateTime == data.chat_messages[data.group_chats[0].ID][1].CreatedDateTime
+ finally:
+ client.close()
+
+def test_send_message_in_chat_integration(httpserver):
+
+ data = setup_fake_server(httpserver)
+
+ client = TeamsClient(auto_init=False)
+ try:
+ init_fake_client(client, httpserver.url_for(""))
+
+ message = client.chats.send_message(
+ chat_ref=ChatRef(
+ Ref=data.group_chats[0].Topic,
+ Type=ChatType.GROUP,
+ ),
+ body=MessageBody(
+ Content=data.newMessageTemplate.Content,
+ ContentType=MessageContentType(data.newMessageTemplate.ContentType),
+ Mentions=[],
+ )
+ )
+
+ assert message.ID == data.newMessageTemplate.ID
+ assert message.Content == data.newMessageTemplate.Content
+ assert message.ContentType == MessageContentType(data.newMessageTemplate.ContentType)
+ assert message.From.UserID == data.newMessageTemplate.From.UserID
+ assert message.From.DisplayName == data.newMessageTemplate.From.DisplayName
+ assert message.ReplyCount == data.newMessageTemplate.ReplyCount
+ assert message.CreatedDateTime == data.newMessageTemplate.CreatedDateTime
+
+ finally:
+ client.close()
+
+def test_delete_message_in_chat_integration(httpserver):
+
+ data = setup_fake_server(httpserver)
+
+ client = TeamsClient(auto_init=False)
+ try:
+ init_fake_client(client, httpserver.url_for(""))
+
+ result = client.chats.delete_message(
+ chat_ref=ChatRef(
+ Ref=data.group_chats[0].Topic,
+ Type=ChatType.GROUP,
+ ),
+ message_id=data.chat_messages[data.group_chats[0].ID][0].ID,
+ )
+
+ assert result is True
+
finally:
client.close()
\ No newline at end of file
From 8d75893bb7fa16db992a4d064203d45534d06caf Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Fri, 2 Jan 2026 15:10:59 +0100
Subject: [PATCH 49/99] feat: implemented get message in chat test
---
python/tests/fake_server/data.py | 20 ++++++++++++++++++++
python/tests/fake_server/setup.py | 17 +++++++++++++++--
python/tests/test_chats.py | 26 ++++++++++++++++++++++++++
3 files changed, 61 insertions(+), 2 deletions(-)
diff --git a/python/tests/fake_server/data.py b/python/tests/fake_server/data.py
index 80d4287..ecc97b2 100644
--- a/python/tests/fake_server/data.py
+++ b/python/tests/fake_server/data.py
@@ -745,3 +745,23 @@ def get_send_message_in_chat_response(self, chat_id: str, request_json: dict) ->
"createdDateTime": self.newMessageTemplate.CreatedDateTime,
}
+ def get_get_message_in_chat_response(self, chat_id: str, message_id: str) -> dict | None:
+ message = next((m for m in self.chat_messages.get(chat_id, []) if m.ID == message_id), None)
+ if not message:
+ return None
+
+ return {
+ "id": message.ID,
+ "body": {
+ "content": message.Content,
+ "contentType": message.ContentType,
+ },
+ "from": {
+ "user": {
+ "id": message.From.UserID,
+ "displayName": message.From.DisplayName,
+ }
+ },
+ "createdDateTime": message.CreatedDateTime,
+ "replies": [{"id": f"dummy-reply-{i}"} for i in range(message.ReplyCount)]
+ }
diff --git a/python/tests/fake_server/setup.py b/python/tests/fake_server/setup.py
index 58b450e..4e840b9 100644
--- a/python/tests/fake_server/setup.py
+++ b/python/tests/fake_server/setup.py
@@ -66,6 +66,20 @@ def handle_chats_request(req):
f"List Chats ({chat_type})"
)
+
+ #GET /chats/{chat_id}/messages/{message_id}
+ httpserver.expect_request(
+ re.compile(r"^/v1.0/chats/([^/]+)/messages/([^/]+)"),
+ method="GET"
+ ).respond_with_handler(lambda req: make_log_response(
+ req,
+ data.get_get_message_in_chat_response(
+ re.search(r"/chats/([^/]+)/messages/([^/]+)", req.path).group(1),
+ re.search(r"/chats/([^/]+)/messages/([^/]+)", req.path).group(2)
+ ),
+ "Get Chat Message"
+ ))
+
#GET /chats/{chat_id}/messages
httpserver.expect_request(
re.compile(r"^/v1.0/chats/([^/]+)/messages"),
@@ -167,8 +181,6 @@ def handle_chats_request(req):
"Update Chat"
))
-
-
#POST /chats/{chat_id}/messages
httpserver.expect_request(
re.compile(r"^/v1.0/chats/([^/]+)/messages"),
@@ -190,6 +202,7 @@ def handle_chats_request(req):
Response(status=204)
)
+
#POST /teams/{team_id}/channels/{channel_id}/members
httpserver.expect_request(
re.compile(r"^/v1.0/teams/([^/]+)/channels/([^/]+)/members"),
diff --git a/python/tests/test_chats.py b/python/tests/test_chats.py
index 3602482..4b4797e 100644
--- a/python/tests/test_chats.py
+++ b/python/tests/test_chats.py
@@ -277,5 +277,31 @@ def test_delete_message_in_chat_integration(httpserver):
assert result is True
+ finally:
+ client.close()
+
+def test_get_message_in_chat_integration(httpserver):
+
+ data = setup_fake_server(httpserver)
+
+ client = TeamsClient(auto_init=False)
+ try:
+ init_fake_client(client, httpserver.url_for(""))
+
+ message = client.chats.get_message(
+ chat_ref=ChatRef(
+ Ref=data.group_chats[0].Topic,
+ Type=ChatType.GROUP,
+ ),
+ message_id=data.chat_messages[data.group_chats[0].ID][0].ID,
+ )
+
+ assert message.ID == data.chat_messages[data.group_chats[0].ID][0].ID
+ assert message.Content == data.chat_messages[data.group_chats[0].ID][0].Content
+ assert message.ContentType == MessageContentType(data.chat_messages[data.group_chats[0].ID][0].ContentType)
+ assert message.From.UserID == data.chat_messages[data.group_chats[0].ID][0].From.UserID
+ assert message.From.DisplayName == data.chat_messages[data.group_chats[0].ID][0].From.DisplayName
+ assert message.ReplyCount == data.chat_messages[data.group_chats[0].ID][0].ReplyCount
+ assert message.CreatedDateTime == data.chat_messages[data.group_chats[0].ID][0].CreatedDateTime
finally:
client.close()
\ No newline at end of file
From c69a7eda5fa4e7023aab9a995893044181a72ec7 Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Fri, 2 Jan 2026 16:06:06 +0100
Subject: [PATCH 50/99] feat: implemented test for list All my messeges
---
.../json-client/decoders/decode-timeRange.go | 33 +++++++++++++++++
go/internal/json-client/requests-chats.go | 11 ++++--
python/tests/fake_server/data.py | 28 ++++++++++++++
python/tests/fake_server/setup.py | 13 +++++++
python/tests/test_chats.py | 37 +++++++++++++++++++
5 files changed, 118 insertions(+), 4 deletions(-)
create mode 100644 go/internal/json-client/decoders/decode-timeRange.go
diff --git a/go/internal/json-client/decoders/decode-timeRange.go b/go/internal/json-client/decoders/decode-timeRange.go
new file mode 100644
index 0000000..e6f74b6
--- /dev/null
+++ b/go/internal/json-client/decoders/decode-timeRange.go
@@ -0,0 +1,33 @@
+package decoders
+
+import (
+ "fmt"
+ "time"
+)
+
+func DecodeTimeRange(startTime string, endTime string) (*time.Time, *time.Time, error) {
+ parseTime := func(s string) (time.Time, error) {
+ t, err := time.Parse(time.RFC3339, s)
+ if err == nil {
+ return t, nil
+ }
+ layoutNaive := "2006-01-02T15:04:05"
+ if len(s) > 19 {
+ layoutNaive = "2006-01-02T15:04:05.999999"
+ }
+
+ return time.Parse(layoutNaive, s)
+ }
+
+ parsedStartTime, err := parseTime(startTime)
+ if err != nil {
+ return nil, nil, fmt.Errorf("error parsing startTime ('%s'): %v", startTime, err)
+ }
+
+ parsedEndTime, err := parseTime(endTime)
+ if err != nil {
+ return nil, nil, fmt.Errorf("error parsing endTime ('%s'): %v", endTime, err)
+ }
+
+ return &parsedStartTime, &parsedEndTime, nil
+}
diff --git a/go/internal/json-client/requests-chats.go b/go/internal/json-client/requests-chats.go
index f843158..68eca9d 100644
--- a/go/internal/json-client/requests-chats.go
+++ b/go/internal/json-client/requests-chats.go
@@ -2,7 +2,6 @@ package jsonclient
import (
"context"
- "time"
"github.com/pzsp-teams/lib-python/internal/json-client/decoders"
"github.com/pzsp-teams/lib/chats"
@@ -144,14 +143,18 @@ func (c *TeamsJSONClient) ListMyChats(p map[string]interface{}) (interface{}, er
}
type listChatMessagesParams struct {
- StartTime time.Time `json:"startTime"`
- EndTime time.Time `json:"endTime"`
+ StartTime string `json:"startTime"`
+ EndTime string `json:"endTime"`
Top *int32 `json:"top"`
}
func (c *TeamsJSONClient) ListMyChatMessages(p map[string]interface{}) (interface{}, error) {
return execute(p, func(params listChatMessagesParams) (interface{}, error) {
- return c.client.Chats.ListAllMessages(context.TODO(), ¶ms.StartTime, ¶ms.EndTime, params.Top)
+ parsedStartTime, parsedEndTime, err := decoders.DecodeTimeRange(params.StartTime, params.EndTime)
+ if err != nil {
+ return nil, err
+ }
+ return c.client.Chats.ListAllMessages(context.TODO(), parsedStartTime, parsedEndTime, params.Top)
})
}
diff --git a/python/tests/fake_server/data.py b/python/tests/fake_server/data.py
index ecc97b2..2278cf1 100644
--- a/python/tests/fake_server/data.py
+++ b/python/tests/fake_server/data.py
@@ -765,3 +765,31 @@ def get_get_message_in_chat_response(self, chat_id: str, message_id: str) -> dic
"createdDateTime": message.CreatedDateTime,
"replies": [{"id": f"dummy-reply-{i}"} for i in range(message.ReplyCount)]
}
+
+ def get_all_messeges_in_chats_response(self) -> dict:
+ return {
+ "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#Collection(chatMessage)",
+ "@odata.count": len(self.chat_messages),
+ "value": [
+ {
+ "@odata.type": "#microsoft.graph.chatMessage",
+ "id": message.ID,
+ "etag": message.ID,
+ "messageType": "message",
+ "chatId": self.group_chats[0].ID,
+ "body": {
+ "content": message.Content,
+ "contentType": message.ContentType,
+ },
+ "from": {
+ "user": {
+ "id": message.From.UserID,
+ "displayName": message.From.DisplayName,
+ }
+ },
+ "createdDateTime": message.CreatedDateTime,
+ "lastModifiedDateTime": message.CreatedDateTime,
+ }
+ for message in self.chat_messages[self.group_chats[0].ID]
+ ],
+ }
diff --git a/python/tests/fake_server/setup.py b/python/tests/fake_server/setup.py
index 4e840b9..d815fc8 100644
--- a/python/tests/fake_server/setup.py
+++ b/python/tests/fake_server/setup.py
@@ -4,6 +4,7 @@
import sys
from werkzeug.wrappers import Response
from teams_lib_pzsp2_z1.model.chat import ChatType
+import traceback
def setup_fake_server(httpserver) -> FakeServerData:
"""
@@ -45,6 +46,16 @@ def make_log_response(request, data_dict: dict, context_msg: str = "") -> Respon
req, data.get_myJoinedTeams_response(), "List Joined Teams"
))
+ #GET /users/me/chats/getAllMessages()
+ httpserver.expect_request(
+ "/v1.0/users/me-token-to-replace/chats/getAllMessages()",
+ method="GET"
+ ).respond_with_handler(lambda req: make_log_response(
+ req,
+ data.get_all_messeges_in_chats_response(),
+ "List All Chat Messages"
+ ))
+
def handle_chats_request(req):
filter_param = req.args.get('$filter', '')
chat_type = None
@@ -67,6 +78,8 @@ def handle_chats_request(req):
)
+
+
#GET /chats/{chat_id}/messages/{message_id}
httpserver.expect_request(
re.compile(r"^/v1.0/chats/([^/]+)/messages/([^/]+)"),
diff --git a/python/tests/test_chats.py b/python/tests/test_chats.py
index 4b4797e..a2bc139 100644
--- a/python/tests/test_chats.py
+++ b/python/tests/test_chats.py
@@ -3,6 +3,7 @@
from tests.fake_server.setup import setup_fake_server
from teams_lib_pzsp2_z1.model.chat import ChatType, ChatRef
from teams_lib_pzsp2_z1.model.message import MessageContentType, MessageBody
+from datetime import datetime
def test_list_my_group_chats_integration(httpserver):
@@ -303,5 +304,41 @@ def test_get_message_in_chat_integration(httpserver):
assert message.From.DisplayName == data.chat_messages[data.group_chats[0].ID][0].From.DisplayName
assert message.ReplyCount == data.chat_messages[data.group_chats[0].ID][0].ReplyCount
assert message.CreatedDateTime == data.chat_messages[data.group_chats[0].ID][0].CreatedDateTime
+ finally:
+ client.close()
+
+
+def test_get_all_messages_in_chat_integration(httpserver):
+
+ data = setup_fake_server(httpserver)
+
+ client = TeamsClient(auto_init=False)
+ try:
+ init_fake_client(client, httpserver.url_for(""))
+
+ messages = client.chats.list_all_messages(
+ start_time=datetime.min,
+ end_time=datetime.max,
+ top=50,
+ )
+
+ assert len(messages) == len(data.chat_messages[data.group_chats[0].ID])
+
+ assert messages[0].ID == data.chat_messages[data.group_chats[0].ID][0].ID
+ assert messages[0].Content == data.chat_messages[data.group_chats[0].ID][0].Content
+ assert messages[0].ContentType == MessageContentType(data.chat_messages[data.group_chats[0].ID][0].ContentType)
+ assert messages[0].From.UserID == data.chat_messages[data.group_chats[0].ID][0].From.UserID
+ assert messages[0].From.DisplayName == data.chat_messages[data.group_chats[0].ID][0].From.DisplayName
+ assert messages[0].ReplyCount == 0
+ assert messages[0].CreatedDateTime == data.chat_messages[data.group_chats[0].ID][0].CreatedDateTime
+
+ assert messages[1].ID == data.chat_messages[data.group_chats[0].ID][1].ID
+ assert messages[1].Content == data.chat_messages[data.group_chats[0].ID][1].Content
+ assert messages[1].ContentType == MessageContentType(data.chat_messages[data.group_chats[0].ID][1].ContentType)
+ assert messages[1].From.UserID == data.chat_messages[data.group_chats[0].ID][1].From.UserID
+ assert messages[1].From.DisplayName == data.chat_messages[data.group_chats[0].ID][1].From.DisplayName
+ assert messages[1].ReplyCount == 0
+ assert messages[1].CreatedDateTime == data.chat_messages[data.group_chats[0].ID][1].CreatedDateTime
+
finally:
client.close()
\ No newline at end of file
From c7f793561c28f92f86d652ec72694aa6822903bc Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Fri, 2 Jan 2026 16:43:04 +0100
Subject: [PATCH 51/99] feat: implemented tests for pinned messages
---
python/tests/fake_server/data.py | 15 ++++--
python/tests/fake_server/setup.py | 30 ++++++++++-
python/tests/test_chats.py | 84 ++++++++++++++++++++++++++++++-
3 files changed, 123 insertions(+), 6 deletions(-)
diff --git a/python/tests/fake_server/data.py b/python/tests/fake_server/data.py
index 2278cf1..b9b766a 100644
--- a/python/tests/fake_server/data.py
+++ b/python/tests/fake_server/data.py
@@ -617,9 +617,6 @@ def get_list_chats_response(self, chat_type: ChatType) -> dict:
else:
chats = self.group_chats
- print(chat_type)
- print(chat_type == ChatType.ONEONONE)
-
return {
"@odata.context": "https://graph.microsoft.com/v1.0/$metadata#chats",
"@odata.count": len(chats),
@@ -793,3 +790,15 @@ def get_all_messeges_in_chats_response(self) -> dict:
for message in self.chat_messages[self.group_chats[0].ID]
],
}
+
+ def get_list_pinned_messages_in_chat_response(self, chat_id: str) -> dict:
+ return {
+ "value": [
+ {
+ "@odata.type": "#microsoft.graph.pinnedChatMessageInfo",
+ "id": message.ID,
+ }
+ for message in self.chat_messages.get(chat_id, [])
+ ],
+ }
+
diff --git a/python/tests/fake_server/setup.py b/python/tests/fake_server/setup.py
index d815fc8..979bbcf 100644
--- a/python/tests/fake_server/setup.py
+++ b/python/tests/fake_server/setup.py
@@ -56,6 +56,34 @@ def make_log_response(request, data_dict: dict, context_msg: str = "") -> Respon
"List All Chat Messages"
))
+ #GET /chats/{chat_id}/pinnedMessages
+ httpserver.expect_request(
+ re.compile(r"^/v1.0/chats/([^/]+)/pinnedMessages"),
+ method="GET"
+ ).respond_with_handler(lambda req: make_log_response(
+ req,
+ data.get_list_pinned_messages_in_chat_response(
+ re.search(r"/chats/([^/]+)/pinnedMessages", req.path).group(1)
+ ),
+ "List Pinned Chat Messages"
+ ))
+
+ # POST /chats/{chat_id}/pinnedMessages
+ httpserver.expect_request(
+ re.compile(r"^/v1.0/chats/([^/]+)/pinnedMessages"),
+ method="POST"
+ ).respond_with_response(
+ Response(status=204)
+ )
+
+ # DELETE /chats/{chat_id}/pinnedMessages/{message_id}
+ httpserver.expect_request(
+ re.compile(r"^/v1.0/chats/([^/]+)/pinnedMessages/([^/]+)"),
+ method="DELETE"
+ ).respond_with_response(
+ Response(status=204)
+ )
+
def handle_chats_request(req):
filter_param = req.args.get('$filter', '')
chat_type = None
@@ -64,8 +92,6 @@ def handle_chats_request(req):
if match:
chat_type = match.group(1)
- print(f"DEBUG: Parsowanie filtra: '{filter_param}' -> Typ: '{chat_type}'")
-
if chat_type == "oneOnOne":
chat_type = ChatType.ONEONONE
elif chat_type == "group":
diff --git a/python/tests/test_chats.py b/python/tests/test_chats.py
index a2bc139..4a48c6d 100644
--- a/python/tests/test_chats.py
+++ b/python/tests/test_chats.py
@@ -341,4 +341,86 @@ def test_get_all_messages_in_chat_integration(httpserver):
assert messages[1].CreatedDateTime == data.chat_messages[data.group_chats[0].ID][1].CreatedDateTime
finally:
- client.close()
\ No newline at end of file
+ client.close()
+
+
+def test_list_pinned_messages_integration(httpserver):
+
+ data = setup_fake_server(httpserver)
+
+ client = TeamsClient(auto_init=False)
+ try:
+ init_fake_client(client, httpserver.url_for(""))
+
+ messages = client.chats.list_pinned_messages(
+ chat_ref=ChatRef(
+ Ref=data.group_chats[0].Topic,
+ Type=ChatType.GROUP,
+ )
+ )
+
+ assert len(messages) == len(data.chat_messages[data.group_chats[0].ID])
+
+ assert messages[0].ID == data.chat_messages[data.group_chats[0].ID][0].ID
+ assert messages[0].Content == data.chat_messages[data.group_chats[0].ID][0].Content
+ assert messages[0].ContentType == MessageContentType(data.chat_messages[data.group_chats[0].ID][0].ContentType)
+ assert messages[0].From.UserID == data.chat_messages[data.group_chats[0].ID][0].From.UserID
+ assert messages[0].From.DisplayName == data.chat_messages[data.group_chats[0].ID][0].From.DisplayName
+ assert messages[0].ReplyCount == 0
+ assert messages[0].CreatedDateTime == data.chat_messages[data.group_chats[0].ID][0].CreatedDateTime
+
+ assert messages[1].ID == data.chat_messages[data.group_chats[0].ID][1].ID
+ assert messages[1].Content == data.chat_messages[data.group_chats[0].ID][1].Content
+ assert messages[1].ContentType == MessageContentType(data.chat_messages[data.group_chats[0].ID][1].ContentType)
+ assert messages[1].From.UserID == data.chat_messages[data.group_chats[0].ID][1].From.UserID
+ assert messages[1].From.DisplayName == data.chat_messages[data.group_chats[0].ID][1].From.DisplayName
+ assert messages[1].ReplyCount == 0
+ assert messages[1].CreatedDateTime == data.chat_messages[data.group_chats[0].ID][1].CreatedDateTime
+
+ finally:
+ client.close()
+
+
+def test_pin_message_in_chat_integration(httpserver):
+
+ data = setup_fake_server(httpserver)
+
+ client = TeamsClient(auto_init=False)
+ try:
+ init_fake_client(client, httpserver.url_for(""))
+
+ result = client.chats.pin_message(
+ chat_ref=ChatRef(
+ Ref=data.group_chats[0].Topic,
+ Type=ChatType.GROUP,
+ ),
+ message_id=data.chat_messages[data.group_chats[0].ID][0].ID,
+ )
+
+ assert result is True
+
+ finally:
+ client.close()
+
+
+def test_unpin_message_in_chat_integration(httpserver):
+
+ data = setup_fake_server(httpserver)
+
+ client = TeamsClient(auto_init=False)
+ try:
+ init_fake_client(client, httpserver.url_for(""))
+
+ result = client.chats.unpin_message(
+ chat_ref=ChatRef(
+ Ref=data.group_chats[0].Topic,
+ Type=ChatType.GROUP,
+ ),
+ message_id=data.chat_messages[data.group_chats[0].ID][0].ID,
+ )
+
+ assert result is True
+
+ finally:
+ client.close()
+
From 0f5713d2be66da811e68dfe4230a5d9acc2b5283 Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Fri, 2 Jan 2026 16:56:01 +0100
Subject: [PATCH 52/99] feat: implemented tests for get mentions
---
.../teams_lib_pzsp2_z1/services/channels.py | 6 ++---
python/teams_lib_pzsp2_z1/services/chats.py | 7 ++---
python/tests/test_channels.py | 23 ++++++++++++++++
python/tests/test_chats.py | 26 +++++++++++++++++++
4 files changed, 56 insertions(+), 6 deletions(-)
diff --git a/python/teams_lib_pzsp2_z1/services/channels.py b/python/teams_lib_pzsp2_z1/services/channels.py
index 084e529..b2bd3d8 100644
--- a/python/teams_lib_pzsp2_z1/services/channels.py
+++ b/python/teams_lib_pzsp2_z1/services/channels.py
@@ -293,15 +293,15 @@ def remove_member(self, teamRef: str, channelRef: str, userRef: str) -> bool:
return response == "removed"
def get_mentions(
- self, teamRef: str, channelRef: str, rawMentions: list[str]
+ self, teamRef: str, channelRef: str, raw_mentions: list[str]
) -> list[Mention]:
response = self.client.execute(
cmd_type="request",
- method="getChannelMentions",
+ method="getMentionsInChannel",
params={
"teamRef": teamRef,
"channelRef": channelRef,
- "rawMentions": rawMentions,
+ "rawMentions": raw_mentions,
},
)
return [Mention(**mention) for mention in response]
diff --git a/python/teams_lib_pzsp2_z1/services/chats.py b/python/teams_lib_pzsp2_z1/services/chats.py
index 2e705b3..dab5f1b 100644
--- a/python/teams_lib_pzsp2_z1/services/chats.py
+++ b/python/teams_lib_pzsp2_z1/services/chats.py
@@ -334,9 +334,10 @@ def get_mentions(self, chat_ref: ChatRef, raw_mentions: list[str]) -> list[Menti
return [
Mention(
- ID=mention["ID"],
- Mentioned=mention["Mentioned"],
- MentionText=mention["MentionText"],
+ Kind=mention["Kind"],
+ AtID=mention["AtID"],
+ Text=mention["Text"],
+ TargetID=mention["TargetID"],
)
for mention in mentions
]
diff --git a/python/tests/test_channels.py b/python/tests/test_channels.py
index e0695d0..74135e7 100644
--- a/python/tests/test_channels.py
+++ b/python/tests/test_channels.py
@@ -2,6 +2,7 @@
from tests.init_fake_client import init_fake_client
from tests.fake_server.setup import setup_fake_server
from teams_lib_pzsp2_z1.model.message import MessageContentType, MessageBody
+from teams_lib_pzsp2_z1.model.mention import MentionKind
def test_list_channels_integration(httpserver):
@@ -371,6 +372,28 @@ def test_remove_member_integration(httpserver):
finally:
client.close()
+def test_get_mention_integration(httpserver):
+
+ data = setup_fake_server(httpserver)
+
+ client = TeamsClient(auto_init=False)
+ try:
+ init_fake_client(client, httpserver.url_for(""))
+
+ mention = client.channels.get_mentions(
+ teamRef=data.teams[0].DisplayName,
+ channelRef=data.channels[data.teams[0].ID][0].Name,
+ raw_mentions=MentionKind.TEAM.value,
+ )
+
+ assert mention[0].Kind == MentionKind.TEAM.value
+ assert mention[0].AtID == 0
+ assert mention[0].Text == data.teams[0].DisplayName
+ assert mention[0].TargetID == data.teams[0].ID
+
+ finally:
+ client.close()
+
diff --git a/python/tests/test_chats.py b/python/tests/test_chats.py
index 4a48c6d..8966601 100644
--- a/python/tests/test_chats.py
+++ b/python/tests/test_chats.py
@@ -3,6 +3,7 @@
from tests.fake_server.setup import setup_fake_server
from teams_lib_pzsp2_z1.model.chat import ChatType, ChatRef
from teams_lib_pzsp2_z1.model.message import MessageContentType, MessageBody
+from teams_lib_pzsp2_z1.model.mention import MentionKind
from datetime import datetime
@@ -424,3 +425,28 @@ def test_unpin_message_in_chat_integration(httpserver):
finally:
client.close()
+
+def test_get_mention_integration(httpserver):
+
+ data = setup_fake_server(httpserver)
+
+ client = TeamsClient(auto_init=False)
+ try:
+ init_fake_client(client, httpserver.url_for(""))
+
+ mention = client.chats.get_mentions(
+ chat_ref=ChatRef(
+ Ref=data.group_chats[0].Topic,
+ Type=ChatType.GROUP,
+ ),
+ raw_mentions=MentionKind.EVERYONE.value,
+ )
+
+ assert mention[0].Kind == MentionKind.EVERYONE.value
+ assert mention[0].AtID == 0
+ assert mention[0].Text == "Everyone"
+ assert mention[0].TargetID == data.group_chats[0].ID
+
+ finally:
+ client.close()
+
From 854263aa97f4e804a9c84d306b9d6afe5bcae14d Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Fri, 2 Jan 2026 17:08:04 +0100
Subject: [PATCH 53/99] chore: cleaned mock server code formatting a bit
---
python/tests/fake_server/data.py | 629 +++++++++++++-----------------
python/tests/fake_server/setup.py | 419 ++++++++++----------
2 files changed, 486 insertions(+), 562 deletions(-)
diff --git a/python/tests/fake_server/data.py b/python/tests/fake_server/data.py
index b9b766a..525b4fe 100644
--- a/python/tests/fake_server/data.py
+++ b/python/tests/fake_server/data.py
@@ -1,256 +1,169 @@
-from dataclasses import dataclass
+from dataclasses import dataclass, field
+from typing import Optional, List, Dict, Any
+
from teams_lib_pzsp2_z1.model.team import Team
from teams_lib_pzsp2_z1.model.chat import Chat, ChatType
from teams_lib_pzsp2_z1.model.channel import Channel
from teams_lib_pzsp2_z1.model.message import Message, MessageFrom
from teams_lib_pzsp2_z1.model.member import Member
-from dataclasses import field
+
+# --- Constants for OData ---
+ODATA_CONTEXT = "@odata.context"
+ODATA_TYPE = "@odata.type"
+ODATA_COUNT = "@odata.count"
+GRAPH_URL = "https://graph.microsoft.com/v1.0/$metadata"
@dataclass
class FakeServerData:
- teams: list[Team]
- channels: dict[str, list[Channel]]
+ teams: List[Team]
+ channels: Dict[str, List[Channel]]
+ # State tracking variables
newGroupID: str
newTeamName: str
newGroupMailNickname: str
newTeamVisibility: str
- potentialTeams: list[Team] = field(default_factory=list)
-
- def __init__(self) -> FakeServerData:
+ potentialTeams: List[Team] = field(default_factory=list)
+
+ # Initial data containers
+ messages: Dict[str, List[Message]] = field(default_factory=dict)
+ replies: Dict[str, List[Message]] = field(default_factory=dict)
+ members: Dict[str, Dict[str, List[Member]]] = field(default_factory=dict)
+ group_chat_members: Dict[str, List[Member]] = field(default_factory=dict)
+ chat_messages: Dict[str, List[Message]] = field(default_factory=dict)
+ users: List[Member] = field(default_factory=list)
+ me: Member = field(default_factory=Member) # type: ignore
+ group_chats: List[Chat] = field(default_factory=list)
+ oneonone_chats: List[Chat] = field(default_factory=list)
+
+ # Templates and temporary state
+ newChannelName: str = "New Channel"
+ newChannelID: str = "19:newchannelid@thread.tacv2"
+ newMessageTemplate: Message = field(default_factory=lambda: Message(
+ ID="new-message-id",
+ Content="This is a new message.",
+ ContentType="text",
+ From=MessageFrom(UserID="user-new-001", DisplayName="New User"),
+ CreatedDateTime="2024-01-02T10:00:00Z",
+ ReplyCount=0,
+ ))
+ newMemberTemplate: Member = field(default_factory=lambda: Member(
+ ID="user-new-002", UserID="new-user-002", DisplayName="New Member",
+ Role="owner", Email="newmember@example.com"
+ ))
+ newChatTemplate: Chat = field(default_factory=lambda: Chat(
+ ID="new-chat-001", Type=ChatType.GROUP, IsHidden=False, Topic="New Chat Topic"
+ ))
+ updatedGroupChatTopic: str = "Updated Chat Topic"
+
+ def __init__(self) -> None:
+ # --- Teams ---
self.teams = [
- Team(
- ID="team-123-abc",
- DisplayName="Test Team",
- Description="A team for testing",
- IsArchived=False,
- Visibility="private",
- ),
- Team(
- ID="team-456-def",
- DisplayName="Another Team",
- Description="Another team for testing",
- IsArchived=False,
- Visibility="public",
- ),
- Team(
- ID="archived-team-789-ghi",
- DisplayName="Archived Team",
- Description="An archived team for testing",
- IsArchived=True,
- Visibility="private",
- )
+ Team(ID="team-123-abc", DisplayName="Test Team", Description="A team for testing", IsArchived=False, Visibility="private"),
+ Team(ID="team-456-def", DisplayName="Another Team", Description="Another team for testing", IsArchived=False, Visibility="public"),
+ Team(ID="archived-team-789-ghi", DisplayName="Archived Team", Description="An archived team for testing", IsArchived=True, Visibility="private")
]
+ self.potentialTeams = []
+
+ # --- Channels ---
self.channels = {
"team-123-abc": [
- Channel(
- ID="19:123123@thread.tacv2",
- Name="Something",
- IsGeneral=False,
- ),
- Channel(
- ID="19:999999@thread.tacv2",
- Name="Development",
- IsGeneral=False,
- ),
+ Channel(ID="19:123123@thread.tacv2", Name="Something", IsGeneral=False),
+ Channel(ID="19:999999@thread.tacv2", Name="Development", IsGeneral=False),
],
}
+
+ # --- Messages (Channel) ---
self.messages = {
"19:123123@thread.tacv2": [
- Message(
- ID="msg-001",
- Content="Hello, team!",
- ContentType="text",
- From=MessageFrom(
- UserID="user-123-abc",
- DisplayName="Alice"
- ),
- CreatedDateTime="2024-01-01T10:00:00Z",
- ReplyCount=0,
- ),
- Message(
- ID="msg-002",
- Content="Don't forget the meeting at 3 PM.",
- ContentType="text",
- From=MessageFrom(
- UserID="user-456-def",
- DisplayName="Bob"
- ),
- CreatedDateTime="2024-01-01T11:00:00Z",
- ReplyCount=2,
- ),
+ Message(ID="msg-001", Content="Hello, team!", ContentType="text", From=MessageFrom(UserID="user-123-abc", DisplayName="Alice"), CreatedDateTime="2024-01-01T10:00:00Z", ReplyCount=0),
+ Message(ID="msg-002", Content="Don't forget the meeting at 3 PM.", ContentType="text", From=MessageFrom(UserID="user-456-def", DisplayName="Bob"), CreatedDateTime="2024-01-01T11:00:00Z", ReplyCount=2),
],
}
+
+ # --- Replies ---
self.replies = {
"msg-002": [
- Message(
- ID="msg-002-reply-001",
- Content="Thanks for the reminder!",
- ContentType="text",
- From=MessageFrom(
- UserID="user-789-ghi",
- DisplayName="Charlie"
- ),
- CreatedDateTime="2024-01-01T12:00:00Z",
- ReplyCount=0,
- ),
- Message(
- ID="msg-002-reply-002",
- Content="I'll be there.",
- ContentType="text",
- From=MessageFrom(
- UserID="user-123-abc",
- DisplayName="Alice"
- ),
- CreatedDateTime="2024-01-01T12:30:00Z",
- ReplyCount=0,
- ),
+ Message(ID="msg-002-reply-001", Content="Thanks for the reminder!", ContentType="text", From=MessageFrom(UserID="user-789-ghi", DisplayName="Charlie"), CreatedDateTime="2024-01-01T12:00:00Z", ReplyCount=0),
+ Message(ID="msg-002-reply-002", Content="I'll be there.", ContentType="text", From=MessageFrom(UserID="user-123-abc", DisplayName="Alice"), CreatedDateTime="2024-01-01T12:30:00Z", ReplyCount=0),
],
}
+
+ # --- Members (Channel) ---
self.members = {
"team-123-abc": {
"19:123123@thread.tacv2": [
- Member(
- ID="user-123-abc",
- UserID="user-123-abc",
- DisplayName="Alice",
- Role="owner",
- Email="alice@example.com"
- ),
- Member(
- ID="user-456-def",
- UserID="user-456-def",
- DisplayName="Bob",
- Role="member",
- Email="bob@example.com"
- ),
+ Member(ID="user-123-abc", UserID="user-123-abc", DisplayName="Alice", Role="owner", Email="alice@example.com"),
+ Member(ID="user-456-def", UserID="user-456-def", DisplayName="Bob", Role="member", Email="bob@example.com"),
],
}
}
+
+ # --- Users ---
+ self.users = [
+ Member(ID="user-123-abc", UserID="user-123-abc", DisplayName="Alice", Role="owner", Email="alice@example.com"),
+ Member(ID="user-456-def", UserID="user-456-def", DisplayName="Bob", Role="member", Email="bob@example.com"),
+ ]
+ self.me = Member(ID="user-me-001", UserID="user-me-001", DisplayName="Current User", Role="member", Email="me@example.com")
+
+ # --- Chats & Chat Members/Messages ---
+ self.group_chats = [
+ Chat(ID="chat-123-abc", Type=ChatType.GROUP, IsHidden=False, Topic="Project Discussion"),
+ Chat(ID="chat-456-def", Type=ChatType.GROUP, IsHidden=True, Topic="Secret Plans"),
+ ]
+ self.oneonone_chats = [
+ Chat(ID="chat-789-ghi", Type=ChatType.ONEONONE, IsHidden=False, Topic=""),
+ ]
+
self.group_chat_members = {
"chat-123-abc": [
- Member(
- ID="user-123-abc",
- UserID="user-123-abc",
- DisplayName="Alice",
- Role="owner",
- Email="alice@example.com"
- ),
- Member(
- ID="user-456-def",
- UserID="user-456-def",
- DisplayName="Bob",
- Role="member",
- Email="bob@example.com"
- ),
+ Member(ID="user-123-abc", UserID="user-123-abc", DisplayName="Alice", Role="owner", Email="alice@example.com"),
+ Member(ID="user-456-def", UserID="user-456-def", DisplayName="Bob", Role="member", Email="bob@example.com"),
],
}
+
self.chat_messages = {
"chat-123-abc": [
- Message(
- ID="msg-001",
- Content="Hello, team!",
- ContentType="text",
- From=MessageFrom(
- UserID="user-123-abc",
- DisplayName="Alice"
- ),
- CreatedDateTime="2024-01-01T10:00:00Z",
- ReplyCount=0,
- ),
- Message(
- ID="msg-002",
- Content="Don't forget the meeting at 3 PM.",
- ContentType="text",
- From=MessageFrom(
- UserID="user-456-def",
- DisplayName="Bob"
- ),
- CreatedDateTime="2024-01-01T11:00:00Z",
- ReplyCount=2,
- ),
+ Message(ID="msg-001", Content="Hello, team!", ContentType="text", From=MessageFrom(UserID="user-123-abc", DisplayName="Alice"), CreatedDateTime="2024-01-01T10:00:00Z", ReplyCount=0),
+ Message(ID="msg-002", Content="Don't forget the meeting at 3 PM.", ContentType="text", From=MessageFrom(UserID="user-456-def", DisplayName="Bob"), CreatedDateTime="2024-01-01T11:00:00Z", ReplyCount=2),
],
}
- self.users = [
- Member(
- ID="user-123-abc",
- UserID="user-123-abc",
- DisplayName="Alice",
- Role="owner",
- Email="alice@example.com"
- ),
- Member(
- ID="user-456-def",
- UserID="user-456-def",
- DisplayName="Bob",
- Role="member",
- Email="bob@example.com"
- ),
- ]
- self.me = Member(
- ID="user-me-001",
- UserID="user-me-001",
- DisplayName="Current User",
- Role="member",
- Email="me@example.com"
- )
- self.group_chats = [
- Chat(
- ID="chat-123-abc",
- Type=ChatType.GROUP,
- IsHidden=False,
- Topic="Project Discussion",
- ),
- Chat(
- ID="chat-456-def",
- Type=ChatType.GROUP,
- IsHidden=True,
- Topic="Secret Plans",
- ),
- ]
- self.oneonone_chats = [
- Chat(
- ID="chat-789-ghi",
- Type=ChatType.ONEONONE,
- IsHidden=False,
- Topic="",
- ),
- ]
+
+ # --- State Variables ---
self.newGroupID = "group-789-ghi"
self.newTeamName = "New Team"
self.newGroupMailNickname = "new-team-nickname"
self.newTeamVisibility = "private"
- self.potentialTeams = []
+
+ # Init Templates (re-assigning to ensure freshness if needed, though default_factory handles it)
self.newChannelName = "New Channel"
self.newChannelID = "19:newchannelid@thread.tacv2"
+ self.updatedGroupChatTopic = "Updated Chat Topic"
+
+ # Initialize templates manually to match original structure exactly if factories behave differently
self.newMessageTemplate = Message(
- ID="new-message-id",
- Content="This is a new message.",
- ContentType="text",
- From=MessageFrom(
- UserID="user-new-001",
- DisplayName="New User"
- ),
- CreatedDateTime="2024-01-02T10:00:00Z",
- ReplyCount=0,
+ ID="new-message-id", Content="This is a new message.", ContentType="text",
+ From=MessageFrom(UserID="user-new-001", DisplayName="New User"),
+ CreatedDateTime="2024-01-02T10:00:00Z", ReplyCount=0
)
self.newMemberTemplate = Member(
- ID="user-new-002",
- UserID="new-user-002",
- DisplayName="New Member",
- Role="owner",
- Email="newmember@example.com"
+ ID="user-new-002", UserID="new-user-002", DisplayName="New Member",
+ Role="owner", Email="newmember@example.com"
)
self.newChatTemplate = Chat(
- ID="new-chat-001",
- Type=ChatType.GROUP,
- IsHidden=False,
- Topic="New Chat Topic"
+ ID="new-chat-001", Type=ChatType.GROUP, IsHidden=False, Topic="New Chat Topic"
)
- self.updatedGroupChatTopic = "Updated Chat Topic"
+ # --- Helpers ---
+ def _find_team(self, team_id: str) -> Optional[Team]:
+ return next((t for t in self.teams if t.ID == team_id), None)
+ def _find_channel(self, team_id: str, channel_id: str) -> Optional[Channel]:
+ return next((c for c in self.channels.get(team_id, []) if c.ID == channel_id), None)
+ # ==========================================
+ # TEAMS & GROUPS
+ # ==========================================
def get_myJoinedTeams_response(self) -> dict:
return {
@@ -266,33 +179,20 @@ def get_myJoinedTeams_response(self) -> dict:
],
}
- def get_listChannels_response(self, team_id: str) -> dict:
- return {
- "@odata.context": f"https://graph.microsoft.com/v1.0/$metadata#teams('{team_id}')/channels",
- "value": [
- {
- "@odata.type": "#microsoft.graph.channel",
- "id": channel.ID,
- "displayName": channel.Name,
- "isGeneral": channel.IsGeneral,
- "membershipType": "standard",
- "email": ""
- }
- for channel in self.channels.get(team_id, [])
- ],
- }
-
- def get_team_response(self, team_id: str) -> dict | None:
+ def get_team_response(self, team_id: str) -> Optional[dict]:
+ team = self._find_team(team_id)
+ if not team:
+ return None
return {
"id": team.ID,
"displayName": team.DisplayName,
"description": team.Description,
"isArchived": team.IsArchived,
"visibility": team.Visibility,
- } if (team := next((t for t in self.teams if t.ID == team_id), None)) else None
+ }
- def get_updateTeam_response(self, team_id: str, update_json: dict) -> dict | None:
- team = next((t for t in self.teams if t.ID == team_id), None)
+ def get_updateTeam_response(self, team_id: str, update_json: dict) -> Optional[dict]:
+ team = self._find_team(team_id)
if not team:
return None
@@ -312,7 +212,7 @@ def get_updateTeam_response(self, team_id: str, update_json: dict) -> dict | Non
}
def get_createGroup_response(self, request_json: dict) -> dict:
- visibility = request_json.get("visibility").lower()
+ visibility = request_json.get("visibility", "").lower()
if visibility not in ("private", "public"):
visibility = "private"
@@ -364,31 +264,26 @@ def get_createTeamFromTemplate_response(self, request_json: dict) -> dict:
)
self.teams.append(new_team)
- return {
- "id": new_team_id,
- }
+ return {"id": new_team_id}
def get_archiveTeam_response(self, team_id: str) -> dict:
- team = next((t for t in self.teams if t.ID == team_id), None)
+ team = self._find_team(team_id)
if not team:
return {"success": False}
-
team.IsArchived = True
return {"success": True}
def get_unarchiveTeam_response(self, team_id: str) -> dict:
- team = next((t for t in self.teams if t.ID == team_id), None)
+ team = self._find_team(team_id)
if not team:
return {"success": False}
-
team.IsArchived = False
return {"success": True}
def get_deleteTeam_response(self, team_id: str) -> dict:
- team = next((t for t in self.teams if t.ID == team_id), None)
+ team = self._find_team(team_id)
if not team:
return {"success": False}
-
self.teams.remove(team)
return {"success": True}
@@ -407,11 +302,30 @@ def get_restoreTeam_response(self, team_id: str) -> dict:
"mailNickname": self.newGroupMailNickname
}
- def get_channel_response(self, team_id: str, channel_id: str) -> dict | None:
- channel = next((c for c in self.channels.get(team_id, []) if c.ID == channel_id), None)
+ # ==========================================
+ # CHANNELS
+ # ==========================================
+
+ def get_listChannels_response(self, team_id: str) -> dict:
+ return {
+ ODATA_CONTEXT: f"{GRAPH_URL}#teams('{team_id}')/channels",
+ "value": [
+ {
+ ODATA_TYPE: "#microsoft.graph.channel",
+ "id": channel.ID,
+ "displayName": channel.Name,
+ "isGeneral": channel.IsGeneral,
+ "membershipType": "standard",
+ "email": ""
+ }
+ for channel in self.channels.get(team_id, [])
+ ],
+ }
+
+ def get_channel_response(self, team_id: str, channel_id: str) -> Optional[dict]:
+ channel = self._find_channel(team_id, channel_id)
if not channel:
return None
-
return {
"id": channel.ID,
"displayName": channel.Name,
@@ -430,7 +344,6 @@ def get_create_channel_response(self, team_id: str, request_json: dict) -> dict:
if team_id not in self.channels:
self.channels[team_id] = []
-
self.channels[team_id].append(new_channel)
return {
@@ -442,28 +355,15 @@ def get_create_channel_response(self, team_id: str, request_json: dict) -> dict:
}
def get_delete_channel_response(self, team_id: str, channel_id: str) -> dict:
- channel = next((c for c in self.channels.get(team_id, []) if c.ID == channel_id), None)
+ channel = self._find_channel(team_id, channel_id)
if not channel:
return {"success": False}
-
self.channels[team_id].remove(channel)
return {"success": True}
- def get_send_message_response(self, team_id: str, channel_id: str, request_json: dict) -> dict:
- return {
- "id": self.newMessageTemplate.ID,
- "body": {
- "content": request_json.get("body", {}).get("content"),
- "contentType": request_json.get("body", {}).get("contentType"),
- },
- "from": {
- "user": {
- "id": self.newMessageTemplate.From.UserID,
- "displayName": self.newMessageTemplate.From.DisplayName,
- }
- },
- "createdDateTime": self.newMessageTemplate.CreatedDateTime,
- }
+ # ==========================================
+ # MESSAGES (CHANNEL)
+ # ==========================================
def get_list_messages_response(self, team_id: str, channel_id: str) -> dict:
return {
@@ -487,7 +387,7 @@ def get_list_messages_response(self, team_id: str, channel_id: str) -> dict:
],
}
- def get_message_response(self, team_id: str, channel_id: str, message_id: str) -> dict | None:
+ def get_message_response(self, team_id: str, channel_id: str, message_id: str) -> Optional[dict]:
message = next((m for m in self.messages.get(channel_id, []) if m.ID == message_id), None)
if not message:
return None
@@ -508,6 +408,22 @@ def get_message_response(self, team_id: str, channel_id: str, message_id: str) -
"replies": [{"id": f"dummy-reply-{i}"} for i in range(message.ReplyCount)]
}
+ def get_send_message_response(self, team_id: str, channel_id: str, request_json: dict) -> dict:
+ return {
+ "id": self.newMessageTemplate.ID,
+ "body": {
+ "content": request_json.get("body", {}).get("content"),
+ "contentType": request_json.get("body", {}).get("contentType"),
+ },
+ "from": {
+ "user": {
+ "id": self.newMessageTemplate.From.UserID,
+ "displayName": self.newMessageTemplate.From.DisplayName,
+ }
+ },
+ "createdDateTime": self.newMessageTemplate.CreatedDateTime,
+ }
+
def get_list_replies_response(self, team_id: str, channel_id: str, message_id: str) -> dict:
return {
"value": [
@@ -529,11 +445,10 @@ def get_list_replies_response(self, team_id: str, channel_id: str, message_id: s
],
}
- def get_reply_response(self, team_id: str, channel_id: str, message_id: str, reply_id: str) -> dict | None:
+ def get_reply_response(self, team_id: str, channel_id: str, message_id: str, reply_id: str) -> Optional[dict]:
reply = next((r for r in self.replies.get(message_id, []) if r.ID == reply_id), None)
if not reply:
return None
-
return {
"id": reply.ID,
"body": {
@@ -549,15 +464,31 @@ def get_reply_response(self, team_id: str, channel_id: str, message_id: str, rep
"createdDateTime": reply.CreatedDateTime,
}
- def get_add_member_response(self, team_id: str, request_json: dict) -> dict:
- roles = []
- if self.newMemberTemplate.Role == "owner":
- roles = ["owner"]
+ # ==========================================
+ # MEMBERS (CHANNEL)
+ # ==========================================
+ def get_list_members_response(self, team_id: str, channel_id: str) -> dict:
return {
- "@odata.context": f"https://graph.microsoft.com/v1.0/$metadata#teams('{team_id}')/members/$entity",
- "@odata.type": "#microsoft.graph.aadUserConversationMember",
+ ODATA_CONTEXT: f"{GRAPH_URL}#teams('{team_id}')/channels('{channel_id}')/members",
+ "value": [
+ {
+ ODATA_TYPE: "#microsoft.graph.aadUserConversationMember",
+ "id": member.ID,
+ "userId": member.UserID,
+ "displayName": member.DisplayName,
+ "roles": [member.Role] if member.Role == "owner" else [],
+ "email": member.Email,
+ }
+ for member in self.members.get(team_id, {}).get(channel_id, [])
+ ],
+ }
+ def get_add_member_response(self, team_id: str, request_json: dict) -> dict:
+ roles = ["owner"] if self.newMemberTemplate.Role == "owner" else []
+ return {
+ ODATA_CONTEXT: f"{GRAPH_URL}#teams('{team_id}')/members/$entity",
+ ODATA_TYPE: "#microsoft.graph.aadUserConversationMember",
"id": self.newMemberTemplate.ID,
"roles": roles,
"displayName": self.newMemberTemplate.DisplayName,
@@ -565,9 +496,8 @@ def get_add_member_response(self, team_id: str, request_json: dict) -> dict:
"email": self.newMemberTemplate.Email,
}
- def get_update_member_role_response(self, team_id: str, channel_id: str, member_id: str, request_json: dict) -> dict | None:
+ def get_update_member_role_response(self, team_id: str, channel_id: str, member_id: str, request_json: dict) -> Optional[dict]:
channel_members = self.members.get(team_id, {}).get(channel_id, [])
-
member = next((m for m in channel_members if m.ID == member_id), None)
if not member:
return None
@@ -576,8 +506,8 @@ def get_update_member_role_response(self, team_id: str, channel_id: str, member_
member.Role = "owner" if "owner" in request_json["roles"] else "member"
return {
- "@odata.context": f"https://graph.microsoft.com/v1.0/$metadata#teams('{team_id}')/channels('{channel_id}')/members/$entity",
- "@odata.type": "#microsoft.graph.aadUserConversationMember",
+ ODATA_CONTEXT: f"{GRAPH_URL}#teams('{team_id}')/channels('{channel_id}')/members/$entity",
+ ODATA_TYPE: "#microsoft.graph.aadUserConversationMember",
"id": member.ID,
"userId": member.UserID,
"displayName": member.DisplayName,
@@ -585,41 +515,23 @@ def get_update_member_role_response(self, team_id: str, channel_id: str, member_
"email": member.Email,
}
- def get_list_members_response(self, team_id: str, channel_id: str) -> dict:
- return {
- "@odata.context": f"https://graph.microsoft.com/v1.0/$metadata#teams('{team_id}')/channels('{channel_id}')/members",
- "value": [
- {
- "@odata.type": "#microsoft.graph.aadUserConversationMember",
- "id": member.ID,
- "userId": member.UserID,
- "displayName": member.DisplayName,
- "roles": [member.Role] if member.Role == "owner" else [],
- "email": member.Email,
- }
- for member in self.members.get(team_id, {}).get(channel_id, [])
- ],
- }
-
def get_remove_member_response(self, team_id: str, channel_id: str, member_id: str) -> dict:
channel_members = self.members.get(team_id, {}).get(channel_id, [])
-
member = next((m for m in channel_members if m.ID == member_id), None)
if not member:
return {"success": False}
-
channel_members.remove(member)
return {"success": True}
- def get_list_chats_response(self, chat_type: ChatType) -> dict:
- if chat_type == ChatType.ONEONONE:
- chats = self.oneonone_chats
- else:
- chats = self.group_chats
+ # ==========================================
+ # CHATS
+ # ==========================================
+ def get_list_chats_response(self, chat_type: ChatType) -> dict:
+ chats = self.oneonone_chats if chat_type == ChatType.ONEONONE else self.group_chats
return {
- "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#chats",
- "@odata.count": len(chats),
+ ODATA_CONTEXT: f"{GRAPH_URL}#chats",
+ ODATA_COUNT: len(chats),
"value": [
{
"id": chat.ID,
@@ -631,54 +543,41 @@ def get_list_chats_response(self, chat_type: ChatType) -> dict:
],
}
- def get_me_response(self) -> dict:
- return {
- "id": self.me.ID,
- "displayName": self.me.DisplayName,
- "email": self.me.Email,
- }
-
def get_create_chat_response(self, request_json: dict) -> dict:
- if request_json["chatType"] == "oneOnOne":
- return {
- "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#chats/$entity",
- "id": self.newChatTemplate.ID,
- "chatType": "oneOnOne",
- "isHiddenForAllMembers": self.newChatTemplate.IsHidden,
- "topic": None,
- }
- else:
- return {
- "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#chats/$entity",
- "id": self.newChatTemplate.ID,
- "chatType": "group",
- "isHiddenForAllMembers": self.newChatTemplate.IsHidden,
- "topic": self.newChatTemplate.Topic,
- }
+ is_one_on_one = request_json["chatType"] == "oneOnOne"
+ response = {
+ ODATA_CONTEXT: f"{GRAPH_URL}#chats/$entity",
+ "id": self.newChatTemplate.ID,
+ "chatType": "oneOnOne" if is_one_on_one else "group",
+ "isHiddenForAllMembers": self.newChatTemplate.IsHidden,
+ "topic": None if is_one_on_one else self.newChatTemplate.Topic,
+ }
+ return response
+ def get_update_group_chat_topic_response(self, chat_id: str, request_json: dict) -> dict:
+ chat = next((c for c in self.group_chats if c.ID == chat_id), None)
+ if not chat:
+ return {}
- def get_add_member_to_group_chat_response(self, chat_id: str, request_json: dict) -> dict:
+ chat.Topic = request_json.get("topic", chat.Topic)
return {
- "@odata.context": f"https://graph.microsoft.com/v1.0/$metadata#chats('{chat_id}')/members/$entity",
- "@odata.type": "#microsoft.graph.aadUserConversationMember",
- "id": self.newMemberTemplate.ID,
- "roles": [self.newMemberTemplate.Role] if self.newMemberTemplate.Role == "owner" else [],
- "displayName": self.newMemberTemplate.DisplayName,
- "userId": self.newMemberTemplate.UserID,
- "email": self.newMemberTemplate.Email,
+ ODATA_CONTEXT: f"{GRAPH_URL}#chats('{chat_id}')",
+ "id": chat.ID,
+ "chatType": "group",
+ "isHiddenForAllMembers": chat.IsHidden,
+ "topic": chat.Topic,
}
- def get_remove_member_from_group_chat_response(self, chat_id: str, member_id: str) -> dict:
- return {
- "success": True
- }
+ # ==========================================
+ # CHAT MEMBERS
+ # ==========================================
def get_list_group_chat_members_response(self, chat_id: str) -> dict:
return {
- "@odata.context": f"https://graph.microsoft.com/v1.0/$metadata#chats('{chat_id}')/members",
+ ODATA_CONTEXT: f"{GRAPH_URL}#chats('{chat_id}')/members",
"value": [
{
- "@odata.type": "#microsoft.graph.aadUserConversationMember",
+ ODATA_TYPE: "#microsoft.graph.aadUserConversationMember",
"id": member.ID,
"userId": member.UserID,
"displayName": member.DisplayName,
@@ -689,21 +588,24 @@ def get_list_group_chat_members_response(self, chat_id: str) -> dict:
],
}
- def get_update_group_chat_topic_response(self, chat_id: str, request_json: dict) -> dict:
- chat = next((c for c in self.group_chats if c.ID == chat_id), None)
- if not chat:
- return {}
-
- chat.Topic = request_json.get("topic", chat.Topic)
-
+ def get_add_member_to_group_chat_response(self, chat_id: str, request_json: dict) -> dict:
return {
- "@odata.context": f"https://graph.microsoft.com/v1.0/$metadata#chats('{chat_id}')",
- "id": chat.ID,
- "chatType": "group",
- "isHiddenForAllMembers": chat.IsHidden,
- "topic": chat.Topic,
+ ODATA_CONTEXT: f"{GRAPH_URL}#chats('{chat_id}')/members/$entity",
+ ODATA_TYPE: "#microsoft.graph.aadUserConversationMember",
+ "id": self.newMemberTemplate.ID,
+ "roles": [self.newMemberTemplate.Role] if self.newMemberTemplate.Role == "owner" else [],
+ "displayName": self.newMemberTemplate.DisplayName,
+ "userId": self.newMemberTemplate.UserID,
+ "email": self.newMemberTemplate.Email,
}
+ def get_remove_member_from_group_chat_response(self, chat_id: str, member_id: str) -> dict:
+ return {"success": True}
+
+ # ==========================================
+ # CHAT MESSAGES
+ # ==========================================
+
def get_list_messages_in_chat_response(self, chat_id: str) -> dict:
return {
"value": [
@@ -726,23 +628,7 @@ def get_list_messages_in_chat_response(self, chat_id: str) -> dict:
],
}
- def get_send_message_in_chat_response(self, chat_id: str, request_json: dict) -> dict:
- return {
- "id": self.newMessageTemplate.ID,
- "body": {
- "content": request_json.get("body", {}).get("content"),
- "contentType": request_json.get("body", {}).get("contentType"),
- },
- "from": {
- "user": {
- "id": self.newMessageTemplate.From.UserID,
- "displayName": self.newMessageTemplate.From.DisplayName,
- }
- },
- "createdDateTime": self.newMessageTemplate.CreatedDateTime,
- }
-
- def get_get_message_in_chat_response(self, chat_id: str, message_id: str) -> dict | None:
+ def get_get_message_in_chat_response(self, chat_id: str, message_id: str) -> Optional[dict]:
message = next((m for m in self.chat_messages.get(chat_id, []) if m.ID == message_id), None)
if not message:
return None
@@ -763,17 +649,34 @@ def get_get_message_in_chat_response(self, chat_id: str, message_id: str) -> dic
"replies": [{"id": f"dummy-reply-{i}"} for i in range(message.ReplyCount)]
}
+ def get_send_message_in_chat_response(self, chat_id: str, request_json: dict) -> dict:
+ return {
+ "id": self.newMessageTemplate.ID,
+ "body": {
+ "content": request_json.get("body", {}).get("content"),
+ "contentType": request_json.get("body", {}).get("contentType"),
+ },
+ "from": {
+ "user": {
+ "id": self.newMessageTemplate.From.UserID,
+ "displayName": self.newMessageTemplate.From.DisplayName,
+ }
+ },
+ "createdDateTime": self.newMessageTemplate.CreatedDateTime,
+ }
+
def get_all_messeges_in_chats_response(self) -> dict:
+ target_chat_id = self.group_chats[0].ID
return {
- "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#Collection(chatMessage)",
- "@odata.count": len(self.chat_messages),
+ ODATA_CONTEXT: f"{GRAPH_URL}#Collection(chatMessage)",
+ ODATA_COUNT: len(self.chat_messages),
"value": [
{
- "@odata.type": "#microsoft.graph.chatMessage",
+ ODATA_TYPE: "#microsoft.graph.chatMessage",
"id": message.ID,
"etag": message.ID,
"messageType": "message",
- "chatId": self.group_chats[0].ID,
+ "chatId": target_chat_id,
"body": {
"content": message.Content,
"contentType": message.ContentType,
@@ -787,7 +690,7 @@ def get_all_messeges_in_chats_response(self) -> dict:
"createdDateTime": message.CreatedDateTime,
"lastModifiedDateTime": message.CreatedDateTime,
}
- for message in self.chat_messages[self.group_chats[0].ID]
+ for message in self.chat_messages[target_chat_id]
],
}
@@ -795,10 +698,20 @@ def get_list_pinned_messages_in_chat_response(self, chat_id: str) -> dict:
return {
"value": [
{
- "@odata.type": "#microsoft.graph.pinnedChatMessageInfo",
+ ODATA_TYPE: "#microsoft.graph.pinnedChatMessageInfo",
"id": message.ID,
}
for message in self.chat_messages.get(chat_id, [])
],
}
+ # ==========================================
+ # USERS
+ # ==========================================
+
+ def get_me_response(self) -> dict:
+ return {
+ "id": self.me.ID,
+ "displayName": self.me.DisplayName,
+ "email": self.me.Email,
+ }
\ No newline at end of file
diff --git a/python/tests/fake_server/setup.py b/python/tests/fake_server/setup.py
index 979bbcf..9edbe3d 100644
--- a/python/tests/fake_server/setup.py
+++ b/python/tests/fake_server/setup.py
@@ -39,6 +39,10 @@ def make_log_response(request, data_dict: dict, context_msg: str = "") -> Respon
status=status_code
)
+ # ==========================================
+ # USERS
+ # ==========================================
+
# GET /users/me/joinedTeams
httpserver.expect_request(
"/v1.0/users/me-token-to-replace/joinedTeams", method="GET"
@@ -46,7 +50,7 @@ def make_log_response(request, data_dict: dict, context_msg: str = "") -> Respon
req, data.get_myJoinedTeams_response(), "List Joined Teams"
))
- #GET /users/me/chats/getAllMessages()
+ # GET /users/me/chats/getAllMessages()
httpserver.expect_request(
"/v1.0/users/me-token-to-replace/chats/getAllMessages()",
method="GET"
@@ -56,34 +60,26 @@ def make_log_response(request, data_dict: dict, context_msg: str = "") -> Respon
"List All Chat Messages"
))
- #GET /chats/{chat_id}/pinnedMessages
+ # GET users/me/chats/{chat_id}/messages
httpserver.expect_request(
- re.compile(r"^/v1.0/chats/([^/]+)/pinnedMessages"),
+ re.compile(r"^/v1.0/users/me-token-to-replace/chats/([^/]+)/messages"),
method="GET"
).respond_with_handler(lambda req: make_log_response(
req,
- data.get_list_pinned_messages_in_chat_response(
- re.search(r"/chats/([^/]+)/pinnedMessages", req.path).group(1)
+ data.get_list_messages_in_chat_response(
+ re.search(r"/users/me-token-to-replace/chats/([^/]+)/messages", req.path).group(1)
),
- "List Pinned Chat Messages"
+ "List Chat Messages (me)"
))
- # POST /chats/{chat_id}/pinnedMessages
+ # DELETE users/me/chats/{chat_id}/messages/{message_id}/softDelete
httpserver.expect_request(
- re.compile(r"^/v1.0/chats/([^/]+)/pinnedMessages"),
+ re.compile(r"/v1.0/users/[^/]+/chats/[^/]+/messages/[^/]+/softDelete"),
method="POST"
).respond_with_response(
Response(status=204)
)
- # DELETE /chats/{chat_id}/pinnedMessages/{message_id}
- httpserver.expect_request(
- re.compile(r"^/v1.0/chats/([^/]+)/pinnedMessages/([^/]+)"),
- method="DELETE"
- ).respond_with_response(
- Response(status=204)
- )
-
def handle_chats_request(req):
filter_param = req.args.get('$filter', '')
chat_type = None
@@ -103,46 +99,6 @@ def handle_chats_request(req):
f"List Chats ({chat_type})"
)
-
-
-
- #GET /chats/{chat_id}/messages/{message_id}
- httpserver.expect_request(
- re.compile(r"^/v1.0/chats/([^/]+)/messages/([^/]+)"),
- method="GET"
- ).respond_with_handler(lambda req: make_log_response(
- req,
- data.get_get_message_in_chat_response(
- re.search(r"/chats/([^/]+)/messages/([^/]+)", req.path).group(1),
- re.search(r"/chats/([^/]+)/messages/([^/]+)", req.path).group(2)
- ),
- "Get Chat Message"
- ))
-
- #GET /chats/{chat_id}/messages
- httpserver.expect_request(
- re.compile(r"^/v1.0/chats/([^/]+)/messages"),
- method="GET"
- ).respond_with_handler(lambda req: make_log_response(
- req,
- data.get_list_messages_in_chat_response(
- re.search(r"/chats/([^/]+)/messages", req.path).group(1)
- ),
- "List Chat Messages"
- ))
-
- #GET users/me/chats/{chat_id}/messages
- httpserver.expect_request(
- re.compile(r"^/v1.0/users/me-token-to-replace/chats/([^/]+)/messages"),
- method="GET"
- ).respond_with_handler(lambda req: make_log_response(
- req,
- data.get_list_messages_in_chat_response(
- re.search(r"/users/me-token-to-replace/chats/([^/]+)/messages", req.path).group(1)
- ),
- "List Chat Messages (me)"
- ))
-
# GET /users/me/chats
httpserver.expect_request(
"/v1.0/users/me-token-to-replace/chats",
@@ -159,90 +115,11 @@ def handle_chats_request(req):
"Get Users"
))
- #POST /chats
- httpserver.expect_request(
- "/v1.0/chats",
- method="POST"
- ).respond_with_handler(lambda req: make_log_response(
- req,
- data.get_create_chat_response(req.json),
- "Create Chat"
- ))
-
- #GET /chats/{chat_id}/members
- httpserver.expect_request(
- re.compile(r"^/v1.0/chats/([^/]+)/members"),
- method="GET"
- ).respond_with_handler(lambda req: make_log_response(
- req,
- data.get_list_group_chat_members_response(
- re.search(r"/chats/([^/]+)/members", req.path).group(1)
- ),
- "List Chat Members"
- ))
-
- #POST /chats/{chat_id}/members
- httpserver.expect_request(
- re.compile(r"^/v1.0/chats/([^/]+)/members"),
- method="POST"
- ).respond_with_handler(lambda req: make_log_response(
- req,
- data.get_add_member_to_group_chat_response(
- re.search(r"/chats/([^/]+)/members", req.path).group(1),
- req.json
- ),
- "Add Chat Member"
- ))
+ # ==========================================
+ # TEAMS
+ # ==========================================
- #DELETE /chats/{chat_id}/members/{member_id}
- httpserver.expect_request(
- re.compile(r"^/v1.0/chats/([^/]+)/members/([^/]+)"),
- method="DELETE"
- ).respond_with_handler(lambda req: make_log_response(
- req,
- data.get_remove_member_from_group_chat_response(
- re.search(r"/chats/([^/]+)/members/([^/]+)", req.path).group(1),
- re.search(r"/chats/([^/]+)/members/([^/]+)", req.path).group(2)
- ),
- "Remove Chat Member"
- ))
-
- #PATCH /chats/{chat_id}
- httpserver.expect_request(
- re.compile(r"^/v1.0/chats/([^/]+)"),
- method="PATCH"
- ).respond_with_handler(lambda req: make_log_response(
- req,
- data.get_update_group_chat_topic_response(
- re.search(r"/chats/([^/]+)", req.path).group(1),
- req.json
- ),
- "Update Chat"
- ))
-
- #POST /chats/{chat_id}/messages
- httpserver.expect_request(
- re.compile(r"^/v1.0/chats/([^/]+)/messages"),
- method="POST"
- ).respond_with_handler(lambda req: make_log_response(
- req,
- data.get_send_message_in_chat_response(
- re.search(r"/chats/([^/]+)/messages", req.path).group(1),
- req.json
- ),
- "Send Chat Message"
- ))
-
- #DELETE users/me/chats/{chat_id}/messages/{message_id}/softDelete
- httpserver.expect_request(
- re.compile(r"/v1.0/users/[^/]+/chats/[^/]+/messages/[^/]+/softDelete"),
- method="POST"
- ).respond_with_response(
- Response(status=204)
- )
-
-
- #POST /teams/{team_id}/channels/{channel_id}/members
+ # POST /teams/{id}/channels/{channel_id}/members
httpserver.expect_request(
re.compile(r"^/v1.0/teams/([^/]+)/channels/([^/]+)/members"),
method="POST"
@@ -255,7 +132,6 @@ def handle_chats_request(req):
"Add member to channel"
))
-
# PATCH /teams/{tid}/channels/{cid}/members/{mid}
httpserver.expect_request(
re.compile(r"^/v1.0/teams/([^/]+)/channels/([^/]+)/members/([^/]+)"),
@@ -285,7 +161,7 @@ def handle_chats_request(req):
"Remove Member from Channel"
))
- #GET /teams/{team_id}/channels/{channel_id}/members
+ # GET /teams/{team_id}/channels/{channel_id}/members
httpserver.expect_request(
re.compile(r"^/v1.0/teams/([^/]+)/channels/([^/]+)/members"),
method="GET"
@@ -298,22 +174,6 @@ def handle_chats_request(req):
"List channel members"
))
-
- #POST /teams/{team_id}/channels/{channel_id}/messages
- httpserver.expect_request(
- re.compile(r"^/v1.0/teams/([^/]+)/channels/([^/]+)/messages"),
- method="POST"
- ).respond_with_handler(lambda req: make_log_response(
- req,
- data.get_send_message_response(
- re.search(r"/teams/([^/]+)/channels/([^/]+)/messages", req.path).group(1),
- re.search(r"/teams/([^/]+)/channels/([^/]+)/messages", req.path).group(2),
- req.json
- ),
- "Create Channel Message"
- ))
-
-
# GET /teams/{team_id}/channels/{channel_id}/messages/{message_id}/replies/{reply_id}
httpserver.expect_request(
re.compile(r"^/v1.0/teams/([^/]+)/channels/([^/]+)/messages/([^/]+)/replies/([^/]+)"),
@@ -357,6 +217,20 @@ def handle_chats_request(req):
"Get Channel Message"
))
+ # POST /teams/{team_id}/channels/{channel_id}/messages
+ httpserver.expect_request(
+ re.compile(r"^/v1.0/teams/([^/]+)/channels/([^/]+)/messages"),
+ method="POST"
+ ).respond_with_handler(lambda req: make_log_response(
+ req,
+ data.get_send_message_response(
+ re.search(r"/teams/([^/]+)/channels/([^/]+)/messages", req.path).group(1),
+ re.search(r"/teams/([^/]+)/channels/([^/]+)/messages", req.path).group(2),
+ req.json
+ ),
+ "Create Channel Message"
+ ))
+
# GET /teams/{team_id}/channels/{channel_id}/messages
httpserver.expect_request(
re.compile(r"^/v1.0/teams/([^/]+)/channels/([^/]+)/messages"),
@@ -370,6 +244,19 @@ def handle_chats_request(req):
"List Channel Messages"
))
+ # DELETE /teams/{team_id}/channels/{channel_id}
+ httpserver.expect_request(
+ re.compile(r"^/v1.0/teams/([^/]+)/channels/([^/]+)"),
+ method="DELETE"
+ ).respond_with_handler(lambda req: make_log_response(
+ req,
+ data.get_delete_channel_response(
+ re.search(r"/teams/([^/]+)/channels/([^/]+)", req.path).group(1),
+ re.search(r"/teams/([^/]+)/channels/([^/]+)", req.path).group(2)
+ ),
+ "Delete Channel (DELETE)"
+ ))
+
# GET /teams/{team_id}/channels/{channel_id}
httpserver.expect_request(
re.compile(r"^/v1.0/teams/([^/]+)/channels/([^/]+)"),
@@ -383,44 +270,47 @@ def handle_chats_request(req):
"Get Channel Details"
))
- # GET /teams/{id}/channels
+ # POST /teams/{id}/channels
httpserver.expect_request(
re.compile(r"^/v1.0/teams/([^/]+)/channels"),
- method="GET"
+ method="POST"
).respond_with_handler(lambda req: make_log_response(
req,
- data.get_listChannels_response(re.search(r"/teams/([^/]+)/channels", req.path).group(1)),
- "List Channels"
+ data.get_create_channel_response(
+ re.search(r"/teams/([^/]+)/channels", req.path).group(1),
+ req.json
+ ),
+ "Create Channel (POST)"
))
- # POST /groups
+ # GET /teams/{id}/channels
httpserver.expect_request(
- "/v1.0/groups",
- method="POST"
+ re.compile(r"^/v1.0/teams/([^/]+)/channels"),
+ method="GET"
).respond_with_handler(lambda req: make_log_response(
req,
- data.get_createGroup_response(req.json),
- "Create Group"
+ data.get_listChannels_response(re.search(r"/teams/([^/]+)/channels", req.path).group(1)),
+ "List Channels"
))
- # PUT /groups/{id}/team
+ # POST /teams/{id}/archive
httpserver.expect_request(
- re.compile(r"^/v1.0/groups/([^/]+)/team"),
- method="PUT"
+ re.compile(r"^/v1.0/teams/([^/]+)/archive"),
+ method="POST"
).respond_with_handler(lambda req: make_log_response(
req,
- data.get_createTeamViaGroup_response(re.search(r"/groups/([^/]+)/team", req.path).group(1)),
- "Teamify Group (PUT)"
+ data.get_archiveTeam_response(re.search(r"/teams/([^/]+)/archive", req.path).group(1)),
+ "Archive Team (POST)"
))
- # GET /teams/{id}
+ # POST /teams/{id}/unarchive
httpserver.expect_request(
- re.compile(r"^/v1.0/teams/([^/]+)(?:$|\?)"),
- method="GET"
+ re.compile(r"^/v1.0/teams/([^/]+)/unarchive"),
+ method="POST"
).respond_with_handler(lambda req: make_log_response(
req,
- data.get_team_response(re.search(r"/teams/([^/?]+)", req.path).group(1)),
- "Get Team Details"
+ data.get_unarchiveTeam_response(re.search(r"/teams/([^/]+)/unarchive", req.path).group(1)),
+ "Unarchive Team (POST)"
))
# PATCH /teams/{id}
@@ -436,37 +326,41 @@ def handle_chats_request(req):
"Update Team (PATCH)"
))
- # POST /teams
+ # GET /teams/{id}
httpserver.expect_request(
- "/v1.0/teams",
- method="POST"
+ re.compile(r"^/v1.0/teams/([^/]+)(?:$|\?)"),
+ method="GET"
).respond_with_handler(lambda req: make_log_response(
req,
- data.get_createTeamFromTemplate_response(req.json),
- "Create Team from Template (POST)"
+ data.get_team_response(re.search(r"/teams/([^/?]+)", req.path).group(1)),
+ "Get Team Details"
))
- # POST /teams/{id}/archive
+ # POST /teams
httpserver.expect_request(
- re.compile(r"^/v1.0/teams/([^/]+)/archive"),
+ "/v1.0/teams",
method="POST"
).respond_with_handler(lambda req: make_log_response(
req,
- data.get_archiveTeam_response(re.search(r"/teams/([^/]+)/archive", req.path).group(1)),
- "Archive Team (POST)"
+ data.get_createTeamFromTemplate_response(req.json),
+ "Create Team from Template (POST)"
))
- # POST /teams/{id}/unarchive
+ # ==========================================
+ # GROUPS
+ # ==========================================
+
+ # PUT /groups/{id}/team
httpserver.expect_request(
- re.compile(r"^/v1.0/teams/([^/]+)/unarchive"),
- method="POST"
+ re.compile(r"^/v1.0/groups/([^/]+)/team"),
+ method="PUT"
).respond_with_handler(lambda req: make_log_response(
req,
- data.get_unarchiveTeam_response(re.search(r"/teams/([^/]+)/unarchive", req.path).group(1)),
- "Unarchive Team (POST)"
+ data.get_createTeamViaGroup_response(re.search(r"/groups/([^/]+)/team", req.path).group(1)),
+ "Teamify Group (PUT)"
))
- # DELETE /teams/{id}/delete
+ # DELETE /groups/{id} (Delete Team uses group endpoint)
httpserver.expect_request(
re.compile(r"^/v1.0/groups/([^/]+)"),
method="DELETE"
@@ -476,7 +370,21 @@ def handle_chats_request(req):
"Delete Team (DELETE)"
))
- # POST /teams/{id}/restore
+ # POST /groups
+ httpserver.expect_request(
+ "/v1.0/groups",
+ method="POST"
+ ).respond_with_handler(lambda req: make_log_response(
+ req,
+ data.get_createGroup_response(req.json),
+ "Create Group"
+ ))
+
+ # ==========================================
+ # DIRECTORY / OTHER
+ # ==========================================
+
+ # POST /directory/deletedItems/{id}/restore
httpserver.expect_request(
re.compile(r"^/v1.0/directory/deletedItems/([^/]+)/restore"),
method="POST"
@@ -486,33 +394,136 @@ def handle_chats_request(req):
"Restore Deleted Team (POST)"
))
- # POST /teams/{id}/channels
+ # ==========================================
+ # CHATS
+ # ==========================================
+
+ # DELETE /chats/{chat_id}/pinnedMessages/{message_id}
httpserver.expect_request(
- re.compile(r"^/v1.0/teams/([^/]+)/channels"),
+ re.compile(r"^/v1.0/chats/([^/]+)/pinnedMessages/([^/]+)"),
+ method="DELETE"
+ ).respond_with_response(
+ Response(status=204)
+ )
+
+ # POST /chats/{chat_id}/pinnedMessages
+ httpserver.expect_request(
+ re.compile(r"^/v1.0/chats/([^/]+)/pinnedMessages"),
method="POST"
+ ).respond_with_response(
+ Response(status=204)
+ )
+
+ # GET /chats/{chat_id}/pinnedMessages
+ httpserver.expect_request(
+ re.compile(r"^/v1.0/chats/([^/]+)/pinnedMessages"),
+ method="GET"
).respond_with_handler(lambda req: make_log_response(
req,
- data.get_create_channel_response(
- re.search(r"/teams/([^/]+)/channels", req.path).group(1),
- req.json
+ data.get_list_pinned_messages_in_chat_response(
+ re.search(r"/chats/([^/]+)/pinnedMessages", req.path).group(1)
),
- "Create Channel (POST)"
+ "List Pinned Chat Messages"
))
- # DELETE /teams/{team_id}/channels/{channel_id}
+ # DELETE /chats/{chat_id}/members/{member_id}
httpserver.expect_request(
- re.compile(r"^/v1.0/teams/([^/]+)/channels/([^/]+)"),
+ re.compile(r"^/v1.0/chats/([^/]+)/members/([^/]+)"),
method="DELETE"
).respond_with_handler(lambda req: make_log_response(
req,
- data.get_delete_channel_response(
- re.search(r"/teams/([^/]+)/channels/([^/]+)", req.path).group(1),
- re.search(r"/teams/([^/]+)/channels/([^/]+)", req.path).group(2)
+ data.get_remove_member_from_group_chat_response(
+ re.search(r"/chats/([^/]+)/members/([^/]+)", req.path).group(1),
+ re.search(r"/chats/([^/]+)/members/([^/]+)", req.path).group(2)
),
- "Delete Channel (DELETE)"
+ "Remove Chat Member"
+ ))
+
+ # POST /chats/{chat_id}/members
+ httpserver.expect_request(
+ re.compile(r"^/v1.0/chats/([^/]+)/members"),
+ method="POST"
+ ).respond_with_handler(lambda req: make_log_response(
+ req,
+ data.get_add_member_to_group_chat_response(
+ re.search(r"/chats/([^/]+)/members", req.path).group(1),
+ req.json
+ ),
+ "Add Chat Member"
+ ))
+
+ # GET /chats/{chat_id}/members
+ httpserver.expect_request(
+ re.compile(r"^/v1.0/chats/([^/]+)/members"),
+ method="GET"
+ ).respond_with_handler(lambda req: make_log_response(
+ req,
+ data.get_list_group_chat_members_response(
+ re.search(r"/chats/([^/]+)/members", req.path).group(1)
+ ),
+ "List Chat Members"
+ ))
+
+ # GET /chats/{chat_id}/messages/{message_id}
+ httpserver.expect_request(
+ re.compile(r"^/v1.0/chats/([^/]+)/messages/([^/]+)"),
+ method="GET"
+ ).respond_with_handler(lambda req: make_log_response(
+ req,
+ data.get_get_message_in_chat_response(
+ re.search(r"/chats/([^/]+)/messages/([^/]+)", req.path).group(1),
+ re.search(r"/chats/([^/]+)/messages/([^/]+)", req.path).group(2)
+ ),
+ "Get Chat Message"
+ ))
+
+ # POST /chats/{chat_id}/messages
+ httpserver.expect_request(
+ re.compile(r"^/v1.0/chats/([^/]+)/messages"),
+ method="POST"
+ ).respond_with_handler(lambda req: make_log_response(
+ req,
+ data.get_send_message_in_chat_response(
+ re.search(r"/chats/([^/]+)/messages", req.path).group(1),
+ req.json
+ ),
+ "Send Chat Message"
+ ))
+
+ # GET /chats/{chat_id}/messages
+ httpserver.expect_request(
+ re.compile(r"^/v1.0/chats/([^/]+)/messages"),
+ method="GET"
+ ).respond_with_handler(lambda req: make_log_response(
+ req,
+ data.get_list_messages_in_chat_response(
+ re.search(r"/chats/([^/]+)/messages", req.path).group(1)
+ ),
+ "List Chat Messages"
))
+ # PATCH /chats/{chat_id}
+ httpserver.expect_request(
+ re.compile(r"^/v1.0/chats/([^/]+)"),
+ method="PATCH"
+ ).respond_with_handler(lambda req: make_log_response(
+ req,
+ data.get_update_group_chat_topic_response(
+ re.search(r"/chats/([^/]+)", req.path).group(1),
+ req.json
+ ),
+ "Update Chat"
+ ))
+ # POST /chats
+ httpserver.expect_request(
+ "/v1.0/chats",
+ method="POST"
+ ).respond_with_handler(lambda req: make_log_response(
+ req,
+ data.get_create_chat_response(req.json),
+ "Create Chat"
+ ))
# Fallback for unmatched routes
httpserver.expect_request(re.compile(".*")).respond_with_handler(
From 26c325cf0e8c8c7f8425a3d8dd00ce5f69606d37 Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Wed, 7 Jan 2026 21:21:40 +0100
Subject: [PATCH 54/99] feat: commented and formatted channels service
---
.../teams_lib_pzsp2_z1/services/channels.py | 300 ++++++++++++++----
python/tests/test_channels.py | 70 ++--
2 files changed, 276 insertions(+), 94 deletions(-)
diff --git a/python/teams_lib_pzsp2_z1/services/channels.py b/python/teams_lib_pzsp2_z1/services/channels.py
index b2bd3d8..0f025b4 100644
--- a/python/teams_lib_pzsp2_z1/services/channels.py
+++ b/python/teams_lib_pzsp2_z1/services/channels.py
@@ -1,3 +1,7 @@
+"""
+This module provides the ChannelsService class for managing Microsoft Teams channels.
+"""
+
from teams_lib_pzsp2_z1.model.channel import Channel
from teams_lib_pzsp2_z1.model.member import Member
from teams_lib_pzsp2_z1.model.mention import Mention
@@ -11,12 +15,35 @@
class ChannelsService(BaseService):
- def list_channels(self, teamRef: str) -> list[Channel]:
+ """Service for managing Microsoft Teams channels via the Go backend.
+
+ This class acts as a high-level Python wrapper. It delegates logical operations
+ to the underlying Go library through the `self.client.execute` bridge.
+
+ **Architecture & Caching:**
+ The decision to use caching or direct API calls is handled internally by the
+ compiled Go library, based on the configuration provided during Client initialization.
+ This Python class provides a unified interface regardless of the underlying mode.
+
+ **Concepts:**
+ * **References**: Arguments like `team_ref` or `channel_ref` accept either UUIDs
+ or Display Names. The Go backend resolves these automatically.
+ * **Identity**: Users are identified by UUID or Email.
+ """
+
+ def list_channels(self, team_ref: str) -> list[Channel]:
+ """Retrieves all channels associated with a specific team.
+
+ Args:
+ team_ref (str): The reference to the team (UUID or Display Name).
+ Returns:
+ List[Channel]: A list of channel objects found in the team.
+ """
response = self.client.execute(
cmd_type="request",
method="listChannels",
params={
- "teamRef": teamRef,
+ "teamRef": team_ref,
},
)
return [
@@ -28,13 +55,25 @@ def list_channels(self, teamRef: str) -> list[Channel]:
for channel in response
]
- def get(self, teamRef: str, channelRef: str) -> Channel:
+ def get(self, team_ref: str, channel_ref: str) -> Channel:
+ """Retrieves a specific channel by its reference within a team.
+
+ Args:
+ team_ref (str): The reference to the team (UUID or Display Name).
+ channel_ref (str): The reference to the channel (ID or Display Name).
+
+ Returns:
+ Channel: The requested channel object.
+
+ Raises:
+ Exception: If the reference is ambiguous or not found (propagated from Go).
+ """
response = self.client.execute(
cmd_type="request",
method="getChannel",
params={
- "teamRef": teamRef,
- "channelRef": channelRef,
+ "teamRef": team_ref,
+ "channelRef": channel_ref,
},
)
@@ -44,12 +83,21 @@ def get(self, teamRef: str, channelRef: str) -> Channel:
IsGeneral=(True if response["IsGeneral"] else False),
)
- def create_standard(self, teamRef: str, display_name: str) -> Channel:
+ def create_standard(self, team_ref: str, display_name: str) -> Channel:
+ """Creates a standard (public) channel within a team.
+
+ Args:
+ team_ref (str): The reference to the team.
+ display_name (str): The display name for the new channel.
+
+ Returns:
+ Channel: The newly created channel object.
+ """
response = self.client.execute(
cmd_type="request",
method="createStandardChannel",
params={
- "teamRef": teamRef,
+ "teamRef": team_ref,
"name": display_name,
},
)
@@ -62,16 +110,28 @@ def create_standard(self, teamRef: str, display_name: str) -> Channel:
def create_private(
self,
- teamRef: str,
+ team_ref: str,
display_name: str,
member_refs: list[str],
owner_refs: list[str],
) -> Channel:
+ """Creates a private channel restricted to specific members.
+
+ Args:
+ team_ref (str): The reference to the team.
+ display_name (str): The display name for the new private channel.
+ member_refs (list[str]): List of user references (IDs or emails) to add as members.
+ owner_refs (list[str]): List of user references to add as owners.
+ At least one owner is required.
+
+ Returns:
+ Channel: The newly created private channel object.
+ """
response = self.client.execute(
cmd_type="request",
method="createPrivateChannel",
params={
- "teamRef": teamRef,
+ "teamRef": team_ref,
"name": display_name,
"memberRefs": member_refs,
"ownerRefs": owner_refs,
@@ -84,24 +144,46 @@ def create_private(
IsGeneral=(True if response["IsGeneral"] else False),
)
- def delete(self, teamRef: str, channelRef: str) -> bool:
+ def delete(self, team_ref: str, channel_ref: str) -> bool:
+ """Removes a channel from a team.
+
+ Args:
+ team_ref (str): The reference to the team.
+ channel_ref (str): The reference to the channel to be deleted.
+
+ Returns:
+ bool: True if the operation was successful.
+ """
response = self.client.execute(
cmd_type="request",
method="deleteChannel",
params={
- "teamRef": teamRef,
- "channelRef": channelRef,
+ "teamRef": team_ref,
+ "channelRef": channel_ref,
},
)
return response == "deleted"
- def send_message(self, teamRef: str, channelRef: str, body: MessageBody) -> Message:
+ def send_message(
+ self, team_ref: str, channel_ref: str, body: MessageBody
+ ) -> Message:
+ """Sends a new message to a channel.
+
+ Args:
+ team_ref (str): The reference to the team.
+ channel_ref (str): The reference to the channel.
+ body (MessageBody): The message payload containing content, content type
+ (Text/HTML), and optional mentions.
+
+ Returns:
+ Message: The created message object.
+ """
response = self.client.execute(
cmd_type="request",
method="sendMessageToChannel",
params={
- "teamRef": teamRef,
- "channelRef": channelRef,
+ "teamRef": team_ref,
+ "channelRef": channel_ref,
"body": dict(body),
},
)
@@ -120,17 +202,28 @@ def send_message(self, teamRef: str, channelRef: str, body: MessageBody) -> Mess
def list_messages(
self,
- teamRef: str,
- channelRef: str,
+ team_ref: str,
+ channel_ref: str,
top: int | None = 10,
expand_replies: bool = False,
- ):
+ ) -> list[Message]:
+ """Retrieves messages from a channel.
+
+ Args:
+ team_ref (str): The reference to the team.
+ channel_ref (str): The reference to the channel.
+ top (Optional[int]): The maximum number of messages to retrieve. Defaults to 10.
+ expand_replies (bool): If True, system fetches replies for each message. Defaults to False.
+
+ Returns:
+ List[Message]: A list of messages from the channel.
+ """
response = self.client.execute(
cmd_type="request",
method="listMessagesInChannel",
params={
- "teamRef": teamRef,
- "channelRef": channelRef,
+ "teamRef": team_ref,
+ "channelRef": channel_ref,
"options": {
"top": top,
"expandReplies": expand_replies,
@@ -153,14 +246,24 @@ def list_messages(
for message in response
]
- def get_message(self, teamRef: str, channelRef: str, messageID: str) -> Message:
+ def get_message(self, team_ref: str, channel_ref: str, message_id: str) -> Message:
+ """Retrieves a specific message by its ID.
+
+ Args:
+ team_ref (str): The reference to the team.
+ channel_ref (str): The reference to the channel.
+ message_id (str): The unique identifier of the message.
+
+ Returns:
+ Message: The requested message object.
+ """
response = self.client.execute(
cmd_type="request",
method="getMessageInChannel",
params={
- "teamRef": teamRef,
- "channelRef": channelRef,
- "messageID": messageID,
+ "teamRef": team_ref,
+ "channelRef": channel_ref,
+ "messageID": message_id,
},
)
@@ -178,18 +281,29 @@ def get_message(self, teamRef: str, channelRef: str, messageID: str) -> Message:
def list_message_replies(
self,
- teamRef: str,
- channelRef: str,
- messageID: str,
+ team_ref: str,
+ channel_ref: str,
+ message_id: str,
top: int | None = 10,
- ):
+ ) -> list[Message]:
+ """Retrieves all replies to a specific message thread.
+
+ Args:
+ team_ref (str): The reference to the team.
+ channel_ref (str): The reference to the channel.
+ message_id (str): The ID of the parent message.
+ top (Optional[int]): Max number of replies to fetch. Defaults to 10.
+
+ Returns:
+ List[Message]: A list of replies.
+ """
response = self.client.execute(
cmd_type="request",
method="listMessageRepliesInChannel",
params={
- "teamRef": teamRef,
- "channelRef": channelRef,
- "messageID": messageID,
+ "teamRef": team_ref,
+ "channelRef": channel_ref,
+ "messageID": message_id,
"top": top,
},
)
@@ -211,19 +325,30 @@ def list_message_replies(
def get_message_reply(
self,
- teamRef: str,
- channelRef: str,
- messageID: str,
- replyID: str,
+ team_ref: str,
+ channel_ref: str,
+ message_id: str,
+ reply_id: str,
) -> Message:
+ """Retrieves a specific reply from a thread.
+
+ Args:
+ team_ref (str): The reference to the team.
+ channel_ref (str): The reference to the channel.
+ message_id (str): The ID of the parent message.
+ reply_id (str): The ID of the reply to retrieve.
+
+ Returns:
+ Message: The requested reply object.
+ """
response = self.client.execute(
cmd_type="request",
method="getMessageReplyInChannel",
params={
- "teamRef": teamRef,
- "channelRef": channelRef,
- "messageID": messageID,
- "replyID": replyID,
+ "teamRef": team_ref,
+ "channelRef": channel_ref,
+ "messageID": message_id,
+ "replyID": reply_id,
},
)
@@ -239,68 +364,125 @@ def get_message_reply(
ReplyCount=response["ReplyCount"],
)
- def list_members(self, teamRef: str, channelRef: str) -> list[Member]:
+ def list_members(self, team_ref: str, channel_ref: str) -> list[Member]:
+ """Lists all members of a channel.
+
+ Args:
+ team_ref (str): The reference to the team.
+ channel_ref (str): The reference to the channel.
+
+ Returns:
+ List[Member]: A list of channel members.
+ """
response = self.client.execute(
cmd_type="request",
method="listChannelMembers",
params={
- "teamRef": teamRef,
- "channelRef": channelRef,
+ "teamRef": team_ref,
+ "channelRef": channel_ref,
},
)
return [Member(**member) for member in response]
def add_member(
- self, teamRef: str, channelRef: str, userRef: str, isOwner: bool
+ self, team_ref: str, channel_ref: str, user_ref: str, is_owner: bool
) -> Member:
+ """Adds a user to a channel.
+
+ Args:
+ team_ref (str): The reference to the team.
+ channel_ref (str): The reference to the channel.
+ user_ref (str): The user to add (User ID or Email).
+ is_owner (bool): Whether to grant Owner privileges.
+
+ Returns:
+ Member: The newly added member object.
+ """
response = self.client.execute(
cmd_type="request",
method="addMemberToChannel",
params={
- "teamRef": teamRef,
- "channelRef": channelRef,
- "userRef": userRef,
- "isOwner": isOwner,
+ "teamRef": team_ref,
+ "channelRef": channel_ref,
+ "userRef": user_ref,
+ "isOwner": is_owner,
},
)
return Member(**response)
def update_member_role(
- self, teamRef: str, channelRef: str, userRef: str, isOwner: bool
+ self, team_ref: str, channel_ref: str, user_ref: str, is_owner: bool
) -> Member:
+ """Updates the role of an existing channel member.
+
+ Args:
+ team_ref (str): The reference to the team.
+ channel_ref (str): The reference to the channel.
+ user_ref (str): The user reference (ID or Email).
+ is_owner (bool): True for Owner role, False for Member role.
+
+ Returns:
+ Member: The updated member object.
+ """
response = self.client.execute(
cmd_type="request",
method="updateMemberRoleInChannel",
params={
- "teamRef": teamRef,
- "channelRef": channelRef,
- "userRef": userRef,
- "isOwner": isOwner,
+ "teamRef": team_ref,
+ "channelRef": channel_ref,
+ "userRef": user_ref,
+ "isOwner": is_owner,
},
)
return Member(**response)
- def remove_member(self, teamRef: str, channelRef: str, userRef: str) -> bool:
+ def remove_member(self, team_ref: str, channel_ref: str, user_ref: str) -> bool:
+ """Removes a user from a channel.
+
+ Args:
+ team_ref (str): The reference to the team.
+ channel_ref (str): The reference to the channel.
+ user_ref (str): The user reference (ID or Email) to remove.
+
+ Returns:
+ bool: True if the operation was successful.
+ """
response = self.client.execute(
cmd_type="request",
method="removeMemberFromChannel",
params={
- "teamRef": teamRef,
- "channelRef": channelRef,
- "userRef": userRef,
+ "teamRef": team_ref,
+ "channelRef": channel_ref,
+ "userRef": user_ref,
},
)
return response == "removed"
def get_mentions(
- self, teamRef: str, channelRef: str, raw_mentions: list[str]
+ self, team_ref: str, channel_ref: str, raw_mentions: list[str]
) -> list[Mention]:
+ """Resolves raw mention strings into formal Mention objects.
+
+ This is used to construct the `mentions` field for messages before sending.
+
+ Args:
+ team_ref (str): The reference to the team.
+ channel_ref (str): The reference to the channel.
+ raw_mentions (List[str]): A list of raw strings to resolve.
+ Supported formats:
+ * **Email/User ID**: Resolves to a specific user.
+ * **'channel'**: Mentions the current channel.
+ * **'team'**: Mentions the parent team.
+
+ Returns:
+ List[Mention]: A list of resolved Mention objects ready for the API.
+ """
response = self.client.execute(
cmd_type="request",
method="getMentionsInChannel",
params={
- "teamRef": teamRef,
- "channelRef": channelRef,
+ "teamRef": team_ref,
+ "channelRef": channel_ref,
"rawMentions": raw_mentions,
},
)
diff --git a/python/tests/test_channels.py b/python/tests/test_channels.py
index 74135e7..89d4f66 100644
--- a/python/tests/test_channels.py
+++ b/python/tests/test_channels.py
@@ -42,8 +42,8 @@ def test_get_channel_integration(httpserver):
init_fake_client(client, httpserver.url_for(""))
channel = client.channels.get(
- teamRef=data.teams[0].DisplayName,
- channelRef=data.channels[data.teams[0].ID][1].Name,
+ team_ref=data.teams[0].DisplayName,
+ channel_ref=data.channels[data.teams[0].ID][1].Name,
)
assert channel.Name == data.channels[data.teams[0].ID][1].Name
@@ -63,7 +63,7 @@ def test_create_standard_channel_integration(httpserver):
init_fake_client(client, httpserver.url_for(""))
channel = client.channels.create_standard(
- teamRef=data.teams[0].DisplayName,
+ team_ref=data.teams[0].DisplayName,
display_name=data.newChannelName,
)
@@ -84,7 +84,7 @@ def test_create_private_channel_integration(httpserver):
init_fake_client(client, httpserver.url_for(""))
channel = client.channels.create_private(
- teamRef=data.teams[0].DisplayName,
+ team_ref=data.teams[0].DisplayName,
display_name=data.newChannelName,
member_refs=[],
owner_refs=[],
@@ -107,8 +107,8 @@ def test_delete_channel_integration(httpserver):
init_fake_client(client, httpserver.url_for(""))
success = client.channels.delete(
- teamRef=data.teams[0].DisplayName,
- channelRef=data.channels[data.teams[0].ID][1].Name,
+ team_ref=data.teams[0].DisplayName,
+ channel_ref=data.channels[data.teams[0].ID][1].Name,
)
assert success is True
@@ -126,8 +126,8 @@ def test_send_message_integration(httpserver):
init_fake_client(client, httpserver.url_for(""))
message = client.channels.send_message(
- teamRef=data.teams[0].DisplayName,
- channelRef=data.channels[data.teams[0].ID][0].Name,
+ team_ref=data.teams[0].DisplayName,
+ channel_ref=data.channels[data.teams[0].ID][0].Name,
body=MessageBody(
Content=data.newMessageTemplate.Content,
ContentType=MessageContentType.TEXT,
@@ -156,8 +156,8 @@ def test_list_messages_integration(httpserver):
init_fake_client(client, httpserver.url_for(""))
messages = client.channels.list_messages(
- teamRef=data.teams[0].DisplayName,
- channelRef=data.channels[data.teams[0].ID][0].Name,
+ team_ref=data.teams[0].DisplayName,
+ channel_ref=data.channels[data.teams[0].ID][0].Name,
)
assert len(messages) == len(data.messages[data.channels[data.teams[0].ID][0].ID])
@@ -190,9 +190,9 @@ def test_get_message_integration(httpserver):
init_fake_client(client, httpserver.url_for(""))
message = client.channels.get_message(
- teamRef=data.teams[0].DisplayName,
- channelRef=data.channels[data.teams[0].ID][0].Name,
- messageID=data.messages[data.channels[data.teams[0].ID][0].ID][0].ID,
+ team_ref=data.teams[0].DisplayName,
+ channel_ref=data.channels[data.teams[0].ID][0].Name,
+ message_id=data.messages[data.channels[data.teams[0].ID][0].ID][0].ID,
)
assert message.ID == data.messages[data.channels[data.teams[0].ID][0].ID][0].ID
@@ -216,9 +216,9 @@ def test_list_replies_integration(httpserver):
init_fake_client(client, httpserver.url_for(""))
replies = client.channels.list_message_replies(
- teamRef=data.teams[0].DisplayName,
- channelRef=data.channels[data.teams[0].ID][0].Name,
- messageID=data.messages[data.channels[data.teams[0].ID][0].ID][1].ID,
+ team_ref=data.teams[0].DisplayName,
+ channel_ref=data.channels[data.teams[0].ID][0].Name,
+ message_id=data.messages[data.channels[data.teams[0].ID][0].ID][1].ID,
)
assert len(replies) == len(data.replies[data.messages[data.channels[data.teams[0].ID][0].ID][1].ID])
@@ -249,10 +249,10 @@ def test_get_reply_integration(httpserver):
init_fake_client(client, httpserver.url_for(""))
reply = client.channels.get_message_reply(
- teamRef=data.teams[0].DisplayName,
- channelRef=data.channels[data.teams[0].ID][0].Name,
- messageID=data.messages[data.channels[data.teams[0].ID][0].ID][1].ID,
- replyID=data.replies[data.messages[data.channels[data.teams[0].ID][0].ID][1].ID][0].ID,
+ team_ref=data.teams[0].DisplayName,
+ channel_ref=data.channels[data.teams[0].ID][0].Name,
+ message_id=data.messages[data.channels[data.teams[0].ID][0].ID][1].ID,
+ reply_id=data.replies[data.messages[data.channels[data.teams[0].ID][0].ID][1].ID][0].ID,
)
assert reply.ID == data.replies[data.messages[data.channels[data.teams[0].ID][0].ID][1].ID][0].ID
@@ -278,8 +278,8 @@ def test_list_members_integration(httpserver):
channel_id = channel.ID
members = client.channels.list_members(
- teamRef=data.teams[0].DisplayName,
- channelRef=channel.Name,
+ team_ref=data.teams[0].DisplayName,
+ channel_ref=channel.Name,
)
expected_members = data.members[team_id][channel_id]
@@ -313,10 +313,10 @@ def test_add_member_integration(httpserver):
init_fake_client(client, httpserver.url_for(""))
member = client.channels.add_member(
- teamRef=data.teams[0].DisplayName,
- channelRef=data.channels[data.teams[0].ID][1].Name,
- userRef=data.newMemberTemplate.DisplayName,
- isOwner=True if data.newMemberTemplate.Role == "owner" else False,
+ team_ref=data.teams[0].DisplayName,
+ channel_ref=data.channels[data.teams[0].ID][1].Name,
+ user_ref=data.newMemberTemplate.DisplayName,
+ is_owner=True if data.newMemberTemplate.Role == "owner" else False,
)
assert member.UserID == data.newMemberTemplate.UserID
@@ -338,10 +338,10 @@ def test_update_member_role_integration(httpserver):
init_fake_client(client, httpserver.url_for(""))
member = client.channels.update_member_role(
- teamRef=data.teams[0].DisplayName,
- channelRef=data.channels[data.teams[0].ID][0].Name,
- userRef=data.members[data.teams[0].ID][data.channels[data.teams[0].ID][0].ID][1].Email,
- isOwner=True
+ team_ref=data.teams[0].DisplayName,
+ channel_ref=data.channels[data.teams[0].ID][0].Name,
+ user_ref=data.members[data.teams[0].ID][data.channels[data.teams[0].ID][0].ID][1].Email,
+ is_owner=True
)
assert member.UserID == data.members[data.teams[0].ID][data.channels[data.teams[0].ID][0].ID][1].UserID
@@ -362,9 +362,9 @@ def test_remove_member_integration(httpserver):
init_fake_client(client, httpserver.url_for(""))
success = client.channels.remove_member(
- teamRef=data.teams[0].DisplayName,
- channelRef=data.channels[data.teams[0].ID][0].Name,
- userRef=data.members[data.teams[0].ID][data.channels[data.teams[0].ID][0].ID][1].Email,
+ team_ref=data.teams[0].DisplayName,
+ channel_ref=data.channels[data.teams[0].ID][0].Name,
+ user_ref=data.members[data.teams[0].ID][data.channels[data.teams[0].ID][0].ID][1].Email,
)
assert success is True
@@ -381,8 +381,8 @@ def test_get_mention_integration(httpserver):
init_fake_client(client, httpserver.url_for(""))
mention = client.channels.get_mentions(
- teamRef=data.teams[0].DisplayName,
- channelRef=data.channels[data.teams[0].ID][0].Name,
+ team_ref=data.teams[0].DisplayName,
+ channel_ref=data.channels[data.teams[0].ID][0].Name,
raw_mentions=MentionKind.TEAM.value,
)
From 19b9f1f370408942a672d982bb107f6b78103a22 Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Wed, 7 Jan 2026 21:27:40 +0100
Subject: [PATCH 55/99] feat: added docstrings for chat service
---
python/teams_lib_pzsp2_z1/services/chats.py | 176 ++++++++++++++++++++
1 file changed, 176 insertions(+)
diff --git a/python/teams_lib_pzsp2_z1/services/chats.py b/python/teams_lib_pzsp2_z1/services/chats.py
index dab5f1b..86e53a1 100644
--- a/python/teams_lib_pzsp2_z1/services/chats.py
+++ b/python/teams_lib_pzsp2_z1/services/chats.py
@@ -1,3 +1,7 @@
+"""
+This module contains the service for managing Microsoft Teams chats via the Go backend.
+"""
+
from datetime import datetime
from teams_lib_pzsp2_z1.model.chat import Chat, ChatRef, ChatType
@@ -13,7 +17,34 @@
class ChatsService(BaseService):
+ """Service for managing Microsoft Teams chats via the Go backend.
+
+ This class acts as a high-level wrapper, delegating operations to the
+ underlying Go library. It handles One-on-One chats, Group chats, messages,
+ and membership management.
+
+ **Concepts:**
+ * **Chat Types**: Chats are distinct entities, either **One-on-One** (between two users)
+ or **Group** (multiple users, has a topic).
+ * **References**:
+ * `recipient_ref` / `user_ref`: Can be a User ID (UUID) or an Email.
+ * `group_chat_ref`: Can be the Chat ID or the Group Topic (if unique).
+ * `chat_ref` (Object): A specific model `ChatRef` containing the ID/Name and the `ChatType`.
+ * **Permissions**: Some operations (like `list_all_messages`) require Application Permissions
+ and will not work in Delegated (user context) mode.
+ """
+
def create_one_on_one(self, recipient_ref: str) -> Chat:
+ """Creates a One-on-One chat with a specific user.
+
+ The authenticated user is automatically added to the chat.
+
+ Args:
+ recipient_ref (str): The reference to the other user (User ID or Email).
+
+ Returns:
+ Chat: The created chat object.
+ """
response = self.client.execute(
cmd_type="request",
method="createOneOnOneChat",
@@ -32,6 +63,16 @@ def create_one_on_one(self, recipient_ref: str) -> Chat:
def create_group_chat(
self, recipient_refs: list[str], topic: str, include_me: bool
) -> Chat:
+ """Creates a Group Chat with multiple participants.
+
+ Args:
+ recipient_refs (list[str]): A list of user references (IDs or Emails) to include.
+ topic (str): The subject/topic of the group chat.
+ include_me (bool): If True, the authenticated user is added to the group.
+
+ Returns:
+ Chat: The created group chat object.
+ """
response = self.client.execute(
cmd_type="request",
method="createGroupChat",
@@ -50,6 +91,15 @@ def create_group_chat(
)
def add_member_to_group_chat(self, group_chat_ref: str, user_ref: str) -> Member:
+ """Adds a user to an existing Group Chat.
+
+ Args:
+ group_chat_ref (str): The reference to the group chat (ID or Topic).
+ user_ref (str): The user to add (User ID or Email).
+
+ Returns:
+ Member: The newly added member details.
+ """
member = self.client.execute(
cmd_type="request",
method="addMemberToGroupChat",
@@ -70,6 +120,15 @@ def add_member_to_group_chat(self, group_chat_ref: str, user_ref: str) -> Member
def remove_member_from_group_chat(
self, group_chat_ref: str, member_ref: str
) -> bool:
+ """Removes a member from a Group Chat.
+
+ Args:
+ group_chat_ref (str): The reference to the group chat (ID or Topic).
+ member_ref (str): The user reference to remove (User ID or Email).
+
+ Returns:
+ bool: True if the member was successfully removed.
+ """
result = self.client.execute(
cmd_type="request",
method="removeMemberFromGroupChat",
@@ -82,6 +141,14 @@ def remove_member_from_group_chat(
return result == "removed"
def list_group_chat_members(self, group_chat_ref: str) -> list[Member]:
+ """Lists all members of a Group Chat.
+
+ Args:
+ group_chat_ref (str): The reference to the group chat (ID or Topic).
+
+ Returns:
+ list[Member]: A list of members in the chat.
+ """
members = self.client.execute(
cmd_type="request",
method="listMembersInGroupChat",
@@ -102,6 +169,15 @@ def list_group_chat_members(self, group_chat_ref: str) -> list[Member]:
]
def update_group_chat_topic(self, group_chat_ref: str, new_topic: str) -> Chat:
+ """Updates the topic of a Group Chat.
+
+ Args:
+ group_chat_ref (str): The reference to the group chat (ID or old Topic).
+ new_topic (str): The new topic string.
+
+ Returns:
+ Chat: The updated chat object.
+ """
response = self.client.execute(
cmd_type="request",
method="updateGroupChatTopic",
@@ -119,6 +195,14 @@ def update_group_chat_topic(self, group_chat_ref: str, new_topic: str) -> Chat:
)
def list_messages(self, chat_ref: ChatRef) -> list[Message]:
+ """Retrieves messages from a specific chat.
+
+ Args:
+ chat_ref (ChatRef): The chat reference object containing the ID/Name and ChatType.
+
+ Returns:
+ list[Message]: A list of messages from the chat history.
+ """
messages = self.client.execute(
cmd_type="request",
method="listMessagesInChat",
@@ -146,6 +230,15 @@ def list_messages(self, chat_ref: ChatRef) -> list[Message]:
]
def send_message(self, chat_ref: ChatRef, body: MessageBody) -> Message:
+ """Sends a message to a chat.
+
+ Args:
+ chat_ref (ChatRef): The chat reference object.
+ body (MessageBody): The message payload (content, type, mentions).
+
+ Returns:
+ Message: The created message object.
+ """
message = self.client.execute(
cmd_type="request",
method="sendMessageInChat",
@@ -174,6 +267,17 @@ def send_message(self, chat_ref: ChatRef, body: MessageBody) -> Message:
)
def delete_message(self, chat_ref: ChatRef, message_id: str) -> bool:
+ """Soft-deletes a message from a chat.
+
+ This action is reversible via the Graph API (though not exposed here).
+
+ Args:
+ chat_ref (ChatRef): The chat reference object.
+ message_id (str): The unique identifier of the message to delete.
+
+ Returns:
+ bool: True if the message was successfully deleted.
+ """
result = self.client.execute(
cmd_type="request",
method="deleteMessageInChat",
@@ -189,6 +293,15 @@ def delete_message(self, chat_ref: ChatRef, message_id: str) -> bool:
return result == "deleted"
def get_message(self, chat_ref: ChatRef, message_id: str) -> Message:
+ """Retrieves a single message by its ID.
+
+ Args:
+ chat_ref (ChatRef): The chat reference object.
+ message_id (str): The unique identifier of the message.
+
+ Returns:
+ Message: The requested message object.
+ """
message = self.client.execute(
cmd_type="request",
method="getMessageInChat",
@@ -214,6 +327,15 @@ def get_message(self, chat_ref: ChatRef, message_id: str) -> Message:
)
def list_my_joined(self, chat_type: ChatType | None = None) -> list[Chat]:
+ """Lists all chats the authenticated user is part of.
+
+ Args:
+ chat_type (ChatType | None, optional): Filter by chat type (e.g., only OneOnOne).
+ Defaults to None (return all).
+
+ Returns:
+ list[Chat]: A list of chat objects.
+ """
params = {}
if chat_type:
params["chatType"] = chat_type.value
@@ -237,6 +359,20 @@ def list_my_joined(self, chat_type: ChatType | None = None) -> list[Chat]:
def list_all_messages(
self, start_time: datetime, end_time: datetime, top: int
) -> list[Message]:
+ """Retrieves messages across ALL chats within a time range.
+
+ Note:
+ This operation typically requires **Application Permissions** and may not
+ work with standard Delegated (User) credentials depending on the organization's policy.
+
+ Args:
+ start_time (datetime): The start of the time range.
+ end_time (datetime): The end of the time range.
+ top (int): Maximum number of messages to retrieve.
+
+ Returns:
+ list[Message]: A list of messages from all chats.
+ """
messages = self.client.execute(
cmd_type="request",
method="listMyChatMessages",
@@ -263,6 +399,14 @@ def list_all_messages(
]
def list_pinned_messages(self, chat_ref: ChatRef) -> list[Message]:
+ """Lists all pinned messages in a specific chat.
+
+ Args:
+ chat_ref (ChatRef): The chat reference object.
+
+ Returns:
+ list[Message]: A list of pinned messages.
+ """
messages = self.client.execute(
cmd_type="request",
method="listPinnedMessagesInChat",
@@ -290,6 +434,15 @@ def list_pinned_messages(self, chat_ref: ChatRef) -> list[Message]:
]
def pin_message(self, chat_ref: ChatRef, message_id: str) -> bool:
+ """Pins a message in the chat.
+
+ Args:
+ chat_ref (ChatRef): The chat reference object.
+ message_id (str): The ID of the message to pin.
+
+ Returns:
+ bool: True if the message was successfully pinned.
+ """
result = self.client.execute(
cmd_type="request",
method="pinMessageInChat",
@@ -305,6 +458,15 @@ def pin_message(self, chat_ref: ChatRef, message_id: str) -> bool:
return result == "pinned"
def unpin_message(self, chat_ref: ChatRef, message_id: str) -> bool:
+ """Unpins a message in the chat.
+
+ Args:
+ chat_ref (ChatRef): The chat reference object.
+ message_id (str): The ID of the message to unpin.
+
+ Returns:
+ bool: True if the message was successfully unpinned.
+ """
result = self.client.execute(
cmd_type="request",
method="unpinMessageInChat",
@@ -320,6 +482,20 @@ def unpin_message(self, chat_ref: ChatRef, message_id: str) -> bool:
return result == "unpinned"
def get_mentions(self, chat_ref: ChatRef, raw_mentions: list[str]) -> list[Mention]:
+ """Resolves raw mention strings into formal Mention objects.
+
+ This helps in processing @mentions within message content before sending.
+
+ Args:
+ chat_ref (ChatRef): The chat reference object.
+ raw_mentions (list[str]): A list of raw strings to resolve.
+ Supported formats:
+ * **Email/User ID**: Resolves to a specific user.
+ * **'Everyone'**: Mentions all members (Group Chats only).
+
+ Returns:
+ list[Mention]: A list of resolved Mention objects.
+ """
mentions = self.client.execute(
cmd_type="request",
method="getMentionsInChat",
From 0dd0eaec3a35c1fc8689b9f19e253142fd5c2e3f Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Wed, 7 Jan 2026 21:29:41 +0100
Subject: [PATCH 56/99] style: ignored ruff line too long warnings
---
python/pyproject.toml | 1 +
1 file changed, 1 insertion(+)
diff --git a/python/pyproject.toml b/python/pyproject.toml
index fcf29b1..d709581 100644
--- a/python/pyproject.toml
+++ b/python/pyproject.toml
@@ -23,6 +23,7 @@ fix = true
show-fixes = true
src = ["teams_lib_pzsp2_z1"]
exclude = ["test_*.py", "tests/*"]
+ignore = ["E501"]
[tool.ruff.lint]
From 652653de9bcd9bb093691069661e3d25bd5acf61 Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Wed, 7 Jan 2026 21:43:18 +0100
Subject: [PATCH 57/99] chore: adjusted go channels requests to new lib version
---
go/go.mod | 4 +--
go/go.sum | 4 +++
go/internal/json-client/requests-channels.go | 36 ++++++++------------
3 files changed, 20 insertions(+), 24 deletions(-)
diff --git a/go/go.mod b/go/go.mod
index fee5462..21c34a4 100644
--- a/go/go.mod
+++ b/go/go.mod
@@ -4,9 +4,9 @@ go 1.25.5
require (
github.com/microsoft/kiota-abstractions-go v1.9.3
- github.com/microsoftgraph/msgraph-sdk-go v1.92.0
+ github.com/microsoftgraph/msgraph-sdk-go v1.93.0
github.com/mitchellh/mapstructure v1.5.0
- github.com/pzsp-teams/lib v0.0.0-20260101185821-fa4110d82a33
+ github.com/pzsp-teams/lib v0.0.0-20260107201101-e9e6f48e39e8
)
require (
diff --git a/go/go.sum b/go/go.sum
index 7327d45..d6423b7 100644
--- a/go/go.sum
+++ b/go/go.sum
@@ -53,6 +53,8 @@ github.com/microsoft/kiota-serialization-text-go v1.1.3 h1:8z7Cebn0YAAr++xswVgfd
github.com/microsoft/kiota-serialization-text-go v1.1.3/go.mod h1:NDSvz4A3QalGMjNboKKQI9wR+8k+ih8UuagNmzIRgTQ=
github.com/microsoftgraph/msgraph-sdk-go v1.92.0 h1:LOTZ5un7tsSki0S072D+TIHsiOIpmGJnDkNps9Em6cw=
github.com/microsoftgraph/msgraph-sdk-go v1.92.0/go.mod h1:sue5+4Z9FCOon6pHgvC1djjybs9ZYB3LZaAGYI1Qcfo=
+github.com/microsoftgraph/msgraph-sdk-go v1.93.0 h1:H4WgYetjWYpr79S2ylNJchFDQTBE0ldKx8sR0qxyRGQ=
+github.com/microsoftgraph/msgraph-sdk-go v1.93.0/go.mod h1:sue5+4Z9FCOon6pHgvC1djjybs9ZYB3LZaAGYI1Qcfo=
github.com/microsoftgraph/msgraph-sdk-go-core v1.4.0 h1:0SrIoFl7TQnMRrsi5TFaeNe0q8KO5lRzRp4GSCCL2So=
github.com/microsoftgraph/msgraph-sdk-go-core v1.4.0/go.mod h1:A1iXs+vjsRjzANxF6UeKv2ACExG7fqTwHHbwh1FL+EE=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
@@ -65,6 +67,8 @@ github.com/pzsp-teams/lib v0.0.0-20260101113908-bafa8ca37eae h1:Q0NnGhGtyUbPzDHx
github.com/pzsp-teams/lib v0.0.0-20260101113908-bafa8ca37eae/go.mod h1:OUTZq+jo2AAzjV5p0w7c9VVuOKiHVWxuZhdzOhjOq5s=
github.com/pzsp-teams/lib v0.0.0-20260101185821-fa4110d82a33 h1:snmBQwIaJV1MBynhm8OKzvaKtpUva4wem3VKmyaVU0Q=
github.com/pzsp-teams/lib v0.0.0-20260101185821-fa4110d82a33/go.mod h1:OUTZq+jo2AAzjV5p0w7c9VVuOKiHVWxuZhdzOhjOq5s=
+github.com/pzsp-teams/lib v0.0.0-20260107201101-e9e6f48e39e8 h1:1ifLBNVdw8TCLPzHnlQfgxlL8pbj/9MVquTLNiX2t0Q=
+github.com/pzsp-teams/lib v0.0.0-20260107201101-e9e6f48e39e8/go.mod h1:BQwULf+u8TtbKR0U23MlZXgm2CVcLBJn2cS5gmR7L8M=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/std-uritemplate/std-uritemplate/go/v2 v2.0.8 h1:gMBdYMTHt2mmTdXW8YfvRjRUZ0GhyGV+IqSH9H15bGw=
diff --git a/go/internal/json-client/requests-channels.go b/go/internal/json-client/requests-channels.go
index f249de1..e3c8473 100644
--- a/go/internal/json-client/requests-channels.go
+++ b/go/internal/json-client/requests-channels.go
@@ -2,8 +2,6 @@ package jsonclient
import (
"context"
- "fmt"
- "os"
"github.com/pzsp-teams/lib-python/internal/json-client/decoders"
"github.com/pzsp-teams/lib/models"
@@ -100,9 +98,11 @@ type listMessagesOptionsDTO struct {
}
type listMessagesParams struct {
- TeamRef string `json:"teamRef"`
- ChannelRef string `json:"channelRef"`
- Options listMessagesOptionsDTO `json:"options"`
+ TeamRef string `json:"teamRef"`
+ ChannelRef string `json:"channelRef"`
+ Options listMessagesOptionsDTO `json:"options"`
+ IncludeSystem bool `json:"includeSystem"`
+ NextLink string `json:"nextLink"`
}
func (c *TeamsJSONClient) ListMessagesInChannel(p map[string]interface{}) (interface{}, error) {
@@ -111,7 +111,7 @@ func (c *TeamsJSONClient) ListMessagesInChannel(p map[string]interface{}) (inter
if err != nil {
return nil, err
}
- return c.client.Channels.ListMessages(context.TODO(), params.TeamRef, params.ChannelRef, options)
+ return c.client.Channels.ListMessages(context.TODO(), params.TeamRef, params.ChannelRef, options, params.IncludeSystem, ¶ms.NextLink)
})
}
@@ -128,15 +128,17 @@ func (c *TeamsJSONClient) GetMessageInChannel(p map[string]interface{}) (interfa
}
type listRepliesParams struct {
- TeamRef string `json:"teamRef"`
- ChannelRef string `json:"channelRef"`
- MessageID string `json:"messageId"`
- Top *int32 `json:"top"`
+ TeamRef string `json:"teamRef"`
+ ChannelRef string `json:"channelRef"`
+ MessageID string `json:"messageId"`
+ Top *int32 `json:"top"`
+ IncludeSystem bool `json:"includeSystem"`
+ NextLink string `json:"nextLink"`
}
func (c *TeamsJSONClient) ListMessageRepliesInChannel(p map[string]interface{}) (interface{}, error) {
return execute(p, func(params listRepliesParams) (interface{}, error) {
- return c.client.Channels.ListReplies(context.TODO(), params.TeamRef, params.ChannelRef, params.MessageID, params.Top)
+ return c.client.Channels.ListReplies(context.TODO(), params.TeamRef, params.ChannelRef, params.MessageID, params.Top, params.IncludeSystem, ¶ms.NextLink)
})
}
@@ -174,12 +176,7 @@ func (c *TeamsJSONClient) AddMemberToChannel(p map[string]interface{}) (interfac
func (c *TeamsJSONClient) UpdateMemberInChannel(p map[string]interface{}) (interface{}, error) {
return execute(p, func(params addOrUpdateMemberToChannelParams) (interface{}, error) {
-
- // --- LOGOWANIE NA STDERR ---
- fmt.Fprintf(os.Stderr, "\n[GO DEBUG] UpdateMemberInChannel PARAMS:\n -> TeamRef: '%s'\n -> ChannelRef: '%s'\n -> UserRef: '%s'\n -> IsOwner: %v\n",
- params.TeamRef, params.ChannelRef, params.UserRef, params.IsOwner)
- // ---------------------------
- return c.client.Channels.UpdateMemberRole(context.TODO(), params.TeamRef, params.ChannelRef, params.UserRef, params.IsOwner)
+ return c.client.Channels.UpdateMemberRoles(context.TODO(), params.TeamRef, params.ChannelRef, params.UserRef, params.IsOwner)
})
}
@@ -191,11 +188,6 @@ type removeMemberFromChannelParams struct {
func (c *TeamsJSONClient) RemoveMemberFromChannel(p map[string]interface{}) (interface{}, error) {
return execute(p, func(params removeMemberFromChannelParams) (interface{}, error) {
-
- // --- LOGOWANIE NA STDERR ---
- fmt.Fprintf(os.Stderr, "\n[GO DEBUG] RemoveMemberFromChannel PARAMS:\n -> TeamRef: '%s'\n -> ChannelRef: '%s'\n -> UserRef: '%s'\n",
- params.TeamRef, params.ChannelRef, params.UserRef)
- // ---------------------------
err := c.client.Channels.RemoveMember(context.TODO(), params.TeamRef, params.ChannelRef, params.UserRef)
return "removed", err
})
From 757a703a63785f7093385919cee36f606b576d17 Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Wed, 7 Jan 2026 22:24:21 +0100
Subject: [PATCH 58/99] chore: adjusted chats and teams in go bridge to new lib
version
---
go/go.mod | 2 +-
go/go.sum | 2 +
.../json-client/decoders/decode-msTeam.go | 134 ------------------
.../json-client/decoders/decode-updateTeam.go | 7 +
go/internal/json-client/requests-chats.go | 18 ++-
go/internal/json-client/requests-teams.go | 19 ++-
6 files changed, 35 insertions(+), 147 deletions(-)
delete mode 100644 go/internal/json-client/decoders/decode-msTeam.go
create mode 100644 go/internal/json-client/decoders/decode-updateTeam.go
diff --git a/go/go.mod b/go/go.mod
index 21c34a4..5bbfd43 100644
--- a/go/go.mod
+++ b/go/go.mod
@@ -6,7 +6,7 @@ require (
github.com/microsoft/kiota-abstractions-go v1.9.3
github.com/microsoftgraph/msgraph-sdk-go v1.93.0
github.com/mitchellh/mapstructure v1.5.0
- github.com/pzsp-teams/lib v0.0.0-20260107201101-e9e6f48e39e8
+ github.com/pzsp-teams/lib v0.0.0-20260107211439-3d301706e02c
)
require (
diff --git a/go/go.sum b/go/go.sum
index d6423b7..14ee7df 100644
--- a/go/go.sum
+++ b/go/go.sum
@@ -69,6 +69,8 @@ github.com/pzsp-teams/lib v0.0.0-20260101185821-fa4110d82a33 h1:snmBQwIaJV1MBynh
github.com/pzsp-teams/lib v0.0.0-20260101185821-fa4110d82a33/go.mod h1:OUTZq+jo2AAzjV5p0w7c9VVuOKiHVWxuZhdzOhjOq5s=
github.com/pzsp-teams/lib v0.0.0-20260107201101-e9e6f48e39e8 h1:1ifLBNVdw8TCLPzHnlQfgxlL8pbj/9MVquTLNiX2t0Q=
github.com/pzsp-teams/lib v0.0.0-20260107201101-e9e6f48e39e8/go.mod h1:BQwULf+u8TtbKR0U23MlZXgm2CVcLBJn2cS5gmR7L8M=
+github.com/pzsp-teams/lib v0.0.0-20260107211439-3d301706e02c h1:ZtUkM66y/jg5g5kEQhteQeACuwKl+cOkRuGPlX1mkGg=
+github.com/pzsp-teams/lib v0.0.0-20260107211439-3d301706e02c/go.mod h1:BQwULf+u8TtbKR0U23MlZXgm2CVcLBJn2cS5gmR7L8M=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/std-uritemplate/std-uritemplate/go/v2 v2.0.8 h1:gMBdYMTHt2mmTdXW8YfvRjRUZ0GhyGV+IqSH9H15bGw=
diff --git a/go/internal/json-client/decoders/decode-msTeam.go b/go/internal/json-client/decoders/decode-msTeam.go
deleted file mode 100644
index 3447881..0000000
--- a/go/internal/json-client/decoders/decode-msTeam.go
+++ /dev/null
@@ -1,134 +0,0 @@
-package decoders
-
-import(
- msmodels "github.com/microsoftgraph/msgraph-sdk-go/models"
-)
-
-type TeamPatchDTO struct {
- DisplayName *string `json:"displayName"`
- Description *string `json:"description"`
- Classification *string `json:"classification"`
- Visibility *string `json:"visibility"`
- IsArchived *bool `json:"isArchived"`
-
- MemberSettings *TeamMemberSettingsDTO `json:"memberSettings"`
- GuestSettings *TeamGuestSettingsDTO `json:"guestSettings"`
- MessagingSettings *TeamMessagingSettingsDTO `json:"messagingSettings"`
- FunSettings *TeamFunSettingsDTO `json:"funSettings"`
-}
-
-type TeamMemberSettingsDTO struct {
- AllowCreateUpdateChannels *bool `json:"allowCreateUpdateChannels"`
- AllowDeleteChannels *bool `json:"allowDeleteChannels"`
- AllowAddRemoveApps *bool `json:"allowAddRemoveApps"`
- AllowCreateUpdateRemoveTabs *bool `json:"allowCreateUpdateRemoveTabs"`
- AllowCreateUpdateRemoveConnectors *bool `json:"allowCreateUpdateRemoveConnectors"`
-}
-
-type TeamGuestSettingsDTO struct {
- AllowCreateUpdateChannels *bool `json:"allowCreateUpdateChannels"`
- AllowDeleteChannels *bool `json:"allowDeleteChannels"`
-}
-
-type TeamMessagingSettingsDTO struct {
- AllowUserEditMessages *bool `json:"allowUserEditMessages"`
- AllowUserDeleteMessages *bool `json:"allowUserDeleteMessages"`
- AllowOwnerDeleteMessages *bool `json:"allowOwnerDeleteMessages"`
- AllowTeamMentions *bool `json:"allowTeamMentions"`
- AllowChannelMentions *bool `json:"allowChannelMentions"`
-}
-
-type TeamFunSettingsDTO struct {
- AllowGiphy *bool `json:"allowGiphy"`
- GiphyContentRating *string `json:"giphyContentRating"`
- AllowStickersAndMemes *bool `json:"allowStickersAndMemes"`
- AllowCustomMemes *bool `json:"allowCustomMemes"`
-}
-
-func GetMSTeam(patch *TeamPatchDTO) *msmodels.Team {
- team := msmodels.NewTeam()
-
- if patch.DisplayName != nil {
- team.SetDisplayName(patch.DisplayName)
- }
- if patch.Description != nil {
- team.SetDescription(patch.Description)
- }
- if patch.Classification != nil {
- team.SetClassification(patch.Classification)
- }
- if patch.IsArchived != nil {
- team.SetIsArchived(patch.IsArchived)
- }
- if patch.Visibility != nil {
- switch *patch.Visibility {
- case "Public":
- v := msmodels.PUBLIC_TEAMVISIBILITYTYPE
- team.SetVisibility(&v)
- case "Private":
- v := msmodels.PRIVATE_TEAMVISIBILITYTYPE
- team.SetVisibility(&v)
- case "HiddenMembership":
- v := msmodels.HIDDENMEMBERSHIP_TEAMVISIBILITYTYPE
- team.SetVisibility(&v)
- }
- }
-
- if patch.MemberSettings != nil {
- msSettings := msmodels.NewTeamMemberSettings()
- p := patch.MemberSettings
- if p.AllowCreateUpdateChannels != nil { msSettings.SetAllowCreateUpdateChannels(p.AllowCreateUpdateChannels) }
- if p.AllowDeleteChannels != nil { msSettings.SetAllowDeleteChannels(p.AllowDeleteChannels) }
- if p.AllowAddRemoveApps != nil { msSettings.SetAllowAddRemoveApps(p.AllowAddRemoveApps) }
- if p.AllowCreateUpdateRemoveTabs != nil { msSettings.SetAllowCreateUpdateRemoveTabs(p.AllowCreateUpdateRemoveTabs) }
- if p.AllowCreateUpdateRemoveConnectors != nil { msSettings.SetAllowCreateUpdateRemoveConnectors(p.AllowCreateUpdateRemoveConnectors) }
-
- team.SetMemberSettings(msSettings)
- }
-
- if patch.GuestSettings != nil {
- msSettings := msmodels.NewTeamGuestSettings()
- p := patch.GuestSettings
- if p.AllowCreateUpdateChannels != nil { msSettings.SetAllowCreateUpdateChannels(p.AllowCreateUpdateChannels) }
- if p.AllowDeleteChannels != nil { msSettings.SetAllowDeleteChannels(p.AllowDeleteChannels) }
-
- team.SetGuestSettings(msSettings)
- }
-
- if patch.MessagingSettings != nil {
- msSettings := msmodels.NewTeamMessagingSettings()
- p := patch.MessagingSettings
- if p.AllowUserEditMessages != nil { msSettings.SetAllowUserEditMessages(p.AllowUserEditMessages) }
- if p.AllowUserDeleteMessages != nil { msSettings.SetAllowUserDeleteMessages(p.AllowUserDeleteMessages) }
- if p.AllowOwnerDeleteMessages != nil { msSettings.SetAllowOwnerDeleteMessages(p.AllowOwnerDeleteMessages) }
- if p.AllowTeamMentions != nil { msSettings.SetAllowTeamMentions(p.AllowTeamMentions) }
- if p.AllowChannelMentions != nil { msSettings.SetAllowChannelMentions(p.AllowChannelMentions) }
-
- team.SetMessagingSettings(msSettings)
- }
-
- if patch.FunSettings != nil {
- msSettings := msmodels.NewTeamFunSettings()
- p := patch.FunSettings
- if p.AllowGiphy != nil { msSettings.SetAllowGiphy(p.AllowGiphy) }
- if p.GiphyContentRating != nil {
- switch *p.GiphyContentRating {
- case "moderate":
- v := msmodels.MODERATE_GIPHYRATINGTYPE
- msSettings.SetGiphyContentRating(&v)
- case "strict":
- v := msmodels.STRICT_GIPHYRATINGTYPE
- msSettings.SetGiphyContentRating(&v)
- default:
- v := msmodels.MODERATE_GIPHYRATINGTYPE
- msSettings.SetGiphyContentRating(&v)
- }
-}
- if p.AllowStickersAndMemes != nil { msSettings.SetAllowStickersAndMemes(p.AllowStickersAndMemes) }
- if p.AllowCustomMemes != nil { msSettings.SetAllowCustomMemes(p.AllowCustomMemes) }
-
- team.SetFunSettings(msSettings)
- }
-
- return team
-}
\ No newline at end of file
diff --git a/go/internal/json-client/decoders/decode-updateTeam.go b/go/internal/json-client/decoders/decode-updateTeam.go
new file mode 100644
index 0000000..40207a4
--- /dev/null
+++ b/go/internal/json-client/decoders/decode-updateTeam.go
@@ -0,0 +1,7 @@
+package decoders
+
+type UpdateTeamDTO struct {
+ DisplayName *string `json:"displayName"`
+ Description *string `json:"description"`
+ Visibility *string `json:"visibility"`
+}
diff --git a/go/internal/json-client/requests-chats.go b/go/internal/json-client/requests-chats.go
index 68eca9d..cab3135 100644
--- a/go/internal/json-client/requests-chats.go
+++ b/go/internal/json-client/requests-chats.go
@@ -14,7 +14,7 @@ type createOneToOneChatParams struct {
func (c *TeamsJSONClient) CreateOneToOneChat(p map[string]interface{}) (interface{}, error) {
return execute(p, func(params createOneToOneChatParams) (interface{}, error) {
- return c.client.Chats.CreateOneOneOne(context.TODO(), params.RecipientRef)
+ return c.client.Chats.CreateOneOnOne(context.TODO(), params.RecipientRef)
})
}
@@ -69,17 +69,19 @@ func (c *TeamsJSONClient) UpdateGroupChatTopic(p map[string]interface{}) (interf
})
}
-type baseChatParams struct {
- ChatRef decoders.ChatRefDTO `json:"chatRef"`
+type listMessagesInChatParams struct {
+ ChatRef decoders.ChatRefDTO `json:"chatRef"`
+ IncludeSystem bool `json:"includeSystem"`
+ NextLink string `json:"nextLink"`
}
func (c *TeamsJSONClient) ListMessagesInChat(p map[string]interface{}) (interface{}, error) {
- return execute(p, func(params baseChatParams) (interface{}, error) {
+ return execute(p, func(params listMessagesInChatParams) (interface{}, error) {
chatRef, err := decoders.GetChatRef(params.ChatRef)
if err != nil {
return nil, err
}
- return c.client.Chats.ListMessages(context.TODO(), chatRef)
+ return c.client.Chats.ListMessages(context.TODO(), chatRef, params.IncludeSystem, ¶ms.NextLink)
})
}
@@ -145,7 +147,7 @@ func (c *TeamsJSONClient) ListMyChats(p map[string]interface{}) (interface{}, er
type listChatMessagesParams struct {
StartTime string `json:"startTime"`
EndTime string `json:"endTime"`
- Top *int32 `json:"top"`
+ Top *int32 `json:"top"`
}
func (c *TeamsJSONClient) ListMyChatMessages(p map[string]interface{}) (interface{}, error) {
@@ -158,6 +160,10 @@ func (c *TeamsJSONClient) ListMyChatMessages(p map[string]interface{}) (interfac
})
}
+type baseChatParams struct {
+ ChatRef decoders.ChatRefDTO `json:"chatRef"`
+}
+
func (c *TeamsJSONClient) ListPinnedMessagesInChat(p map[string]interface{}) (interface{}, error) {
return execute(p, func(params baseChatParams) (interface{}, error) {
chatRef, err := decoders.GetChatRef(params.ChatRef)
diff --git a/go/internal/json-client/requests-teams.go b/go/internal/json-client/requests-teams.go
index 8cbc140..0b8479a 100644
--- a/go/internal/json-client/requests-teams.go
+++ b/go/internal/json-client/requests-teams.go
@@ -4,6 +4,7 @@ import (
"context"
"github.com/pzsp-teams/lib-python/internal/json-client/decoders"
+ "github.com/pzsp-teams/lib/models"
)
type getTeamParams struct {
@@ -21,14 +22,17 @@ func (c *TeamsJSONClient) ListMyJoined() (interface{}, error) {
}
type updateTeamParams struct {
- TeamRef string `json:"teamRef"`
- TeamPatch decoders.TeamPatchDTO `json:"team"`
+ TeamRef string `json:"teamRef"`
+ TeamUpdate decoders.UpdateTeamDTO `json:"team"`
}
func (c *TeamsJSONClient) UpdateTeam(p map[string]interface{}) (interface{}, error) {
return execute(p, func(params updateTeamParams) (interface{}, error) {
- msTeam := decoders.GetMSTeam(¶ms.TeamPatch)
- return c.client.Teams.Update(context.TODO(), params.TeamRef, msTeam)
+ updateTeam, err := decoders.DecodeParams[models.TeamUpdate](¶ms.TeamUpdate)
+ if err != nil {
+ return nil, err
+ }
+ return c.client.Teams.UpdateTeam(context.TODO(), params.TeamRef, updateTeam)
})
}
@@ -48,11 +52,14 @@ type createFromTemplateParams struct {
DisplayName string `json:"displayName"`
Description string `json:"description"`
Owners []string `json:"owners"`
+ Members []string `json:"members"`
+ Visibility string `json:"visibility"`
+ IncludeMe bool `json:"includeMe"`
}
func (c *TeamsJSONClient) CreateTeamFromTemplate(p map[string]interface{}) (interface{}, error) {
return execute(p, func(params createFromTemplateParams) (interface{}, error) {
- return c.client.Teams.CreateFromTemplate(context.TODO(), params.DisplayName, params.Description, params.Owners)
+ return c.client.Teams.CreateFromTemplate(context.TODO(), params.DisplayName, params.Description, params.Owners, params.Members, params.Visibility, params.IncludeMe)
})
}
@@ -98,4 +105,4 @@ func (c *TeamsJSONClient) RestoreDeletedTeam(p map[string]interface{}) (interfac
return execute(p, func(params restoreTeamParams) (interface{}, error) {
return c.client.Teams.RestoreDeleted(context.TODO(), params.DeletedGroupID)
})
-}
\ No newline at end of file
+}
From 72fe0af79bac26ea620d3c565340e21c09d16030 Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Wed, 7 Jan 2026 22:26:17 +0100
Subject: [PATCH 59/99] feat: added new data models to python
---
python/teams_lib_pzsp2_z1/model/message.py | 5 +++
python/teams_lib_pzsp2_z1/model/team.py | 36 +---------------------
2 files changed, 6 insertions(+), 35 deletions(-)
diff --git a/python/teams_lib_pzsp2_z1/model/message.py b/python/teams_lib_pzsp2_z1/model/message.py
index 52f8139..e3decf2 100644
--- a/python/teams_lib_pzsp2_z1/model/message.py
+++ b/python/teams_lib_pzsp2_z1/model/message.py
@@ -47,3 +47,8 @@ def __iter__(self):
class ListMessagesOptions:
Top: int | None = None
ExpandReplies: bool = False
+
+@dataclass
+class MessageCollection:
+ Messages: list[Message]
+ NextLink: str | None = None
diff --git a/python/teams_lib_pzsp2_z1/model/team.py b/python/teams_lib_pzsp2_z1/model/team.py
index d53ee4a..3e332e6 100644
--- a/python/teams_lib_pzsp2_z1/model/team.py
+++ b/python/teams_lib_pzsp2_z1/model/team.py
@@ -11,41 +11,7 @@ class Team:
@dataclass
-class MSTeamsUpdate:
+class UpdateTeam:
DisplayName: str | None = None
Description: str | None = None
- Classification: str | None = None
Visibility: str | None = None
- IsArchived: bool | None = None
- MemberSettings: "TeamMemberSettingsDTO | None" = None
- GuestSettings: "TeamGuestSettingsDTO | None" = None
- MessagingSettings: "TeamMessagingSettingsDTO | None" = None
- FunSettings: "TeamFunSettingsDTO | None" = None
-
- @dataclass
- class TeamMemberSettingsDTO:
- AllowCreateUpdateChannels: bool | None = None
- AllowDeleteChannels: bool | None = None
- AllowAddRemoveApps: bool | None = None
- AllowCreateUpdateRemoveTabs: bool | None = None
- AllowCreateUpdateRemoveConnectors: bool | None = None
-
- @dataclass
- class TeamGuestSettingsDTO:
- AllowCreateUpdateChannels: bool | None = None
- AllowDeleteChannels: bool | None = None
-
- @dataclass
- class TeamMessagingSettingsDTO:
- AllowUserEditMessages: bool | None = None
- AllowUserDeleteMessages: bool | None = None
- AllowOwnerDeleteMessages: bool | None = None
- AllowTeamMentions: bool | None = None
- AllowChannelMentions: bool | None = None
-
- @dataclass
- class TeamFunSettingsDTO:
- AllowGiphy: bool | None = None
- GiphyContentRating: str | None = None
- AllowStickersAndMemes: bool | None = None
- AllowCustomMemes: bool | None = None
From dd97bde4fb07b1f365ca553223fcd94d0cb3fd72 Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Wed, 7 Jan 2026 22:45:16 +0100
Subject: [PATCH 60/99] chore: adjusted update team python and test
---
.../json-client/decoders/decode-updateTeam.go | 26 ++++++++++--
go/internal/json-client/requests-teams.go | 8 +---
python/teams_lib_pzsp2_z1/model/team.py | 6 +--
.../teams_lib_pzsp2_z1/services/channels.py | 42 ++++++++++++-------
python/teams_lib_pzsp2_z1/services/teams.py | 4 +-
python/tests/fake_server/setup.py | 27 +++++++++---
python/tests/test_channels.py | 35 ++++++++--------
python/tests/test_teams.py | 6 +--
8 files changed, 98 insertions(+), 56 deletions(-)
diff --git a/go/internal/json-client/decoders/decode-updateTeam.go b/go/internal/json-client/decoders/decode-updateTeam.go
index 40207a4..b5a1eea 100644
--- a/go/internal/json-client/decoders/decode-updateTeam.go
+++ b/go/internal/json-client/decoders/decode-updateTeam.go
@@ -1,7 +1,27 @@
package decoders
+import (
+ "github.com/pzsp-teams/lib/models"
+)
+
type UpdateTeamDTO struct {
- DisplayName *string `json:"displayName"`
- Description *string `json:"description"`
- Visibility *string `json:"visibility"`
+ DisplayName string `json:"displayName"`
+ Description string `json:"description"`
+ Visibility string `json:"visibility"`
+}
+
+func GetUpdateTeam(dto *UpdateTeamDTO) models.TeamUpdate {
+ update := models.TeamUpdate{}
+
+ if dto.DisplayName != "" {
+ update.DisplayName = &dto.DisplayName
+ }
+ if dto.Description != "" {
+ update.Description = &dto.Description
+ }
+ if dto.Visibility != "" {
+ update.Visibility = &dto.Visibility
+ }
+
+ return update
}
diff --git a/go/internal/json-client/requests-teams.go b/go/internal/json-client/requests-teams.go
index 0b8479a..49d267c 100644
--- a/go/internal/json-client/requests-teams.go
+++ b/go/internal/json-client/requests-teams.go
@@ -4,7 +4,6 @@ import (
"context"
"github.com/pzsp-teams/lib-python/internal/json-client/decoders"
- "github.com/pzsp-teams/lib/models"
)
type getTeamParams struct {
@@ -28,11 +27,8 @@ type updateTeamParams struct {
func (c *TeamsJSONClient) UpdateTeam(p map[string]interface{}) (interface{}, error) {
return execute(p, func(params updateTeamParams) (interface{}, error) {
- updateTeam, err := decoders.DecodeParams[models.TeamUpdate](¶ms.TeamUpdate)
- if err != nil {
- return nil, err
- }
- return c.client.Teams.UpdateTeam(context.TODO(), params.TeamRef, updateTeam)
+ updateTeam := decoders.GetUpdateTeam(¶ms.TeamUpdate)
+ return c.client.Teams.UpdateTeam(context.TODO(), params.TeamRef, &updateTeam)
})
}
diff --git a/python/teams_lib_pzsp2_z1/model/team.py b/python/teams_lib_pzsp2_z1/model/team.py
index 3e332e6..0d17576 100644
--- a/python/teams_lib_pzsp2_z1/model/team.py
+++ b/python/teams_lib_pzsp2_z1/model/team.py
@@ -12,6 +12,6 @@ class Team:
@dataclass
class UpdateTeam:
- DisplayName: str | None = None
- Description: str | None = None
- Visibility: str | None = None
+ displayName: str | None = None
+ description: str | None = None
+ visibility: str | None = None
diff --git a/python/teams_lib_pzsp2_z1/services/channels.py b/python/teams_lib_pzsp2_z1/services/channels.py
index 0f025b4..4c3d790 100644
--- a/python/teams_lib_pzsp2_z1/services/channels.py
+++ b/python/teams_lib_pzsp2_z1/services/channels.py
@@ -8,6 +8,7 @@
from teams_lib_pzsp2_z1.model.message import (
Message,
MessageBody,
+ MessageCollection,
MessageContentType,
MessageFrom,
)
@@ -200,13 +201,15 @@ def send_message(
ReplyCount=response["ReplyCount"],
)
- def list_messages(
+ def list_messages( # noqa: PLR0913
self,
team_ref: str,
channel_ref: str,
top: int | None = 10,
expand_replies: bool = False,
- ) -> list[Message]:
+ include_system_messages: bool = False,
+ next_link: str | None = None,
+ ) -> MessageCollection:
"""Retrieves messages from a channel.
Args:
@@ -214,6 +217,8 @@ def list_messages(
channel_ref (str): The reference to the channel.
top (Optional[int]): The maximum number of messages to retrieve. Defaults to 10.
expand_replies (bool): If True, system fetches replies for each message. Defaults to False.
+ include_system_messages (bool): If True, includes system-generated messages. Defaults to False.
+ next_link (Optional[str]): A link for pagination to fetch the next set of messages.
Returns:
List[Message]: A list of messages from the channel.
@@ -227,24 +232,29 @@ def list_messages(
"options": {
"top": top,
"expandReplies": expand_replies,
+ "includeSystem": include_system_messages,
+ "nextLink": next_link,
},
},
)
- return [
- Message(
- ID=message["ID"],
- Content=message["Content"],
- ContentType=MessageContentType(message["ContentType"]),
- CreatedDateTime=message["CreatedDateTime"],
- From=MessageFrom(
- UserID=message["From"]["UserID"],
- DisplayName=message["From"]["DisplayName"],
- ),
- ReplyCount=message["ReplyCount"],
- )
- for message in response
- ]
+ return MessageCollection(
+ Messages=[
+ Message(
+ ID=message["ID"],
+ Content=message["Content"],
+ ContentType=MessageContentType(message["ContentType"]),
+ CreatedDateTime=message["CreatedDateTime"],
+ From=MessageFrom(
+ UserID=message["From"]["UserID"],
+ DisplayName=message["From"]["DisplayName"],
+ ),
+ ReplyCount=message["ReplyCount"],
+ )
+ for message in response
+ ],
+ NextLink=response["NextLink"],
+ )
def get_message(self, team_ref: str, channel_ref: str, message_id: str) -> Message:
"""Retrieves a specific message by its ID.
diff --git a/python/teams_lib_pzsp2_z1/services/teams.py b/python/teams_lib_pzsp2_z1/services/teams.py
index 47602a4..9c1976c 100644
--- a/python/teams_lib_pzsp2_z1/services/teams.py
+++ b/python/teams_lib_pzsp2_z1/services/teams.py
@@ -1,4 +1,4 @@
-from teams_lib_pzsp2_z1.model.team import MSTeamsUpdate, Team
+from teams_lib_pzsp2_z1.model.team import Team, UpdateTeam
from teams_lib_pzsp2_z1.services.base_service import BaseService
@@ -37,7 +37,7 @@ def list_my_joined(self) -> list[Team]:
for team in response
]
- def update(self, teamRef: str, update: MSTeamsUpdate) -> Team:
+ def update(self, teamRef: str, update: UpdateTeam) -> Team:
params = {"teamRef": teamRef}
update_dict = {k: v for k, v in update.__dict__.items() if v is not None}
params["team"] = update_dict
diff --git a/python/tests/fake_server/setup.py b/python/tests/fake_server/setup.py
index 9edbe3d..5b5579c 100644
--- a/python/tests/fake_server/setup.py
+++ b/python/tests/fake_server/setup.py
@@ -326,6 +326,19 @@ def handle_chats_request(req):
"Update Team (PATCH)"
))
+ # PATCH /groups/{id}
+ httpserver.expect_request(
+ re.compile(r"^/v1.0/groups/([^/]+)"),
+ method="PATCH"
+ ).respond_with_handler(lambda req: make_log_response(
+ req,
+ data.get_updateTeam_response(
+ re.search(r"/groups/([^/]+)", req.path).group(1),
+ req.json
+ ),
+ "Update Team (PATCH)"
+ ))
+
# GET /teams/{id}
httpserver.expect_request(
re.compile(r"^/v1.0/teams/([^/]+)(?:$|\?)"),
@@ -527,11 +540,15 @@ def handle_chats_request(req):
# Fallback for unmatched routes
httpserver.expect_request(re.compile(".*")).respond_with_handler(
- lambda req: Response(
- json.dumps({"error": f"Route not matched in Mock: {req.method} {req.path}"}),
- status=404,
- mimetype="application/json"
- )
+ lambda req: (
+ print(f"[MOCK SERVER] ⚠️ UNMATCHED: {req.method} {req.path}", file=sys.stdout),
+ print(f"[MOCK SERVER] Body: {req.get_data(as_text=True)}", file=sys.stdout),
+ Response(
+ json.dumps({"error": f"Route not matched in Mock: {req.method} {req.path}"}),
+ status=404,
+ mimetype="application/json"
+ )
+ )[-1]
)
return data
\ No newline at end of file
diff --git a/python/tests/test_channels.py b/python/tests/test_channels.py
index 89d4f66..979d81f 100644
--- a/python/tests/test_channels.py
+++ b/python/tests/test_channels.py
@@ -155,28 +155,27 @@ def test_list_messages_integration(httpserver):
try:
init_fake_client(client, httpserver.url_for(""))
- messages = client.channels.list_messages(
+ collection = client.channels.list_messages(
team_ref=data.teams[0].DisplayName,
channel_ref=data.channels[data.teams[0].ID][0].Name,
)
- assert len(messages) == len(data.messages[data.channels[data.teams[0].ID][0].ID])
- assert messages[0].ID == data.messages[data.channels[data.teams[0].ID][0].ID][0].ID
- assert messages[0].Content == data.messages[data.channels[data.teams[0].ID][0].ID][0].Content
- assert messages[0].ContentType == MessageContentType(data.messages[data.channels[data.teams[0].ID][0].ID][0].ContentType)
- assert messages[0].From.UserID == data.messages[data.channels[data.teams[0].ID][0].ID][0].From.UserID
- assert messages[0].From.DisplayName == data.messages[data.channels[data.teams[0].ID][0].ID][0].From.DisplayName
- assert messages[0].ReplyCount == data.messages[data.channels[data.teams[0].ID][0].ID][0].ReplyCount
- assert messages[0].CreatedDateTime == data.messages[data.channels[data.teams[0].ID][0].ID][0].CreatedDateTime
-
- assert messages[1].ID == data.messages[data.channels[data.teams[0].ID][0].ID][1].ID
- assert messages[1].Content == data.messages[data.channels[data.teams[0].ID][0].ID][1].Content
- assert messages[1].ContentType == MessageContentType(data.messages[data.channels[data.teams[0].ID][0].ID][1].ContentType)
- assert messages[1].From.UserID == data.messages[data.channels[data.teams[0].ID][0].ID][1].From.UserID
- assert messages[1].From.DisplayName == data.messages[data.channels[data.teams[0].ID][0].ID][1].From.DisplayName
- assert messages[1].ReplyCount == data.messages[data.channels[data.teams[0].ID][0].ID][1].ReplyCount
- assert messages[1].CreatedDateTime == data.messages[data.channels[data.teams[0].ID][0].ID][1].CreatedDateTime
-
+ assert len(collection) == len(data.messages[data.channels[data.teams[0].ID][0].ID])
+ assert collection.Messages[0].ID == data.messages[data.channels[data.teams[0].ID][0].ID][0].ID
+ assert collection.Messages[0].Content == data.messages[data.channels[data.teams[0].ID][0].ID][0].Content
+ assert collection.Messages[0].ContentType == MessageContentType(data.messages[data.channels[data.teams[0].ID][0].ID][0].ContentType)
+ assert collection.Messages[0].From.UserID == data.messages[data.channels[data.teams[0].ID][0].ID][0].From.UserID
+ assert collection.Messages[0].From.DisplayName == data.messages[data.channels[data.teams[0].ID][0].ID][0].From.DisplayName
+ assert collection.Messages[0].ReplyCount == data.messages[data.channels[data.teams[0].ID][0].ID][0].ReplyCount
+ assert collection.Messages[0].CreatedDateTime == data.messages[data.channels[data.teams[0].ID][0].ID][0].CreatedDateTime
+
+ assert collection.Messages[1].ID == data.messages[data.channels[data.teams[0].ID][0].ID][1].ID
+ assert collection.Messages[1].Content == data.messages[data.channels[data.teams[0].ID][0].ID][1].Content
+ assert collection.Messages[1].ContentType == MessageContentType(data.messages[data.channels[data.teams[0].ID][0].ID][1].ContentType)
+ assert collection.Messages[1].From.UserID == data.messages[data.channels[data.teams[0].ID][0].ID][1].From.UserID
+ assert collection.Messages[1].From.DisplayName == data.messages[data.channels[data.teams[0].ID][0].ID][1].From.DisplayName
+ assert collection.Messages[1].ReplyCount == data.messages[data.channels[data.teams[0].ID][0].ID][1].ReplyCount
+ assert collection.Messages[1].CreatedDateTime == data.messages[data.channels[data.teams[0].ID][0].ID][1].CreatedDateTime
finally:
client.close()
diff --git a/python/tests/test_teams.py b/python/tests/test_teams.py
index a234c9f..9ab7d75 100644
--- a/python/tests/test_teams.py
+++ b/python/tests/test_teams.py
@@ -1,4 +1,4 @@
-from teams_lib_pzsp2_z1.model.team import MSTeamsUpdate
+from teams_lib_pzsp2_z1.model.team import UpdateTeam
from teams_lib_pzsp2_z1.client import TeamsClient
from tests.init_fake_client import init_fake_client
from tests.fake_server.setup import setup_fake_server
@@ -68,8 +68,8 @@ def test_update_team_integration(httpserver):
updated_description = "Updated team description"
team = client.teams.update(
teamRef=data.teams[0].DisplayName,
- update=MSTeamsUpdate(
- Description=updated_description,
+ update=UpdateTeam(
+ description=updated_description,
),
)
From 5d0e60b8c92481b12b1d1fed836f6aa337b3ea84 Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Wed, 7 Jan 2026 22:54:54 +0100
Subject: [PATCH 61/99] chore: adjusted list channels messages
---
.../json-client/decoders/decode-nextLink.go | 12 +++++++++++
go/internal/json-client/requests-channels.go | 20 ++++++++++--------
go/internal/json-client/requests-chats.go | 9 ++++----
.../teams_lib_pzsp2_z1/services/channels.py | 21 ++++++++++---------
python/tests/test_channels.py | 2 +-
5 files changed, 40 insertions(+), 24 deletions(-)
create mode 100644 go/internal/json-client/decoders/decode-nextLink.go
diff --git a/go/internal/json-client/decoders/decode-nextLink.go b/go/internal/json-client/decoders/decode-nextLink.go
new file mode 100644
index 0000000..584689d
--- /dev/null
+++ b/go/internal/json-client/decoders/decode-nextLink.go
@@ -0,0 +1,12 @@
+package decoders
+
+type NextLinkDTO struct {
+ NextLink string `json:"nextLink"`
+}
+
+func GetNextLink(dto *NextLinkDTO) *string {
+ if dto.NextLink == "" {
+ return nil
+ }
+ return &dto.NextLink
+}
\ No newline at end of file
diff --git a/go/internal/json-client/requests-channels.go b/go/internal/json-client/requests-channels.go
index e3c8473..81b4f2c 100644
--- a/go/internal/json-client/requests-channels.go
+++ b/go/internal/json-client/requests-channels.go
@@ -102,7 +102,7 @@ type listMessagesParams struct {
ChannelRef string `json:"channelRef"`
Options listMessagesOptionsDTO `json:"options"`
IncludeSystem bool `json:"includeSystem"`
- NextLink string `json:"nextLink"`
+ NextLink decoders.NextLinkDTO `json:"nextLink"`
}
func (c *TeamsJSONClient) ListMessagesInChannel(p map[string]interface{}) (interface{}, error) {
@@ -111,7 +111,8 @@ func (c *TeamsJSONClient) ListMessagesInChannel(p map[string]interface{}) (inter
if err != nil {
return nil, err
}
- return c.client.Channels.ListMessages(context.TODO(), params.TeamRef, params.ChannelRef, options, params.IncludeSystem, ¶ms.NextLink)
+ nextLink := decoders.GetNextLink(¶ms.NextLink)
+ return c.client.Channels.ListMessages(context.TODO(), params.TeamRef, params.ChannelRef, options, params.IncludeSystem, nextLink)
})
}
@@ -128,17 +129,18 @@ func (c *TeamsJSONClient) GetMessageInChannel(p map[string]interface{}) (interfa
}
type listRepliesParams struct {
- TeamRef string `json:"teamRef"`
- ChannelRef string `json:"channelRef"`
- MessageID string `json:"messageId"`
- Top *int32 `json:"top"`
- IncludeSystem bool `json:"includeSystem"`
- NextLink string `json:"nextLink"`
+ TeamRef string `json:"teamRef"`
+ ChannelRef string `json:"channelRef"`
+ MessageID string `json:"messageId"`
+ Top *int32 `json:"top"`
+ IncludeSystem bool `json:"includeSystem"`
+ NextLink decoders.NextLinkDTO `json:"nextLink"`
}
func (c *TeamsJSONClient) ListMessageRepliesInChannel(p map[string]interface{}) (interface{}, error) {
return execute(p, func(params listRepliesParams) (interface{}, error) {
- return c.client.Channels.ListReplies(context.TODO(), params.TeamRef, params.ChannelRef, params.MessageID, params.Top, params.IncludeSystem, ¶ms.NextLink)
+ nextLink := decoders.GetNextLink(¶ms.NextLink)
+ return c.client.Channels.ListReplies(context.TODO(), params.TeamRef, params.ChannelRef, params.MessageID, params.Top, params.IncludeSystem, nextLink)
})
}
diff --git a/go/internal/json-client/requests-chats.go b/go/internal/json-client/requests-chats.go
index cab3135..9a9241b 100644
--- a/go/internal/json-client/requests-chats.go
+++ b/go/internal/json-client/requests-chats.go
@@ -70,9 +70,9 @@ func (c *TeamsJSONClient) UpdateGroupChatTopic(p map[string]interface{}) (interf
}
type listMessagesInChatParams struct {
- ChatRef decoders.ChatRefDTO `json:"chatRef"`
- IncludeSystem bool `json:"includeSystem"`
- NextLink string `json:"nextLink"`
+ ChatRef decoders.ChatRefDTO `json:"chatRef"`
+ IncludeSystem bool `json:"includeSystem"`
+ NextLink decoders.NextLinkDTO `json:"nextLink"`
}
func (c *TeamsJSONClient) ListMessagesInChat(p map[string]interface{}) (interface{}, error) {
@@ -81,7 +81,8 @@ func (c *TeamsJSONClient) ListMessagesInChat(p map[string]interface{}) (interfac
if err != nil {
return nil, err
}
- return c.client.Chats.ListMessages(context.TODO(), chatRef, params.IncludeSystem, ¶ms.NextLink)
+ nextLink := decoders.GetNextLink(¶ms.NextLink)
+ return c.client.Chats.ListMessages(context.TODO(), chatRef, params.IncludeSystem, nextLink)
})
}
diff --git a/python/teams_lib_pzsp2_z1/services/channels.py b/python/teams_lib_pzsp2_z1/services/channels.py
index 4c3d790..a82bd47 100644
--- a/python/teams_lib_pzsp2_z1/services/channels.py
+++ b/python/teams_lib_pzsp2_z1/services/channels.py
@@ -239,23 +239,24 @@ def list_messages( # noqa: PLR0913
)
return MessageCollection(
- Messages=[
+ Messages=[
Message(
- ID=message["ID"],
- Content=message["Content"],
- ContentType=MessageContentType(message["ContentType"]),
- CreatedDateTime=message["CreatedDateTime"],
+ ID=msg["ID"],
+ Content=msg["Content"],
+ ContentType=MessageContentType(msg["ContentType"]),
+ CreatedDateTime=msg["CreatedDateTime"],
From=MessageFrom(
- UserID=message["From"]["UserID"],
- DisplayName=message["From"]["DisplayName"],
+ UserID=msg["From"]["UserID"],
+ DisplayName=msg["From"]["DisplayName"],
),
- ReplyCount=message["ReplyCount"],
+ ReplyCount=msg["ReplyCount"],
)
- for message in response
+ for msg in response["Messages"]
],
- NextLink=response["NextLink"],
+ NextLink=response.get("NextLink"),
)
+
def get_message(self, team_ref: str, channel_ref: str, message_id: str) -> Message:
"""Retrieves a specific message by its ID.
diff --git a/python/tests/test_channels.py b/python/tests/test_channels.py
index 979d81f..ccb8047 100644
--- a/python/tests/test_channels.py
+++ b/python/tests/test_channels.py
@@ -160,7 +160,7 @@ def test_list_messages_integration(httpserver):
channel_ref=data.channels[data.teams[0].ID][0].Name,
)
- assert len(collection) == len(data.messages[data.channels[data.teams[0].ID][0].ID])
+ assert len(collection.Messages) == len(data.messages[data.channels[data.teams[0].ID][0].ID])
assert collection.Messages[0].ID == data.messages[data.channels[data.teams[0].ID][0].ID][0].ID
assert collection.Messages[0].Content == data.messages[data.channels[data.teams[0].ID][0].ID][0].Content
assert collection.Messages[0].ContentType == MessageContentType(data.messages[data.channels[data.teams[0].ID][0].ID][0].ContentType)
From 712e3dff75db2dab58c4ee83ad6b3922baddf772 Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Wed, 7 Jan 2026 22:59:57 +0100
Subject: [PATCH 62/99] chore: adjusted list replies in channels
---
.../teams_lib_pzsp2_z1/services/channels.py | 46 +++++++++++--------
python/tests/test_channels.py | 28 +++++------
2 files changed, 41 insertions(+), 33 deletions(-)
diff --git a/python/teams_lib_pzsp2_z1/services/channels.py b/python/teams_lib_pzsp2_z1/services/channels.py
index a82bd47..0af5dc0 100644
--- a/python/teams_lib_pzsp2_z1/services/channels.py
+++ b/python/teams_lib_pzsp2_z1/services/channels.py
@@ -221,7 +221,7 @@ def list_messages( # noqa: PLR0913
next_link (Optional[str]): A link for pagination to fetch the next set of messages.
Returns:
- List[Message]: A list of messages from the channel.
+ MessageCollection: An object containing the list of messages and a next link for pagination.
"""
response = self.client.execute(
cmd_type="request",
@@ -256,7 +256,6 @@ def list_messages( # noqa: PLR0913
NextLink=response.get("NextLink"),
)
-
def get_message(self, team_ref: str, channel_ref: str, message_id: str) -> Message:
"""Retrieves a specific message by its ID.
@@ -290,13 +289,15 @@ def get_message(self, team_ref: str, channel_ref: str, message_id: str) -> Messa
ReplyCount=response["ReplyCount"],
)
- def list_message_replies(
+ def list_message_replies( # noqa: PLR0913
self,
team_ref: str,
channel_ref: str,
message_id: str,
top: int | None = 10,
- ) -> list[Message]:
+ include_system_messages: bool = False,
+ next_link: str | None = None,
+ ) -> MessageCollection:
"""Retrieves all replies to a specific message thread.
Args:
@@ -304,9 +305,11 @@ def list_message_replies(
channel_ref (str): The reference to the channel.
message_id (str): The ID of the parent message.
top (Optional[int]): Max number of replies to fetch. Defaults to 10.
+ include_system_messages (bool): If True, includes system-generated messages. Defaults to False.
+ next_link (Optional[str]): A link for pagination to fetch the next set of replies.
Returns:
- List[Message]: A list of replies.
+ MessageCollection: An object containing the list of replies and a next link for pagination.
"""
response = self.client.execute(
cmd_type="request",
@@ -316,23 +319,28 @@ def list_message_replies(
"channelRef": channel_ref,
"messageID": message_id,
"top": top,
+ "includeSystem": include_system_messages,
+ "nextLink": next_link,
},
)
- return [
- Message(
- ID=message["ID"],
- Content=message["Content"],
- ContentType=MessageContentType(message["ContentType"]),
- CreatedDateTime=message["CreatedDateTime"],
- From=MessageFrom(
- UserID=message["From"]["UserID"],
- DisplayName=message["From"]["DisplayName"],
- ),
- ReplyCount=message["ReplyCount"],
- )
- for message in response
- ]
+ return MessageCollection(
+ Messages=[
+ Message(
+ ID=msg["ID"],
+ Content=msg["Content"],
+ ContentType=MessageContentType(msg["ContentType"]),
+ CreatedDateTime=msg["CreatedDateTime"],
+ From=MessageFrom(
+ UserID=msg["From"]["UserID"],
+ DisplayName=msg["From"]["DisplayName"],
+ ),
+ ReplyCount=msg["ReplyCount"],
+ )
+ for msg in response["Messages"]
+ ],
+ NextLink=response.get("NextLink"),
+ )
def get_message_reply(
self,
diff --git a/python/tests/test_channels.py b/python/tests/test_channels.py
index ccb8047..5eb8831 100644
--- a/python/tests/test_channels.py
+++ b/python/tests/test_channels.py
@@ -220,20 +220,20 @@ def test_list_replies_integration(httpserver):
message_id=data.messages[data.channels[data.teams[0].ID][0].ID][1].ID,
)
- assert len(replies) == len(data.replies[data.messages[data.channels[data.teams[0].ID][0].ID][1].ID])
- assert replies[0].ID == data.replies[data.messages[data.channels[data.teams[0].ID][0].ID][1].ID][0].ID
- assert replies[0].Content == data.replies[data.messages[data.channels[data.teams[0].ID][0].ID][1].ID][0].Content
- assert replies[0].ContentType == MessageContentType(data.replies[data.messages[data.channels[data.teams[0].ID][0].ID][1].ID][0].ContentType)
- assert replies[0].From.UserID == data.replies[data.messages[data.channels[data.teams[0].ID][0].ID][1].ID][0].From.UserID
- assert replies[0].From.DisplayName == data.replies[data.messages[data.channels[data.teams[0].ID][0].ID][1].ID][0].From.DisplayName
- assert replies[0].CreatedDateTime == data.replies[data.messages[data.channels[data.teams[0].ID][0].ID][1].ID][0].CreatedDateTime
-
- assert replies[1].ID == data.replies[data.messages[data.channels[data.teams[0].ID][0].ID][1].ID][1].ID
- assert replies[1].Content == data.replies[data.messages[data.channels[data.teams[0].ID][0].ID][1].ID][1].Content
- assert replies[1].ContentType == MessageContentType(data.replies[data.messages[data.channels[data.teams[0].ID][0].ID][1].ID][1].ContentType)
- assert replies[1].From.UserID == data.replies[data.messages[data.channels[data.teams[0].ID][0].ID][1].ID][1].From.UserID
- assert replies[1].From.DisplayName == data.replies[data.messages[data.channels[data.teams[0].ID][0].ID][1].ID][1].From.DisplayName
- assert replies[1].CreatedDateTime == data.replies[data.messages[data.channels[data.teams[0].ID][0].ID][1].ID][1].CreatedDateTime
+ assert len(replies.Messages) == len(data.replies[data.messages[data.channels[data.teams[0].ID][0].ID][1].ID])
+ assert replies.Messages[0].ID == data.replies[data.messages[data.channels[data.teams[0].ID][0].ID][1].ID][0].ID
+ assert replies.Messages[0].Content == data.replies[data.messages[data.channels[data.teams[0].ID][0].ID][1].ID][0].Content
+ assert replies.Messages[0].ContentType == MessageContentType(data.replies[data.messages[data.channels[data.teams[0].ID][0].ID][1].ID][0].ContentType)
+ assert replies.Messages[0].From.UserID == data.replies[data.messages[data.channels[data.teams[0].ID][0].ID][1].ID][0].From.UserID
+ assert replies.Messages[0].From.DisplayName == data.replies[data.messages[data.channels[data.teams[0].ID][0].ID][1].ID][0].From.DisplayName
+ assert replies.Messages[0].CreatedDateTime == data.replies[data.messages[data.channels[data.teams[0].ID][0].ID][1].ID][0].CreatedDateTime
+
+ assert replies.Messages[1].ID == data.replies[data.messages[data.channels[data.teams[0].ID][0].ID][1].ID][1].ID
+ assert replies.Messages[1].Content == data.replies[data.messages[data.channels[data.teams[0].ID][0].ID][1].ID][1].Content
+ assert replies.Messages[1].ContentType == MessageContentType(data.replies[data.messages[data.channels[data.teams[0].ID][0].ID][1].ID][1].ContentType)
+ assert replies.Messages[1].From.UserID == data.replies[data.messages[data.channels[data.teams[0].ID][0].ID][1].ID][1].From.UserID
+ assert replies.Messages[1].From.DisplayName == data.replies[data.messages[data.channels[data.teams[0].ID][0].ID][1].ID][1].From.DisplayName
+ assert replies.Messages[1].CreatedDateTime == data.replies[data.messages[data.channels[data.teams[0].ID][0].ID][1].ID][1].CreatedDateTime
finally:
client.close()
From b3d9bea4034af1be30e517e8065d990bc61567c5 Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Wed, 7 Jan 2026 23:05:27 +0100
Subject: [PATCH 63/99] chore: adjusted list messages in chats
---
python/teams_lib_pzsp2_z1/services/chats.py | 43 ++++++++++++++-------
python/tests/test_chats.py | 36 ++++++++---------
2 files changed, 46 insertions(+), 33 deletions(-)
diff --git a/python/teams_lib_pzsp2_z1/services/chats.py b/python/teams_lib_pzsp2_z1/services/chats.py
index 86e53a1..11f6c07 100644
--- a/python/teams_lib_pzsp2_z1/services/chats.py
+++ b/python/teams_lib_pzsp2_z1/services/chats.py
@@ -10,6 +10,7 @@
from teams_lib_pzsp2_z1.model.message import (
Message,
MessageBody,
+ MessageCollection,
MessageContentType,
MessageFrom,
)
@@ -194,11 +195,18 @@ def update_group_chat_topic(self, group_chat_ref: str, new_topic: str) -> Chat:
Topic=response["Topic"],
)
- def list_messages(self, chat_ref: ChatRef) -> list[Message]:
+ def list_messages(
+ self,
+ chat_ref: ChatRef,
+ include_system_messages: bool = False,
+ next_link: str | None = None,
+ ) -> list[Message]:
"""Retrieves messages from a specific chat.
Args:
chat_ref (ChatRef): The chat reference object containing the ID/Name and ChatType.
+ include_system_messages (bool): If True, includes system-generated messages. Defaults to False.
+ next_link (str | None): A link for pagination to fetch the next set of messages. Defaults to None.
Returns:
list[Message]: A list of messages from the chat history.
@@ -211,23 +219,28 @@ def list_messages(self, chat_ref: ChatRef) -> list[Message]:
"ref": chat_ref.Ref,
"type": chat_ref.Type.value,
},
+ "includeSystem": include_system_messages,
+ "nextLink": next_link,
},
)
- return [
- Message(
- ID=message["ID"],
- Content=message["Content"],
- ContentType=MessageContentType(message["ContentType"]),
- From=MessageFrom(
- UserID=message["From"]["UserID"],
- DisplayName=message["From"]["DisplayName"],
- ),
- CreatedDateTime=message["CreatedDateTime"],
- ReplyCount=message["ReplyCount"],
- )
- for message in messages
- ]
+ return MessageCollection(
+ Messages=[
+ Message(
+ ID=msg["ID"],
+ Content=msg["Content"],
+ ContentType=MessageContentType(msg["ContentType"]),
+ CreatedDateTime=msg["CreatedDateTime"],
+ From=MessageFrom(
+ UserID=msg["From"]["UserID"],
+ DisplayName=msg["From"]["DisplayName"],
+ ),
+ ReplyCount=msg["ReplyCount"],
+ )
+ for msg in messages["Messages"]
+ ],
+ NextLink=messages.get("NextLink"),
+ )
def send_message(self, chat_ref: ChatRef, body: MessageBody) -> Message:
"""Sends a message to a chat.
diff --git a/python/tests/test_chats.py b/python/tests/test_chats.py
index 8966601..2ef176c 100644
--- a/python/tests/test_chats.py
+++ b/python/tests/test_chats.py
@@ -202,30 +202,30 @@ def test_list_messeges_in_chat_integration(httpserver):
try:
init_fake_client(client, httpserver.url_for(""))
- messages = client.chats.list_messages(
+ collection = client.chats.list_messages(
chat_ref=ChatRef(
Ref=data.group_chats[0].Topic,
Type=ChatType.GROUP,
)
)
- assert len(messages) == len(data.chat_messages[data.group_chats[0].ID])
-
- assert messages[0].ID == data.chat_messages[data.group_chats[0].ID][0].ID
- assert messages[0].Content == data.chat_messages[data.group_chats[0].ID][0].Content
- assert messages[0].ContentType == MessageContentType(data.chat_messages[data.group_chats[0].ID][0].ContentType)
- assert messages[0].From.UserID == data.chat_messages[data.group_chats[0].ID][0].From.UserID
- assert messages[0].From.DisplayName == data.chat_messages[data.group_chats[0].ID][0].From.DisplayName
- assert messages[0].ReplyCount == data.chat_messages[data.group_chats[0].ID][0].ReplyCount
- assert messages[0].CreatedDateTime == data.chat_messages[data.group_chats[0].ID][0].CreatedDateTime
-
- assert messages[1].ID == data.chat_messages[data.group_chats[0].ID][1].ID
- assert messages[1].Content == data.chat_messages[data.group_chats[0].ID][1].Content
- assert messages[1].ContentType == MessageContentType(data.chat_messages[data.group_chats[0].ID][1].ContentType)
- assert messages[1].From.UserID == data.chat_messages[data.group_chats[0].ID][1].From.UserID
- assert messages[1].From.DisplayName == data.chat_messages[data.group_chats[0].ID][1].From.DisplayName
- assert messages[1].ReplyCount == data.chat_messages[data.group_chats[0].ID][1].ReplyCount
- assert messages[1].CreatedDateTime == data.chat_messages[data.group_chats[0].ID][1].CreatedDateTime
+ assert len(collection.Messages) == len(data.chat_messages[data.group_chats[0].ID])
+
+ assert collection.Messages[0].ID == data.chat_messages[data.group_chats[0].ID][0].ID
+ assert collection.Messages[0].Content == data.chat_messages[data.group_chats[0].ID][0].Content
+ assert collection.Messages[0].ContentType == MessageContentType(data.chat_messages[data.group_chats[0].ID][0].ContentType)
+ assert collection.Messages[0].From.UserID == data.chat_messages[data.group_chats[0].ID][0].From.UserID
+ assert collection.Messages[0].From.DisplayName == data.chat_messages[data.group_chats[0].ID][0].From.DisplayName
+ assert collection.Messages[0].ReplyCount == data.chat_messages[data.group_chats[0].ID][0].ReplyCount
+ assert collection.Messages[0].CreatedDateTime == data.chat_messages[data.group_chats[0].ID][0].CreatedDateTime
+
+ assert collection.Messages[1].ID == data.chat_messages[data.group_chats[0].ID][1].ID
+ assert collection.Messages[1].Content == data.chat_messages[data.group_chats[0].ID][1].Content
+ assert collection.Messages[1].ContentType == MessageContentType(data.chat_messages[data.group_chats[0].ID][1].ContentType)
+ assert collection.Messages[1].From.UserID == data.chat_messages[data.group_chats[0].ID][1].From.UserID
+ assert collection.Messages[1].From.DisplayName == data.chat_messages[data.group_chats[0].ID][1].From.DisplayName
+ assert collection.Messages[1].ReplyCount == data.chat_messages[data.group_chats[0].ID][1].ReplyCount
+ assert collection.Messages[1].CreatedDateTime == data.chat_messages[data.group_chats[0].ID][1].CreatedDateTime
finally:
client.close()
From 6ec5db7bfbee09cf66e0dcce0fe299ea83eca6c8 Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Wed, 7 Jan 2026 23:42:32 +0100
Subject: [PATCH 64/99] chore: adjusted create team from template to lib
changes
---
python/teams_lib_pzsp2_z1/services/teams.py | 8 +++++++-
python/tests/fake_server/data.py | 18 +++++++++++++++---
python/tests/fake_server/setup.py | 17 +++++++++++++++--
python/tests/test_teams.py | 4 ++--
4 files changed, 39 insertions(+), 8 deletions(-)
diff --git a/python/teams_lib_pzsp2_z1/services/teams.py b/python/teams_lib_pzsp2_z1/services/teams.py
index 9c1976c..830b248 100644
--- a/python/teams_lib_pzsp2_z1/services/teams.py
+++ b/python/teams_lib_pzsp2_z1/services/teams.py
@@ -75,11 +75,14 @@ def create_via_group(
Visibility=response["Visibility"],
)
- def create_from_template(
+ def create_from_template( # noqa: PLR0913
self,
display_name: str,
description: str,
owners: list[str],
+ members: list[str] | None = None,
+ visibility: str = "public",
+ include_me: bool = True,
) -> str:
response = self.client.execute(
cmd_type="request",
@@ -88,6 +91,9 @@ def create_from_template(
"displayName": display_name,
"description": description,
"owners": owners,
+ "members": members,
+ "visibility": visibility,
+ "includeMe": include_me,
},
)
return response
diff --git a/python/tests/fake_server/data.py b/python/tests/fake_server/data.py
index 525b4fe..733aa0b 100644
--- a/python/tests/fake_server/data.py
+++ b/python/tests/fake_server/data.py
@@ -134,6 +134,7 @@ def __init__(self) -> None:
self.newTeamName = "New Team"
self.newGroupMailNickname = "new-team-nickname"
self.newTeamVisibility = "private"
+ self.newTeamID = "f47ac10b-58cc-4372-a567-0e02b2c3d479"
# Init Templates (re-assigning to ensure freshness if needed, though default_factory handles it)
self.newChannelName = "New Channel"
@@ -254,9 +255,8 @@ def get_createTeamViaGroup_response(self, group_id: str) -> dict:
}
def get_createTeamFromTemplate_response(self, request_json: dict) -> dict:
- new_team_id = "team-from-template-001"
new_team = Team(
- ID=new_team_id,
+ ID=self.newTeamID,
DisplayName=request_json.get("displayName"),
Description=request_json.get("description"),
IsArchived=False,
@@ -264,7 +264,19 @@ def get_createTeamFromTemplate_response(self, request_json: dict) -> dict:
)
self.teams.append(new_team)
- return {"id": new_team_id}
+ # self.newTeamID == "f47ac10b-58cc-4372-a567-0e02b2c3d479"
+ op_id = "00000000-0000-0000-0000-000000000000"
+
+ return {
+ "status": 202,
+ "headers": {
+ "Location": f"/teams('{self.newTeamID}')/operations('{op_id}')",
+ "Content-Location": f"/teams('{self.newTeamID}')",
+ "Content-Type": "application/json",
+ "Content-Length": "0"
+ },
+ "body": ""
+ }
def get_archiveTeam_response(self, team_id: str) -> dict:
team = self._find_team(team_id)
diff --git a/python/tests/fake_server/setup.py b/python/tests/fake_server/setup.py
index 5b5579c..4e6033b 100644
--- a/python/tests/fake_server/setup.py
+++ b/python/tests/fake_server/setup.py
@@ -353,10 +353,23 @@ def handle_chats_request(req):
httpserver.expect_request(
"/v1.0/teams",
method="POST"
+ ).respond_with_handler(lambda req: Response(
+ response=data.get_createTeamFromTemplate_response(req.json)["body"],
+ status=data.get_createTeamFromTemplate_response(req.json)["status"],
+ headers=data.get_createTeamFromTemplate_response(req.json)["headers"]
+ ))
+
+ # POST /teams/{team_id}/members
+ httpserver.expect_request(
+ re.compile(r"^/v1.0/teams/([^/]+)/members"),
+ method="POST"
).respond_with_handler(lambda req: make_log_response(
req,
- data.get_createTeamFromTemplate_response(req.json),
- "Create Team from Template (POST)"
+ data.get_add_member_response(
+ re.search(r"/teams/([^/]+)/members", req.path).group(1),
+ req.json
+ ),
+ "Add Team Member"
))
# ==========================================
diff --git a/python/tests/test_teams.py b/python/tests/test_teams.py
index 9ab7d75..9fd61ca 100644
--- a/python/tests/test_teams.py
+++ b/python/tests/test_teams.py
@@ -113,13 +113,13 @@ def test_create_team_from_template_integration(httpserver):
try:
init_fake_client(client, httpserver.url_for(""))
- msg = client.teams.create_from_template(
+ id = client.teams.create_from_template(
display_name=data.newTeamName,
description="A team created from a template",
owners=["user-123-abc"],
)
- assert msg == "id will be given later (async)"
+ assert id == data.newTeamID
finally:
client.close()
From cf8e8129b676c12acc392a80c244a2caa5185613 Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Thu, 8 Jan 2026 10:27:40 +0100
Subject: [PATCH 65/99] docs: added docstrings for team service
---
python/teams_lib_pzsp2_z1/services/teams.py | 119 ++++++++++++++++++--
python/tests/test_teams.py | 8 +-
2 files changed, 113 insertions(+), 14 deletions(-)
diff --git a/python/teams_lib_pzsp2_z1/services/teams.py b/python/teams_lib_pzsp2_z1/services/teams.py
index 830b248..e04f738 100644
--- a/python/teams_lib_pzsp2_z1/services/teams.py
+++ b/python/teams_lib_pzsp2_z1/services/teams.py
@@ -1,14 +1,39 @@
+"""
+This module contains the service for managing Microsoft Teams via the Go backend.
+"""
+
from teams_lib_pzsp2_z1.model.team import Team, UpdateTeam
from teams_lib_pzsp2_z1.services.base_service import BaseService
class TeamsService(BaseService):
- def get(self, teamRef: str) -> Team:
+ """Service for managing Microsoft Teams lifecycle and properties via the Go backend.
+
+ This class acts as a high-level Python wrapper. It delegates logical operations
+ to the underlying Go library.
+
+ **Concepts:**
+ * **References**: The `teamRef` argument accepts either the Team UUID or the
+ Team Display Name. The Go backend resolves these automatically.
+ * **Context**: Operations are executed on behalf of the authenticated user
+ (derived from the MSAL context).
+ * **Caching**: If the Go client was initialized with cache enabled, this service
+ will transparently use cached team references to reduce API calls.
+ """
+
+ def get(self, team_ref: str) -> Team:
+ """Retrieves a specific team by its reference.
+
+ Args:
+ team_ref (str): The reference to the team (UUID or Display Name).
+ Returns:
+ Team: The requested team object containing details like ID, DisplayName, and Visibility.
+ """
response = self.client.execute(
cmd_type="request",
method="getTeam",
params={
- "teamRef": teamRef,
+ "teamRef": team_ref,
},
)
@@ -21,6 +46,11 @@ def get(self, teamRef: str) -> Team:
)
def list_my_joined(self) -> list[Team]:
+ """Lists all teams that the authenticated user has joined.
+
+ Returns:
+ list[Team]: A list of team objects the user is a member of.
+ """
response = self.client.execute(
cmd_type="request",
method="listMyJoined",
@@ -37,8 +67,20 @@ def list_my_joined(self) -> list[Team]:
for team in response
]
- def update(self, teamRef: str, update: UpdateTeam) -> Team:
- params = {"teamRef": teamRef}
+ def update(self, team_ref: str, update: UpdateTeam) -> Team:
+ """Applies updates to a team's properties.
+
+ Only fields that are not None in the `update` object will be modified.
+
+ Args:
+ team_ref (str): The reference to the team (UUID or Display Name).
+ update (UpdateTeam): An object containing the fields to update (e.g., DisplayName).
+
+ Returns:
+ Team: The updated team object.
+ """
+ params = {"teamRef": team_ref}
+ # Filter out None values to perform a PATCH-like update
update_dict = {k: v for k, v in update.__dict__.items() if v is not None}
params["team"] = update_dict
@@ -58,6 +100,16 @@ def update(self, teamRef: str, update: UpdateTeam) -> Team:
def create_via_group(
self, display_name: str, mail_nickname: str, visibility: str
) -> Team:
+ """Creates a new team associated with a standard Microsoft 365 group.
+
+ Args:
+ display_name (str): The name of the new team.
+ mail_nickname (str): The mail alias for the underlying group.
+ visibility (str): The privacy level ('Public' or 'Private').
+
+ Returns:
+ Team: The newly created team object.
+ """
response = self.client.execute(
cmd_type="request",
method="createTeamViaGroup",
@@ -84,6 +136,21 @@ def create_from_template( # noqa: PLR0913
visibility: str = "public",
include_me: bool = True,
) -> str:
+ """Creates a new team based on a template.
+
+ This operation is often asynchronous.
+
+ Args:
+ display_name (str): The name of the team.
+ description (str): A brief description of the team.
+ owners (list[str]): A list of user references (IDs or emails) to be owners.
+ members (list[str] | None, optional): A list of user references to be members. Defaults to None.
+ visibility (str, optional): The privacy level ('public' or 'private'). Defaults to "public".
+ include_me (bool, optional): Whether to add the authenticated user as an owner. Defaults to True.
+
+ Returns:
+ str: The ID of the newly created team (or the async operation ID).
+ """
response = self.client.execute(
cmd_type="request",
method="createTeamFromTemplate",
@@ -98,38 +165,70 @@ def create_from_template( # noqa: PLR0913
)
return response
- def archive(self, teamRef: str, spo_read_only_from_members: bool) -> bool:
+ def archive(self, team_ref: str, spo_read_only_from_members: bool) -> bool:
+ """Archives a team.
+
+ Args:
+ team_ref (str): The reference to the team (UUID or Display Name).
+ spo_read_only_from_members (bool): If True, sets the associated SharePoint site
+ to read-only for members.
+
+ Returns:
+ bool: True if the team was successfully archived.
+ """
response = self.client.execute(
cmd_type="request",
method="archiveTeam",
params={
- "teamRef": teamRef,
+ "teamRef": team_ref,
"spoReadOnlyFromMembers": spo_read_only_from_members,
},
)
return response == "archived"
- def unarchive(self, teamRef: str) -> bool:
+ def unarchive(self, team_ref: str) -> bool:
+ """Restores an archived team to an active state.
+
+ Args:
+ team_ref (str): The reference to the team (UUID or Display Name).
+ Returns:
+ bool: True if the team was successfully unarchived.
+ """
response = self.client.execute(
cmd_type="request",
method="unarchiveTeam",
params={
- "teamRef": teamRef,
+ "teamRef": team_ref,
},
)
return response == "unarchived"
- def delete(self, teamRef: str) -> bool:
+ def delete(self, team_ref: str) -> bool:
+ """Deletes a team (soft delete).
+
+ Args:
+ team_ref (str): The reference to the team (UUID or Display Name).
+ Returns:
+ bool: True if the delete request was successful.
+ """
response = self.client.execute(
cmd_type="request",
method="deleteTeam",
params={
- "teamRef": teamRef,
+ "teamRef": team_ref,
},
)
return response == "deleted"
def restore_deleted(self, deleted_team_ID: str) -> str:
+ """Restores a previously deleted team.
+
+ Args:
+ deleted_team_ID (str): The ID of the deleted group/team.
+
+ Returns:
+ str: The ID of the restored team/group.
+ """
response = self.client.execute(
cmd_type="request",
method="restoreDeletedTeam",
diff --git a/python/tests/test_teams.py b/python/tests/test_teams.py
index 9fd61ca..c9e6bb7 100644
--- a/python/tests/test_teams.py
+++ b/python/tests/test_teams.py
@@ -67,7 +67,7 @@ def test_update_team_integration(httpserver):
updated_description = "Updated team description"
team = client.teams.update(
- teamRef=data.teams[0].DisplayName,
+ team_ref=data.teams[0].DisplayName,
update=UpdateTeam(
description=updated_description,
),
@@ -134,7 +134,7 @@ def test_archive_team_integration(httpserver):
init_fake_client(client, httpserver.url_for(""))
success = client.teams.archive(
- teamRef=data.teams[0].DisplayName,
+ team_ref=data.teams[0].DisplayName,
spo_read_only_from_members=True,
)
@@ -153,7 +153,7 @@ def test_unarchive_team_integration(httpserver):
init_fake_client(client, httpserver.url_for(""))
success = client.teams.unarchive(
- teamRef=data.teams[2].DisplayName,
+ team_ref=data.teams[2].DisplayName,
)
assert success is True
@@ -171,7 +171,7 @@ def test_delete_team_integration(httpserver):
init_fake_client(client, httpserver.url_for(""))
success = client.teams.delete(
- teamRef=data.teams[0].DisplayName,
+ team_ref=data.teams[0].DisplayName,
)
assert success is True
From 426bcbb1478e8f2477f4ddc7e74d5b7c17113cac Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Thu, 8 Jan 2026 10:33:43 +0100
Subject: [PATCH 66/99] docs: added comments for channel and chat models
---
python/teams_lib_pzsp2_z1/model/channel.py | 18 +-
python/teams_lib_pzsp2_z1/model/chat.py | 47 +++-
.../teams_lib_pzsp2_z1/services/channels.py | 24 +-
python/teams_lib_pzsp2_z1/services/chats.py | 64 ++---
python/tests/fake_server/data.py | 68 +++---
python/tests/fake_server/setup.py | 2 +-
python/tests/test_channels.py | 162 ++++++-------
python/tests/test_chats.py | 222 +++++++++---------
8 files changed, 326 insertions(+), 281 deletions(-)
diff --git a/python/teams_lib_pzsp2_z1/model/channel.py b/python/teams_lib_pzsp2_z1/model/channel.py
index ace8276..32959ce 100644
--- a/python/teams_lib_pzsp2_z1/model/channel.py
+++ b/python/teams_lib_pzsp2_z1/model/channel.py
@@ -3,6 +3,18 @@
@dataclass
class Channel:
- ID: str
- Name: str
- IsGeneral: bool
+ """Represents a Microsoft Teams channel.
+
+ This model serves as a data container for channel properties returned
+ by the underlying Go library.
+
+ Attributes:
+ id (str): The unique identifier of the channel.
+ name (str): The display name of the channel (e.g., "General", "Development").
+ is_general (bool): Indicates whether this is the default 'General' channel.
+ The General channel typically cannot be deleted or renamed.
+ """
+
+ id: str
+ name: str
+ is_general: bool
diff --git a/python/teams_lib_pzsp2_z1/model/chat.py b/python/teams_lib_pzsp2_z1/model/chat.py
index 63be741..fe99164 100644
--- a/python/teams_lib_pzsp2_z1/model/chat.py
+++ b/python/teams_lib_pzsp2_z1/model/chat.py
@@ -3,19 +3,52 @@
class ChatType(Enum):
- ONEONONE = "one-on-one"
+ """Represents the type of chat in Microsoft Teams.
+
+ Attributes:
+ ONE_ON_ONE: Represents a direct conversation between two users.
+ GROUP: Represents a conversation with multiple participants.
+ """
+
+ ONE_ON_ONE = "one-on-one"
GROUP = "group"
@dataclass
class Chat:
- ID: str
- Type: ChatType
- IsHidden: bool
- Topic: str
+ """Represents a chat in Microsoft Teams.
+
+ Attributes:
+ id (str): The unique identifier of the chat.
+ chat_type (ChatType): The type of the conversation (One-on-One or Group).
+ is_hidden (bool): Indicates whether the chat is hidden for the user.
+ topic (Optional[str]): The subject or topic of the chat.
+ This is typically None for one-on-one chats.
+ """
+
+ id: str
+ type: ChatType
+ is_hidden: bool
+ topic: str | None
@dataclass
class ChatRef:
- Ref: str
- Type: ChatType
+ """Represents a polymorphic reference to a chat used for API lookups.
+
+ This class corresponds to `GroupChatRef` and `OneOnOneChatRef` in the Go library.
+ The interpretation of the `ref` field depends on the `chat_type`.
+
+ Attributes:
+ ref (str): The identifier string.
+ * If **chat_type is GROUP**: Can be a unique Chat ID or a Chat Topic.
+ **Note**: Using a topic may lead to ambiguities if multiple group chats
+ share the same name.
+ * If **chat_type is ONE_ON_ONE**: Can be a unique Chat ID or the
+ Recipient's reference (User ID or Email).
+ **Note**: For resolution by User ID/Email to work, the chat must
+ already exist between the logged-in user and the recipient.
+ chat_type (ChatType): Determines how the `ref` string is interpreted.
+ """
+ ref: str
+ type: ChatType
diff --git a/python/teams_lib_pzsp2_z1/services/channels.py b/python/teams_lib_pzsp2_z1/services/channels.py
index 0af5dc0..8bc0207 100644
--- a/python/teams_lib_pzsp2_z1/services/channels.py
+++ b/python/teams_lib_pzsp2_z1/services/channels.py
@@ -49,9 +49,9 @@ def list_channels(self, team_ref: str) -> list[Channel]:
)
return [
Channel(
- ID=channel["ID"],
- Name=channel["Name"],
- IsGeneral=(True if channel["IsGeneral"] else False),
+ id=channel["ID"],
+ name=channel["Name"],
+ is_general=(True if channel["IsGeneral"] else False),
)
for channel in response
]
@@ -79,9 +79,9 @@ def get(self, team_ref: str, channel_ref: str) -> Channel:
)
return Channel(
- ID=response["ID"],
- Name=response["Name"],
- IsGeneral=(True if response["IsGeneral"] else False),
+ id=response["ID"],
+ name=response["Name"],
+ is_general=(True if response["IsGeneral"] else False),
)
def create_standard(self, team_ref: str, display_name: str) -> Channel:
@@ -104,9 +104,9 @@ def create_standard(self, team_ref: str, display_name: str) -> Channel:
)
return Channel(
- ID=response["ID"],
- Name=response["Name"],
- IsGeneral=(True if response["IsGeneral"] else False),
+ id=response["ID"],
+ name=response["Name"],
+ is_general=(True if response["IsGeneral"] else False),
)
def create_private(
@@ -140,9 +140,9 @@ def create_private(
)
return Channel(
- ID=response["ID"],
- Name=response["Name"],
- IsGeneral=(True if response["IsGeneral"] else False),
+ id=response["ID"],
+ name=response["Name"],
+ is_general=(True if response["IsGeneral"] else False),
)
def delete(self, team_ref: str, channel_ref: str) -> bool:
diff --git a/python/teams_lib_pzsp2_z1/services/chats.py b/python/teams_lib_pzsp2_z1/services/chats.py
index 11f6c07..07dd0a3 100644
--- a/python/teams_lib_pzsp2_z1/services/chats.py
+++ b/python/teams_lib_pzsp2_z1/services/chats.py
@@ -55,10 +55,10 @@ def create_one_on_one(self, recipient_ref: str) -> Chat:
)
return Chat(
- ID=response["ID"],
- Type=ChatType(response["Type"]),
- IsHidden=(True if response["IsHidden"] else False),
- Topic=response["Topic"],
+ id=response["ID"],
+ type=ChatType(response["Type"]),
+ is_hidden=(True if response["IsHidden"] else False),
+ topic=response["Topic"],
)
def create_group_chat(
@@ -85,10 +85,10 @@ def create_group_chat(
)
return Chat(
- ID=response["ID"],
- Type=ChatType(response["Type"]),
- IsHidden=(True if response["IsHidden"] else False),
- Topic=response["Topic"],
+ id=response["ID"],
+ type=ChatType(response["Type"]),
+ is_hidden=(True if response["IsHidden"] else False),
+ topic=response["Topic"],
)
def add_member_to_group_chat(self, group_chat_ref: str, user_ref: str) -> Member:
@@ -189,10 +189,10 @@ def update_group_chat_topic(self, group_chat_ref: str, new_topic: str) -> Chat:
)
return Chat(
- ID=response["ID"],
- Type=ChatType(response["Type"]),
- IsHidden=(True if response["IsHidden"] else False),
- Topic=response["Topic"],
+ id=response["ID"],
+ type=ChatType(response["Type"]),
+ is_hidden=(True if response["IsHidden"] else False),
+ topic=response["Topic"],
)
def list_messages(
@@ -216,8 +216,8 @@ def list_messages(
method="listMessagesInChat",
params={
"chatRef": {
- "ref": chat_ref.Ref,
- "type": chat_ref.Type.value,
+ "ref": chat_ref.ref,
+ "type": chat_ref.type.value,
},
"includeSystem": include_system_messages,
"nextLink": next_link,
@@ -257,8 +257,8 @@ def send_message(self, chat_ref: ChatRef, body: MessageBody) -> Message:
method="sendMessageInChat",
params={
"chatRef": {
- "ref": chat_ref.Ref,
- "type": chat_ref.Type.value,
+ "ref": chat_ref.ref,
+ "type": chat_ref.type.value,
},
"body": {
"content": body.Content,
@@ -296,8 +296,8 @@ def delete_message(self, chat_ref: ChatRef, message_id: str) -> bool:
method="deleteMessageInChat",
params={
"chatRef": {
- "ref": chat_ref.Ref,
- "type": chat_ref.Type.value,
+ "ref": chat_ref.ref,
+ "type": chat_ref.type.value,
},
"messageID": message_id,
},
@@ -320,8 +320,8 @@ def get_message(self, chat_ref: ChatRef, message_id: str) -> Message:
method="getMessageInChat",
params={
"chatRef": {
- "ref": chat_ref.Ref,
- "type": chat_ref.Type.value,
+ "ref": chat_ref.ref,
+ "type": chat_ref.type.value,
},
"messageID": message_id,
},
@@ -361,10 +361,10 @@ def list_my_joined(self, chat_type: ChatType | None = None) -> list[Chat]:
return [
Chat(
- ID=chat["ID"],
- Type=ChatType(chat["Type"]),
- IsHidden=(True if chat["IsHidden"] else False),
- Topic=chat["Topic"],
+ id=chat["ID"],
+ type=ChatType(chat["Type"]),
+ is_hidden=(True if chat["IsHidden"] else False),
+ topic=chat["Topic"],
)
for chat in chats
]
@@ -425,8 +425,8 @@ def list_pinned_messages(self, chat_ref: ChatRef) -> list[Message]:
method="listPinnedMessagesInChat",
params={
"chatRef": {
- "ref": chat_ref.Ref,
- "type": chat_ref.Type.value,
+ "ref": chat_ref.ref,
+ "type": chat_ref.type.value,
},
},
)
@@ -461,8 +461,8 @@ def pin_message(self, chat_ref: ChatRef, message_id: str) -> bool:
method="pinMessageInChat",
params={
"chatRef": {
- "ref": chat_ref.Ref,
- "type": chat_ref.Type.value,
+ "ref": chat_ref.ref,
+ "type": chat_ref.type.value,
},
"messageID": message_id,
},
@@ -485,8 +485,8 @@ def unpin_message(self, chat_ref: ChatRef, message_id: str) -> bool:
method="unpinMessageInChat",
params={
"chatRef": {
- "ref": chat_ref.Ref,
- "type": chat_ref.Type.value,
+ "ref": chat_ref.ref,
+ "type": chat_ref.type.value,
},
"messageID": message_id,
},
@@ -514,8 +514,8 @@ def get_mentions(self, chat_ref: ChatRef, raw_mentions: list[str]) -> list[Menti
method="getMentionsInChat",
params={
"chatRef": {
- "ref": chat_ref.Ref,
- "type": chat_ref.Type.value,
+ "ref": chat_ref.ref,
+ "type": chat_ref.type.value,
},
"rawMentions": raw_mentions,
},
diff --git a/python/tests/fake_server/data.py b/python/tests/fake_server/data.py
index 733aa0b..0a2581a 100644
--- a/python/tests/fake_server/data.py
+++ b/python/tests/fake_server/data.py
@@ -52,7 +52,7 @@ class FakeServerData:
Role="owner", Email="newmember@example.com"
))
newChatTemplate: Chat = field(default_factory=lambda: Chat(
- ID="new-chat-001", Type=ChatType.GROUP, IsHidden=False, Topic="New Chat Topic"
+ id="new-chat-001", type=ChatType.GROUP, is_hidden=False, topic="New Chat Topic"
))
updatedGroupChatTopic: str = "Updated Chat Topic"
@@ -68,8 +68,8 @@ def __init__(self) -> None:
# --- Channels ---
self.channels = {
"team-123-abc": [
- Channel(ID="19:123123@thread.tacv2", Name="Something", IsGeneral=False),
- Channel(ID="19:999999@thread.tacv2", Name="Development", IsGeneral=False),
+ Channel(id="19:123123@thread.tacv2", name="Something", is_general=False),
+ Channel(id="19:999999@thread.tacv2", name="Development", is_general=False),
],
}
@@ -108,11 +108,11 @@ def __init__(self) -> None:
# --- Chats & Chat Members/Messages ---
self.group_chats = [
- Chat(ID="chat-123-abc", Type=ChatType.GROUP, IsHidden=False, Topic="Project Discussion"),
- Chat(ID="chat-456-def", Type=ChatType.GROUP, IsHidden=True, Topic="Secret Plans"),
+ Chat(id="chat-123-abc", type=ChatType.GROUP, is_hidden=False, topic="Project Discussion"),
+ Chat(id="chat-456-def", type=ChatType.GROUP, is_hidden=True, topic="Secret Plans"),
]
self.oneonone_chats = [
- Chat(ID="chat-789-ghi", Type=ChatType.ONEONONE, IsHidden=False, Topic=""),
+ Chat(id="chat-789-ghi", type=ChatType.ONE_ON_ONE, is_hidden=False, topic=""),
]
self.group_chat_members = {
@@ -152,7 +152,7 @@ def __init__(self) -> None:
Role="owner", Email="newmember@example.com"
)
self.newChatTemplate = Chat(
- ID="new-chat-001", Type=ChatType.GROUP, IsHidden=False, Topic="New Chat Topic"
+ id="new-chat-001", type=ChatType.GROUP, is_hidden=False, topic="New Chat Topic"
)
# --- Helpers ---
@@ -160,7 +160,7 @@ def _find_team(self, team_id: str) -> Optional[Team]:
return next((t for t in self.teams if t.ID == team_id), None)
def _find_channel(self, team_id: str, channel_id: str) -> Optional[Channel]:
- return next((c for c in self.channels.get(team_id, []) if c.ID == channel_id), None)
+ return next((c for c in self.channels.get(team_id, []) if c.id == channel_id), None)
# ==========================================
# TEAMS & GROUPS
@@ -324,9 +324,9 @@ def get_listChannels_response(self, team_id: str) -> dict:
"value": [
{
ODATA_TYPE: "#microsoft.graph.channel",
- "id": channel.ID,
- "displayName": channel.Name,
- "isGeneral": channel.IsGeneral,
+ "id": channel.id,
+ "displayName": channel.name,
+ "isGeneral": channel.is_general,
"membershipType": "standard",
"email": ""
}
@@ -339,9 +339,9 @@ def get_channel_response(self, team_id: str, channel_id: str) -> Optional[dict]:
if not channel:
return None
return {
- "id": channel.ID,
- "displayName": channel.Name,
- "isGeneral": channel.IsGeneral,
+ "id": channel.id,
+ "displayName": channel.name,
+ "isGeneral": channel.is_general,
}
def get_create_channel_response(self, team_id: str, request_json: dict) -> dict:
@@ -349,9 +349,9 @@ def get_create_channel_response(self, team_id: str, request_json: dict) -> dict:
description = request_json.get("description", display_name)
new_channel = Channel(
- ID=self.newChannelID,
- Name=display_name,
- IsGeneral=False,
+ id=self.newChannelID,
+ name=display_name,
+ is_general=False,
)
if team_id not in self.channels:
@@ -359,10 +359,10 @@ def get_create_channel_response(self, team_id: str, request_json: dict) -> dict:
self.channels[team_id].append(new_channel)
return {
- "id": new_channel.ID,
- "displayName": new_channel.Name,
+ "id": new_channel.id,
+ "displayName": new_channel.name,
"description": description,
- "isGeneral": new_channel.IsGeneral,
+ "isGeneral": new_channel.is_general,
"membershipType": "standard"
}
@@ -540,16 +540,16 @@ def get_remove_member_response(self, team_id: str, channel_id: str, member_id: s
# ==========================================
def get_list_chats_response(self, chat_type: ChatType) -> dict:
- chats = self.oneonone_chats if chat_type == ChatType.ONEONONE else self.group_chats
+ chats = self.oneonone_chats if chat_type == ChatType.ONE_ON_ONE else self.group_chats
return {
ODATA_CONTEXT: f"{GRAPH_URL}#chats",
ODATA_COUNT: len(chats),
"value": [
{
- "id": chat.ID,
- "chatType": "oneOnOne" if chat.Type == ChatType.ONEONONE else "group",
- "isHiddenForAllMembers": chat.IsHidden,
- "topic": chat.Topic,
+ "id": chat.id,
+ "chatType": "oneOnOne" if chat.type == ChatType.ONE_ON_ONE else "group",
+ "isHiddenForAllMembers": chat.is_hidden,
+ "topic": chat.topic,
}
for chat in chats
],
@@ -559,25 +559,25 @@ def get_create_chat_response(self, request_json: dict) -> dict:
is_one_on_one = request_json["chatType"] == "oneOnOne"
response = {
ODATA_CONTEXT: f"{GRAPH_URL}#chats/$entity",
- "id": self.newChatTemplate.ID,
+ "id": self.newChatTemplate.id,
"chatType": "oneOnOne" if is_one_on_one else "group",
- "isHiddenForAllMembers": self.newChatTemplate.IsHidden,
- "topic": None if is_one_on_one else self.newChatTemplate.Topic,
+ "isHiddenForAllMembers": self.newChatTemplate.is_hidden,
+ "topic": None if is_one_on_one else self.newChatTemplate.topic,
}
return response
def get_update_group_chat_topic_response(self, chat_id: str, request_json: dict) -> dict:
- chat = next((c for c in self.group_chats if c.ID == chat_id), None)
+ chat = next((c for c in self.group_chats if c.id == chat_id), None)
if not chat:
return {}
- chat.Topic = request_json.get("topic", chat.Topic)
+ chat.topic = request_json.get("topic", chat.topic)
return {
ODATA_CONTEXT: f"{GRAPH_URL}#chats('{chat_id}')",
- "id": chat.ID,
+ "id": chat.id,
"chatType": "group",
- "isHiddenForAllMembers": chat.IsHidden,
- "topic": chat.Topic,
+ "isHiddenForAllMembers": chat.is_hidden,
+ "topic": chat.topic,
}
# ==========================================
@@ -678,7 +678,7 @@ def get_send_message_in_chat_response(self, chat_id: str, request_json: dict) ->
}
def get_all_messeges_in_chats_response(self) -> dict:
- target_chat_id = self.group_chats[0].ID
+ target_chat_id = self.group_chats[0].id
return {
ODATA_CONTEXT: f"{GRAPH_URL}#Collection(chatMessage)",
ODATA_COUNT: len(self.chat_messages),
diff --git a/python/tests/fake_server/setup.py b/python/tests/fake_server/setup.py
index 4e6033b..f1f2ff2 100644
--- a/python/tests/fake_server/setup.py
+++ b/python/tests/fake_server/setup.py
@@ -89,7 +89,7 @@ def handle_chats_request(req):
chat_type = match.group(1)
if chat_type == "oneOnOne":
- chat_type = ChatType.ONEONONE
+ chat_type = ChatType.ONE_ON_ONE
elif chat_type == "group":
chat_type = ChatType.GROUP
diff --git a/python/tests/test_channels.py b/python/tests/test_channels.py
index 5eb8831..62ba85b 100644
--- a/python/tests/test_channels.py
+++ b/python/tests/test_channels.py
@@ -21,13 +21,13 @@ def test_list_channels_integration(httpserver):
assert len(channels) == len(data.channels[data.teams[0].ID])
- assert channels[0].Name == data.channels[data.teams[0].ID][0].Name
- assert channels[0].ID == data.channels[data.teams[0].ID][0].ID
- assert channels[0].IsGeneral == data.channels[data.teams[0].ID][0].IsGeneral
+ assert channels[0].name == data.channels[data.teams[0].ID][0].name
+ assert channels[0].id == data.channels[data.teams[0].ID][0].id
+ assert channels[0].is_general == data.channels[data.teams[0].ID][0].is_general
- assert channels[1].Name == data.channels[data.teams[0].ID][1].Name
- assert channels[1].ID == data.channels[data.teams[0].ID][1].ID
- assert channels[1].IsGeneral == data.channels[data.teams[0].ID][1].IsGeneral
+ assert channels[1].name == data.channels[data.teams[0].ID][1].name
+ assert channels[1].id == data.channels[data.teams[0].ID][1].id
+ assert channels[1].is_general == data.channels[data.teams[0].ID][1].is_general
finally:
client.close()
@@ -43,12 +43,12 @@ def test_get_channel_integration(httpserver):
channel = client.channels.get(
team_ref=data.teams[0].DisplayName,
- channel_ref=data.channels[data.teams[0].ID][1].Name,
+ channel_ref=data.channels[data.teams[0].ID][1].name,
)
- assert channel.Name == data.channels[data.teams[0].ID][1].Name
- assert channel.ID == data.channels[data.teams[0].ID][1].ID
- assert channel.IsGeneral == data.channels[data.teams[0].ID][1].IsGeneral
+ assert channel.name == data.channels[data.teams[0].ID][1].name
+ assert channel.id == data.channels[data.teams[0].ID][1].id
+ assert channel.is_general == data.channels[data.teams[0].ID][1].is_general
finally:
client.close()
@@ -67,9 +67,9 @@ def test_create_standard_channel_integration(httpserver):
display_name=data.newChannelName,
)
- assert channel.Name == data.newChannelName
- assert channel.ID == data.newChannelID
- assert channel.IsGeneral is False
+ assert channel.name == data.newChannelName
+ assert channel.id == data.newChannelID
+ assert channel.is_general is False
finally:
client.close()
@@ -90,9 +90,9 @@ def test_create_private_channel_integration(httpserver):
owner_refs=[],
)
- assert channel.Name == data.newChannelName
- assert channel.ID == data.newChannelID
- assert channel.IsGeneral is False
+ assert channel.name == data.newChannelName
+ assert channel.id == data.newChannelID
+ assert channel.is_general is False
finally:
client.close()
@@ -108,7 +108,7 @@ def test_delete_channel_integration(httpserver):
success = client.channels.delete(
team_ref=data.teams[0].DisplayName,
- channel_ref=data.channels[data.teams[0].ID][1].Name,
+ channel_ref=data.channels[data.teams[0].ID][1].name,
)
assert success is True
@@ -127,7 +127,7 @@ def test_send_message_integration(httpserver):
message = client.channels.send_message(
team_ref=data.teams[0].DisplayName,
- channel_ref=data.channels[data.teams[0].ID][0].Name,
+ channel_ref=data.channels[data.teams[0].ID][0].name,
body=MessageBody(
Content=data.newMessageTemplate.Content,
ContentType=MessageContentType.TEXT,
@@ -157,25 +157,25 @@ def test_list_messages_integration(httpserver):
collection = client.channels.list_messages(
team_ref=data.teams[0].DisplayName,
- channel_ref=data.channels[data.teams[0].ID][0].Name,
+ channel_ref=data.channels[data.teams[0].ID][0].name,
)
- assert len(collection.Messages) == len(data.messages[data.channels[data.teams[0].ID][0].ID])
- assert collection.Messages[0].ID == data.messages[data.channels[data.teams[0].ID][0].ID][0].ID
- assert collection.Messages[0].Content == data.messages[data.channels[data.teams[0].ID][0].ID][0].Content
- assert collection.Messages[0].ContentType == MessageContentType(data.messages[data.channels[data.teams[0].ID][0].ID][0].ContentType)
- assert collection.Messages[0].From.UserID == data.messages[data.channels[data.teams[0].ID][0].ID][0].From.UserID
- assert collection.Messages[0].From.DisplayName == data.messages[data.channels[data.teams[0].ID][0].ID][0].From.DisplayName
- assert collection.Messages[0].ReplyCount == data.messages[data.channels[data.teams[0].ID][0].ID][0].ReplyCount
- assert collection.Messages[0].CreatedDateTime == data.messages[data.channels[data.teams[0].ID][0].ID][0].CreatedDateTime
-
- assert collection.Messages[1].ID == data.messages[data.channels[data.teams[0].ID][0].ID][1].ID
- assert collection.Messages[1].Content == data.messages[data.channels[data.teams[0].ID][0].ID][1].Content
- assert collection.Messages[1].ContentType == MessageContentType(data.messages[data.channels[data.teams[0].ID][0].ID][1].ContentType)
- assert collection.Messages[1].From.UserID == data.messages[data.channels[data.teams[0].ID][0].ID][1].From.UserID
- assert collection.Messages[1].From.DisplayName == data.messages[data.channels[data.teams[0].ID][0].ID][1].From.DisplayName
- assert collection.Messages[1].ReplyCount == data.messages[data.channels[data.teams[0].ID][0].ID][1].ReplyCount
- assert collection.Messages[1].CreatedDateTime == data.messages[data.channels[data.teams[0].ID][0].ID][1].CreatedDateTime
+ assert len(collection.Messages) == len(data.messages[data.channels[data.teams[0].ID][0].id])
+ assert collection.Messages[0].ID == data.messages[data.channels[data.teams[0].ID][0].id][0].ID
+ assert collection.Messages[0].Content == data.messages[data.channels[data.teams[0].ID][0].id][0].Content
+ assert collection.Messages[0].ContentType == MessageContentType(data.messages[data.channels[data.teams[0].ID][0].id][0].ContentType)
+ assert collection.Messages[0].From.UserID == data.messages[data.channels[data.teams[0].ID][0].id][0].From.UserID
+ assert collection.Messages[0].From.DisplayName == data.messages[data.channels[data.teams[0].ID][0].id][0].From.DisplayName
+ assert collection.Messages[0].ReplyCount == data.messages[data.channels[data.teams[0].ID][0].id][0].ReplyCount
+ assert collection.Messages[0].CreatedDateTime == data.messages[data.channels[data.teams[0].ID][0].id][0].CreatedDateTime
+
+ assert collection.Messages[1].ID == data.messages[data.channels[data.teams[0].ID][0].id][1].ID
+ assert collection.Messages[1].Content == data.messages[data.channels[data.teams[0].ID][0].id][1].Content
+ assert collection.Messages[1].ContentType == MessageContentType(data.messages[data.channels[data.teams[0].ID][0].id][1].ContentType)
+ assert collection.Messages[1].From.UserID == data.messages[data.channels[data.teams[0].ID][0].id][1].From.UserID
+ assert collection.Messages[1].From.DisplayName == data.messages[data.channels[data.teams[0].ID][0].id][1].From.DisplayName
+ assert collection.Messages[1].ReplyCount == data.messages[data.channels[data.teams[0].ID][0].id][1].ReplyCount
+ assert collection.Messages[1].CreatedDateTime == data.messages[data.channels[data.teams[0].ID][0].id][1].CreatedDateTime
finally:
client.close()
@@ -190,17 +190,17 @@ def test_get_message_integration(httpserver):
message = client.channels.get_message(
team_ref=data.teams[0].DisplayName,
- channel_ref=data.channels[data.teams[0].ID][0].Name,
- message_id=data.messages[data.channels[data.teams[0].ID][0].ID][0].ID,
+ channel_ref=data.channels[data.teams[0].ID][0].name,
+ message_id=data.messages[data.channels[data.teams[0].ID][0].id][0].ID,
)
- assert message.ID == data.messages[data.channels[data.teams[0].ID][0].ID][0].ID
- assert message.Content == data.messages[data.channels[data.teams[0].ID][0].ID][0].Content
- assert message.ContentType == MessageContentType(data.messages[data.channels[data.teams[0].ID][0].ID][0].ContentType)
- assert message.From.UserID == data.messages[data.channels[data.teams[0].ID][0].ID][0].From.UserID
- assert message.From.DisplayName == data.messages[data.channels[data.teams[0].ID][0].ID][0].From.DisplayName
- assert message.ReplyCount == data.messages[data.channels[data.teams[0].ID][0].ID][0].ReplyCount
- assert message.CreatedDateTime == data.messages[data.channels[data.teams[0].ID][0].ID][0].CreatedDateTime
+ assert message.ID == data.messages[data.channels[data.teams[0].ID][0].id][0].ID
+ assert message.Content == data.messages[data.channels[data.teams[0].ID][0].id][0].Content
+ assert message.ContentType == MessageContentType(data.messages[data.channels[data.teams[0].ID][0].id][0].ContentType)
+ assert message.From.UserID == data.messages[data.channels[data.teams[0].ID][0].id][0].From.UserID
+ assert message.From.DisplayName == data.messages[data.channels[data.teams[0].ID][0].id][0].From.DisplayName
+ assert message.ReplyCount == data.messages[data.channels[data.teams[0].ID][0].id][0].ReplyCount
+ assert message.CreatedDateTime == data.messages[data.channels[data.teams[0].ID][0].id][0].CreatedDateTime
finally:
client.close()
@@ -216,24 +216,24 @@ def test_list_replies_integration(httpserver):
replies = client.channels.list_message_replies(
team_ref=data.teams[0].DisplayName,
- channel_ref=data.channels[data.teams[0].ID][0].Name,
- message_id=data.messages[data.channels[data.teams[0].ID][0].ID][1].ID,
+ channel_ref=data.channels[data.teams[0].ID][0].name,
+ message_id=data.messages[data.channels[data.teams[0].ID][0].id][1].ID,
)
- assert len(replies.Messages) == len(data.replies[data.messages[data.channels[data.teams[0].ID][0].ID][1].ID])
- assert replies.Messages[0].ID == data.replies[data.messages[data.channels[data.teams[0].ID][0].ID][1].ID][0].ID
- assert replies.Messages[0].Content == data.replies[data.messages[data.channels[data.teams[0].ID][0].ID][1].ID][0].Content
- assert replies.Messages[0].ContentType == MessageContentType(data.replies[data.messages[data.channels[data.teams[0].ID][0].ID][1].ID][0].ContentType)
- assert replies.Messages[0].From.UserID == data.replies[data.messages[data.channels[data.teams[0].ID][0].ID][1].ID][0].From.UserID
- assert replies.Messages[0].From.DisplayName == data.replies[data.messages[data.channels[data.teams[0].ID][0].ID][1].ID][0].From.DisplayName
- assert replies.Messages[0].CreatedDateTime == data.replies[data.messages[data.channels[data.teams[0].ID][0].ID][1].ID][0].CreatedDateTime
-
- assert replies.Messages[1].ID == data.replies[data.messages[data.channels[data.teams[0].ID][0].ID][1].ID][1].ID
- assert replies.Messages[1].Content == data.replies[data.messages[data.channels[data.teams[0].ID][0].ID][1].ID][1].Content
- assert replies.Messages[1].ContentType == MessageContentType(data.replies[data.messages[data.channels[data.teams[0].ID][0].ID][1].ID][1].ContentType)
- assert replies.Messages[1].From.UserID == data.replies[data.messages[data.channels[data.teams[0].ID][0].ID][1].ID][1].From.UserID
- assert replies.Messages[1].From.DisplayName == data.replies[data.messages[data.channels[data.teams[0].ID][0].ID][1].ID][1].From.DisplayName
- assert replies.Messages[1].CreatedDateTime == data.replies[data.messages[data.channels[data.teams[0].ID][0].ID][1].ID][1].CreatedDateTime
+ assert len(replies.Messages) == len(data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].ID])
+ assert replies.Messages[0].ID == data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].ID][0].ID
+ assert replies.Messages[0].Content == data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].ID][0].Content
+ assert replies.Messages[0].ContentType == MessageContentType(data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].ID][0].ContentType)
+ assert replies.Messages[0].From.UserID == data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].ID][0].From.UserID
+ assert replies.Messages[0].From.DisplayName == data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].ID][0].From.DisplayName
+ assert replies.Messages[0].CreatedDateTime == data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].ID][0].CreatedDateTime
+
+ assert replies.Messages[1].ID == data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].ID][1].ID
+ assert replies.Messages[1].Content == data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].ID][1].Content
+ assert replies.Messages[1].ContentType == MessageContentType(data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].ID][1].ContentType)
+ assert replies.Messages[1].From.UserID == data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].ID][1].From.UserID
+ assert replies.Messages[1].From.DisplayName == data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].ID][1].From.DisplayName
+ assert replies.Messages[1].CreatedDateTime == data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].ID][1].CreatedDateTime
finally:
client.close()
@@ -249,17 +249,17 @@ def test_get_reply_integration(httpserver):
reply = client.channels.get_message_reply(
team_ref=data.teams[0].DisplayName,
- channel_ref=data.channels[data.teams[0].ID][0].Name,
- message_id=data.messages[data.channels[data.teams[0].ID][0].ID][1].ID,
- reply_id=data.replies[data.messages[data.channels[data.teams[0].ID][0].ID][1].ID][0].ID,
+ channel_ref=data.channels[data.teams[0].ID][0].name,
+ message_id=data.messages[data.channels[data.teams[0].ID][0].id][1].ID,
+ reply_id=data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].ID][0].ID,
)
- assert reply.ID == data.replies[data.messages[data.channels[data.teams[0].ID][0].ID][1].ID][0].ID
- assert reply.Content == data.replies[data.messages[data.channels[data.teams[0].ID][0].ID][1].ID][0].Content
- assert reply.ContentType == MessageContentType(data.replies[data.messages[data.channels[data.teams[0].ID][0].ID][1].ID][0].ContentType)
- assert reply.From.UserID == data.replies[data.messages[data.channels[data.teams[0].ID][0].ID][1].ID][0].From.UserID
- assert reply.From.DisplayName == data.replies[data.messages[data.channels[data.teams[0].ID][0].ID][1].ID][0].From.DisplayName
- assert reply.CreatedDateTime == data.replies[data.messages[data.channels[data.teams[0].ID][0].ID][1].ID][0].CreatedDateTime
+ assert reply.ID == data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].ID][0].ID
+ assert reply.Content == data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].ID][0].Content
+ assert reply.ContentType == MessageContentType(data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].ID][0].ContentType)
+ assert reply.From.UserID == data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].ID][0].From.UserID
+ assert reply.From.DisplayName == data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].ID][0].From.DisplayName
+ assert reply.CreatedDateTime == data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].ID][0].CreatedDateTime
finally:
client.close()
@@ -274,11 +274,11 @@ def test_list_members_integration(httpserver):
team_id = data.teams[0].ID
channel = data.channels[team_id][0]
- channel_id = channel.ID
+ channel_id = channel.id
members = client.channels.list_members(
team_ref=data.teams[0].DisplayName,
- channel_ref=channel.Name,
+ channel_ref=channel.name,
)
expected_members = data.members[team_id][channel_id]
@@ -313,7 +313,7 @@ def test_add_member_integration(httpserver):
member = client.channels.add_member(
team_ref=data.teams[0].DisplayName,
- channel_ref=data.channels[data.teams[0].ID][1].Name,
+ channel_ref=data.channels[data.teams[0].ID][1].name,
user_ref=data.newMemberTemplate.DisplayName,
is_owner=True if data.newMemberTemplate.Role == "owner" else False,
)
@@ -338,16 +338,16 @@ def test_update_member_role_integration(httpserver):
member = client.channels.update_member_role(
team_ref=data.teams[0].DisplayName,
- channel_ref=data.channels[data.teams[0].ID][0].Name,
- user_ref=data.members[data.teams[0].ID][data.channels[data.teams[0].ID][0].ID][1].Email,
+ channel_ref=data.channels[data.teams[0].ID][0].name,
+ user_ref=data.members[data.teams[0].ID][data.channels[data.teams[0].ID][0].id][1].Email,
is_owner=True
)
- assert member.UserID == data.members[data.teams[0].ID][data.channels[data.teams[0].ID][0].ID][1].UserID
- assert member.DisplayName == data.members[data.teams[0].ID][data.channels[data.teams[0].ID][0].ID][1].DisplayName
+ assert member.UserID == data.members[data.teams[0].ID][data.channels[data.teams[0].ID][0].id][1].UserID
+ assert member.DisplayName == data.members[data.teams[0].ID][data.channels[data.teams[0].ID][0].id][1].DisplayName
assert member.Role == "owner"
- assert member.Email == data.members[data.teams[0].ID][data.channels[data.teams[0].ID][0].ID][1].Email
- assert member.ID == data.members[data.teams[0].ID][data.channels[data.teams[0].ID][0].ID][1].ID
+ assert member.Email == data.members[data.teams[0].ID][data.channels[data.teams[0].ID][0].id][1].Email
+ assert member.ID == data.members[data.teams[0].ID][data.channels[data.teams[0].ID][0].id][1].ID
finally:
client.close()
@@ -362,8 +362,8 @@ def test_remove_member_integration(httpserver):
success = client.channels.remove_member(
team_ref=data.teams[0].DisplayName,
- channel_ref=data.channels[data.teams[0].ID][0].Name,
- user_ref=data.members[data.teams[0].ID][data.channels[data.teams[0].ID][0].ID][1].Email,
+ channel_ref=data.channels[data.teams[0].ID][0].name,
+ user_ref=data.members[data.teams[0].ID][data.channels[data.teams[0].ID][0].id][1].Email,
)
assert success is True
@@ -381,7 +381,7 @@ def test_get_mention_integration(httpserver):
mention = client.channels.get_mentions(
team_ref=data.teams[0].DisplayName,
- channel_ref=data.channels[data.teams[0].ID][0].Name,
+ channel_ref=data.channels[data.teams[0].ID][0].name,
raw_mentions=MentionKind.TEAM.value,
)
diff --git a/python/tests/test_chats.py b/python/tests/test_chats.py
index 2ef176c..adaf71e 100644
--- a/python/tests/test_chats.py
+++ b/python/tests/test_chats.py
@@ -22,15 +22,15 @@ def test_list_my_group_chats_integration(httpserver):
chats = client.chats.list_my_joined(ChatType.GROUP)
assert len(chats) == len(data.group_chats)
- assert chats[0].ID == data.group_chats[0].ID
- assert chats[0].Type == data.group_chats[0].Type
- assert chats[0].Topic == data.group_chats[0].Topic
- assert chats[0].IsHidden == data.group_chats[0].IsHidden
+ assert chats[0].id == data.group_chats[0].id
+ assert chats[0].type == data.group_chats[0].type
+ assert chats[0].topic == data.group_chats[0].topic
+ assert chats[0].is_hidden == data.group_chats[0].is_hidden
- assert chats[1].ID == data.group_chats[1].ID
- assert chats[1].Type == data.group_chats[1].Type
- assert chats[1].Topic == data.group_chats[1].Topic
- assert chats[1].IsHidden == data.group_chats[1].IsHidden
+ assert chats[1].id == data.group_chats[1].id
+ assert chats[1].type == data.group_chats[1].type
+ assert chats[1].topic == data.group_chats[1].topic
+ assert chats[1].is_hidden == data.group_chats[1].is_hidden
finally:
client.close()
@@ -43,13 +43,13 @@ def test_list_my_one_on_one_chats_integration(httpserver):
try:
init_fake_client(client, httpserver.url_for(""))
- chats = client.chats.list_my_joined(ChatType.ONEONONE)
+ chats = client.chats.list_my_joined(ChatType.ONE_ON_ONE)
assert len(chats) == len(data.oneonone_chats)
- assert chats[0].ID == data.oneonone_chats[0].ID
- assert chats[0].Type == data.oneonone_chats[0].Type
- assert chats[0].Topic == data.oneonone_chats[0].Topic
- assert chats[0].IsHidden == data.oneonone_chats[0].IsHidden
+ assert chats[0].id == data.oneonone_chats[0].id
+ assert chats[0].type == data.oneonone_chats[0].type
+ assert chats[0].topic == data.oneonone_chats[0].topic
+ assert chats[0].is_hidden == data.oneonone_chats[0].is_hidden
finally:
client.close()
@@ -64,7 +64,7 @@ def test_create_group_chat_integration(httpserver):
init_fake_client(client, httpserver.url_for(""))
chat = client.chats.create_group_chat(
- topic=data.newChatTemplate.Topic,
+ topic=data.newChatTemplate.topic,
recipient_refs=[
data.users[0].Email,
data.users[1].Email,
@@ -72,10 +72,10 @@ def test_create_group_chat_integration(httpserver):
include_me=True,
)
- assert chat.ID == data.newChatTemplate.ID
- assert chat.Type == ChatType.GROUP
- assert chat.Topic == data.newChatTemplate.Topic
- assert chat.IsHidden == data.newChatTemplate.IsHidden
+ assert chat.id == data.newChatTemplate.id
+ assert chat.type == ChatType.GROUP
+ assert chat.topic == data.newChatTemplate.topic
+ assert chat.is_hidden == data.newChatTemplate.is_hidden
finally:
client.close()
@@ -92,10 +92,10 @@ def test_create_one_on_one_chat_integration(httpserver):
recipient_ref=data.users[0].Email,
)
- assert chat.ID == data.newChatTemplate.ID
- assert chat.Type == ChatType.ONEONONE
- assert chat.Topic == None
- assert chat.IsHidden == data.newChatTemplate.IsHidden
+ assert chat.id == data.newChatTemplate.id
+ assert chat.type == ChatType.ONE_ON_ONE
+ assert chat.topic == None
+ assert chat.is_hidden == data.newChatTemplate.is_hidden
finally:
client.close()
@@ -109,23 +109,23 @@ def test_list_group_chat_members_integration(httpserver):
try:
init_fake_client(client, httpserver.url_for(""))
- members = client.chats.list_group_chat_members(data.group_chats[0].Topic)
+ members = client.chats.list_group_chat_members(data.group_chats[0].topic)
- assert len(members) == len(data.group_chat_members[data.group_chats[0].ID])
+ assert len(members) == len(data.group_chat_members[data.group_chats[0].id])
- assert members[0].ID == data.group_chat_members[data.group_chats[0].ID][0].ID
- assert members[0].UserID == data.group_chat_members[data.group_chats[0].ID][0].UserID
- assert members[0].DisplayName == data.group_chat_members[data.group_chats[0].ID][0].DisplayName
- if data.group_chat_members[data.group_chats[0].ID][0].Role == "owner":
+ assert members[0].ID == data.group_chat_members[data.group_chats[0].id][0].ID
+ assert members[0].UserID == data.group_chat_members[data.group_chats[0].id][0].UserID
+ assert members[0].DisplayName == data.group_chat_members[data.group_chats[0].id][0].DisplayName
+ if data.group_chat_members[data.group_chats[0].id][0].Role == "owner":
assert members[0].Role == "owner"
- assert members[0].Email == data.group_chat_members[data.group_chats[0].ID][0].Email
+ assert members[0].Email == data.group_chat_members[data.group_chats[0].id][0].Email
- assert members[1].ID == data.group_chat_members[data.group_chats[0].ID][1].ID
- assert members[1].UserID == data.group_chat_members[data.group_chats[0].ID][1].UserID
- assert members[1].DisplayName == data.group_chat_members[data.group_chats[0].ID][1].DisplayName
- if data.group_chat_members[data.group_chats[0].ID][1].Role == "owner":
+ assert members[1].ID == data.group_chat_members[data.group_chats[0].id][1].ID
+ assert members[1].UserID == data.group_chat_members[data.group_chats[0].id][1].UserID
+ assert members[1].DisplayName == data.group_chat_members[data.group_chats[0].id][1].DisplayName
+ if data.group_chat_members[data.group_chats[0].id][1].Role == "owner":
assert members[1].Role == "owner"
- assert members[1].Email == data.group_chat_members[data.group_chats[0].ID][1].Email
+ assert members[1].Email == data.group_chat_members[data.group_chats[0].id][1].Email
finally:
client.close()
@@ -140,7 +140,7 @@ def test_add_group_chat_member_integration(httpserver):
init_fake_client(client, httpserver.url_for(""))
member = client.chats.add_member_to_group_chat(
- group_chat_ref=data.group_chats[0].Topic,
+ group_chat_ref=data.group_chats[0].topic,
user_ref=data.newMemberTemplate.Email,
)
@@ -162,8 +162,8 @@ def test_remove_group_chat_member_integration(httpserver):
init_fake_client(client, httpserver.url_for(""))
result = client.chats.remove_member_from_group_chat(
- group_chat_ref=data.group_chats[0].Topic,
- member_ref=data.group_chat_members[data.group_chats[0].ID][0].Email,
+ group_chat_ref=data.group_chats[0].topic,
+ member_ref=data.group_chat_members[data.group_chats[0].id][0].Email,
)
assert result is True
@@ -181,14 +181,14 @@ def test_update_group_chat_topic_integration(httpserver):
init_fake_client(client, httpserver.url_for(""))
chat = client.chats.update_group_chat_topic(
- group_chat_ref=data.group_chats[0].Topic,
+ group_chat_ref=data.group_chats[0].topic,
new_topic=data.updatedGroupChatTopic,
)
- assert chat.ID == data.group_chats[0].ID
- assert chat.Type == data.group_chats[0].Type
- assert chat.Topic == data.updatedGroupChatTopic
- assert chat.IsHidden == data.group_chats[0].IsHidden
+ assert chat.id == data.group_chats[0].id
+ assert chat.type == data.group_chats[0].type
+ assert chat.topic == data.updatedGroupChatTopic
+ assert chat.is_hidden == data.group_chats[0].is_hidden
finally:
client.close()
@@ -204,28 +204,28 @@ def test_list_messeges_in_chat_integration(httpserver):
collection = client.chats.list_messages(
chat_ref=ChatRef(
- Ref=data.group_chats[0].Topic,
- Type=ChatType.GROUP,
+ ref=data.group_chats[0].topic,
+ type=ChatType.GROUP,
)
)
- assert len(collection.Messages) == len(data.chat_messages[data.group_chats[0].ID])
+ assert len(collection.Messages) == len(data.chat_messages[data.group_chats[0].id])
- assert collection.Messages[0].ID == data.chat_messages[data.group_chats[0].ID][0].ID
- assert collection.Messages[0].Content == data.chat_messages[data.group_chats[0].ID][0].Content
- assert collection.Messages[0].ContentType == MessageContentType(data.chat_messages[data.group_chats[0].ID][0].ContentType)
- assert collection.Messages[0].From.UserID == data.chat_messages[data.group_chats[0].ID][0].From.UserID
- assert collection.Messages[0].From.DisplayName == data.chat_messages[data.group_chats[0].ID][0].From.DisplayName
- assert collection.Messages[0].ReplyCount == data.chat_messages[data.group_chats[0].ID][0].ReplyCount
- assert collection.Messages[0].CreatedDateTime == data.chat_messages[data.group_chats[0].ID][0].CreatedDateTime
+ assert collection.Messages[0].ID == data.chat_messages[data.group_chats[0].id][0].ID
+ assert collection.Messages[0].Content == data.chat_messages[data.group_chats[0].id][0].Content
+ assert collection.Messages[0].ContentType == MessageContentType(data.chat_messages[data.group_chats[0].id][0].ContentType)
+ assert collection.Messages[0].From.UserID == data.chat_messages[data.group_chats[0].id][0].From.UserID
+ assert collection.Messages[0].From.DisplayName == data.chat_messages[data.group_chats[0].id][0].From.DisplayName
+ assert collection.Messages[0].ReplyCount == data.chat_messages[data.group_chats[0].id][0].ReplyCount
+ assert collection.Messages[0].CreatedDateTime == data.chat_messages[data.group_chats[0].id][0].CreatedDateTime
- assert collection.Messages[1].ID == data.chat_messages[data.group_chats[0].ID][1].ID
- assert collection.Messages[1].Content == data.chat_messages[data.group_chats[0].ID][1].Content
- assert collection.Messages[1].ContentType == MessageContentType(data.chat_messages[data.group_chats[0].ID][1].ContentType)
- assert collection.Messages[1].From.UserID == data.chat_messages[data.group_chats[0].ID][1].From.UserID
- assert collection.Messages[1].From.DisplayName == data.chat_messages[data.group_chats[0].ID][1].From.DisplayName
- assert collection.Messages[1].ReplyCount == data.chat_messages[data.group_chats[0].ID][1].ReplyCount
- assert collection.Messages[1].CreatedDateTime == data.chat_messages[data.group_chats[0].ID][1].CreatedDateTime
+ assert collection.Messages[1].ID == data.chat_messages[data.group_chats[0].id][1].ID
+ assert collection.Messages[1].Content == data.chat_messages[data.group_chats[0].id][1].Content
+ assert collection.Messages[1].ContentType == MessageContentType(data.chat_messages[data.group_chats[0].id][1].ContentType)
+ assert collection.Messages[1].From.UserID == data.chat_messages[data.group_chats[0].id][1].From.UserID
+ assert collection.Messages[1].From.DisplayName == data.chat_messages[data.group_chats[0].id][1].From.DisplayName
+ assert collection.Messages[1].ReplyCount == data.chat_messages[data.group_chats[0].id][1].ReplyCount
+ assert collection.Messages[1].CreatedDateTime == data.chat_messages[data.group_chats[0].id][1].CreatedDateTime
finally:
client.close()
@@ -240,8 +240,8 @@ def test_send_message_in_chat_integration(httpserver):
message = client.chats.send_message(
chat_ref=ChatRef(
- Ref=data.group_chats[0].Topic,
- Type=ChatType.GROUP,
+ ref=data.group_chats[0].topic,
+ type=ChatType.GROUP,
),
body=MessageBody(
Content=data.newMessageTemplate.Content,
@@ -271,10 +271,10 @@ def test_delete_message_in_chat_integration(httpserver):
result = client.chats.delete_message(
chat_ref=ChatRef(
- Ref=data.group_chats[0].Topic,
- Type=ChatType.GROUP,
+ ref=data.group_chats[0].topic,
+ type=ChatType.GROUP,
),
- message_id=data.chat_messages[data.group_chats[0].ID][0].ID,
+ message_id=data.chat_messages[data.group_chats[0].id][0].ID,
)
assert result is True
@@ -292,19 +292,19 @@ def test_get_message_in_chat_integration(httpserver):
message = client.chats.get_message(
chat_ref=ChatRef(
- Ref=data.group_chats[0].Topic,
- Type=ChatType.GROUP,
+ ref=data.group_chats[0].topic,
+ type=ChatType.GROUP,
),
- message_id=data.chat_messages[data.group_chats[0].ID][0].ID,
+ message_id=data.chat_messages[data.group_chats[0].id][0].ID,
)
- assert message.ID == data.chat_messages[data.group_chats[0].ID][0].ID
- assert message.Content == data.chat_messages[data.group_chats[0].ID][0].Content
- assert message.ContentType == MessageContentType(data.chat_messages[data.group_chats[0].ID][0].ContentType)
- assert message.From.UserID == data.chat_messages[data.group_chats[0].ID][0].From.UserID
- assert message.From.DisplayName == data.chat_messages[data.group_chats[0].ID][0].From.DisplayName
- assert message.ReplyCount == data.chat_messages[data.group_chats[0].ID][0].ReplyCount
- assert message.CreatedDateTime == data.chat_messages[data.group_chats[0].ID][0].CreatedDateTime
+ assert message.ID == data.chat_messages[data.group_chats[0].id][0].ID
+ assert message.Content == data.chat_messages[data.group_chats[0].id][0].Content
+ assert message.ContentType == MessageContentType(data.chat_messages[data.group_chats[0].id][0].ContentType)
+ assert message.From.UserID == data.chat_messages[data.group_chats[0].id][0].From.UserID
+ assert message.From.DisplayName == data.chat_messages[data.group_chats[0].id][0].From.DisplayName
+ assert message.ReplyCount == data.chat_messages[data.group_chats[0].id][0].ReplyCount
+ assert message.CreatedDateTime == data.chat_messages[data.group_chats[0].id][0].CreatedDateTime
finally:
client.close()
@@ -323,23 +323,23 @@ def test_get_all_messages_in_chat_integration(httpserver):
top=50,
)
- assert len(messages) == len(data.chat_messages[data.group_chats[0].ID])
+ assert len(messages) == len(data.chat_messages[data.group_chats[0].id])
- assert messages[0].ID == data.chat_messages[data.group_chats[0].ID][0].ID
- assert messages[0].Content == data.chat_messages[data.group_chats[0].ID][0].Content
- assert messages[0].ContentType == MessageContentType(data.chat_messages[data.group_chats[0].ID][0].ContentType)
- assert messages[0].From.UserID == data.chat_messages[data.group_chats[0].ID][0].From.UserID
- assert messages[0].From.DisplayName == data.chat_messages[data.group_chats[0].ID][0].From.DisplayName
+ assert messages[0].ID == data.chat_messages[data.group_chats[0].id][0].ID
+ assert messages[0].Content == data.chat_messages[data.group_chats[0].id][0].Content
+ assert messages[0].ContentType == MessageContentType(data.chat_messages[data.group_chats[0].id][0].ContentType)
+ assert messages[0].From.UserID == data.chat_messages[data.group_chats[0].id][0].From.UserID
+ assert messages[0].From.DisplayName == data.chat_messages[data.group_chats[0].id][0].From.DisplayName
assert messages[0].ReplyCount == 0
- assert messages[0].CreatedDateTime == data.chat_messages[data.group_chats[0].ID][0].CreatedDateTime
+ assert messages[0].CreatedDateTime == data.chat_messages[data.group_chats[0].id][0].CreatedDateTime
- assert messages[1].ID == data.chat_messages[data.group_chats[0].ID][1].ID
- assert messages[1].Content == data.chat_messages[data.group_chats[0].ID][1].Content
- assert messages[1].ContentType == MessageContentType(data.chat_messages[data.group_chats[0].ID][1].ContentType)
- assert messages[1].From.UserID == data.chat_messages[data.group_chats[0].ID][1].From.UserID
- assert messages[1].From.DisplayName == data.chat_messages[data.group_chats[0].ID][1].From.DisplayName
+ assert messages[1].ID == data.chat_messages[data.group_chats[0].id][1].ID
+ assert messages[1].Content == data.chat_messages[data.group_chats[0].id][1].Content
+ assert messages[1].ContentType == MessageContentType(data.chat_messages[data.group_chats[0].id][1].ContentType)
+ assert messages[1].From.UserID == data.chat_messages[data.group_chats[0].id][1].From.UserID
+ assert messages[1].From.DisplayName == data.chat_messages[data.group_chats[0].id][1].From.DisplayName
assert messages[1].ReplyCount == 0
- assert messages[1].CreatedDateTime == data.chat_messages[data.group_chats[0].ID][1].CreatedDateTime
+ assert messages[1].CreatedDateTime == data.chat_messages[data.group_chats[0].id][1].CreatedDateTime
finally:
client.close()
@@ -355,28 +355,28 @@ def test_list_pinned_messages_integration(httpserver):
messages = client.chats.list_pinned_messages(
chat_ref=ChatRef(
- Ref=data.group_chats[0].Topic,
- Type=ChatType.GROUP,
+ ref=data.group_chats[0].topic,
+ type=ChatType.GROUP,
)
)
- assert len(messages) == len(data.chat_messages[data.group_chats[0].ID])
+ assert len(messages) == len(data.chat_messages[data.group_chats[0].id])
- assert messages[0].ID == data.chat_messages[data.group_chats[0].ID][0].ID
- assert messages[0].Content == data.chat_messages[data.group_chats[0].ID][0].Content
- assert messages[0].ContentType == MessageContentType(data.chat_messages[data.group_chats[0].ID][0].ContentType)
- assert messages[0].From.UserID == data.chat_messages[data.group_chats[0].ID][0].From.UserID
- assert messages[0].From.DisplayName == data.chat_messages[data.group_chats[0].ID][0].From.DisplayName
+ assert messages[0].ID == data.chat_messages[data.group_chats[0].id][0].ID
+ assert messages[0].Content == data.chat_messages[data.group_chats[0].id][0].Content
+ assert messages[0].ContentType == MessageContentType(data.chat_messages[data.group_chats[0].id][0].ContentType)
+ assert messages[0].From.UserID == data.chat_messages[data.group_chats[0].id][0].From.UserID
+ assert messages[0].From.DisplayName == data.chat_messages[data.group_chats[0].id][0].From.DisplayName
assert messages[0].ReplyCount == 0
- assert messages[0].CreatedDateTime == data.chat_messages[data.group_chats[0].ID][0].CreatedDateTime
+ assert messages[0].CreatedDateTime == data.chat_messages[data.group_chats[0].id][0].CreatedDateTime
- assert messages[1].ID == data.chat_messages[data.group_chats[0].ID][1].ID
- assert messages[1].Content == data.chat_messages[data.group_chats[0].ID][1].Content
- assert messages[1].ContentType == MessageContentType(data.chat_messages[data.group_chats[0].ID][1].ContentType)
- assert messages[1].From.UserID == data.chat_messages[data.group_chats[0].ID][1].From.UserID
- assert messages[1].From.DisplayName == data.chat_messages[data.group_chats[0].ID][1].From.DisplayName
+ assert messages[1].ID == data.chat_messages[data.group_chats[0].id][1].ID
+ assert messages[1].Content == data.chat_messages[data.group_chats[0].id][1].Content
+ assert messages[1].ContentType == MessageContentType(data.chat_messages[data.group_chats[0].id][1].ContentType)
+ assert messages[1].From.UserID == data.chat_messages[data.group_chats[0].id][1].From.UserID
+ assert messages[1].From.DisplayName == data.chat_messages[data.group_chats[0].id][1].From.DisplayName
assert messages[1].ReplyCount == 0
- assert messages[1].CreatedDateTime == data.chat_messages[data.group_chats[0].ID][1].CreatedDateTime
+ assert messages[1].CreatedDateTime == data.chat_messages[data.group_chats[0].id][1].CreatedDateTime
finally:
client.close()
@@ -392,10 +392,10 @@ def test_pin_message_in_chat_integration(httpserver):
result = client.chats.pin_message(
chat_ref=ChatRef(
- Ref=data.group_chats[0].Topic,
- Type=ChatType.GROUP,
+ ref=data.group_chats[0].topic,
+ type=ChatType.GROUP,
),
- message_id=data.chat_messages[data.group_chats[0].ID][0].ID,
+ message_id=data.chat_messages[data.group_chats[0].id][0].ID,
)
assert result is True
@@ -414,10 +414,10 @@ def test_unpin_message_in_chat_integration(httpserver):
result = client.chats.unpin_message(
chat_ref=ChatRef(
- Ref=data.group_chats[0].Topic,
- Type=ChatType.GROUP,
+ ref=data.group_chats[0].topic,
+ type=ChatType.GROUP,
),
- message_id=data.chat_messages[data.group_chats[0].ID][0].ID,
+ message_id=data.chat_messages[data.group_chats[0].id][0].ID,
)
assert result is True
@@ -436,8 +436,8 @@ def test_get_mention_integration(httpserver):
mention = client.chats.get_mentions(
chat_ref=ChatRef(
- Ref=data.group_chats[0].Topic,
- Type=ChatType.GROUP,
+ ref=data.group_chats[0].topic,
+ type=ChatType.GROUP,
),
raw_mentions=MentionKind.EVERYONE.value,
)
@@ -445,7 +445,7 @@ def test_get_mention_integration(httpserver):
assert mention[0].Kind == MentionKind.EVERYONE.value
assert mention[0].AtID == 0
assert mention[0].Text == "Everyone"
- assert mention[0].TargetID == data.group_chats[0].ID
+ assert mention[0].TargetID == data.group_chats[0].id
finally:
client.close()
From 63d26bbd2b3fb8f256c8881001324d1a133866f1 Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Thu, 8 Jan 2026 10:37:08 +0100
Subject: [PATCH 67/99] docs: added docstrings for member and mention models
---
python/teams_lib_pzsp2_z1/model/member.py | 22 ++++--
python/teams_lib_pzsp2_z1/model/mention.py | 35 ++++++++-
python/teams_lib_pzsp2_z1/services/chats.py | 28 +++----
python/tests/fake_server/data.py | 84 ++++++++++-----------
python/tests/test_channels.py | 62 +++++++--------
python/tests/test_chats.py | 52 ++++++-------
6 files changed, 161 insertions(+), 122 deletions(-)
diff --git a/python/teams_lib_pzsp2_z1/model/member.py b/python/teams_lib_pzsp2_z1/model/member.py
index 4d8b88e..09b721b 100644
--- a/python/teams_lib_pzsp2_z1/model/member.py
+++ b/python/teams_lib_pzsp2_z1/model/member.py
@@ -3,8 +3,20 @@
@dataclass
class Member:
- ID: str
- UserID: str
- DisplayName: str
- Role: str
- Email: str
+ """Represents a member of a Microsoft Teams channel, direct chat, or team.
+
+ Attributes:
+ id (str): The unique identifier of the membership object itself.
+ **Note:** This is distinct from the `user_id`.
+ user_id (str): The unique Azure Active Directory (AAD) identifier of the user.
+ display_name (str): The visible name of the user.
+ role (str): The role of the user within the specific context
+ (e.g., "owner", "member").
+ email (str): The email address of the user.
+ """
+
+ id: str
+ user_id: str
+ display_name: str
+ role: str
+ email: str
diff --git a/python/teams_lib_pzsp2_z1/model/mention.py b/python/teams_lib_pzsp2_z1/model/mention.py
index 361df1b..273be4f 100644
--- a/python/teams_lib_pzsp2_z1/model/mention.py
+++ b/python/teams_lib_pzsp2_z1/model/mention.py
@@ -3,14 +3,41 @@
class MentionKind(Enum):
+ """Represents the kind of mention in a Microsoft Teams message.
+
+ Attributes:
+ USER: Represents a mention of a specific user.
+ CHANNEL: Represents a mention of the current channel.
+ TEAM: Represents a mention of the entire team.
+ EVERYONE: Represents a mention of all participants.
+ **Note:** This is applicable to Group Chats only.
+ """
+
USER = "user"
CHANNEL = "channel"
TEAM = "team"
EVERYONE = "everyone"
+
@dataclass
class Mention:
- Kind: MentionKind
- AtID: int
- Text: str
- TargetID: str
+ """Represents a mention entity within a Microsoft Teams message.
+
+ This object maps a specific entity (User, Channel, etc.) to a placeholder
+ in the message content.
+
+ Attributes:
+ kind (MentionKind): The type of the mention.
+ at_id (int): The internal identifier used to link this mention to the
+ message content.
+ *Example:* If the message body contains `@John`,
+ this field should be `0`.
+ text (str): The text to display in the message (e.g., "@John Doe").
+ target_id (str): The unique identifier (UUID) of the entity being mentioned
+ (User ID, Channel ID, or Team ID).
+ """
+
+ kind: MentionKind
+ at_id: int
+ text: str
+ target_id: str
diff --git a/python/teams_lib_pzsp2_z1/services/chats.py b/python/teams_lib_pzsp2_z1/services/chats.py
index 07dd0a3..1b9705c 100644
--- a/python/teams_lib_pzsp2_z1/services/chats.py
+++ b/python/teams_lib_pzsp2_z1/services/chats.py
@@ -111,11 +111,11 @@ def add_member_to_group_chat(self, group_chat_ref: str, user_ref: str) -> Member
)
return Member(
- ID=member["ID"],
- DisplayName=member["DisplayName"],
- UserID=member["UserID"],
- Role=member["Role"],
- Email=member["Email"],
+ id=member["ID"],
+ display_name=member["DisplayName"],
+ user_id=member["UserID"],
+ role=member["Role"],
+ email=member["Email"],
)
def remove_member_from_group_chat(
@@ -160,11 +160,11 @@ def list_group_chat_members(self, group_chat_ref: str) -> list[Member]:
return [
Member(
- ID=member["ID"],
- DisplayName=member["DisplayName"],
- UserID=member["UserID"],
- Role=member["Role"],
- Email=member["Email"],
+ id=member["ID"],
+ display_name=member["DisplayName"],
+ user_id=member["UserID"],
+ role=member["Role"],
+ email=member["Email"],
)
for member in members
]
@@ -523,10 +523,10 @@ def get_mentions(self, chat_ref: ChatRef, raw_mentions: list[str]) -> list[Menti
return [
Mention(
- Kind=mention["Kind"],
- AtID=mention["AtID"],
- Text=mention["Text"],
- TargetID=mention["TargetID"],
+ kind=mention["Kind"],
+ at_id=mention["AtID"],
+ text=mention["Text"],
+ target_id=mention["TargetID"],
)
for mention in mentions
]
diff --git a/python/tests/fake_server/data.py b/python/tests/fake_server/data.py
index 0a2581a..ab24160 100644
--- a/python/tests/fake_server/data.py
+++ b/python/tests/fake_server/data.py
@@ -48,8 +48,8 @@ class FakeServerData:
ReplyCount=0,
))
newMemberTemplate: Member = field(default_factory=lambda: Member(
- ID="user-new-002", UserID="new-user-002", DisplayName="New Member",
- Role="owner", Email="newmember@example.com"
+ id="user-new-002", user_id="new-user-002", display_name="New Member",
+ role="owner", email="newmember@example.com"
))
newChatTemplate: Chat = field(default_factory=lambda: Chat(
id="new-chat-001", type=ChatType.GROUP, is_hidden=False, topic="New Chat Topic"
@@ -93,18 +93,18 @@ def __init__(self) -> None:
self.members = {
"team-123-abc": {
"19:123123@thread.tacv2": [
- Member(ID="user-123-abc", UserID="user-123-abc", DisplayName="Alice", Role="owner", Email="alice@example.com"),
- Member(ID="user-456-def", UserID="user-456-def", DisplayName="Bob", Role="member", Email="bob@example.com"),
+ Member(id="user-123-abc", user_id="user-123-abc", display_name="Alice", role="owner", email="alice@example.com"),
+ Member(id="user-456-def", user_id="user-456-def", display_name="Bob", role="member", email="bob@example.com"),
],
}
}
# --- Users ---
self.users = [
- Member(ID="user-123-abc", UserID="user-123-abc", DisplayName="Alice", Role="owner", Email="alice@example.com"),
- Member(ID="user-456-def", UserID="user-456-def", DisplayName="Bob", Role="member", Email="bob@example.com"),
+ Member(id="user-123-abc", user_id="user-123-abc", display_name="Alice", role="owner", email="alice@example.com"),
+ Member(id="user-456-def", user_id="user-456-def", display_name="Bob", role="member", email="bob@example.com"),
]
- self.me = Member(ID="user-me-001", UserID="user-me-001", DisplayName="Current User", Role="member", Email="me@example.com")
+ self.me = Member(id="user-me-001", user_id="user-me-001", display_name="Current User", role="member", email="me@example.com")
# --- Chats & Chat Members/Messages ---
self.group_chats = [
@@ -117,8 +117,8 @@ def __init__(self) -> None:
self.group_chat_members = {
"chat-123-abc": [
- Member(ID="user-123-abc", UserID="user-123-abc", DisplayName="Alice", Role="owner", Email="alice@example.com"),
- Member(ID="user-456-def", UserID="user-456-def", DisplayName="Bob", Role="member", Email="bob@example.com"),
+ Member(id="user-123-abc", user_id="user-123-abc", display_name="Alice", role="owner", email="alice@example.com"),
+ Member(id="user-456-def", user_id="user-456-def", display_name="Bob", role="member", email="bob@example.com"),
],
}
@@ -148,8 +148,8 @@ def __init__(self) -> None:
CreatedDateTime="2024-01-02T10:00:00Z", ReplyCount=0
)
self.newMemberTemplate = Member(
- ID="user-new-002", UserID="new-user-002", DisplayName="New Member",
- Role="owner", Email="newmember@example.com"
+ id="user-new-002", user_id="new-user-002", display_name="New Member",
+ role="owner", email="newmember@example.com"
)
self.newChatTemplate = Chat(
id="new-chat-001", type=ChatType.GROUP, is_hidden=False, topic="New Chat Topic"
@@ -486,50 +486,50 @@ def get_list_members_response(self, team_id: str, channel_id: str) -> dict:
"value": [
{
ODATA_TYPE: "#microsoft.graph.aadUserConversationMember",
- "id": member.ID,
- "userId": member.UserID,
- "displayName": member.DisplayName,
- "roles": [member.Role] if member.Role == "owner" else [],
- "email": member.Email,
+ "id": member.id,
+ "userId": member.user_id,
+ "displayName": member.display_name,
+ "roles": [member.role] if member.role == "owner" else [],
+ "email": member.email,
}
for member in self.members.get(team_id, {}).get(channel_id, [])
],
}
def get_add_member_response(self, team_id: str, request_json: dict) -> dict:
- roles = ["owner"] if self.newMemberTemplate.Role == "owner" else []
+ roles = ["owner"] if self.newMemberTemplate.role == "owner" else []
return {
ODATA_CONTEXT: f"{GRAPH_URL}#teams('{team_id}')/members/$entity",
ODATA_TYPE: "#microsoft.graph.aadUserConversationMember",
- "id": self.newMemberTemplate.ID,
+ "id": self.newMemberTemplate.id,
"roles": roles,
- "displayName": self.newMemberTemplate.DisplayName,
- "userId": self.newMemberTemplate.UserID,
- "email": self.newMemberTemplate.Email,
+ "displayName": self.newMemberTemplate.display_name,
+ "userId": self.newMemberTemplate.user_id,
+ "email": self.newMemberTemplate.email,
}
def get_update_member_role_response(self, team_id: str, channel_id: str, member_id: str, request_json: dict) -> Optional[dict]:
channel_members = self.members.get(team_id, {}).get(channel_id, [])
- member = next((m for m in channel_members if m.ID == member_id), None)
+ member = next((m for m in channel_members if m.id == member_id), None)
if not member:
return None
if "roles" in request_json:
- member.Role = "owner" if "owner" in request_json["roles"] else "member"
+ member.role = "owner" if "owner" in request_json["roles"] else "member"
return {
ODATA_CONTEXT: f"{GRAPH_URL}#teams('{team_id}')/channels('{channel_id}')/members/$entity",
ODATA_TYPE: "#microsoft.graph.aadUserConversationMember",
- "id": member.ID,
- "userId": member.UserID,
- "displayName": member.DisplayName,
- "roles": [member.Role] if member.Role == "owner" else [],
- "email": member.Email,
+ "id": member.id,
+ "userId": member.user_id,
+ "displayName": member.display_name,
+ "roles": [member.role] if member.role == "owner" else [],
+ "email": member.email,
}
def get_remove_member_response(self, team_id: str, channel_id: str, member_id: str) -> dict:
channel_members = self.members.get(team_id, {}).get(channel_id, [])
- member = next((m for m in channel_members if m.ID == member_id), None)
+ member = next((m for m in channel_members if m.id == member_id), None)
if not member:
return {"success": False}
channel_members.remove(member)
@@ -590,11 +590,11 @@ def get_list_group_chat_members_response(self, chat_id: str) -> dict:
"value": [
{
ODATA_TYPE: "#microsoft.graph.aadUserConversationMember",
- "id": member.ID,
- "userId": member.UserID,
- "displayName": member.DisplayName,
- "roles": [member.Role] if member.Role == "owner" else [],
- "email": member.Email,
+ "id": member.id,
+ "userId": member.user_id,
+ "displayName": member.display_name,
+ "roles": [member.role] if member.role == "owner" else [],
+ "email": member.email,
}
for member in self.group_chat_members.get(chat_id, [])
],
@@ -604,11 +604,11 @@ def get_add_member_to_group_chat_response(self, chat_id: str, request_json: dict
return {
ODATA_CONTEXT: f"{GRAPH_URL}#chats('{chat_id}')/members/$entity",
ODATA_TYPE: "#microsoft.graph.aadUserConversationMember",
- "id": self.newMemberTemplate.ID,
- "roles": [self.newMemberTemplate.Role] if self.newMemberTemplate.Role == "owner" else [],
- "displayName": self.newMemberTemplate.DisplayName,
- "userId": self.newMemberTemplate.UserID,
- "email": self.newMemberTemplate.Email,
+ "id": self.newMemberTemplate.id,
+ "roles": [self.newMemberTemplate.role] if self.newMemberTemplate.role == "owner" else [],
+ "displayName": self.newMemberTemplate.display_name,
+ "userId": self.newMemberTemplate.user_id,
+ "email": self.newMemberTemplate.email,
}
def get_remove_member_from_group_chat_response(self, chat_id: str, member_id: str) -> dict:
@@ -723,7 +723,7 @@ def get_list_pinned_messages_in_chat_response(self, chat_id: str) -> dict:
def get_me_response(self) -> dict:
return {
- "id": self.me.ID,
- "displayName": self.me.DisplayName,
- "email": self.me.Email,
+ "id": self.me.id,
+ "displayName": self.me.display_name,
+ "email": self.me.email,
}
\ No newline at end of file
diff --git a/python/tests/test_channels.py b/python/tests/test_channels.py
index 62ba85b..8830458 100644
--- a/python/tests/test_channels.py
+++ b/python/tests/test_channels.py
@@ -285,19 +285,19 @@ def test_list_members_integration(httpserver):
assert len(members) == len(expected_members)
- assert members[0].UserID == expected_members[0].UserID
- assert members[0].DisplayName == expected_members[0].DisplayName
- expected_role_0 = "owner" if expected_members[0].Role == "owner" else ""
- assert members[0].Role == expected_role_0
- assert members[0].Email == expected_members[0].Email
- assert members[0].ID == expected_members[0].ID
-
- assert members[1].UserID == expected_members[1].UserID
- assert members[1].DisplayName == expected_members[1].DisplayName
- expected_role_1 = "owner" if expected_members[1].Role == "owner" else ""
- assert members[1].Role == expected_role_1
- assert members[1].Email == expected_members[1].Email
- assert members[1].ID == expected_members[1].ID
+ assert members[0].user_id == expected_members[0].user_id
+ assert members[0].display_name == expected_members[0].display_name
+ expected_role_0 = "owner" if expected_members[0].role == "owner" else ""
+ assert members[0].role == expected_role_0
+ assert members[0].email == expected_members[0].email
+ assert members[0].id == expected_members[0].id
+
+ assert members[1].user_id == expected_members[1].user_id
+ assert members[1].display_name == expected_members[1].display_name
+ expected_role_1 = "owner" if expected_members[1].role == "owner" else ""
+ assert members[1].role == expected_role_1
+ assert members[1].email == expected_members[1].email
+ assert members[1].id == expected_members[1].id
finally:
client.close()
@@ -314,15 +314,15 @@ def test_add_member_integration(httpserver):
member = client.channels.add_member(
team_ref=data.teams[0].DisplayName,
channel_ref=data.channels[data.teams[0].ID][1].name,
- user_ref=data.newMemberTemplate.DisplayName,
- is_owner=True if data.newMemberTemplate.Role == "owner" else False,
+ user_ref=data.newMemberTemplate.display_name,
+ is_owner=True if data.newMemberTemplate.role == "owner" else False,
)
- assert member.UserID == data.newMemberTemplate.UserID
- assert member.DisplayName == data.newMemberTemplate.DisplayName
- assert member.Role == data.newMemberTemplate.Role
- assert member.Email == data.newMemberTemplate.Email
- assert member.ID == data.newMemberTemplate.ID
+ assert member.user_id == data.newMemberTemplate.user_id
+ assert member.display_name == data.newMemberTemplate.display_name
+ assert member.role == data.newMemberTemplate.role
+ assert member.email == data.newMemberTemplate.email
+ assert member.id == data.newMemberTemplate.id
finally:
client.close()
@@ -339,15 +339,15 @@ def test_update_member_role_integration(httpserver):
member = client.channels.update_member_role(
team_ref=data.teams[0].DisplayName,
channel_ref=data.channels[data.teams[0].ID][0].name,
- user_ref=data.members[data.teams[0].ID][data.channels[data.teams[0].ID][0].id][1].Email,
+ user_ref=data.members[data.teams[0].ID][data.channels[data.teams[0].ID][0].id][1].email,
is_owner=True
)
- assert member.UserID == data.members[data.teams[0].ID][data.channels[data.teams[0].ID][0].id][1].UserID
- assert member.DisplayName == data.members[data.teams[0].ID][data.channels[data.teams[0].ID][0].id][1].DisplayName
- assert member.Role == "owner"
- assert member.Email == data.members[data.teams[0].ID][data.channels[data.teams[0].ID][0].id][1].Email
- assert member.ID == data.members[data.teams[0].ID][data.channels[data.teams[0].ID][0].id][1].ID
+ assert member.user_id == data.members[data.teams[0].ID][data.channels[data.teams[0].ID][0].id][1].user_id
+ assert member.display_name == data.members[data.teams[0].ID][data.channels[data.teams[0].ID][0].id][1].display_name
+ assert member.role == "owner"
+ assert member.email == data.members[data.teams[0].ID][data.channels[data.teams[0].ID][0].id][1].email
+ assert member.id == data.members[data.teams[0].ID][data.channels[data.teams[0].ID][0].id][1].id
finally:
client.close()
@@ -363,7 +363,7 @@ def test_remove_member_integration(httpserver):
success = client.channels.remove_member(
team_ref=data.teams[0].DisplayName,
channel_ref=data.channels[data.teams[0].ID][0].name,
- user_ref=data.members[data.teams[0].ID][data.channels[data.teams[0].ID][0].id][1].Email,
+ user_ref=data.members[data.teams[0].ID][data.channels[data.teams[0].ID][0].id][1].email,
)
assert success is True
@@ -385,10 +385,10 @@ def test_get_mention_integration(httpserver):
raw_mentions=MentionKind.TEAM.value,
)
- assert mention[0].Kind == MentionKind.TEAM.value
- assert mention[0].AtID == 0
- assert mention[0].Text == data.teams[0].DisplayName
- assert mention[0].TargetID == data.teams[0].ID
+ assert mention[0].kind == MentionKind.TEAM.value
+ assert mention[0].at_id == 0
+ assert mention[0].text == data.teams[0].DisplayName
+ assert mention[0].target_id == data.teams[0].ID
finally:
client.close()
diff --git a/python/tests/test_chats.py b/python/tests/test_chats.py
index adaf71e..cb971de 100644
--- a/python/tests/test_chats.py
+++ b/python/tests/test_chats.py
@@ -66,8 +66,8 @@ def test_create_group_chat_integration(httpserver):
chat = client.chats.create_group_chat(
topic=data.newChatTemplate.topic,
recipient_refs=[
- data.users[0].Email,
- data.users[1].Email,
+ data.users[0].email,
+ data.users[1].email,
],
include_me=True,
)
@@ -89,7 +89,7 @@ def test_create_one_on_one_chat_integration(httpserver):
init_fake_client(client, httpserver.url_for(""))
chat = client.chats.create_one_on_one(
- recipient_ref=data.users[0].Email,
+ recipient_ref=data.users[0].email,
)
assert chat.id == data.newChatTemplate.id
@@ -113,19 +113,19 @@ def test_list_group_chat_members_integration(httpserver):
assert len(members) == len(data.group_chat_members[data.group_chats[0].id])
- assert members[0].ID == data.group_chat_members[data.group_chats[0].id][0].ID
- assert members[0].UserID == data.group_chat_members[data.group_chats[0].id][0].UserID
- assert members[0].DisplayName == data.group_chat_members[data.group_chats[0].id][0].DisplayName
- if data.group_chat_members[data.group_chats[0].id][0].Role == "owner":
- assert members[0].Role == "owner"
- assert members[0].Email == data.group_chat_members[data.group_chats[0].id][0].Email
+ assert members[0].id == data.group_chat_members[data.group_chats[0].id][0].id
+ assert members[0].user_id == data.group_chat_members[data.group_chats[0].id][0].user_id
+ assert members[0].display_name == data.group_chat_members[data.group_chats[0].id][0].display_name
+ if data.group_chat_members[data.group_chats[0].id][0].role == "owner":
+ assert members[0].role == "owner"
+ assert members[0].email == data.group_chat_members[data.group_chats[0].id][0].email
- assert members[1].ID == data.group_chat_members[data.group_chats[0].id][1].ID
- assert members[1].UserID == data.group_chat_members[data.group_chats[0].id][1].UserID
- assert members[1].DisplayName == data.group_chat_members[data.group_chats[0].id][1].DisplayName
- if data.group_chat_members[data.group_chats[0].id][1].Role == "owner":
- assert members[1].Role == "owner"
- assert members[1].Email == data.group_chat_members[data.group_chats[0].id][1].Email
+ assert members[1].id == data.group_chat_members[data.group_chats[0].id][1].id
+ assert members[1].user_id == data.group_chat_members[data.group_chats[0].id][1].user_id
+ assert members[1].display_name == data.group_chat_members[data.group_chats[0].id][1].display_name
+ if data.group_chat_members[data.group_chats[0].id][1].role == "owner":
+ assert members[1].role == "owner"
+ assert members[1].email == data.group_chat_members[data.group_chats[0].id][1].email
finally:
client.close()
@@ -141,14 +141,14 @@ def test_add_group_chat_member_integration(httpserver):
member = client.chats.add_member_to_group_chat(
group_chat_ref=data.group_chats[0].topic,
- user_ref=data.newMemberTemplate.Email,
+ user_ref=data.newMemberTemplate.email,
)
- assert member.ID == data.newMemberTemplate.ID
- assert member.UserID == data.newMemberTemplate.UserID
- assert member.DisplayName == data.newMemberTemplate.DisplayName
- assert member.Role == data.newMemberTemplate.Role
- assert member.Email == data.newMemberTemplate.Email
+ assert member.id == data.newMemberTemplate.id
+ assert member.user_id == data.newMemberTemplate.user_id
+ assert member.display_name == data.newMemberTemplate.display_name
+ assert member.role == data.newMemberTemplate.role
+ assert member.email == data.newMemberTemplate.email
finally:
client.close()
@@ -163,7 +163,7 @@ def test_remove_group_chat_member_integration(httpserver):
result = client.chats.remove_member_from_group_chat(
group_chat_ref=data.group_chats[0].topic,
- member_ref=data.group_chat_members[data.group_chats[0].id][0].Email,
+ member_ref=data.group_chat_members[data.group_chats[0].id][0].email,
)
assert result is True
@@ -442,10 +442,10 @@ def test_get_mention_integration(httpserver):
raw_mentions=MentionKind.EVERYONE.value,
)
- assert mention[0].Kind == MentionKind.EVERYONE.value
- assert mention[0].AtID == 0
- assert mention[0].Text == "Everyone"
- assert mention[0].TargetID == data.group_chats[0].id
+ assert mention[0].kind == MentionKind.EVERYONE.value
+ assert mention[0].at_id == 0
+ assert mention[0].text == "Everyone"
+ assert mention[0].target_id == data.group_chats[0].id
finally:
client.close()
From 07b2da5b9ef01830479465274327cf35edc116a4 Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Thu, 8 Jan 2026 10:43:36 +0100
Subject: [PATCH 68/99] fix: tests after changing models fields to snake_case
---
python/teams_lib_pzsp2_z1/model/mention.py | 2 +-
python/teams_lib_pzsp2_z1/model/message.py | 39 ++---
.../teams_lib_pzsp2_z1/services/channels.py | 121 ++++++++------
python/teams_lib_pzsp2_z1/services/chats.py | 88 +++++-----
python/tests/fake_server/data.py | 150 +++++++++---------
python/tests/test_channels.py | 116 +++++++-------
python/tests/test_chats.py | 136 ++++++++--------
7 files changed, 338 insertions(+), 314 deletions(-)
diff --git a/python/teams_lib_pzsp2_z1/model/mention.py b/python/teams_lib_pzsp2_z1/model/mention.py
index 273be4f..950f49f 100644
--- a/python/teams_lib_pzsp2_z1/model/mention.py
+++ b/python/teams_lib_pzsp2_z1/model/mention.py
@@ -24,7 +24,7 @@ class Mention:
"""Represents a mention entity within a Microsoft Teams message.
This object maps a specific entity (User, Channel, etc.) to a placeholder
- in the message content.
+ in the message content. Can be obtained by get_mentions() method in the Chats and Channels service.
Attributes:
kind (MentionKind): The type of the mention.
diff --git a/python/teams_lib_pzsp2_z1/model/message.py b/python/teams_lib_pzsp2_z1/model/message.py
index e3decf2..485acf6 100644
--- a/python/teams_lib_pzsp2_z1/model/message.py
+++ b/python/teams_lib_pzsp2_z1/model/message.py
@@ -12,43 +12,44 @@ class MessageContentType(Enum):
@dataclass
class MessageFrom:
- UserID: str
- DisplayName: str
+ user_id: str
+ display_name: str
@dataclass
class Message:
- ID: str
- Content: str
- ContentType: MessageContentType
- CreatedDateTime: datetime
- From: MessageFrom
- ReplyCount: int
+ id: str
+ content: str
+ content_type: MessageContentType
+ created_date_time: datetime
+ sender: MessageFrom
+ reply_count: int
@dataclass
class MessageBody:
- ContentType: MessageContentType
- Content: str
- Mentions: list[Mention]
+ content_type: MessageContentType
+ content: str
+ mentions: list[Mention]
def __dict__(self):
return {
- "ContentType": self.ContentType.value,
- "Content": self.Content,
+ "ContentType": self.content_type.value,
+ "Content": self.content,
}
def __iter__(self):
- yield "ContentType", self.ContentType.value
- yield "Content", self.Content
+ yield "ContentType", self.content_type.value
+ yield "Content", self.content
@dataclass
class ListMessagesOptions:
- Top: int | None = None
- ExpandReplies: bool = False
+ top: int | None = None
+ expand_replies: bool = False
+
@dataclass
class MessageCollection:
- Messages: list[Message]
- NextLink: str | None = None
+ messages: list[Message]
+ next_link: str | None = None
diff --git a/python/teams_lib_pzsp2_z1/services/channels.py b/python/teams_lib_pzsp2_z1/services/channels.py
index 8bc0207..6c39c77 100644
--- a/python/teams_lib_pzsp2_z1/services/channels.py
+++ b/python/teams_lib_pzsp2_z1/services/channels.py
@@ -4,7 +4,7 @@
from teams_lib_pzsp2_z1.model.channel import Channel
from teams_lib_pzsp2_z1.model.member import Member
-from teams_lib_pzsp2_z1.model.mention import Mention
+from teams_lib_pzsp2_z1.model.mention import Mention, MentionKind
from teams_lib_pzsp2_z1.model.message import (
Message,
MessageBody,
@@ -190,15 +190,15 @@ def send_message(
)
return Message(
- ID=response["ID"],
- Content=response["Content"],
- ContentType=MessageContentType(response["ContentType"]),
- CreatedDateTime=response["CreatedDateTime"],
- From=MessageFrom(
- UserID=response["From"]["UserID"],
- DisplayName=response["From"]["DisplayName"],
+ id=response["ID"],
+ content=response["Content"],
+ content_type=MessageContentType(response["ContentType"]),
+ created_date_time=response["CreatedDateTime"],
+ sender=MessageFrom(
+ user_id=response["From"]["UserID"],
+ display_name=response["From"]["DisplayName"],
),
- ReplyCount=response["ReplyCount"],
+ reply_count=response["ReplyCount"],
)
def list_messages( # noqa: PLR0913
@@ -239,21 +239,21 @@ def list_messages( # noqa: PLR0913
)
return MessageCollection(
- Messages=[
+ messages=[
Message(
- ID=msg["ID"],
- Content=msg["Content"],
- ContentType=MessageContentType(msg["ContentType"]),
- CreatedDateTime=msg["CreatedDateTime"],
- From=MessageFrom(
- UserID=msg["From"]["UserID"],
- DisplayName=msg["From"]["DisplayName"],
+ id=msg["ID"],
+ content=msg["Content"],
+ content_type=MessageContentType(msg["ContentType"]),
+ created_date_time=msg["CreatedDateTime"],
+ sender=MessageFrom(
+ user_id=msg["From"]["UserID"],
+ display_name=msg["From"]["DisplayName"],
),
- ReplyCount=msg["ReplyCount"],
+ reply_count=msg["ReplyCount"],
)
for msg in response["Messages"]
],
- NextLink=response.get("NextLink"),
+ next_link=response.get("NextLink"),
)
def get_message(self, team_ref: str, channel_ref: str, message_id: str) -> Message:
@@ -278,15 +278,15 @@ def get_message(self, team_ref: str, channel_ref: str, message_id: str) -> Messa
)
return Message(
- ID=response["ID"],
- Content=response["Content"],
- ContentType=MessageContentType(response["ContentType"]),
- CreatedDateTime=response["CreatedDateTime"],
- From=MessageFrom(
- UserID=response["From"]["UserID"],
- DisplayName=response["From"]["DisplayName"],
+ id=response["ID"],
+ content=response["Content"],
+ content_type=MessageContentType(response["ContentType"]),
+ created_date_time=response["CreatedDateTime"],
+ sender=MessageFrom(
+ user_id=response["From"]["UserID"],
+ display_name=response["From"]["DisplayName"],
),
- ReplyCount=response["ReplyCount"],
+ reply_count=response["ReplyCount"],
)
def list_message_replies( # noqa: PLR0913
@@ -325,21 +325,21 @@ def list_message_replies( # noqa: PLR0913
)
return MessageCollection(
- Messages=[
+ messages=[
Message(
- ID=msg["ID"],
- Content=msg["Content"],
- ContentType=MessageContentType(msg["ContentType"]),
- CreatedDateTime=msg["CreatedDateTime"],
- From=MessageFrom(
- UserID=msg["From"]["UserID"],
- DisplayName=msg["From"]["DisplayName"],
+ id=msg["ID"],
+ content=msg["Content"],
+ content_type=MessageContentType(msg["ContentType"]),
+ created_date_time=msg["CreatedDateTime"],
+ sender=MessageFrom(
+ user_id=msg["From"]["UserID"],
+ display_name=msg["From"]["DisplayName"],
),
- ReplyCount=msg["ReplyCount"],
+ reply_count=msg["ReplyCount"],
)
for msg in response["Messages"]
],
- NextLink=response.get("NextLink"),
+ next_link=response.get("NextLink"),
)
def get_message_reply(
@@ -372,15 +372,15 @@ def get_message_reply(
)
return Message(
- ID=response["ID"],
- Content=response["Content"],
- ContentType=MessageContentType(response["ContentType"]),
- CreatedDateTime=response["CreatedDateTime"],
- From=MessageFrom(
- UserID=response["From"]["UserID"],
- DisplayName=response["From"]["DisplayName"],
+ id=response["ID"],
+ content=response["Content"],
+ content_type=MessageContentType(response["ContentType"]),
+ created_date_time=response["CreatedDateTime"],
+ sender=MessageFrom(
+ user_id=response["From"]["UserID"],
+ display_name=response["From"]["DisplayName"],
),
- ReplyCount=response["ReplyCount"],
+ reply_count=response["ReplyCount"],
)
def list_members(self, team_ref: str, channel_ref: str) -> list[Member]:
@@ -401,7 +401,13 @@ def list_members(self, team_ref: str, channel_ref: str) -> list[Member]:
"channelRef": channel_ref,
},
)
- return [Member(**member) for member in response]
+ return [Member(
+ id=member["ID"],
+ user_id=member["UserID"],
+ display_name=member["DisplayName"],
+ role=member["Role"],
+ email=member["Email"],
+ ) for member in response]
def add_member(
self, team_ref: str, channel_ref: str, user_ref: str, is_owner: bool
@@ -427,7 +433,13 @@ def add_member(
"isOwner": is_owner,
},
)
- return Member(**response)
+ return Member(
+ id=response["ID"],
+ user_id=response["UserID"],
+ display_name=response["DisplayName"],
+ role=response["Role"],
+ email=response["Email"],
+ )
def update_member_role(
self, team_ref: str, channel_ref: str, user_ref: str, is_owner: bool
@@ -453,7 +465,13 @@ def update_member_role(
"isOwner": is_owner,
},
)
- return Member(**response)
+ return Member(
+ id=response["ID"],
+ user_id=response["UserID"],
+ display_name=response["DisplayName"],
+ role=response["Role"],
+ email=response["Email"],
+ )
def remove_member(self, team_ref: str, channel_ref: str, user_ref: str) -> bool:
"""Removes a user from a channel.
@@ -505,4 +523,9 @@ def get_mentions(
"rawMentions": raw_mentions,
},
)
- return [Mention(**mention) for mention in response]
+ return [Mention(
+ kind=MentionKind(mention["Kind"]),
+ at_id=mention["AtID"],
+ text=mention["Text"],
+ target_id=mention["TargetID"],
+ ) for mention in response]
diff --git a/python/teams_lib_pzsp2_z1/services/chats.py b/python/teams_lib_pzsp2_z1/services/chats.py
index 1b9705c..1d072d2 100644
--- a/python/teams_lib_pzsp2_z1/services/chats.py
+++ b/python/teams_lib_pzsp2_z1/services/chats.py
@@ -225,21 +225,21 @@ def list_messages(
)
return MessageCollection(
- Messages=[
+ messages=[
Message(
- ID=msg["ID"],
- Content=msg["Content"],
- ContentType=MessageContentType(msg["ContentType"]),
- CreatedDateTime=msg["CreatedDateTime"],
- From=MessageFrom(
- UserID=msg["From"]["UserID"],
- DisplayName=msg["From"]["DisplayName"],
+ id=msg["ID"],
+ content=msg["Content"],
+ content_type=MessageContentType(msg["ContentType"]),
+ created_date_time=msg["CreatedDateTime"],
+ sender=MessageFrom(
+ user_id=msg["From"]["UserID"],
+ display_name=msg["From"]["DisplayName"],
),
- ReplyCount=msg["ReplyCount"],
+ reply_count=msg["ReplyCount"],
)
for msg in messages["Messages"]
],
- NextLink=messages.get("NextLink"),
+ next_link=messages.get("NextLink"),
)
def send_message(self, chat_ref: ChatRef, body: MessageBody) -> Message:
@@ -261,22 +261,22 @@ def send_message(self, chat_ref: ChatRef, body: MessageBody) -> Message:
"type": chat_ref.type.value,
},
"body": {
- "content": body.Content,
- "contentType": body.ContentType.value,
+ "content": body.content,
+ "contentType": body.content_type.value,
},
},
)
return Message(
- ID=message["ID"],
- Content=message["Content"],
- ContentType=MessageContentType(message["ContentType"]),
- From=MessageFrom(
- UserID=message["From"]["UserID"],
- DisplayName=message["From"]["DisplayName"],
+ id=message["ID"],
+ content=message["Content"],
+ content_type=MessageContentType(message["ContentType"]),
+ sender=MessageFrom(
+ user_id=message["From"]["UserID"],
+ display_name=message["From"]["DisplayName"],
),
- CreatedDateTime=message["CreatedDateTime"],
- ReplyCount=message["ReplyCount"],
+ created_date_time=message["CreatedDateTime"],
+ reply_count=message["ReplyCount"],
)
def delete_message(self, chat_ref: ChatRef, message_id: str) -> bool:
@@ -328,15 +328,15 @@ def get_message(self, chat_ref: ChatRef, message_id: str) -> Message:
)
return Message(
- ID=message["ID"],
- Content=message["Content"],
- ContentType=MessageContentType(message["ContentType"]),
- From=MessageFrom(
- UserID=message["From"]["UserID"],
- DisplayName=message["From"]["DisplayName"],
+ id=message["ID"],
+ content=message["Content"],
+ content_type=MessageContentType(message["ContentType"]),
+ sender=MessageFrom(
+ user_id=message["From"]["UserID"],
+ display_name=message["From"]["DisplayName"],
),
- CreatedDateTime=message["CreatedDateTime"],
- ReplyCount=message["ReplyCount"],
+ created_date_time=message["CreatedDateTime"],
+ reply_count=message["ReplyCount"],
)
def list_my_joined(self, chat_type: ChatType | None = None) -> list[Chat]:
@@ -398,15 +398,15 @@ def list_all_messages(
return [
Message(
- ID=message["ID"],
- Content=message["Content"],
- ContentType=MessageContentType(message["ContentType"]),
- From=MessageFrom(
- UserID=message["From"]["UserID"],
- DisplayName=message["From"]["DisplayName"],
+ id=message["ID"],
+ content=message["Content"],
+ content_type=MessageContentType(message["ContentType"]),
+ sender=MessageFrom(
+ user_id=message["From"]["UserID"],
+ display_name=message["From"]["DisplayName"],
),
- CreatedDateTime=message["CreatedDateTime"],
- ReplyCount=message["ReplyCount"],
+ created_date_time=message["CreatedDateTime"],
+ reply_count=message["ReplyCount"],
)
for message in messages
]
@@ -433,15 +433,15 @@ def list_pinned_messages(self, chat_ref: ChatRef) -> list[Message]:
return [
Message(
- ID=message["ID"],
- Content=message["Content"],
- ContentType=MessageContentType(message["ContentType"]),
- From=MessageFrom(
- UserID=message["From"]["UserID"],
- DisplayName=message["From"]["DisplayName"],
+ id=message["ID"],
+ content=message["Content"],
+ content_type=MessageContentType(message["ContentType"]),
+ sender=MessageFrom(
+ user_id=message["From"]["UserID"],
+ display_name=message["From"]["DisplayName"],
),
- CreatedDateTime=message["CreatedDateTime"],
- ReplyCount=message["ReplyCount"],
+ created_date_time=message["CreatedDateTime"],
+ reply_count=message["ReplyCount"],
)
for message in messages
]
diff --git a/python/tests/fake_server/data.py b/python/tests/fake_server/data.py
index ab24160..82bbf00 100644
--- a/python/tests/fake_server/data.py
+++ b/python/tests/fake_server/data.py
@@ -40,12 +40,12 @@ class FakeServerData:
newChannelName: str = "New Channel"
newChannelID: str = "19:newchannelid@thread.tacv2"
newMessageTemplate: Message = field(default_factory=lambda: Message(
- ID="new-message-id",
- Content="This is a new message.",
- ContentType="text",
- From=MessageFrom(UserID="user-new-001", DisplayName="New User"),
- CreatedDateTime="2024-01-02T10:00:00Z",
- ReplyCount=0,
+ id="new-message-id",
+ content="This is a new message.",
+ content_type="text",
+ sender=MessageFrom(user_id="user-new-001", display_name="New User"),
+ created_date_time="2024-01-02T10:00:00Z",
+ reply_count=0,
))
newMemberTemplate: Member = field(default_factory=lambda: Member(
id="user-new-002", user_id="new-user-002", display_name="New Member",
@@ -76,16 +76,16 @@ def __init__(self) -> None:
# --- Messages (Channel) ---
self.messages = {
"19:123123@thread.tacv2": [
- Message(ID="msg-001", Content="Hello, team!", ContentType="text", From=MessageFrom(UserID="user-123-abc", DisplayName="Alice"), CreatedDateTime="2024-01-01T10:00:00Z", ReplyCount=0),
- Message(ID="msg-002", Content="Don't forget the meeting at 3 PM.", ContentType="text", From=MessageFrom(UserID="user-456-def", DisplayName="Bob"), CreatedDateTime="2024-01-01T11:00:00Z", ReplyCount=2),
+ Message(id="msg-001", content="Hello, team!", content_type="text", sender=MessageFrom(user_id="user-123-abc", display_name="Alice"), created_date_time="2024-01-01T10:00:00Z", reply_count=0),
+ Message(id="msg-002", content="Don't forget the meeting at 3 PM.", content_type="text", sender=MessageFrom(user_id="user-456-def", display_name="Bob"), created_date_time="2024-01-01T11:00:00Z", reply_count=2),
],
}
# --- Replies ---
self.replies = {
"msg-002": [
- Message(ID="msg-002-reply-001", Content="Thanks for the reminder!", ContentType="text", From=MessageFrom(UserID="user-789-ghi", DisplayName="Charlie"), CreatedDateTime="2024-01-01T12:00:00Z", ReplyCount=0),
- Message(ID="msg-002-reply-002", Content="I'll be there.", ContentType="text", From=MessageFrom(UserID="user-123-abc", DisplayName="Alice"), CreatedDateTime="2024-01-01T12:30:00Z", ReplyCount=0),
+ Message(id="msg-002-reply-001", content="Thanks for the reminder!", content_type="text", sender=MessageFrom(user_id="user-789-ghi", display_name="Charlie"), created_date_time="2024-01-01T12:00:00Z", reply_count=0),
+ Message(id="msg-002-reply-002", content="I'll be there.", content_type="text", sender=MessageFrom(user_id="user-123-abc", display_name="Alice"), created_date_time="2024-01-01T12:30:00Z", reply_count=0),
],
}
@@ -124,8 +124,8 @@ def __init__(self) -> None:
self.chat_messages = {
"chat-123-abc": [
- Message(ID="msg-001", Content="Hello, team!", ContentType="text", From=MessageFrom(UserID="user-123-abc", DisplayName="Alice"), CreatedDateTime="2024-01-01T10:00:00Z", ReplyCount=0),
- Message(ID="msg-002", Content="Don't forget the meeting at 3 PM.", ContentType="text", From=MessageFrom(UserID="user-456-def", DisplayName="Bob"), CreatedDateTime="2024-01-01T11:00:00Z", ReplyCount=2),
+ Message(id="msg-001", content="Hello, team!", content_type="text", sender=MessageFrom(user_id="user-123-abc", display_name="Alice"), created_date_time="2024-01-01T10:00:00Z", reply_count=0),
+ Message(id="msg-002", content="Don't forget the meeting at 3 PM.", content_type="text", sender=MessageFrom(user_id="user-456-def", display_name="Bob"), created_date_time="2024-01-01T11:00:00Z", reply_count=2),
],
}
@@ -143,9 +143,9 @@ def __init__(self) -> None:
# Initialize templates manually to match original structure exactly if factories behave differently
self.newMessageTemplate = Message(
- ID="new-message-id", Content="This is a new message.", ContentType="text",
- From=MessageFrom(UserID="user-new-001", DisplayName="New User"),
- CreatedDateTime="2024-01-02T10:00:00Z", ReplyCount=0
+ id="new-message-id", content="This is a new message.", content_type="text",
+ sender=MessageFrom(user_id="user-new-001", display_name="New User"),
+ created_date_time="2024-01-02T10:00:00Z", reply_count=0
)
self.newMemberTemplate = Member(
id="user-new-002", user_id="new-user-002", display_name="New Member",
@@ -381,99 +381,99 @@ def get_list_messages_response(self, team_id: str, channel_id: str) -> dict:
return {
"value": [
{
- "id": message.ID,
+ "id": message.id,
"body": {
- "content": message.Content,
- "contentType": message.ContentType,
+ "content": message.content,
+ "contentType": message.content_type,
},
"from": {
"user": {
- "id": message.From.UserID,
- "displayName": message.From.DisplayName,
+ "id": message.sender.user_id,
+ "displayName": message.sender.display_name,
}
},
- "createdDateTime": message.CreatedDateTime,
- "replies": [{"id": f"dummy-reply-{i}"} for i in range(message.ReplyCount)]
+ "createdDateTime": message.created_date_time,
+ "replies": [{"id": f"dummy-reply-{i}"} for i in range(message.reply_count)]
}
for message in self.messages.get(channel_id, [])
],
}
def get_message_response(self, team_id: str, channel_id: str, message_id: str) -> Optional[dict]:
- message = next((m for m in self.messages.get(channel_id, []) if m.ID == message_id), None)
+ message = next((m for m in self.messages.get(channel_id, []) if m.id == message_id), None)
if not message:
return None
return {
- "id": message.ID,
+ "id": message.id,
"body": {
- "content": message.Content,
- "contentType": message.ContentType,
+ "content": message.content,
+ "contentType": message.content_type,
},
"from": {
"user": {
- "id": message.From.UserID,
- "displayName": message.From.DisplayName,
+ "id": message.sender.user_id,
+ "displayName": message.sender.display_name,
}
},
- "createdDateTime": message.CreatedDateTime,
- "replies": [{"id": f"dummy-reply-{i}"} for i in range(message.ReplyCount)]
+ "createdDateTime": message.created_date_time,
+ "replies": [{"id": f"dummy-reply-{i}"} for i in range(message.reply_count)]
}
def get_send_message_response(self, team_id: str, channel_id: str, request_json: dict) -> dict:
return {
- "id": self.newMessageTemplate.ID,
+ "id": self.newMessageTemplate.id,
"body": {
"content": request_json.get("body", {}).get("content"),
"contentType": request_json.get("body", {}).get("contentType"),
},
"from": {
"user": {
- "id": self.newMessageTemplate.From.UserID,
- "displayName": self.newMessageTemplate.From.DisplayName,
+ "id": self.newMessageTemplate.sender.user_id,
+ "displayName": self.newMessageTemplate.sender.display_name,
}
},
- "createdDateTime": self.newMessageTemplate.CreatedDateTime,
+ "createdDateTime": self.newMessageTemplate.created_date_time,
}
def get_list_replies_response(self, team_id: str, channel_id: str, message_id: str) -> dict:
return {
"value": [
{
- "id": reply.ID,
+ "id": reply.id,
"body": {
- "content": reply.Content,
- "contentType": reply.ContentType,
+ "content": reply.content,
+ "contentType": reply.content_type,
},
"from": {
"user": {
- "id": reply.From.UserID,
- "displayName": reply.From.DisplayName,
+ "id": reply.sender.user_id,
+ "displayName": reply.sender.display_name,
}
},
- "createdDateTime": reply.CreatedDateTime,
+ "createdDateTime": reply.created_date_time,
}
for reply in self.replies.get(message_id, [])
],
}
def get_reply_response(self, team_id: str, channel_id: str, message_id: str, reply_id: str) -> Optional[dict]:
- reply = next((r for r in self.replies.get(message_id, []) if r.ID == reply_id), None)
+ reply = next((r for r in self.replies.get(message_id, []) if r.id == reply_id), None)
if not reply:
return None
return {
- "id": reply.ID,
+ "id": reply.id,
"body": {
- "content": reply.Content,
- "contentType": reply.ContentType,
+ "content": reply.content,
+ "contentType": reply.content_type,
},
"from": {
"user": {
- "id": reply.From.UserID,
- "displayName": reply.From.DisplayName,
+ "id": reply.sender.user_id,
+ "displayName": reply.sender.display_name,
}
},
- "createdDateTime": reply.CreatedDateTime,
+ "createdDateTime": reply.created_date_time,
}
# ==========================================
@@ -622,59 +622,59 @@ def get_list_messages_in_chat_response(self, chat_id: str) -> dict:
return {
"value": [
{
- "id": message.ID,
+ "id": message.id,
"body": {
- "content": message.Content,
- "contentType": message.ContentType,
+ "content": message.content,
+ "contentType": message.content_type,
},
"from": {
"user": {
- "id": message.From.UserID,
- "displayName": message.From.DisplayName,
+ "id": message.sender.user_id,
+ "displayName": message.sender.display_name,
}
},
- "createdDateTime": message.CreatedDateTime,
- "replies": [{"id": f"dummy-reply-{i}"} for i in range(message.ReplyCount)]
+ "createdDateTime": message.created_date_time,
+ "replies": [{"id": f"dummy-reply-{i}"} for i in range(message.reply_count)]
}
for message in self.chat_messages.get(chat_id, [])
],
}
def get_get_message_in_chat_response(self, chat_id: str, message_id: str) -> Optional[dict]:
- message = next((m for m in self.chat_messages.get(chat_id, []) if m.ID == message_id), None)
+ message = next((m for m in self.chat_messages.get(chat_id, []) if m.id == message_id), None)
if not message:
return None
return {
- "id": message.ID,
+ "id": message.id,
"body": {
- "content": message.Content,
- "contentType": message.ContentType,
+ "content": message.content,
+ "contentType": message.content_type,
},
"from": {
"user": {
- "id": message.From.UserID,
- "displayName": message.From.DisplayName,
+ "id": message.sender.user_id,
+ "displayName": message.sender.display_name,
}
},
- "createdDateTime": message.CreatedDateTime,
- "replies": [{"id": f"dummy-reply-{i}"} for i in range(message.ReplyCount)]
+ "createdDateTime": message.created_date_time,
+ "replies": [{"id": f"dummy-reply-{i}"} for i in range(message.reply_count)]
}
def get_send_message_in_chat_response(self, chat_id: str, request_json: dict) -> dict:
return {
- "id": self.newMessageTemplate.ID,
+ "id": self.newMessageTemplate.id,
"body": {
"content": request_json.get("body", {}).get("content"),
"contentType": request_json.get("body", {}).get("contentType"),
},
"from": {
"user": {
- "id": self.newMessageTemplate.From.UserID,
- "displayName": self.newMessageTemplate.From.DisplayName,
+ "id": self.newMessageTemplate.sender.user_id,
+ "displayName": self.newMessageTemplate.sender.display_name,
}
},
- "createdDateTime": self.newMessageTemplate.CreatedDateTime,
+ "createdDateTime": self.newMessageTemplate.created_date_time,
}
def get_all_messeges_in_chats_response(self) -> dict:
@@ -685,22 +685,22 @@ def get_all_messeges_in_chats_response(self) -> dict:
"value": [
{
ODATA_TYPE: "#microsoft.graph.chatMessage",
- "id": message.ID,
- "etag": message.ID,
+ "id": message.id,
+ "etag": message.id,
"messageType": "message",
"chatId": target_chat_id,
"body": {
- "content": message.Content,
- "contentType": message.ContentType,
+ "content": message.content,
+ "contentType": message.content_type,
},
"from": {
"user": {
- "id": message.From.UserID,
- "displayName": message.From.DisplayName,
+ "id": message.sender.user_id,
+ "displayName": message.sender.display_name,
}
},
- "createdDateTime": message.CreatedDateTime,
- "lastModifiedDateTime": message.CreatedDateTime,
+ "createdDateTime": message.created_date_time,
+ "lastModifiedDateTime": message.created_date_time,
}
for message in self.chat_messages[target_chat_id]
],
@@ -711,7 +711,7 @@ def get_list_pinned_messages_in_chat_response(self, chat_id: str) -> dict:
"value": [
{
ODATA_TYPE: "#microsoft.graph.pinnedChatMessageInfo",
- "id": message.ID,
+ "id": message.id,
}
for message in self.chat_messages.get(chat_id, [])
],
diff --git a/python/tests/test_channels.py b/python/tests/test_channels.py
index 8830458..8635287 100644
--- a/python/tests/test_channels.py
+++ b/python/tests/test_channels.py
@@ -129,19 +129,19 @@ def test_send_message_integration(httpserver):
team_ref=data.teams[0].DisplayName,
channel_ref=data.channels[data.teams[0].ID][0].name,
body=MessageBody(
- Content=data.newMessageTemplate.Content,
- ContentType=MessageContentType.TEXT,
- Mentions=[],
+ content=data.newMessageTemplate.content,
+ content_type=MessageContentType.TEXT,
+ mentions=[],
)
)
- assert message.ID == data.newMessageTemplate.ID
- assert message.Content == data.newMessageTemplate.Content
- assert message.ContentType == MessageContentType(data.newMessageTemplate.ContentType)
- assert message.From.UserID == data.newMessageTemplate.From.UserID
- assert message.From.DisplayName == data.newMessageTemplate.From.DisplayName
- assert message.ReplyCount == data.newMessageTemplate.ReplyCount
- assert message.CreatedDateTime == data.newMessageTemplate.CreatedDateTime
+ assert message.id == data.newMessageTemplate.id
+ assert message.content == data.newMessageTemplate.content
+ assert message.content_type == MessageContentType(data.newMessageTemplate.content_type)
+ assert message.sender.user_id == data.newMessageTemplate.sender.user_id
+ assert message.sender.display_name == data.newMessageTemplate.sender.display_name
+ assert message.reply_count == data.newMessageTemplate.reply_count
+ assert message.created_date_time == data.newMessageTemplate.created_date_time
finally:
client.close()
@@ -160,22 +160,22 @@ def test_list_messages_integration(httpserver):
channel_ref=data.channels[data.teams[0].ID][0].name,
)
- assert len(collection.Messages) == len(data.messages[data.channels[data.teams[0].ID][0].id])
- assert collection.Messages[0].ID == data.messages[data.channels[data.teams[0].ID][0].id][0].ID
- assert collection.Messages[0].Content == data.messages[data.channels[data.teams[0].ID][0].id][0].Content
- assert collection.Messages[0].ContentType == MessageContentType(data.messages[data.channels[data.teams[0].ID][0].id][0].ContentType)
- assert collection.Messages[0].From.UserID == data.messages[data.channels[data.teams[0].ID][0].id][0].From.UserID
- assert collection.Messages[0].From.DisplayName == data.messages[data.channels[data.teams[0].ID][0].id][0].From.DisplayName
- assert collection.Messages[0].ReplyCount == data.messages[data.channels[data.teams[0].ID][0].id][0].ReplyCount
- assert collection.Messages[0].CreatedDateTime == data.messages[data.channels[data.teams[0].ID][0].id][0].CreatedDateTime
-
- assert collection.Messages[1].ID == data.messages[data.channels[data.teams[0].ID][0].id][1].ID
- assert collection.Messages[1].Content == data.messages[data.channels[data.teams[0].ID][0].id][1].Content
- assert collection.Messages[1].ContentType == MessageContentType(data.messages[data.channels[data.teams[0].ID][0].id][1].ContentType)
- assert collection.Messages[1].From.UserID == data.messages[data.channels[data.teams[0].ID][0].id][1].From.UserID
- assert collection.Messages[1].From.DisplayName == data.messages[data.channels[data.teams[0].ID][0].id][1].From.DisplayName
- assert collection.Messages[1].ReplyCount == data.messages[data.channels[data.teams[0].ID][0].id][1].ReplyCount
- assert collection.Messages[1].CreatedDateTime == data.messages[data.channels[data.teams[0].ID][0].id][1].CreatedDateTime
+ assert len(collection.messages) == len(data.messages[data.channels[data.teams[0].ID][0].id])
+ assert collection.messages[0].id == data.messages[data.channels[data.teams[0].ID][0].id][0].id
+ assert collection.messages[0].content == data.messages[data.channels[data.teams[0].ID][0].id][0].content
+ assert collection.messages[0].content_type == MessageContentType(data.messages[data.channels[data.teams[0].ID][0].id][0].content_type)
+ assert collection.messages[0].sender.user_id == data.messages[data.channels[data.teams[0].ID][0].id][0].sender.user_id
+ assert collection.messages[0].sender.display_name == data.messages[data.channels[data.teams[0].ID][0].id][0].sender.display_name
+ assert collection.messages[0].reply_count == data.messages[data.channels[data.teams[0].ID][0].id][0].reply_count
+ assert collection.messages[0].created_date_time == data.messages[data.channels[data.teams[0].ID][0].id][0].created_date_time
+
+ assert collection.messages[1].id == data.messages[data.channels[data.teams[0].ID][0].id][1].id
+ assert collection.messages[1].content == data.messages[data.channels[data.teams[0].ID][0].id][1].content
+ assert collection.messages[1].content_type == MessageContentType(data.messages[data.channels[data.teams[0].ID][0].id][1].content_type)
+ assert collection.messages[1].sender.user_id == data.messages[data.channels[data.teams[0].ID][0].id][1].sender.user_id
+ assert collection.messages[1].sender.display_name == data.messages[data.channels[data.teams[0].ID][0].id][1].sender.display_name
+ assert collection.messages[1].reply_count == data.messages[data.channels[data.teams[0].ID][0].id][1].reply_count
+ assert collection.messages[1].created_date_time == data.messages[data.channels[data.teams[0].ID][0].id][1].created_date_time
finally:
client.close()
@@ -191,16 +191,16 @@ def test_get_message_integration(httpserver):
message = client.channels.get_message(
team_ref=data.teams[0].DisplayName,
channel_ref=data.channels[data.teams[0].ID][0].name,
- message_id=data.messages[data.channels[data.teams[0].ID][0].id][0].ID,
+ message_id=data.messages[data.channels[data.teams[0].ID][0].id][0].id,
)
- assert message.ID == data.messages[data.channels[data.teams[0].ID][0].id][0].ID
- assert message.Content == data.messages[data.channels[data.teams[0].ID][0].id][0].Content
- assert message.ContentType == MessageContentType(data.messages[data.channels[data.teams[0].ID][0].id][0].ContentType)
- assert message.From.UserID == data.messages[data.channels[data.teams[0].ID][0].id][0].From.UserID
- assert message.From.DisplayName == data.messages[data.channels[data.teams[0].ID][0].id][0].From.DisplayName
- assert message.ReplyCount == data.messages[data.channels[data.teams[0].ID][0].id][0].ReplyCount
- assert message.CreatedDateTime == data.messages[data.channels[data.teams[0].ID][0].id][0].CreatedDateTime
+ assert message.id == data.messages[data.channels[data.teams[0].ID][0].id][0].id
+ assert message.content == data.messages[data.channels[data.teams[0].ID][0].id][0].content
+ assert message.content_type == MessageContentType(data.messages[data.channels[data.teams[0].ID][0].id][0].content_type)
+ assert message.sender.user_id == data.messages[data.channels[data.teams[0].ID][0].id][0].sender.user_id
+ assert message.sender.display_name == data.messages[data.channels[data.teams[0].ID][0].id][0].sender.display_name
+ assert message.reply_count == data.messages[data.channels[data.teams[0].ID][0].id][0].reply_count
+ assert message.created_date_time == data.messages[data.channels[data.teams[0].ID][0].id][0].created_date_time
finally:
client.close()
@@ -217,23 +217,23 @@ def test_list_replies_integration(httpserver):
replies = client.channels.list_message_replies(
team_ref=data.teams[0].DisplayName,
channel_ref=data.channels[data.teams[0].ID][0].name,
- message_id=data.messages[data.channels[data.teams[0].ID][0].id][1].ID,
+ message_id=data.messages[data.channels[data.teams[0].ID][0].id][1].id,
)
- assert len(replies.Messages) == len(data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].ID])
- assert replies.Messages[0].ID == data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].ID][0].ID
- assert replies.Messages[0].Content == data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].ID][0].Content
- assert replies.Messages[0].ContentType == MessageContentType(data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].ID][0].ContentType)
- assert replies.Messages[0].From.UserID == data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].ID][0].From.UserID
- assert replies.Messages[0].From.DisplayName == data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].ID][0].From.DisplayName
- assert replies.Messages[0].CreatedDateTime == data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].ID][0].CreatedDateTime
-
- assert replies.Messages[1].ID == data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].ID][1].ID
- assert replies.Messages[1].Content == data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].ID][1].Content
- assert replies.Messages[1].ContentType == MessageContentType(data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].ID][1].ContentType)
- assert replies.Messages[1].From.UserID == data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].ID][1].From.UserID
- assert replies.Messages[1].From.DisplayName == data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].ID][1].From.DisplayName
- assert replies.Messages[1].CreatedDateTime == data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].ID][1].CreatedDateTime
+ assert len(replies.messages) == len(data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].id])
+ assert replies.messages[0].id == data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].id][0].id
+ assert replies.messages[0].content == data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].id][0].content
+ assert replies.messages[0].content_type == MessageContentType(data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].id][0].content_type)
+ assert replies.messages[0].sender.user_id == data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].id][0].sender.user_id
+ assert replies.messages[0].sender.display_name == data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].id][0].sender.display_name
+ assert replies.messages[0].created_date_time == data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].id][0].created_date_time
+
+ assert replies.messages[1].id == data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].id][1].id
+ assert replies.messages[1].content == data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].id][1].content
+ assert replies.messages[1].content_type == MessageContentType(data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].id][1].content_type)
+ assert replies.messages[1].sender.user_id == data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].id][1].sender.user_id
+ assert replies.messages[1].sender.display_name == data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].id][1].sender.display_name
+ assert replies.messages[1].created_date_time == data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].id][1].created_date_time
finally:
client.close()
@@ -250,16 +250,16 @@ def test_get_reply_integration(httpserver):
reply = client.channels.get_message_reply(
team_ref=data.teams[0].DisplayName,
channel_ref=data.channels[data.teams[0].ID][0].name,
- message_id=data.messages[data.channels[data.teams[0].ID][0].id][1].ID,
- reply_id=data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].ID][0].ID,
+ message_id=data.messages[data.channels[data.teams[0].ID][0].id][1].id,
+ reply_id=data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].id][0].id,
)
- assert reply.ID == data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].ID][0].ID
- assert reply.Content == data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].ID][0].Content
- assert reply.ContentType == MessageContentType(data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].ID][0].ContentType)
- assert reply.From.UserID == data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].ID][0].From.UserID
- assert reply.From.DisplayName == data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].ID][0].From.DisplayName
- assert reply.CreatedDateTime == data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].ID][0].CreatedDateTime
+ assert reply.id == data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].id][0].id
+ assert reply.content == data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].id][0].content
+ assert reply.content_type == MessageContentType(data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].id][0].content_type)
+ assert reply.sender.user_id == data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].id][0].sender.user_id
+ assert reply.sender.display_name == data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].id][0].sender.display_name
+ assert reply.created_date_time == data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].id][0].created_date_time
finally:
client.close()
@@ -385,7 +385,7 @@ def test_get_mention_integration(httpserver):
raw_mentions=MentionKind.TEAM.value,
)
- assert mention[0].kind == MentionKind.TEAM.value
+ assert mention[0].kind == MentionKind.TEAM
assert mention[0].at_id == 0
assert mention[0].text == data.teams[0].DisplayName
assert mention[0].target_id == data.teams[0].ID
diff --git a/python/tests/test_chats.py b/python/tests/test_chats.py
index cb971de..a4dfe99 100644
--- a/python/tests/test_chats.py
+++ b/python/tests/test_chats.py
@@ -209,23 +209,23 @@ def test_list_messeges_in_chat_integration(httpserver):
)
)
- assert len(collection.Messages) == len(data.chat_messages[data.group_chats[0].id])
-
- assert collection.Messages[0].ID == data.chat_messages[data.group_chats[0].id][0].ID
- assert collection.Messages[0].Content == data.chat_messages[data.group_chats[0].id][0].Content
- assert collection.Messages[0].ContentType == MessageContentType(data.chat_messages[data.group_chats[0].id][0].ContentType)
- assert collection.Messages[0].From.UserID == data.chat_messages[data.group_chats[0].id][0].From.UserID
- assert collection.Messages[0].From.DisplayName == data.chat_messages[data.group_chats[0].id][0].From.DisplayName
- assert collection.Messages[0].ReplyCount == data.chat_messages[data.group_chats[0].id][0].ReplyCount
- assert collection.Messages[0].CreatedDateTime == data.chat_messages[data.group_chats[0].id][0].CreatedDateTime
-
- assert collection.Messages[1].ID == data.chat_messages[data.group_chats[0].id][1].ID
- assert collection.Messages[1].Content == data.chat_messages[data.group_chats[0].id][1].Content
- assert collection.Messages[1].ContentType == MessageContentType(data.chat_messages[data.group_chats[0].id][1].ContentType)
- assert collection.Messages[1].From.UserID == data.chat_messages[data.group_chats[0].id][1].From.UserID
- assert collection.Messages[1].From.DisplayName == data.chat_messages[data.group_chats[0].id][1].From.DisplayName
- assert collection.Messages[1].ReplyCount == data.chat_messages[data.group_chats[0].id][1].ReplyCount
- assert collection.Messages[1].CreatedDateTime == data.chat_messages[data.group_chats[0].id][1].CreatedDateTime
+ assert len(collection.messages) == len(data.chat_messages[data.group_chats[0].id])
+
+ assert collection.messages[0].id == data.chat_messages[data.group_chats[0].id][0].id
+ assert collection.messages[0].content == data.chat_messages[data.group_chats[0].id][0].content
+ assert collection.messages[0].content_type == MessageContentType(data.chat_messages[data.group_chats[0].id][0].content_type)
+ assert collection.messages[0].sender.user_id == data.chat_messages[data.group_chats[0].id][0].sender.user_id
+ assert collection.messages[0].sender.display_name == data.chat_messages[data.group_chats[0].id][0].sender.display_name
+ assert collection.messages[0].reply_count == data.chat_messages[data.group_chats[0].id][0].reply_count
+ assert collection.messages[0].created_date_time == data.chat_messages[data.group_chats[0].id][0].created_date_time
+
+ assert collection.messages[1].id == data.chat_messages[data.group_chats[0].id][1].id
+ assert collection.messages[1].content == data.chat_messages[data.group_chats[0].id][1].content
+ assert collection.messages[1].content_type == MessageContentType(data.chat_messages[data.group_chats[0].id][1].content_type)
+ assert collection.messages[1].sender.user_id == data.chat_messages[data.group_chats[0].id][1].sender.user_id
+ assert collection.messages[1].sender.display_name == data.chat_messages[data.group_chats[0].id][1].sender.display_name
+ assert collection.messages[1].reply_count == data.chat_messages[data.group_chats[0].id][1].reply_count
+ assert collection.messages[1].created_date_time == data.chat_messages[data.group_chats[0].id][1].created_date_time
finally:
client.close()
@@ -244,19 +244,19 @@ def test_send_message_in_chat_integration(httpserver):
type=ChatType.GROUP,
),
body=MessageBody(
- Content=data.newMessageTemplate.Content,
- ContentType=MessageContentType(data.newMessageTemplate.ContentType),
- Mentions=[],
+ content=data.newMessageTemplate.content,
+ content_type=MessageContentType(data.newMessageTemplate.content_type),
+ mentions=[],
)
)
- assert message.ID == data.newMessageTemplate.ID
- assert message.Content == data.newMessageTemplate.Content
- assert message.ContentType == MessageContentType(data.newMessageTemplate.ContentType)
- assert message.From.UserID == data.newMessageTemplate.From.UserID
- assert message.From.DisplayName == data.newMessageTemplate.From.DisplayName
- assert message.ReplyCount == data.newMessageTemplate.ReplyCount
- assert message.CreatedDateTime == data.newMessageTemplate.CreatedDateTime
+ assert message.id == data.newMessageTemplate.id
+ assert message.content == data.newMessageTemplate.content
+ assert message.content_type == MessageContentType(data.newMessageTemplate.content_type)
+ assert message.sender.user_id == data.newMessageTemplate.sender.user_id
+ assert message.sender.display_name == data.newMessageTemplate.sender.display_name
+ assert message.reply_count == data.newMessageTemplate.reply_count
+ assert message.created_date_time == data.newMessageTemplate.created_date_time
finally:
client.close()
@@ -274,7 +274,7 @@ def test_delete_message_in_chat_integration(httpserver):
ref=data.group_chats[0].topic,
type=ChatType.GROUP,
),
- message_id=data.chat_messages[data.group_chats[0].id][0].ID,
+ message_id=data.chat_messages[data.group_chats[0].id][0].id,
)
assert result is True
@@ -295,16 +295,16 @@ def test_get_message_in_chat_integration(httpserver):
ref=data.group_chats[0].topic,
type=ChatType.GROUP,
),
- message_id=data.chat_messages[data.group_chats[0].id][0].ID,
+ message_id=data.chat_messages[data.group_chats[0].id][0].id,
)
- assert message.ID == data.chat_messages[data.group_chats[0].id][0].ID
- assert message.Content == data.chat_messages[data.group_chats[0].id][0].Content
- assert message.ContentType == MessageContentType(data.chat_messages[data.group_chats[0].id][0].ContentType)
- assert message.From.UserID == data.chat_messages[data.group_chats[0].id][0].From.UserID
- assert message.From.DisplayName == data.chat_messages[data.group_chats[0].id][0].From.DisplayName
- assert message.ReplyCount == data.chat_messages[data.group_chats[0].id][0].ReplyCount
- assert message.CreatedDateTime == data.chat_messages[data.group_chats[0].id][0].CreatedDateTime
+ assert message.id == data.chat_messages[data.group_chats[0].id][0].id
+ assert message.content == data.chat_messages[data.group_chats[0].id][0].content
+ assert message.content_type == MessageContentType(data.chat_messages[data.group_chats[0].id][0].content_type)
+ assert message.sender.user_id == data.chat_messages[data.group_chats[0].id][0].sender.user_id
+ assert message.sender.display_name == data.chat_messages[data.group_chats[0].id][0].sender.display_name
+ assert message.reply_count == data.chat_messages[data.group_chats[0].id][0].reply_count
+ assert message.created_date_time == data.chat_messages[data.group_chats[0].id][0].created_date_time
finally:
client.close()
@@ -325,21 +325,21 @@ def test_get_all_messages_in_chat_integration(httpserver):
assert len(messages) == len(data.chat_messages[data.group_chats[0].id])
- assert messages[0].ID == data.chat_messages[data.group_chats[0].id][0].ID
- assert messages[0].Content == data.chat_messages[data.group_chats[0].id][0].Content
- assert messages[0].ContentType == MessageContentType(data.chat_messages[data.group_chats[0].id][0].ContentType)
- assert messages[0].From.UserID == data.chat_messages[data.group_chats[0].id][0].From.UserID
- assert messages[0].From.DisplayName == data.chat_messages[data.group_chats[0].id][0].From.DisplayName
- assert messages[0].ReplyCount == 0
- assert messages[0].CreatedDateTime == data.chat_messages[data.group_chats[0].id][0].CreatedDateTime
-
- assert messages[1].ID == data.chat_messages[data.group_chats[0].id][1].ID
- assert messages[1].Content == data.chat_messages[data.group_chats[0].id][1].Content
- assert messages[1].ContentType == MessageContentType(data.chat_messages[data.group_chats[0].id][1].ContentType)
- assert messages[1].From.UserID == data.chat_messages[data.group_chats[0].id][1].From.UserID
- assert messages[1].From.DisplayName == data.chat_messages[data.group_chats[0].id][1].From.DisplayName
- assert messages[1].ReplyCount == 0
- assert messages[1].CreatedDateTime == data.chat_messages[data.group_chats[0].id][1].CreatedDateTime
+ assert messages[0].id == data.chat_messages[data.group_chats[0].id][0].id
+ assert messages[0].content == data.chat_messages[data.group_chats[0].id][0].content
+ assert messages[0].content_type == MessageContentType(data.chat_messages[data.group_chats[0].id][0].content_type)
+ assert messages[0].sender.user_id == data.chat_messages[data.group_chats[0].id][0].sender.user_id
+ assert messages[0].sender.display_name == data.chat_messages[data.group_chats[0].id][0].sender.display_name
+ assert messages[0].reply_count == 0
+ assert messages[0].created_date_time == data.chat_messages[data.group_chats[0].id][0].created_date_time
+
+ assert messages[1].id == data.chat_messages[data.group_chats[0].id][1].id
+ assert messages[1].content == data.chat_messages[data.group_chats[0].id][1].content
+ assert messages[1].content_type == MessageContentType(data.chat_messages[data.group_chats[0].id][1].content_type)
+ assert messages[1].sender.user_id == data.chat_messages[data.group_chats[0].id][1].sender.user_id
+ assert messages[1].sender.display_name == data.chat_messages[data.group_chats[0].id][1].sender.display_name
+ assert messages[1].reply_count == 0
+ assert messages[1].created_date_time == data.chat_messages[data.group_chats[0].id][1].created_date_time
finally:
client.close()
@@ -362,21 +362,21 @@ def test_list_pinned_messages_integration(httpserver):
assert len(messages) == len(data.chat_messages[data.group_chats[0].id])
- assert messages[0].ID == data.chat_messages[data.group_chats[0].id][0].ID
- assert messages[0].Content == data.chat_messages[data.group_chats[0].id][0].Content
- assert messages[0].ContentType == MessageContentType(data.chat_messages[data.group_chats[0].id][0].ContentType)
- assert messages[0].From.UserID == data.chat_messages[data.group_chats[0].id][0].From.UserID
- assert messages[0].From.DisplayName == data.chat_messages[data.group_chats[0].id][0].From.DisplayName
- assert messages[0].ReplyCount == 0
- assert messages[0].CreatedDateTime == data.chat_messages[data.group_chats[0].id][0].CreatedDateTime
-
- assert messages[1].ID == data.chat_messages[data.group_chats[0].id][1].ID
- assert messages[1].Content == data.chat_messages[data.group_chats[0].id][1].Content
- assert messages[1].ContentType == MessageContentType(data.chat_messages[data.group_chats[0].id][1].ContentType)
- assert messages[1].From.UserID == data.chat_messages[data.group_chats[0].id][1].From.UserID
- assert messages[1].From.DisplayName == data.chat_messages[data.group_chats[0].id][1].From.DisplayName
- assert messages[1].ReplyCount == 0
- assert messages[1].CreatedDateTime == data.chat_messages[data.group_chats[0].id][1].CreatedDateTime
+ assert messages[0].id == data.chat_messages[data.group_chats[0].id][0].id
+ assert messages[0].content == data.chat_messages[data.group_chats[0].id][0].content
+ assert messages[0].content_type == MessageContentType(data.chat_messages[data.group_chats[0].id][0].content_type)
+ assert messages[0].sender.user_id == data.chat_messages[data.group_chats[0].id][0].sender.user_id
+ assert messages[0].sender.display_name == data.chat_messages[data.group_chats[0].id][0].sender.display_name
+ assert messages[0].reply_count == 0
+ assert messages[0].created_date_time == data.chat_messages[data.group_chats[0].id][0].created_date_time
+
+ assert messages[1].id == data.chat_messages[data.group_chats[0].id][1].id
+ assert messages[1].content == data.chat_messages[data.group_chats[0].id][1].content
+ assert messages[1].content_type == MessageContentType(data.chat_messages[data.group_chats[0].id][1].content_type)
+ assert messages[1].sender.user_id == data.chat_messages[data.group_chats[0].id][1].sender.user_id
+ assert messages[1].sender.display_name == data.chat_messages[data.group_chats[0].id][1].sender.display_name
+ assert messages[1].reply_count == 0
+ assert messages[1].created_date_time == data.chat_messages[data.group_chats[0].id][1].created_date_time
finally:
client.close()
@@ -395,7 +395,7 @@ def test_pin_message_in_chat_integration(httpserver):
ref=data.group_chats[0].topic,
type=ChatType.GROUP,
),
- message_id=data.chat_messages[data.group_chats[0].id][0].ID,
+ message_id=data.chat_messages[data.group_chats[0].id][0].id,
)
assert result is True
@@ -417,7 +417,7 @@ def test_unpin_message_in_chat_integration(httpserver):
ref=data.group_chats[0].topic,
type=ChatType.GROUP,
),
- message_id=data.chat_messages[data.group_chats[0].id][0].ID,
+ message_id=data.chat_messages[data.group_chats[0].id][0].id,
)
assert result is True
From 372aac60fcf422e84f14f069cc7f3aa0b93125ed Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Thu, 8 Jan 2026 10:45:41 +0100
Subject: [PATCH 69/99] docs: added docstrings for message models
---
python/teams_lib_pzsp2_z1/model/message.py | 61 ++++++++++++++++++++++
1 file changed, 61 insertions(+)
diff --git a/python/teams_lib_pzsp2_z1/model/message.py b/python/teams_lib_pzsp2_z1/model/message.py
index 485acf6..08d87c0 100644
--- a/python/teams_lib_pzsp2_z1/model/message.py
+++ b/python/teams_lib_pzsp2_z1/model/message.py
@@ -6,18 +6,47 @@
class MessageContentType(Enum):
+ """Represents the type of content in a Microsoft Teams message.
+
+ Attributes:
+ TEXT: Plain text content.
+ HTML: HTML formatted content.
+ """
+
TEXT = "text"
HTML = "html"
@dataclass
class MessageFrom:
+ """Represents the sender of a message in Microsoft Teams.
+
+ Attributes:
+ user_id (str): The unique Azure Active Directory (AAD) identifier of the sender.
+ display_name (str): The visible name of the sender.
+ """
+
user_id: str
display_name: str
@dataclass
class Message:
+ """Represents a Microsoft Teams chat message.
+
+ Used in both direct chats and channels.
+
+ Attributes:
+ id (str): The unique identifier of the message.
+ content (str): The actual body text or HTML of the message.
+ content_type (MessageContentType): The format of the content (Text or HTML).
+ created_date_time (datetime): The timestamp when the message was created.
+ sender (MessageFrom): The user who sent the message.
+ **Note:** Mapped from the `From` field in the API/Go model to avoid
+ conflict with the Python `from` keyword.
+ reply_count (int): The number of replies to this message.
+ """
+
id: str
content: str
content_type: MessageContentType
@@ -28,28 +57,60 @@ class Message:
@dataclass
class MessageBody:
+ """Represents the body of a message to be sent.
+
+ This class contains the payload structure required to send a new message
+ or reply.
+
+ Attributes:
+ content_type (MessageContentType): The format of the content.
+ content (str): The text or HTML content.
+ mentions (List[Mention]): A list of mentions to include in the message.
+ """
+
content_type: MessageContentType
content: str
mentions: list[Mention]
def __dict__(self):
+ """Serializes the object keys to PascalCase for the Go backend."""
+
return {
"ContentType": self.content_type.value,
"Content": self.content,
}
def __iter__(self):
+ """Allows iteration over fields with PascalCase keys."""
+
yield "ContentType", self.content_type.value
yield "Content", self.content
@dataclass
class ListMessagesOptions:
+ """Contains options for listing messages (pagination and expansion).
+
+ Attributes:
+ top (Optional[int]): The maximum number of messages to return in one page.
+ Defaults to None (server default).
+ expand_replies (bool): If True, the response will include replies
+ nested within the messages. Defaults to False.
+ """
+
top: int | None = None
expand_replies: bool = False
@dataclass
class MessageCollection:
+ """Represents a paginated collection of messages.
+
+ Attributes:
+ messages (List[Message]): The list of message objects in the current page.
+ next_link (Optional[str]): The URL to retrieve the next page of results.
+ If None, there are no more messages.
+ """
+
messages: list[Message]
next_link: str | None = None
From c7fb49d1e66f1bb258bbf850b0417cde213f562f Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Thu, 8 Jan 2026 10:47:33 +0100
Subject: [PATCH 70/99] docs: added docstrings for team models
---
python/teams_lib_pzsp2_z1/model/team.py | 35 +++-
python/teams_lib_pzsp2_z1/services/teams.py | 40 ++---
python/tests/fake_server/data.py | 94 +++++-----
python/tests/test_channels.py | 184 ++++++++++----------
python/tests/test_teams.py | 64 +++----
5 files changed, 220 insertions(+), 197 deletions(-)
diff --git a/python/teams_lib_pzsp2_z1/model/team.py b/python/teams_lib_pzsp2_z1/model/team.py
index 0d17576..aa690d7 100644
--- a/python/teams_lib_pzsp2_z1/model/team.py
+++ b/python/teams_lib_pzsp2_z1/model/team.py
@@ -3,15 +3,38 @@
@dataclass
class Team:
- ID: str
- DisplayName: str
- Description: str
- IsArchived: bool
- Visibility: str
+ """Represents a Microsoft Teams team.
+
+ Attributes:
+ id (str): The unique identifier (UUID) of the team.
+ display_name (str): The name of the team.
+ description (str): A description of the team's purpose.
+ is_archived (bool): Indicates whether the team is in an archived state.
+ Archived teams are read-only for members.
+ visibility (str): The privacy setting of the team.
+ Typically "public", "private".
+ """
+
+ id: str
+ display_name: str
+ description: str
+ is_archived: bool
+ visibility: str
@dataclass
class UpdateTeam:
- displayName: str | None = None
+ """Represents the subset of Team fields that can be updated.
+
+ This class is used as a payload for patch/update operations.
+ Only fields that are not None will be sent to the API.
+
+ Attributes:
+ display_name (Optional[str]): The new name for the team.
+ description (Optional[str]): The new description.
+ visibility (Optional[str]): The new visibility setting (e.g., "private").
+ """
+
+ display_name: str | None = None
description: str | None = None
visibility: str | None = None
diff --git a/python/teams_lib_pzsp2_z1/services/teams.py b/python/teams_lib_pzsp2_z1/services/teams.py
index e04f738..06646c6 100644
--- a/python/teams_lib_pzsp2_z1/services/teams.py
+++ b/python/teams_lib_pzsp2_z1/services/teams.py
@@ -38,11 +38,11 @@ def get(self, team_ref: str) -> Team:
)
return Team(
- ID=response["ID"],
- DisplayName=response["DisplayName"],
- Description=response["Description"],
- IsArchived=(True if response["IsArchived"] else False),
- Visibility=response["Visibility"],
+ id=response["ID"],
+ display_name=response["DisplayName"],
+ description=response["Description"],
+ is_archived=(True if response["IsArchived"] else False),
+ visibility=response["Visibility"],
)
def list_my_joined(self) -> list[Team]:
@@ -58,11 +58,11 @@ def list_my_joined(self) -> list[Team]:
)
return [
Team(
- ID=team["ID"],
- DisplayName=team["DisplayName"],
- Description=team["Description"],
- IsArchived=(True if team["IsArchived"] else False),
- Visibility=team["Visibility"],
+ id=team["ID"],
+ display_name=team["DisplayName"],
+ description=team["Description"],
+ is_archived=(True if team["IsArchived"] else False),
+ visibility=team["Visibility"],
)
for team in response
]
@@ -90,11 +90,11 @@ def update(self, team_ref: str, update: UpdateTeam) -> Team:
params=params,
)
return Team(
- ID=response["ID"],
- DisplayName=response["DisplayName"],
- Description=response["Description"],
- IsArchived=(True if response["IsArchived"] else False),
- Visibility=response["Visibility"],
+ id=response["ID"],
+ display_name=response["DisplayName"],
+ description=response["Description"],
+ is_archived=(True if response["IsArchived"] else False),
+ visibility=response["Visibility"],
)
def create_via_group(
@@ -120,11 +120,11 @@ def create_via_group(
},
)
return Team(
- ID=response["ID"],
- DisplayName=response["DisplayName"],
- Description=response["Description"],
- IsArchived=(True if response["IsArchived"] else False),
- Visibility=response["Visibility"],
+ id=response["ID"],
+ display_name=response["DisplayName"],
+ description=response["Description"],
+ is_archived=(True if response["IsArchived"] else False),
+ visibility=response["Visibility"],
)
def create_from_template( # noqa: PLR0913
diff --git a/python/tests/fake_server/data.py b/python/tests/fake_server/data.py
index 82bbf00..50fbeaa 100644
--- a/python/tests/fake_server/data.py
+++ b/python/tests/fake_server/data.py
@@ -59,9 +59,9 @@ class FakeServerData:
def __init__(self) -> None:
# --- Teams ---
self.teams = [
- Team(ID="team-123-abc", DisplayName="Test Team", Description="A team for testing", IsArchived=False, Visibility="private"),
- Team(ID="team-456-def", DisplayName="Another Team", Description="Another team for testing", IsArchived=False, Visibility="public"),
- Team(ID="archived-team-789-ghi", DisplayName="Archived Team", Description="An archived team for testing", IsArchived=True, Visibility="private")
+ Team(id="team-123-abc", display_name="Test Team", description="A team for testing", is_archived=False, visibility="private"),
+ Team(id="team-456-def", display_name="Another Team", description="Another team for testing", is_archived=False, visibility="public"),
+ Team(id="archived-team-789-ghi", display_name="Archived Team", description="An archived team for testing", is_archived=True, visibility="private")
]
self.potentialTeams = []
@@ -157,7 +157,7 @@ def __init__(self) -> None:
# --- Helpers ---
def _find_team(self, team_id: str) -> Optional[Team]:
- return next((t for t in self.teams if t.ID == team_id), None)
+ return next((t for t in self.teams if t.id == team_id), None)
def _find_channel(self, team_id: str, channel_id: str) -> Optional[Channel]:
return next((c for c in self.channels.get(team_id, []) if c.id == channel_id), None)
@@ -170,11 +170,11 @@ def get_myJoinedTeams_response(self) -> dict:
return {
"value": [
{
- "id": team.ID,
- "displayName": team.DisplayName,
- "description": team.Description,
- "isArchived": team.IsArchived,
- "visibility": team.Visibility,
+ "id": team.id,
+ "displayName": team.display_name,
+ "description": team.description,
+ "isArchived": team.is_archived,
+ "visibility": team.visibility,
}
for team in self.teams
],
@@ -185,11 +185,11 @@ def get_team_response(self, team_id: str) -> Optional[dict]:
if not team:
return None
return {
- "id": team.ID,
- "displayName": team.DisplayName,
- "description": team.Description,
- "isArchived": team.IsArchived,
- "visibility": team.Visibility,
+ "id": team.id,
+ "displayName": team.display_name,
+ "description": team.description,
+ "isArchived": team.is_archived,
+ "visibility": team.visibility,
}
def get_updateTeam_response(self, team_id: str, update_json: dict) -> Optional[dict]:
@@ -198,18 +198,18 @@ def get_updateTeam_response(self, team_id: str, update_json: dict) -> Optional[d
return None
if "displayName" in update_json:
- team.DisplayName = update_json["displayName"]
+ team.display_name = update_json["displayName"]
if "description" in update_json:
- team.Description = update_json["description"]
+ team.description = update_json["description"]
if "visibility" in update_json:
- team.Visibility = update_json["visibility"]
+ team.visibility = update_json["visibility"]
return {
- "id": team.ID,
- "displayName": team.DisplayName,
- "description": team.Description,
- "isArchived": team.IsArchived,
- "visibility": team.Visibility,
+ "id": team.id,
+ "displayName": team.display_name,
+ "description": team.description,
+ "isArchived": team.is_archived,
+ "visibility": team.visibility,
}
def get_createGroup_response(self, request_json: dict) -> dict:
@@ -219,11 +219,11 @@ def get_createGroup_response(self, request_json: dict) -> dict:
self.potentialTeams.append(
Team(
- ID=self.newGroupID,
- DisplayName=request_json.get("displayName"),
- Description=request_json.get("description"),
- IsArchived=False,
- Visibility=visibility,
+ id=self.newGroupID,
+ display_name=request_json.get("displayName"),
+ description=request_json.get("description"),
+ is_archived=False,
+ visibility=visibility,
)
)
@@ -240,27 +240,27 @@ def get_createGroup_response(self, request_json: dict) -> dict:
}
def get_createTeamViaGroup_response(self, group_id: str) -> dict:
- team = next((t for t in self.potentialTeams if t.ID == group_id), None)
+ team = next((t for t in self.potentialTeams if t.id == group_id), None)
if not team:
return {}
self.teams.append(team)
return {
- "id": team.ID,
- "displayName": team.DisplayName,
- "description": team.Description,
- "isArchived": team.IsArchived,
- "visibility": team.Visibility,
+ "id": team.id,
+ "displayName": team.display_name,
+ "description": team.description,
+ "isArchived": team.is_archived,
+ "visibility": team.visibility,
}
def get_createTeamFromTemplate_response(self, request_json: dict) -> dict:
new_team = Team(
- ID=self.newTeamID,
- DisplayName=request_json.get("displayName"),
- Description=request_json.get("description"),
- IsArchived=False,
- Visibility="private",
+ id=self.newTeamID,
+ display_name=request_json.get("displayName"),
+ description=request_json.get("description"),
+ is_archived=False,
+ visibility="private",
)
self.teams.append(new_team)
@@ -282,14 +282,14 @@ def get_archiveTeam_response(self, team_id: str) -> dict:
team = self._find_team(team_id)
if not team:
return {"success": False}
- team.IsArchived = True
+ team.is_archived = True
return {"success": True}
def get_unarchiveTeam_response(self, team_id: str) -> dict:
team = self._find_team(team_id)
if not team:
return {"success": False}
- team.IsArchived = False
+ team.is_archived = False
return {"success": True}
def get_deleteTeam_response(self, team_id: str) -> dict:
@@ -300,17 +300,17 @@ def get_deleteTeam_response(self, team_id: str) -> dict:
return {"success": True}
def get_restoreTeam_response(self, team_id: str) -> dict:
- archived_team = next((t for t in self.teams if t.ID == team_id and t.IsArchived), None)
+ archived_team = next((t for t in self.teams if t.id == team_id and t.is_archived), None)
if not archived_team:
return {"success": False}
- archived_team.IsArchived = False
+ archived_team.is_archived = False
return {
- "id": archived_team.ID,
- "displayName": archived_team.DisplayName,
- "description": archived_team.Description,
- "isArchived": archived_team.IsArchived,
- "visibility": archived_team.Visibility,
+ "id": archived_team.id,
+ "displayName": archived_team.display_name,
+ "description": archived_team.description,
+ "isArchived": archived_team.is_archived,
+ "visibility": archived_team.visibility,
"mailNickname": self.newGroupMailNickname
}
diff --git a/python/tests/test_channels.py b/python/tests/test_channels.py
index 8635287..c8989f8 100644
--- a/python/tests/test_channels.py
+++ b/python/tests/test_channels.py
@@ -17,17 +17,17 @@ def test_list_channels_integration(httpserver):
try:
init_fake_client(client, httpserver.url_for(""))
- channels = client.channels.list_channels(data.teams[0].DisplayName)
+ channels = client.channels.list_channels(data.teams[0].display_name)
- assert len(channels) == len(data.channels[data.teams[0].ID])
+ assert len(channels) == len(data.channels[data.teams[0].id])
- assert channels[0].name == data.channels[data.teams[0].ID][0].name
- assert channels[0].id == data.channels[data.teams[0].ID][0].id
- assert channels[0].is_general == data.channels[data.teams[0].ID][0].is_general
+ assert channels[0].name == data.channels[data.teams[0].id][0].name
+ assert channels[0].id == data.channels[data.teams[0].id][0].id
+ assert channels[0].is_general == data.channels[data.teams[0].id][0].is_general
- assert channels[1].name == data.channels[data.teams[0].ID][1].name
- assert channels[1].id == data.channels[data.teams[0].ID][1].id
- assert channels[1].is_general == data.channels[data.teams[0].ID][1].is_general
+ assert channels[1].name == data.channels[data.teams[0].id][1].name
+ assert channels[1].id == data.channels[data.teams[0].id][1].id
+ assert channels[1].is_general == data.channels[data.teams[0].id][1].is_general
finally:
client.close()
@@ -42,13 +42,13 @@ def test_get_channel_integration(httpserver):
init_fake_client(client, httpserver.url_for(""))
channel = client.channels.get(
- team_ref=data.teams[0].DisplayName,
- channel_ref=data.channels[data.teams[0].ID][1].name,
+ team_ref=data.teams[0].display_name,
+ channel_ref=data.channels[data.teams[0].id][1].name,
)
- assert channel.name == data.channels[data.teams[0].ID][1].name
- assert channel.id == data.channels[data.teams[0].ID][1].id
- assert channel.is_general == data.channels[data.teams[0].ID][1].is_general
+ assert channel.name == data.channels[data.teams[0].id][1].name
+ assert channel.id == data.channels[data.teams[0].id][1].id
+ assert channel.is_general == data.channels[data.teams[0].id][1].is_general
finally:
client.close()
@@ -63,7 +63,7 @@ def test_create_standard_channel_integration(httpserver):
init_fake_client(client, httpserver.url_for(""))
channel = client.channels.create_standard(
- team_ref=data.teams[0].DisplayName,
+ team_ref=data.teams[0].display_name,
display_name=data.newChannelName,
)
@@ -84,7 +84,7 @@ def test_create_private_channel_integration(httpserver):
init_fake_client(client, httpserver.url_for(""))
channel = client.channels.create_private(
- team_ref=data.teams[0].DisplayName,
+ team_ref=data.teams[0].display_name,
display_name=data.newChannelName,
member_refs=[],
owner_refs=[],
@@ -107,8 +107,8 @@ def test_delete_channel_integration(httpserver):
init_fake_client(client, httpserver.url_for(""))
success = client.channels.delete(
- team_ref=data.teams[0].DisplayName,
- channel_ref=data.channels[data.teams[0].ID][1].name,
+ team_ref=data.teams[0].display_name,
+ channel_ref=data.channels[data.teams[0].id][1].name,
)
assert success is True
@@ -126,8 +126,8 @@ def test_send_message_integration(httpserver):
init_fake_client(client, httpserver.url_for(""))
message = client.channels.send_message(
- team_ref=data.teams[0].DisplayName,
- channel_ref=data.channels[data.teams[0].ID][0].name,
+ team_ref=data.teams[0].display_name,
+ channel_ref=data.channels[data.teams[0].id][0].name,
body=MessageBody(
content=data.newMessageTemplate.content,
content_type=MessageContentType.TEXT,
@@ -156,26 +156,26 @@ def test_list_messages_integration(httpserver):
init_fake_client(client, httpserver.url_for(""))
collection = client.channels.list_messages(
- team_ref=data.teams[0].DisplayName,
- channel_ref=data.channels[data.teams[0].ID][0].name,
+ team_ref=data.teams[0].display_name,
+ channel_ref=data.channels[data.teams[0].id][0].name,
)
- assert len(collection.messages) == len(data.messages[data.channels[data.teams[0].ID][0].id])
- assert collection.messages[0].id == data.messages[data.channels[data.teams[0].ID][0].id][0].id
- assert collection.messages[0].content == data.messages[data.channels[data.teams[0].ID][0].id][0].content
- assert collection.messages[0].content_type == MessageContentType(data.messages[data.channels[data.teams[0].ID][0].id][0].content_type)
- assert collection.messages[0].sender.user_id == data.messages[data.channels[data.teams[0].ID][0].id][0].sender.user_id
- assert collection.messages[0].sender.display_name == data.messages[data.channels[data.teams[0].ID][0].id][0].sender.display_name
- assert collection.messages[0].reply_count == data.messages[data.channels[data.teams[0].ID][0].id][0].reply_count
- assert collection.messages[0].created_date_time == data.messages[data.channels[data.teams[0].ID][0].id][0].created_date_time
-
- assert collection.messages[1].id == data.messages[data.channels[data.teams[0].ID][0].id][1].id
- assert collection.messages[1].content == data.messages[data.channels[data.teams[0].ID][0].id][1].content
- assert collection.messages[1].content_type == MessageContentType(data.messages[data.channels[data.teams[0].ID][0].id][1].content_type)
- assert collection.messages[1].sender.user_id == data.messages[data.channels[data.teams[0].ID][0].id][1].sender.user_id
- assert collection.messages[1].sender.display_name == data.messages[data.channels[data.teams[0].ID][0].id][1].sender.display_name
- assert collection.messages[1].reply_count == data.messages[data.channels[data.teams[0].ID][0].id][1].reply_count
- assert collection.messages[1].created_date_time == data.messages[data.channels[data.teams[0].ID][0].id][1].created_date_time
+ assert len(collection.messages) == len(data.messages[data.channels[data.teams[0].id][0].id])
+ assert collection.messages[0].id == data.messages[data.channels[data.teams[0].id][0].id][0].id
+ assert collection.messages[0].content == data.messages[data.channels[data.teams[0].id][0].id][0].content
+ assert collection.messages[0].content_type == MessageContentType(data.messages[data.channels[data.teams[0].id][0].id][0].content_type)
+ assert collection.messages[0].sender.user_id == data.messages[data.channels[data.teams[0].id][0].id][0].sender.user_id
+ assert collection.messages[0].sender.display_name == data.messages[data.channels[data.teams[0].id][0].id][0].sender.display_name
+ assert collection.messages[0].reply_count == data.messages[data.channels[data.teams[0].id][0].id][0].reply_count
+ assert collection.messages[0].created_date_time == data.messages[data.channels[data.teams[0].id][0].id][0].created_date_time
+
+ assert collection.messages[1].id == data.messages[data.channels[data.teams[0].id][0].id][1].id
+ assert collection.messages[1].content == data.messages[data.channels[data.teams[0].id][0].id][1].content
+ assert collection.messages[1].content_type == MessageContentType(data.messages[data.channels[data.teams[0].id][0].id][1].content_type)
+ assert collection.messages[1].sender.user_id == data.messages[data.channels[data.teams[0].id][0].id][1].sender.user_id
+ assert collection.messages[1].sender.display_name == data.messages[data.channels[data.teams[0].id][0].id][1].sender.display_name
+ assert collection.messages[1].reply_count == data.messages[data.channels[data.teams[0].id][0].id][1].reply_count
+ assert collection.messages[1].created_date_time == data.messages[data.channels[data.teams[0].id][0].id][1].created_date_time
finally:
client.close()
@@ -189,18 +189,18 @@ def test_get_message_integration(httpserver):
init_fake_client(client, httpserver.url_for(""))
message = client.channels.get_message(
- team_ref=data.teams[0].DisplayName,
- channel_ref=data.channels[data.teams[0].ID][0].name,
- message_id=data.messages[data.channels[data.teams[0].ID][0].id][0].id,
+ team_ref=data.teams[0].display_name,
+ channel_ref=data.channels[data.teams[0].id][0].name,
+ message_id=data.messages[data.channels[data.teams[0].id][0].id][0].id,
)
- assert message.id == data.messages[data.channels[data.teams[0].ID][0].id][0].id
- assert message.content == data.messages[data.channels[data.teams[0].ID][0].id][0].content
- assert message.content_type == MessageContentType(data.messages[data.channels[data.teams[0].ID][0].id][0].content_type)
- assert message.sender.user_id == data.messages[data.channels[data.teams[0].ID][0].id][0].sender.user_id
- assert message.sender.display_name == data.messages[data.channels[data.teams[0].ID][0].id][0].sender.display_name
- assert message.reply_count == data.messages[data.channels[data.teams[0].ID][0].id][0].reply_count
- assert message.created_date_time == data.messages[data.channels[data.teams[0].ID][0].id][0].created_date_time
+ assert message.id == data.messages[data.channels[data.teams[0].id][0].id][0].id
+ assert message.content == data.messages[data.channels[data.teams[0].id][0].id][0].content
+ assert message.content_type == MessageContentType(data.messages[data.channels[data.teams[0].id][0].id][0].content_type)
+ assert message.sender.user_id == data.messages[data.channels[data.teams[0].id][0].id][0].sender.user_id
+ assert message.sender.display_name == data.messages[data.channels[data.teams[0].id][0].id][0].sender.display_name
+ assert message.reply_count == data.messages[data.channels[data.teams[0].id][0].id][0].reply_count
+ assert message.created_date_time == data.messages[data.channels[data.teams[0].id][0].id][0].created_date_time
finally:
client.close()
@@ -215,25 +215,25 @@ def test_list_replies_integration(httpserver):
init_fake_client(client, httpserver.url_for(""))
replies = client.channels.list_message_replies(
- team_ref=data.teams[0].DisplayName,
- channel_ref=data.channels[data.teams[0].ID][0].name,
- message_id=data.messages[data.channels[data.teams[0].ID][0].id][1].id,
+ team_ref=data.teams[0].display_name,
+ channel_ref=data.channels[data.teams[0].id][0].name,
+ message_id=data.messages[data.channels[data.teams[0].id][0].id][1].id,
)
- assert len(replies.messages) == len(data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].id])
- assert replies.messages[0].id == data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].id][0].id
- assert replies.messages[0].content == data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].id][0].content
- assert replies.messages[0].content_type == MessageContentType(data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].id][0].content_type)
- assert replies.messages[0].sender.user_id == data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].id][0].sender.user_id
- assert replies.messages[0].sender.display_name == data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].id][0].sender.display_name
- assert replies.messages[0].created_date_time == data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].id][0].created_date_time
-
- assert replies.messages[1].id == data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].id][1].id
- assert replies.messages[1].content == data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].id][1].content
- assert replies.messages[1].content_type == MessageContentType(data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].id][1].content_type)
- assert replies.messages[1].sender.user_id == data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].id][1].sender.user_id
- assert replies.messages[1].sender.display_name == data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].id][1].sender.display_name
- assert replies.messages[1].created_date_time == data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].id][1].created_date_time
+ assert len(replies.messages) == len(data.replies[data.messages[data.channels[data.teams[0].id][0].id][1].id])
+ assert replies.messages[0].id == data.replies[data.messages[data.channels[data.teams[0].id][0].id][1].id][0].id
+ assert replies.messages[0].content == data.replies[data.messages[data.channels[data.teams[0].id][0].id][1].id][0].content
+ assert replies.messages[0].content_type == MessageContentType(data.replies[data.messages[data.channels[data.teams[0].id][0].id][1].id][0].content_type)
+ assert replies.messages[0].sender.user_id == data.replies[data.messages[data.channels[data.teams[0].id][0].id][1].id][0].sender.user_id
+ assert replies.messages[0].sender.display_name == data.replies[data.messages[data.channels[data.teams[0].id][0].id][1].id][0].sender.display_name
+ assert replies.messages[0].created_date_time == data.replies[data.messages[data.channels[data.teams[0].id][0].id][1].id][0].created_date_time
+
+ assert replies.messages[1].id == data.replies[data.messages[data.channels[data.teams[0].id][0].id][1].id][1].id
+ assert replies.messages[1].content == data.replies[data.messages[data.channels[data.teams[0].id][0].id][1].id][1].content
+ assert replies.messages[1].content_type == MessageContentType(data.replies[data.messages[data.channels[data.teams[0].id][0].id][1].id][1].content_type)
+ assert replies.messages[1].sender.user_id == data.replies[data.messages[data.channels[data.teams[0].id][0].id][1].id][1].sender.user_id
+ assert replies.messages[1].sender.display_name == data.replies[data.messages[data.channels[data.teams[0].id][0].id][1].id][1].sender.display_name
+ assert replies.messages[1].created_date_time == data.replies[data.messages[data.channels[data.teams[0].id][0].id][1].id][1].created_date_time
finally:
client.close()
@@ -248,18 +248,18 @@ def test_get_reply_integration(httpserver):
init_fake_client(client, httpserver.url_for(""))
reply = client.channels.get_message_reply(
- team_ref=data.teams[0].DisplayName,
- channel_ref=data.channels[data.teams[0].ID][0].name,
- message_id=data.messages[data.channels[data.teams[0].ID][0].id][1].id,
- reply_id=data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].id][0].id,
+ team_ref=data.teams[0].display_name,
+ channel_ref=data.channels[data.teams[0].id][0].name,
+ message_id=data.messages[data.channels[data.teams[0].id][0].id][1].id,
+ reply_id=data.replies[data.messages[data.channels[data.teams[0].id][0].id][1].id][0].id,
)
- assert reply.id == data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].id][0].id
- assert reply.content == data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].id][0].content
- assert reply.content_type == MessageContentType(data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].id][0].content_type)
- assert reply.sender.user_id == data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].id][0].sender.user_id
- assert reply.sender.display_name == data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].id][0].sender.display_name
- assert reply.created_date_time == data.replies[data.messages[data.channels[data.teams[0].ID][0].id][1].id][0].created_date_time
+ assert reply.id == data.replies[data.messages[data.channels[data.teams[0].id][0].id][1].id][0].id
+ assert reply.content == data.replies[data.messages[data.channels[data.teams[0].id][0].id][1].id][0].content
+ assert reply.content_type == MessageContentType(data.replies[data.messages[data.channels[data.teams[0].id][0].id][1].id][0].content_type)
+ assert reply.sender.user_id == data.replies[data.messages[data.channels[data.teams[0].id][0].id][1].id][0].sender.user_id
+ assert reply.sender.display_name == data.replies[data.messages[data.channels[data.teams[0].id][0].id][1].id][0].sender.display_name
+ assert reply.created_date_time == data.replies[data.messages[data.channels[data.teams[0].id][0].id][1].id][0].created_date_time
finally:
client.close()
@@ -272,12 +272,12 @@ def test_list_members_integration(httpserver):
try:
init_fake_client(client, httpserver.url_for(""))
- team_id = data.teams[0].ID
+ team_id = data.teams[0].id
channel = data.channels[team_id][0]
channel_id = channel.id
members = client.channels.list_members(
- team_ref=data.teams[0].DisplayName,
+ team_ref=data.teams[0].display_name,
channel_ref=channel.name,
)
@@ -312,8 +312,8 @@ def test_add_member_integration(httpserver):
init_fake_client(client, httpserver.url_for(""))
member = client.channels.add_member(
- team_ref=data.teams[0].DisplayName,
- channel_ref=data.channels[data.teams[0].ID][1].name,
+ team_ref=data.teams[0].display_name,
+ channel_ref=data.channels[data.teams[0].id][1].name,
user_ref=data.newMemberTemplate.display_name,
is_owner=True if data.newMemberTemplate.role == "owner" else False,
)
@@ -337,17 +337,17 @@ def test_update_member_role_integration(httpserver):
init_fake_client(client, httpserver.url_for(""))
member = client.channels.update_member_role(
- team_ref=data.teams[0].DisplayName,
- channel_ref=data.channels[data.teams[0].ID][0].name,
- user_ref=data.members[data.teams[0].ID][data.channels[data.teams[0].ID][0].id][1].email,
+ team_ref=data.teams[0].display_name,
+ channel_ref=data.channels[data.teams[0].id][0].name,
+ user_ref=data.members[data.teams[0].id][data.channels[data.teams[0].id][0].id][1].email,
is_owner=True
)
- assert member.user_id == data.members[data.teams[0].ID][data.channels[data.teams[0].ID][0].id][1].user_id
- assert member.display_name == data.members[data.teams[0].ID][data.channels[data.teams[0].ID][0].id][1].display_name
+ assert member.user_id == data.members[data.teams[0].id][data.channels[data.teams[0].id][0].id][1].user_id
+ assert member.display_name == data.members[data.teams[0].id][data.channels[data.teams[0].id][0].id][1].display_name
assert member.role == "owner"
- assert member.email == data.members[data.teams[0].ID][data.channels[data.teams[0].ID][0].id][1].email
- assert member.id == data.members[data.teams[0].ID][data.channels[data.teams[0].ID][0].id][1].id
+ assert member.email == data.members[data.teams[0].id][data.channels[data.teams[0].id][0].id][1].email
+ assert member.id == data.members[data.teams[0].id][data.channels[data.teams[0].id][0].id][1].id
finally:
client.close()
@@ -361,9 +361,9 @@ def test_remove_member_integration(httpserver):
init_fake_client(client, httpserver.url_for(""))
success = client.channels.remove_member(
- team_ref=data.teams[0].DisplayName,
- channel_ref=data.channels[data.teams[0].ID][0].name,
- user_ref=data.members[data.teams[0].ID][data.channels[data.teams[0].ID][0].id][1].email,
+ team_ref=data.teams[0].display_name,
+ channel_ref=data.channels[data.teams[0].id][0].name,
+ user_ref=data.members[data.teams[0].id][data.channels[data.teams[0].id][0].id][1].email,
)
assert success is True
@@ -380,15 +380,15 @@ def test_get_mention_integration(httpserver):
init_fake_client(client, httpserver.url_for(""))
mention = client.channels.get_mentions(
- team_ref=data.teams[0].DisplayName,
- channel_ref=data.channels[data.teams[0].ID][0].name,
+ team_ref=data.teams[0].display_name,
+ channel_ref=data.channels[data.teams[0].id][0].name,
raw_mentions=MentionKind.TEAM.value,
)
assert mention[0].kind == MentionKind.TEAM
assert mention[0].at_id == 0
- assert mention[0].text == data.teams[0].DisplayName
- assert mention[0].target_id == data.teams[0].ID
+ assert mention[0].text == data.teams[0].display_name
+ assert mention[0].target_id == data.teams[0].id
finally:
client.close()
diff --git a/python/tests/test_teams.py b/python/tests/test_teams.py
index c9e6bb7..3c2d7fb 100644
--- a/python/tests/test_teams.py
+++ b/python/tests/test_teams.py
@@ -20,17 +20,17 @@ def test_list_my_teams_integration(httpserver):
assert len(teams) == len(data.teams)
- assert teams[0].DisplayName == data.teams[0].DisplayName
- assert teams[0].ID == data.teams[0].ID
- assert teams[0].Description == data.teams[0].Description
- assert teams[0].IsArchived == data.teams[0].IsArchived
- assert teams[0].Visibility == data.teams[0].Visibility
-
- assert teams[1].DisplayName == data.teams[1].DisplayName
- assert teams[1].ID == data.teams[1].ID
- assert teams[1].Description == data.teams[1].Description
- assert teams[1].IsArchived == data.teams[1].IsArchived
- assert teams[1].Visibility == data.teams[1].Visibility
+ assert teams[0].display_name == data.teams[0].display_name
+ assert teams[0].id == data.teams[0].id
+ assert teams[0].description == data.teams[0].description
+ assert teams[0].is_archived == data.teams[0].is_archived
+ assert teams[0].visibility == data.teams[0].visibility
+
+ assert teams[1].display_name == data.teams[1].display_name
+ assert teams[1].id == data.teams[1].id
+ assert teams[1].description == data.teams[1].description
+ assert teams[1].is_archived == data.teams[1].is_archived
+ assert teams[1].visibility == data.teams[1].visibility
finally:
client.close()
@@ -43,15 +43,15 @@ def test_get_team_integration(httpserver):
try:
init_fake_client(client, httpserver.url_for(""))
- team = client.teams.get(data.teams[0].DisplayName)
+ team = client.teams.get(data.teams[0].display_name)
- print(data.get_team_response(data.teams[0].ID))
+ print(data.get_team_response(data.teams[0].id))
- assert team.DisplayName == data.teams[0].DisplayName
- assert team.ID == data.teams[0].ID
- assert team.Description == data.teams[0].Description
- assert team.IsArchived == data.teams[0].IsArchived
- assert team.Visibility == data.teams[0].Visibility
+ assert team.display_name == data.teams[0].display_name
+ assert team.id == data.teams[0].id
+ assert team.description == data.teams[0].description
+ assert team.is_archived == data.teams[0].is_archived
+ assert team.visibility == data.teams[0].visibility
finally:
client.close()
@@ -67,17 +67,17 @@ def test_update_team_integration(httpserver):
updated_description = "Updated team description"
team = client.teams.update(
- team_ref=data.teams[0].DisplayName,
+ team_ref=data.teams[0].display_name,
update=UpdateTeam(
description=updated_description,
),
)
- assert team.DisplayName == data.teams[0].DisplayName
- assert team.ID == data.teams[0].ID
- assert team.Description == updated_description
- assert team.IsArchived == data.teams[0].IsArchived
- assert team.Visibility == data.teams[0].Visibility
+ assert team.display_name == data.teams[0].display_name
+ assert team.id == data.teams[0].id
+ assert team.description == updated_description
+ assert team.is_archived == data.teams[0].is_archived
+ assert team.visibility == data.teams[0].visibility
finally:
client.close()
@@ -97,9 +97,9 @@ def test_create_team_via_group_integration(httpserver):
visibility=data.newTeamVisibility,
)
- assert team.ID == data.newGroupID
- assert team.DisplayName == data.newTeamName
- assert team.Visibility == data.newTeamVisibility
+ assert team.id == data.newGroupID
+ assert team.display_name == data.newTeamName
+ assert team.visibility == data.newTeamVisibility
finally:
client.close()
@@ -134,7 +134,7 @@ def test_archive_team_integration(httpserver):
init_fake_client(client, httpserver.url_for(""))
success = client.teams.archive(
- team_ref=data.teams[0].DisplayName,
+ team_ref=data.teams[0].display_name,
spo_read_only_from_members=True,
)
@@ -153,7 +153,7 @@ def test_unarchive_team_integration(httpserver):
init_fake_client(client, httpserver.url_for(""))
success = client.teams.unarchive(
- team_ref=data.teams[2].DisplayName,
+ team_ref=data.teams[2].display_name,
)
assert success is True
@@ -171,7 +171,7 @@ def test_delete_team_integration(httpserver):
init_fake_client(client, httpserver.url_for(""))
success = client.teams.delete(
- team_ref=data.teams[0].DisplayName,
+ team_ref=data.teams[0].display_name,
)
assert success is True
@@ -189,10 +189,10 @@ def test_restore_deleted_team_integration(httpserver):
init_fake_client(client, httpserver.url_for(""))
msg = client.teams.restore_deleted(
- deleted_team_ID=data.teams[2].ID,
+ deleted_team_ID=data.teams[2].id,
)
- assert msg == data.teams[2].ID
+ assert msg == data.teams[2].id
finally:
client.close()
From 46c7046df77ace19350d5ccbde3b7024d664adb0 Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Thu, 8 Jan 2026 10:56:43 +0100
Subject: [PATCH 71/99] docs: client init docstrings
---
python/teams_lib_pzsp2_z1/client.py | 110 +++++++++++++++++-
.../services/base_service.py | 2 +
2 files changed, 108 insertions(+), 4 deletions(-)
diff --git a/python/teams_lib_pzsp2_z1/client.py b/python/teams_lib_pzsp2_z1/client.py
index bf00843..b7f39c7 100644
--- a/python/teams_lib_pzsp2_z1/client.py
+++ b/python/teams_lib_pzsp2_z1/client.py
@@ -13,6 +13,24 @@
class TeamsClient:
+ """The main entry point for interacting with the Microsoft Teams library.
+
+ This class manages the lifecycle of the underlying Go subprocess, handles
+ Inter-Process Communication (IPC) via JSON over stdin/stdout, and exposes
+ high-level services for Teams, Channels, and Chats.
+
+ **Architecture:**
+ The Python library acts as a frontend wrapper. It spawns a compiled Go binary
+ (`teamsClientLib`) as a subprocess. Commands are serialized to JSON and sent
+ to the Go process, which executes the actual Microsoft Graph API calls and
+ returns the results.
+
+ Attributes:
+ channels (ChannelsService): Service for managing channels.
+ teams (TeamsService): Service for managing teams.
+ chats (ChatsService): Service for managing chats and messages.
+ """
+
def __init__(
self,
auto_init: bool = True,
@@ -20,18 +38,37 @@ def __init__(
cache_mode: config.CacheMode = config.CacheMode.DISABLED,
cache_path: str | None = None,
):
+ """Initializes the TeamsClient and spawns the Go subprocess.
+
+ Args:
+ auto_init (bool, optional): If True, automatically calls `init_client`
+ using configuration loaded from the environment. Defaults to True.
+ env_path (str | None, optional): Path to the `.env` file containing
+ credentials (CLIENT_ID, TENANT_ID, etc.). If None, defaults are used.
+ cache_mode (config.CacheMode, optional): The caching strategy to use
+ (e.g., DISABLED, IN_MEMORY, DISK). Defaults to DISABLED.
+ cache_path (str | None, optional): File path for the cache (required if
+ cache_mode is DISK). Defaults to None.
+
+ Raises:
+ RuntimeError: If the operating system is not supported (only Windows/Linux).
+ """
+
self._lock = threading.Lock()
+ # Spawn the Go binary in a subprocess with piped IO
self.proc = subprocess.Popen( # noqa: S603
[str(self._binary())],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
- stderr=sys.stderr,
+ stderr=sys.stderr, # Pass Go logs directly to stderr
text=True,
bufsize=1,
)
self.env_path = env_path
+
+ # Initialize services with a reference to self (to access .execute())
self.channels = ChannelsService(self)
self.teams = TeamsService(self)
self.chats = ChatsService(self)
@@ -40,6 +77,15 @@ def __init__(
self.init_client(cache_mode, cache_path)
def _binary(self):
+ """Resolves the path to the correct Go binary for the current OS.
+
+ Returns:
+ pathlib.Path: The absolute path to the executable.
+
+ Raises:
+ RuntimeError: If the OS is not Windows or Linux.
+ """
+
base = pathlib.Path(__file__).parent / "bin"
osname = platform.system()
@@ -55,6 +101,22 @@ def init_client(
cache_mode: config.CacheMode = config.CacheMode.DISABLED,
cache_path: str | None = None,
) -> Any:
+ """Initializes the Go backend with authentication and cache configuration.
+
+ This method sends the 'init' command to the Go process, which sets up the
+ MSAL token provider and the Graph Service Client.
+
+ Args:
+ cache_mode (config.CacheMode): The caching strategy.
+ cache_path (str | None): Path to the cache file (if applicable).
+
+ Returns:
+ Any: The initialization result from the Go process.
+
+ Raises:
+ RuntimeError: If the Go process reports an initialization error.
+ """
+
sender_config = config.SenderConfig()
auth_config = config.load_auth_config(self.env_path)
return self.execute(
@@ -78,6 +140,18 @@ def init_client(
)
def init_fake_client(self, mock_server_url: str) -> Any:
+ """Initializes the Go backend in test mode using a mock server.
+
+ This bypasses real MSAL authentication and directs Graph API calls to
+ the provided local URL. [For testing purposes only]
+
+ Args:
+ mock_server_url (str): The URL of the mock HTTP server.
+
+ Returns:
+ Any: The result of the initialization.
+ """
+
return self.execute(
cmd_type="init",
params={
@@ -92,6 +166,27 @@ def execute(
config: dict[str, Any] | None = None,
params: dict[str, Any] | None = None,
) -> Any:
+ """Executes a command on the Go subprocess via JSON-IPC.
+
+ This is the low-level bridge method used by services to communicate with the backend.
+ It handles serialization, thread-safe writing to stdin, reading from stdout,
+ and error propagation.
+
+ Args:
+ cmd_type (str): The type of command (e.g., "init", "request").
+ method (str | None): The specific API method to call (e.g., "listChannels").
+ Required if cmd_type is "request".
+ config (dict | None): Configuration payload (used primarily for initialization).
+ params (dict | None): Parameters for the method call (e.g., teamRef, body).
+
+ Returns:
+ Any: The 'result' field from the Go response.
+
+ Raises:
+ RuntimeError: If the Go process crashes, closes the pipe, returns an empty response,
+ or explicitly reports an error in the "error" field.
+ """
+
payload = {"type": cmd_type}
if method:
payload["method"] = method
@@ -102,17 +197,19 @@ def execute(
json_payload = json.dumps(payload)
- # Critical section to avoid interleaving requests/responses
+ # Critical section to avoid interleaving requests/responses#
+ # (e.g., thread A writes request A, thread B writes request B,
+ # then thread A reads response B).
with self._lock:
try:
self.proc.stdin.write(json_payload + "\n")
self.proc.stdin.flush()
- print("Sent to Go process:", json_payload) # Debug print
+ # print("Sent to Go process:", json_payload) # Debug print
raw_response = self.proc.stdout.readline()
- print("Received from Go process:", raw_response.strip()) # Debug print
+ # print("Received from Go process:", raw_response.strip()) # Debug print
except BrokenPipeError:
raise RuntimeError("Go process crashed or closed connection") # noqa: B904
@@ -127,4 +224,9 @@ def execute(
return res.get("result")
def close(self):
+ """
+ Terminates the underlying Go subprocess.
+
+ Should be called when the client is no longer needed to free system resources.
+ """
self.proc.terminate()
diff --git a/python/teams_lib_pzsp2_z1/services/base_service.py b/python/teams_lib_pzsp2_z1/services/base_service.py
index 073288d..b5627b5 100644
--- a/python/teams_lib_pzsp2_z1/services/base_service.py
+++ b/python/teams_lib_pzsp2_z1/services/base_service.py
@@ -6,5 +6,7 @@
from teams_lib_pzsp2_z1.client import TeamsClient
class BaseService:
+ """Base class for all service classes in the Teams library."""
+
def __init__(self, client: TeamsClient):
self.client = client
From 7ab77a883e63f3c14f8823c9df7c54ba26e2c554 Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Thu, 8 Jan 2026 11:12:07 +0100
Subject: [PATCH 72/99] feat: improved lib closing to ensure cache operations
are completed
---
go/bridge/main.go | 7 ++++++
python/teams_lib_pzsp2_z1/client.py | 37 +++++++++++++++++++++++++----
2 files changed, 40 insertions(+), 4 deletions(-)
diff --git a/go/bridge/main.go b/go/bridge/main.go
index d14276c..d598b9e 100644
--- a/go/bridge/main.go
+++ b/go/bridge/main.go
@@ -8,6 +8,7 @@ import (
jsonClientLib "github.com/pzsp-teams/lib-python/internal/json-client"
jsonModel "github.com/pzsp-teams/lib-python/internal/json-model"
+ "github.com/pzsp-teams/lib"
)
var client *jsonClientLib.TeamsJSONClient
@@ -64,5 +65,11 @@ func main() {
respondResult(writer, result)
continue
}
+
+ if req.Type == "close" {
+ lib.Close()
+ respondResult(writer, "closed")
+ return
+ }
}
}
diff --git a/python/teams_lib_pzsp2_z1/client.py b/python/teams_lib_pzsp2_z1/client.py
index b7f39c7..68210f2 100644
--- a/python/teams_lib_pzsp2_z1/client.py
+++ b/python/teams_lib_pzsp2_z1/client.py
@@ -31,6 +31,7 @@ class TeamsClient:
chats (ChatsService): Service for managing chats and messages.
"""
+
def __init__(
self,
auto_init: bool = True,
@@ -73,6 +74,8 @@ def __init__(
self.teams = TeamsService(self)
self.chats = ChatsService(self)
+ self.is_cache_enabled = cache_mode != config.CacheMode.DISABLED
+
if auto_init:
self.init_client(cache_mode, cache_path)
@@ -224,9 +227,35 @@ def execute(
return res.get("result")
def close(self):
- """
- Terminates the underlying Go subprocess.
+ """Gracefully closes the Go backend, ensuring cache is synced.
- Should be called when the client is no longer needed to free system resources.
+ Sends a 'close' command to the Go process, which triggers `lib.Close()`
+ to wait for background cache operations. Then terminates the process.
"""
- self.proc.terminate()
+
+ if self.is_cache_enabled:
+ with self._lock:
+ if self.proc.poll() is None:
+ try:
+ payload = json.dumps({"type": "close"}) + "\n"
+ self.proc.stdin.write(payload)
+ self.proc.stdin.flush()
+ except (BrokenPipeError, OSError):
+ pass
+
+ try:
+ self.proc.wait(timeout=3)
+ except subprocess.TimeoutExpired:
+ self.proc.kill()
+ self.proc.wait()
+
+ try:
+ if self.proc.stdin:
+ self.proc.stdin.close()
+ if self.proc.stdout:
+ self.proc.stdout.close()
+ except (OSError, ValueError):
+ pass
+ else:
+ self.proc.terminate()
+ self.proc.wait()
From 3aec827daa378995d0e54548a421f9582e2a4375 Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Thu, 8 Jan 2026 11:15:34 +0100
Subject: [PATCH 73/99] docs: docstrings in for client configs
---
python/teams_lib_pzsp2_z1/config.py | 87 +++++++++++++++++++++++++++++
1 file changed, 87 insertions(+)
diff --git a/python/teams_lib_pzsp2_z1/config.py b/python/teams_lib_pzsp2_z1/config.py
index f785c0a..fa6f40f 100644
--- a/python/teams_lib_pzsp2_z1/config.py
+++ b/python/teams_lib_pzsp2_z1/config.py
@@ -1,3 +1,11 @@
+"""
+Package config holds configuration structures used across the application.
+
+This module defines data classes for authentication, request sending, and caching,
+as well as utility functions to load and validate these configurations from
+environment variables.
+"""
+
import os
from dataclasses import dataclass
from enum import Enum
@@ -6,11 +14,23 @@
class AuthConfigurationError(Exception):
+ """Raised when the authentication configuration is missing or invalid."""
pass
@dataclass
class SenderConfig:
+ """Defines configuration for the HTTP request sender connecting to Microsoft Graph API.
+
+ Attributes:
+ max_retries (int): The maximum number of retry attempts for failed requests.
+ Defaults to 3.
+ next_retry_delay (int): The delay between retry attempts in **seconds**.
+ Defaults to 2.
+ timeout (int): The maximum time to wait for a request to complete in **seconds**.
+ Defaults to 10.
+ """
+
max_retries: int = 3
next_retry_delay: int = 2
timeout: int = 10
@@ -18,6 +38,21 @@ class SenderConfig:
@dataclass
class AuthConfig:
+ """Holds configuration required for authentication via MSAL.
+
+ All fields are required to successfully acquire tokens.
+
+ Attributes:
+ client_id (str): The Application (client) ID assigned by the Azure portal.
+ tenant (str): The Directory (tenant) ID or domain.
+ email (str): The email address of the user to authenticate.
+ scopes (list[str]): A list of Graph API permission scopes (e.g., "User.Read").
+ auth_method (str): The flow used to acquire tokens.
+ Valid values are:
+ * **"DEVICE_CODE"**: Prints a code to console; user visits a URL.
+ * **"INTERACTIVE"**: Opens a local browser window.
+ """
+
client_id: str
tenant: str
email: str
@@ -26,16 +61,54 @@ class AuthConfig:
class CacheMode(Enum):
+ """Defines the caching strategy used by the application.
+
+ Attributes:
+ DISABLED: Caching is turned off completely.
+ SYNC: Cache operations (read/write) are performed synchronously.
+ Safe but may block the main thread.
+ ASYNC: Cache operations are performed asynchronously in the background.
+ Faster for the main thread but requires `close()` to ensure data consistency.
+ """
+
DISABLED = "DISABLED"
SYNC = "SYNC"
ASYNC = "ASYNC"
@dataclass
class CacheConfig:
+ """Holds configuration for the caching layer.
+
+ Attributes:
+ mode (CacheMode): The caching strategy to use.
+ path (str | None): The file path for the cache storage (e.g., JSON file).
+ Required if mode is not DISABLED. Defaults to None.
+ """
+
mode: CacheMode
path: str | None = None
def load_auth_config(env_path: str | None = None) -> AuthConfig:
+ """Loads authentication configuration from environment variables or a .env file.
+
+ It looks for the following environment variables:
+ * `CLIENT_ID`
+ * `TENANT_ID`
+ * `EMAIL`
+ * `SCOPES` (comma-separated, defaults to "https://graph.microsoft.com/.default")
+ * `AUTH_METHOD` (defaults to "DEVICE_CODE")
+
+ Args:
+ env_path (str | None, optional): Path to the .env file. If None,
+ loads from the current directory.
+
+ Returns:
+ AuthConfig: A populated and validated configuration object.
+
+ Raises:
+ AuthConfigurationError: If required variables are missing or values are invalid.
+ """
+
load_dotenv(env_path)
cfg = AuthConfig(
@@ -51,10 +124,24 @@ def load_auth_config(env_path: str | None = None) -> AuthConfig:
def get_env(key: str, fallback: str) -> str:
+ """Retrieves an environment variable with a fallback value."""
+
return os.getenv(key, fallback)
def validate(cfg: AuthConfig):
+ """Validates the authentication configuration object.
+
+ Checks for the presence of required fields and the validity of the auth method.
+
+ Args:
+ cfg (AuthConfig): The configuration object to validate.
+
+ Raises:
+ AuthConfigurationError: If `client_id`, `tenant`, or `email` are empty,
+ or if `auth_method` is not supported.
+ """
+
missing = []
if not cfg.client_id:
missing.append("CLIENT_ID")
From d7bf4c1e04ad1cd0f607af0197fcef7ffb941d3e Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Thu, 8 Jan 2026 11:17:29 +0100
Subject: [PATCH 74/99] chore: added env template
---
python/.env.template | 5 +++++
1 file changed, 5 insertions(+)
create mode 100644 python/.env.template
diff --git a/python/.env.template b/python/.env.template
new file mode 100644
index 0000000..e1fca19
--- /dev/null
+++ b/python/.env.template
@@ -0,0 +1,5 @@
+CLIENT_ID=xxxxxxxxxxxxxxxxxxxxxxxxx
+TENANT_ID=xxxxxxxxxxxxxxxxxxxxxxxxx
+EMAIL=xxxxxxxxxxxxxxxxxxxxxxxxx
+SCOPES=openid,profile,User.Read.All,Team.ReadBasic.All,Channel.ReadBasic.All,ChannelSettings.ReadWrite.All,Channel.Create,Channel.Delete.All,ChannelMessage.Send,ChannelMember.ReadWrite.All,Group.ReadWrite.All,TeamMember.ReadWrite.All
+AUTH_METHOD=INTERACTIVE
\ No newline at end of file
From d41b0072a034ad80f7e0e0b9353417689565019a Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Thu, 8 Jan 2026 11:53:41 +0100
Subject: [PATCH 75/99] docs: Generated docs using mkdocstrings lib
---
python/docs/index.md | 104 ++++++++++++++++++
python/docs/reference/client.md | 8 ++
python/docs/reference/config.md | 5 +
python/docs/reference/models/channel.md | 3 +
python/docs/reference/models/chat.md | 3 +
python/docs/reference/models/member.md | 3 +
python/docs/reference/models/message.md | 5 +
python/docs/reference/models/team.md | 3 +
python/docs/reference/services/channels.md | 5 +
python/docs/reference/services/chats.md | 5 +
python/docs/reference/services/teams.md | 5 +
python/mkdocs.yml | 52 +++++++++
python/teams_lib_pzsp2_z1/model/__init__.py | 0
.../teams_lib_pzsp2_z1/services/__init__.py | 0
14 files changed, 201 insertions(+)
create mode 100644 python/docs/index.md
create mode 100644 python/docs/reference/client.md
create mode 100644 python/docs/reference/config.md
create mode 100644 python/docs/reference/models/channel.md
create mode 100644 python/docs/reference/models/chat.md
create mode 100644 python/docs/reference/models/member.md
create mode 100644 python/docs/reference/models/message.md
create mode 100644 python/docs/reference/models/team.md
create mode 100644 python/docs/reference/services/channels.md
create mode 100644 python/docs/reference/services/chats.md
create mode 100644 python/docs/reference/services/teams.md
create mode 100644 python/mkdocs.yml
create mode 100644 python/teams_lib_pzsp2_z1/model/__init__.py
create mode 100644 python/teams_lib_pzsp2_z1/services/__init__.py
diff --git a/python/docs/index.md b/python/docs/index.md
new file mode 100644
index 0000000..ce210b3
--- /dev/null
+++ b/python/docs/index.md
@@ -0,0 +1,104 @@
+# Teams Lib Wrapper
+
+Welcome to the documentation for the **Teams API library**.
+This library provides a high-level, Pythonic interface for managing Microsoft Teams, leveraging a compiled Go backend for performance, authentication, and caching.
+
+---
+
+## Overview
+
+This project bridges the gap between Python's ease of use and Go's concurrency model.
+It acts as a wrapper around a Go binary (`teamsClientLib`), communicating via a JSON-based IPC (Inter-Process Communication) protocol.
+
+---
+
+## Key Features
+
+- **Pythonic Data Models**
+ Uses standard Python `dataclasses` and `Enum`s instead of raw JSON or complex Microsoft Graph SDK objects.
+
+- **Automatic Caching**
+ Reduces API calls by caching Team and Channel references (handled transparently by the Go backend).
+
+- **Simple Authentication**
+ Thin wrappers around MSAL (Microsoft Authentication Library) configuration.
+
+- **Type Hinting**
+ Full support for static type checkers like `mypy` and IDE autocompletion.
+
+---
+
+## Installation
+
+Library is available fully via pip
+
+```bash
+pip install pzsp2z1teams
+```
+
+## Quick start
+
+### 1. Configuration
+
+Create a `.env` in your project with your Azure AD credentials:
+
+```ini
+CLIENT_ID=your-client-id-uuid
+TENANT_ID=your-tenant-id-uuid
+EMAIL=user@example.com
+SCOPES=User.Read,Team.ReadBasic.All,Channel.ReadBasic.All
+AUTH_METHOD=DEVICE_CODE [or INTERACTIVE]
+```
+
+Scopes needed by all functions could be found in .env.template.
+**//TODO add url to template**
+
+### 2. Basic usage
+
+Here is a minimal example showing how to initialize the client and list joined teams:
+
+```python
+from teams_lib_pzsp2_z1.client import TeamsClient
+from teams_lib_pzsp2_z1.config import CacheMode
+
+def main():
+ # Initialize the client.
+ # This spawns the Go subprocess and performs authentication.
+ client = TeamsClient()
+
+ try:
+ print("Fetching teams...")
+ teams = client.teams.list_my_joined()
+
+ for team in teams:
+ print(f"Team: {team.display_name} | ID: {team.id}")
+
+ # Example: List channels in the first team
+ channels = client.channels.list_channels(team.id)
+ print(f" -> Found {len(channels)} channels.")
+
+ except RuntimeError as e:
+ print(f"An error occurred: {e}")
+
+ finally:
+ # Crucial: Close the client to ensure background cache writes finish
+ # and the Go subprocess is terminated gracefully.
+ client.close()
+
+if __name__ == "__main__":
+ main()
+```
+
+## Architecture
+
+The library relies on a compiled Go backend to handle performance-critical tasks. When `TeamsClient` is initialized, the following sequence occurs:
+
+1. **OS Detection**: The library checks the host operating system (Windows/Linux).
+2. **Process Launch**: It launches the corresponding Go binary as a background subprocess.
+3. **Initialization**: It sends an `init` command via `stdin` to configure the MSAL authentication provider and the caching engine.
+4. **Request Handling**: Subsequent method calls (e.g., `client.teams.get(...)`) follow this flow:
+ - Arguments are **serialized to JSON**.
+ - The payload is sent to the Go process via **stdin**.
+ - The Go process executes the request against the **Microsoft Graph API**.
+ - The JSON response is read from **stdout** and mapped back to native **Python objects**.
+
diff --git a/python/docs/reference/client.md b/python/docs/reference/client.md
new file mode 100644
index 0000000..00d7b9d
--- /dev/null
+++ b/python/docs/reference/client.md
@@ -0,0 +1,8 @@
+# Client Reference
+
+Main lib entry point.
+
+::: teams_lib_pzsp2_z1.client.TeamsClient
+ options:
+ show_root_heading: true
+ show_source: true
\ No newline at end of file
diff --git a/python/docs/reference/config.md b/python/docs/reference/config.md
new file mode 100644
index 0000000..b6ccc47
--- /dev/null
+++ b/python/docs/reference/config.md
@@ -0,0 +1,5 @@
+# Configuration Reference
+
+Config structures.
+
+::: teams_lib_pzsp2_z1.config
\ No newline at end of file
diff --git a/python/docs/reference/models/channel.md b/python/docs/reference/models/channel.md
new file mode 100644
index 0000000..483fc6e
--- /dev/null
+++ b/python/docs/reference/models/channel.md
@@ -0,0 +1,3 @@
+# Channel Models
+
+::: teams_lib_pzsp2_z1.model.channel
\ No newline at end of file
diff --git a/python/docs/reference/models/chat.md b/python/docs/reference/models/chat.md
new file mode 100644
index 0000000..b1d7ccc
--- /dev/null
+++ b/python/docs/reference/models/chat.md
@@ -0,0 +1,3 @@
+# Message Models
+
+::: teams_lib_pzsp2_z1.model.chat
\ No newline at end of file
diff --git a/python/docs/reference/models/member.md b/python/docs/reference/models/member.md
new file mode 100644
index 0000000..02b83a1
--- /dev/null
+++ b/python/docs/reference/models/member.md
@@ -0,0 +1,3 @@
+# Message Models
+
+::: teams_lib_pzsp2_z1.model.member
\ No newline at end of file
diff --git a/python/docs/reference/models/message.md b/python/docs/reference/models/message.md
new file mode 100644
index 0000000..90ec708
--- /dev/null
+++ b/python/docs/reference/models/message.md
@@ -0,0 +1,5 @@
+# Message Models
+
+::: teams_lib_pzsp2_z1.model.message
+
+::: teams_lib_pzsp2_z1.model.mention
\ No newline at end of file
diff --git a/python/docs/reference/models/team.md b/python/docs/reference/models/team.md
new file mode 100644
index 0000000..ef7a4ea
--- /dev/null
+++ b/python/docs/reference/models/team.md
@@ -0,0 +1,3 @@
+# Team Models
+
+::: teams_lib_pzsp2_z1.model.team
\ No newline at end of file
diff --git a/python/docs/reference/services/channels.md b/python/docs/reference/services/channels.md
new file mode 100644
index 0000000..a0748e0
--- /dev/null
+++ b/python/docs/reference/services/channels.md
@@ -0,0 +1,5 @@
+# Channels Service
+
+Channels in team management.
+
+::: teams_lib_pzsp2_z1.services.channels.ChannelsService
\ No newline at end of file
diff --git a/python/docs/reference/services/chats.md b/python/docs/reference/services/chats.md
new file mode 100644
index 0000000..de4182a
--- /dev/null
+++ b/python/docs/reference/services/chats.md
@@ -0,0 +1,5 @@
+# Chats Service
+
+Handling one-to-one and group chats.
+
+::: teams_lib_pzsp2_z1.services.chats.ChatsService
\ No newline at end of file
diff --git a/python/docs/reference/services/teams.md b/python/docs/reference/services/teams.md
new file mode 100644
index 0000000..d62308d
--- /dev/null
+++ b/python/docs/reference/services/teams.md
@@ -0,0 +1,5 @@
+# Teams Service
+
+Team lifecycle management.
+
+::: teams_lib_pzsp2_z1.services.teams.TeamsService
\ No newline at end of file
diff --git a/python/mkdocs.yml b/python/mkdocs.yml
new file mode 100644
index 0000000..2e1207c
--- /dev/null
+++ b/python/mkdocs.yml
@@ -0,0 +1,52 @@
+site_name: Teams Lib Wrapper Documentation
+site_url: https://pzsp2z1teams.com/
+repo_url: https://github.com/pzsp-teams/lib-python
+edit_uri: edit/main/docs/
+
+theme:
+ name: material
+ features:
+ - content.code.copy
+ - navigation.expand
+ palette:
+ - scheme: default
+ primary: indigo
+ accent: indigo
+ toggle:
+ icon: material/brightness-7
+ name: Switch to dark mode
+ - scheme: slate
+ primary: indigo
+ accent: indigo
+ toggle:
+ icon: material/brightness-4
+ name: Switch to light mode
+
+plugins:
+ - search
+ - mkdocstrings:
+ default_handler: python
+ handlers:
+ python:
+ paths: [.]
+ options:
+ docstring_style: google
+ show_source: true
+ show_root_heading: true
+ show_category_heading: true
+
+nav:
+ - Home: index.md
+ - API Reference:
+ - Client: reference/client.md
+ - Config: reference/config.md
+ - Services:
+ - Teams: reference/services/teams.md
+ - Channels: reference/services/channels.md
+ - Chats: reference/services/chats.md
+ - Models:
+ - Team: reference/models/team.md
+ - Channel: reference/models/channel.md
+ - Message: reference/models/message.md
+ - Member: reference/models/member.md
+ - Chat: reference/models/chat.md
\ No newline at end of file
diff --git a/python/teams_lib_pzsp2_z1/model/__init__.py b/python/teams_lib_pzsp2_z1/model/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/python/teams_lib_pzsp2_z1/services/__init__.py b/python/teams_lib_pzsp2_z1/services/__init__.py
new file mode 100644
index 0000000..e69de29
From 30782b773d121a097e6dbe66c18c51f7e384b2ae Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Thu, 8 Jan 2026 11:59:03 +0100
Subject: [PATCH 76/99] docs: added example flow charts
---
python/docs/index.md | 25 +++++++++++++++++++++++++
python/mkdocs.yml | 9 +++++++++
2 files changed, 34 insertions(+)
diff --git a/python/docs/index.md b/python/docs/index.md
index ce210b3..a34f1bc 100644
--- a/python/docs/index.md
+++ b/python/docs/index.md
@@ -102,3 +102,28 @@ The library relies on a compiled Go backend to handle performance-critical tasks
- The Go process executes the request against the **Microsoft Graph API**.
- The JSON response is read from **stdout** and mapped back to native **Python objects**.
+### Process Flow Diagram
+
+```mermaid
+sequenceDiagram
+ participant Py as Python Client
+ participant Go as Go Subprocess
+ participant MS as Microsoft Graph
+
+ rect rgba(230, 240, 255, 0.7)
+ note right of Py: Phase 1: Initialization
+ Py->>Py: Detect OS & Spawn Process
+ Py->>Go: Send {"type": "init", ...}
+ Go->>Go: Setup MSAL & Cache
+ Go-->>Py: {"result": "initialized"}
+ end
+
+ rect rgba(240, 255, 240, 0.7)
+ note right of Py: Phase 2: Normal Operation Loop
+ Py->>Go: Send {"type": "request", "method": "getTeam", ...}
+ Go->>MS: GET /v1.0/teams/{id}
+ MS-->>Go: HTTP 200 OK (JSON)
+ Go-->>Py: {"result": {...}}
+ Py->>Py: Map JSON to Python DataClass
+ end
+```
diff --git a/python/mkdocs.yml b/python/mkdocs.yml
index 2e1207c..b9a3658 100644
--- a/python/mkdocs.yml
+++ b/python/mkdocs.yml
@@ -35,6 +35,15 @@ plugins:
show_root_heading: true
show_category_heading: true
+# --- TO JEST NOWA SEKCJA ---
+markdown_extensions:
+ - pymdownx.superfences:
+ custom_fences:
+ - name: mermaid
+ class: mermaid
+ format: !!python/name:pymdownx.superfences.fence_code_format
+# ---------------------------
+
nav:
- Home: index.md
- API Reference:
From 1983fe161d1d4f20bd0cbc4a5fcc690524b90582 Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Thu, 8 Jan 2026 12:23:50 +0100
Subject: [PATCH 77/99] docs: added part of README
---
python/.gitignore | 1 +
python/README.md | 35 +++++++++++++++++++++++++++++++++++
python/mkdocs.yml | 2 --
3 files changed, 36 insertions(+), 2 deletions(-)
create mode 100644 python/.gitignore
diff --git a/python/.gitignore b/python/.gitignore
new file mode 100644
index 0000000..45ddf0a
--- /dev/null
+++ b/python/.gitignore
@@ -0,0 +1 @@
+site/
diff --git a/python/README.md b/python/README.md
index e69de29..ba8b54b 100644
--- a/python/README.md
+++ b/python/README.md
@@ -0,0 +1,35 @@
+# Teams API Python Wrapper
+
+[](https://www.python.org/)
+[](LICENSE)
+[](https://pzsp-teams.github.io/lib-python/)
+
+
+
+A high-performance **Python wrapper** for the Microsoft Teams Graph API. It leverages a compiled **Go (Golang)** backend to handle heavy lifting, concurrency, and caching, while providing a clean, fully typed Pythonic interface.
+
+## 🚀 Key Features
+
+- **Performance of Go**: Uses a compiled Go subprocess for efficient API communication and state management.
+- **Pythonic Interface**: Fully typed using `dataclasses` and `Enums`. Native support for IDE autocompletion and type checkers (`mypy`).
+- **Intelligent Cache**: Background caching of Team/Channel IDs to minimize API throttling and latency (handled transparently by the backend).
+- **Simplified Auth**: Wraps MSAL authentication flows configuration.
+
+## 📦 Installation
+
+```bash
+pip install teams-lib-pzsp2-z1
+```
+
+## 🛠️ Architecture
+This library acts as a bridge. When initialized, it spawns a dedicated Go subprocess (`teamsClientLib`) and communicates via JSON-RPC over `stdin`/`stdout`.
+
+The `TeamsClient` aggregates domain-specific services:
+
+- **client.teams**: Manage team lifecycles (create, list, delete).
+
+- **client.channels**: Manage standard and private channels.
+
+- **client.chats**: Handle messages, mentions, and chat participants.
+
+## 💻 Quick Start
\ No newline at end of file
diff --git a/python/mkdocs.yml b/python/mkdocs.yml
index b9a3658..ee115fa 100644
--- a/python/mkdocs.yml
+++ b/python/mkdocs.yml
@@ -35,14 +35,12 @@ plugins:
show_root_heading: true
show_category_heading: true
-# --- TO JEST NOWA SEKCJA ---
markdown_extensions:
- pymdownx.superfences:
custom_fences:
- name: mermaid
class: mermaid
format: !!python/name:pymdownx.superfences.fence_code_format
-# ---------------------------
nav:
- Home: index.md
From 3560098d68448aff1c541173d357ce2db76eba8c Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Thu, 8 Jan 2026 12:30:21 +0100
Subject: [PATCH 78/99] docs: added rest of README file
---
python/README.md | 88 +++++++++++++++++++++++++++++++++++++++++++-
python/docs/index.md | 2 +-
2 files changed, 88 insertions(+), 2 deletions(-)
diff --git a/python/README.md b/python/README.md
index ba8b54b..8e66add 100644
--- a/python/README.md
+++ b/python/README.md
@@ -22,6 +22,7 @@ pip install teams-lib-pzsp2-z1
```
## 🛠️ Architecture
+
This library acts as a bridge. When initialized, it spawns a dedicated Go subprocess (`teamsClientLib`) and communicates via JSON-RPC over `stdin`/`stdout`.
The `TeamsClient` aggregates domain-specific services:
@@ -32,4 +33,89 @@ The `TeamsClient` aggregates domain-specific services:
- **client.chats**: Handle messages, mentions, and chat participants.
-## 💻 Quick Start
\ No newline at end of file
+## 💻 Quick Start
+
+### 1. Configuration
+
+Create a `.env` in your project with your Azure AD credentials:
+
+```ini
+CLIENT_ID=your-client-id-uuid
+TENANT_ID=your-tenant-id-uuid
+EMAIL=user@example.com
+SCOPES=User.Read,Team.ReadBasic.All,Channel.ReadBasic.All
+AUTH_METHOD=DEVICE_CODE [or INTERACTIVE]
+```
+
+Scopes needed by all functions could be found in .env.template.
+**//TODO add url to template**
+
+### 2. Basic usage
+
+Here is a minimal example showing how to initialize the client and list joined teams:
+
+```python
+from teams_lib_pzsp2_z1.client import TeamsClient
+from teams_lib_pzsp2_z1.config import CacheMode
+
+def main():
+ # Initialize the client.
+ # This spawns the Go subprocess and performs authentication.
+ client = TeamsClient()
+
+ try:
+ print("Fetching teams...")
+ teams = client.teams.list_my_joined()
+
+ for team in teams:
+ print(f"Team: {team.display_name} | ID: {team.id}")
+
+ # Example: List channels in the first team
+ channels = client.channels.list_channels(team.id)
+ print(f" -> Found {len(channels)} channels.")
+
+ except RuntimeError as e:
+ print(f"An error occurred: {e}")
+
+ finally:
+ # Crucial: Close the client to ensure background cache writes finish
+ # and the Go subprocess is terminated gracefully.
+ client.close()
+
+if __name__ == "__main__":
+ main()
+```
+
+## 📚 Documentation
+
+Full API reference, architecture details, and configuration guides are available here:
+
+👉 [Read the Documentation](https://pzsp-teams.github.io/lib-python/)
+
+## Authentication
+
+The library loads credentials from environment variables. Ensure your Azure App Registration has the necessary **API Permissions** (e.g., `Team.ReadBasic.All`, `Channel.ReadBasic.All`) granted in the Azure Portal.
+
+Supported Authentication Methods (`AUTH_METHOD`):
+
+- **INTERACTIVE**: Opens a browser window automatically.
+
+- **DEVICE_CODE**: Prints a code to the console to login via microsoft.com/devicelogin.
+
+## Cache & Lifecycle
+
+If caching is enabled (SYNC or ASYNC), the Go backend stores metadata locally (e.g., `teams_cache.json`) to speed up reference resolution.
+
+⚠️ Important: client.close()
+Because the Go backend might be running background goroutines (especially for Async Cache), you **must** call `client.close()` before your script exits. This ensures:
+
+1. Pending cache writes are flushed to disk.
+
+2. The Go subprocess is terminated correctly.
+
+```python
+client.close()
+```
+
+## 📄 License
+This project is licensed under the MIT License - see the [LICENSE](https://github.com/pzsp-teams/lib-python/blob/main/LICENSE) file for details.
diff --git a/python/docs/index.md b/python/docs/index.md
index a34f1bc..c92817b 100644
--- a/python/docs/index.md
+++ b/python/docs/index.md
@@ -33,7 +33,7 @@ It acts as a wrapper around a Go binary (`teamsClientLib`), communicating via a
Library is available fully via pip
```bash
-pip install pzsp2z1teams
+pip install teams_lib_pzsp2_z1
```
## Quick start
From 2ec801f03fe37bb0630699a968d55f6adebcfa58 Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Thu, 8 Jan 2026 12:34:42 +0100
Subject: [PATCH 79/99] chore: removed docs dir from python release branch
---
.github/workflows/integration-tests.yml | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml
index ca0aa05..7289339 100644
--- a/.github/workflows/integration-tests.yml
+++ b/.github/workflows/integration-tests.yml
@@ -2,10 +2,10 @@ name: Python Integration Tests
on:
pull_request:
- branches: [ main ]
+ branches: [main]
types: [review_requested, ready_for_review]
push:
- branches: [ main ]
+ branches: [main]
repository_dispatch:
types: [verify-new-go-lib-version]
@@ -237,6 +237,8 @@ jobs:
# clean up unneeded files
rm -rf python_release/tests
+ rm -rf python_release/docs
+ rm mkdocs.yml
find python_release -type d -name "__pycache__" -exec rm -rf {} +
rm -rf python_release/.venv
From 5a83154fd63af3ecac80b1a18b28ce3f8d8d92cf Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Thu, 8 Jan 2026 12:35:04 +0100
Subject: [PATCH 80/99] fix: typo
---
.github/workflows/integration-tests.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml
index 7289339..96af91a 100644
--- a/.github/workflows/integration-tests.yml
+++ b/.github/workflows/integration-tests.yml
@@ -238,7 +238,7 @@ jobs:
# clean up unneeded files
rm -rf python_release/tests
rm -rf python_release/docs
- rm mkdocs.yml
+ rm python_release/mkdocs.yml
find python_release -type d -name "__pycache__" -exec rm -rf {} +
rm -rf python_release/.venv
From f179f0cee7fe811e18318ab863d3194af5e177f4 Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Thu, 8 Jan 2026 13:45:47 +0100
Subject: [PATCH 81/99] docs: updated main project README
---
README.md | 122 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 121 insertions(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 7727354..8e66add 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,121 @@
-# lib-python
+# Teams API Python Wrapper
+
+[](https://www.python.org/)
+[](LICENSE)
+[](https://pzsp-teams.github.io/lib-python/)
+
+
+
+A high-performance **Python wrapper** for the Microsoft Teams Graph API. It leverages a compiled **Go (Golang)** backend to handle heavy lifting, concurrency, and caching, while providing a clean, fully typed Pythonic interface.
+
+## 🚀 Key Features
+
+- **Performance of Go**: Uses a compiled Go subprocess for efficient API communication and state management.
+- **Pythonic Interface**: Fully typed using `dataclasses` and `Enums`. Native support for IDE autocompletion and type checkers (`mypy`).
+- **Intelligent Cache**: Background caching of Team/Channel IDs to minimize API throttling and latency (handled transparently by the backend).
+- **Simplified Auth**: Wraps MSAL authentication flows configuration.
+
+## 📦 Installation
+
+```bash
+pip install teams-lib-pzsp2-z1
+```
+
+## 🛠️ Architecture
+
+This library acts as a bridge. When initialized, it spawns a dedicated Go subprocess (`teamsClientLib`) and communicates via JSON-RPC over `stdin`/`stdout`.
+
+The `TeamsClient` aggregates domain-specific services:
+
+- **client.teams**: Manage team lifecycles (create, list, delete).
+
+- **client.channels**: Manage standard and private channels.
+
+- **client.chats**: Handle messages, mentions, and chat participants.
+
+## 💻 Quick Start
+
+### 1. Configuration
+
+Create a `.env` in your project with your Azure AD credentials:
+
+```ini
+CLIENT_ID=your-client-id-uuid
+TENANT_ID=your-tenant-id-uuid
+EMAIL=user@example.com
+SCOPES=User.Read,Team.ReadBasic.All,Channel.ReadBasic.All
+AUTH_METHOD=DEVICE_CODE [or INTERACTIVE]
+```
+
+Scopes needed by all functions could be found in .env.template.
+**//TODO add url to template**
+
+### 2. Basic usage
+
+Here is a minimal example showing how to initialize the client and list joined teams:
+
+```python
+from teams_lib_pzsp2_z1.client import TeamsClient
+from teams_lib_pzsp2_z1.config import CacheMode
+
+def main():
+ # Initialize the client.
+ # This spawns the Go subprocess and performs authentication.
+ client = TeamsClient()
+
+ try:
+ print("Fetching teams...")
+ teams = client.teams.list_my_joined()
+
+ for team in teams:
+ print(f"Team: {team.display_name} | ID: {team.id}")
+
+ # Example: List channels in the first team
+ channels = client.channels.list_channels(team.id)
+ print(f" -> Found {len(channels)} channels.")
+
+ except RuntimeError as e:
+ print(f"An error occurred: {e}")
+
+ finally:
+ # Crucial: Close the client to ensure background cache writes finish
+ # and the Go subprocess is terminated gracefully.
+ client.close()
+
+if __name__ == "__main__":
+ main()
+```
+
+## 📚 Documentation
+
+Full API reference, architecture details, and configuration guides are available here:
+
+👉 [Read the Documentation](https://pzsp-teams.github.io/lib-python/)
+
+## Authentication
+
+The library loads credentials from environment variables. Ensure your Azure App Registration has the necessary **API Permissions** (e.g., `Team.ReadBasic.All`, `Channel.ReadBasic.All`) granted in the Azure Portal.
+
+Supported Authentication Methods (`AUTH_METHOD`):
+
+- **INTERACTIVE**: Opens a browser window automatically.
+
+- **DEVICE_CODE**: Prints a code to the console to login via microsoft.com/devicelogin.
+
+## Cache & Lifecycle
+
+If caching is enabled (SYNC or ASYNC), the Go backend stores metadata locally (e.g., `teams_cache.json`) to speed up reference resolution.
+
+⚠️ Important: client.close()
+Because the Go backend might be running background goroutines (especially for Async Cache), you **must** call `client.close()` before your script exits. This ensures:
+
+1. Pending cache writes are flushed to disk.
+
+2. The Go subprocess is terminated correctly.
+
+```python
+client.close()
+```
+
+## 📄 License
+This project is licensed under the MIT License - see the [LICENSE](https://github.com/pzsp-teams/lib-python/blob/main/LICENSE) file for details.
From e4f8523f0d48cde06b4e349eab6e345e2be101ff Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Thu, 8 Jan 2026 13:54:03 +0100
Subject: [PATCH 82/99] docs: added go reference
---
README.md | 3 +++
1 file changed, 3 insertions(+)
diff --git a/README.md b/README.md
index 8e66add..cbc32a9 100644
--- a/README.md
+++ b/README.md
@@ -119,3 +119,6 @@ client.close()
## 📄 License
This project is licensed under the MIT License - see the [LICENSE](https://github.com/pzsp-teams/lib-python/blob/main/LICENSE) file for details.
+
+## Porst
+This lib is also available in [GO](https://github.com/pzsp-teams/lib)
From 8a9f6d468d959823692850df4e242a661a88cf43 Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Sun, 11 Jan 2026 23:15:40 +0100
Subject: [PATCH 83/99] chore: adjusted test for listPinned messages
---
go/go.mod | 3 ++-
go/go.sum | 4 ++++
python/teams_lib_pzsp2_z1/client.py | 4 ++--
python/tests/fake_server/data.py | 20 +++++++++++++++++++-
4 files changed, 27 insertions(+), 4 deletions(-)
diff --git a/go/go.mod b/go/go.mod
index 5bbfd43..ff058ba 100644
--- a/go/go.mod
+++ b/go/go.mod
@@ -6,7 +6,7 @@ require (
github.com/microsoft/kiota-abstractions-go v1.9.3
github.com/microsoftgraph/msgraph-sdk-go v1.93.0
github.com/mitchellh/mapstructure v1.5.0
- github.com/pzsp-teams/lib v0.0.0-20260107211439-3d301706e02c
+ github.com/pzsp-teams/lib v0.0.0-20260111170324-f66ca4f5d369
)
require (
@@ -39,6 +39,7 @@ require (
go.opentelemetry.io/otel/metric v1.39.0 // indirect
go.opentelemetry.io/otel/trace v1.39.0 // indirect
golang.org/x/net v0.48.0 // indirect
+ golang.org/x/sync v0.19.0 // indirect
golang.org/x/sys v0.39.0 // indirect
golang.org/x/text v0.32.0 // indirect
)
diff --git a/go/go.sum b/go/go.sum
index 14ee7df..a25a60c 100644
--- a/go/go.sum
+++ b/go/go.sum
@@ -71,6 +71,8 @@ github.com/pzsp-teams/lib v0.0.0-20260107201101-e9e6f48e39e8 h1:1ifLBNVdw8TCLPzH
github.com/pzsp-teams/lib v0.0.0-20260107201101-e9e6f48e39e8/go.mod h1:BQwULf+u8TtbKR0U23MlZXgm2CVcLBJn2cS5gmR7L8M=
github.com/pzsp-teams/lib v0.0.0-20260107211439-3d301706e02c h1:ZtUkM66y/jg5g5kEQhteQeACuwKl+cOkRuGPlX1mkGg=
github.com/pzsp-teams/lib v0.0.0-20260107211439-3d301706e02c/go.mod h1:BQwULf+u8TtbKR0U23MlZXgm2CVcLBJn2cS5gmR7L8M=
+github.com/pzsp-teams/lib v0.0.0-20260111170324-f66ca4f5d369 h1:TNB4hQ6u8aH0cekG1xbITdkAxgrY4FmwSWgzfI1e21Q=
+github.com/pzsp-teams/lib v0.0.0-20260111170324-f66ca4f5d369/go.mod h1:bpgptBb8PaJV03buetvHnq6plkQHpF0WnY1K5iU31U8=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/std-uritemplate/std-uritemplate/go/v2 v2.0.8 h1:gMBdYMTHt2mmTdXW8YfvRjRUZ0GhyGV+IqSH9H15bGw=
@@ -93,6 +95,8 @@ go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
+golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
+golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
diff --git a/python/teams_lib_pzsp2_z1/client.py b/python/teams_lib_pzsp2_z1/client.py
index 68210f2..b2f6ec1 100644
--- a/python/teams_lib_pzsp2_z1/client.py
+++ b/python/teams_lib_pzsp2_z1/client.py
@@ -208,11 +208,11 @@ def execute(
self.proc.stdin.write(json_payload + "\n")
self.proc.stdin.flush()
- # print("Sent to Go process:", json_payload) # Debug print
+ print("Sent to Go process:", json_payload) # Debug print
raw_response = self.proc.stdout.readline()
- # print("Received from Go process:", raw_response.strip()) # Debug print
+ print("Received from Go process:", raw_response.strip()) # Debug print
except BrokenPipeError:
raise RuntimeError("Go process crashed or closed connection") # noqa: B904
diff --git a/python/tests/fake_server/data.py b/python/tests/fake_server/data.py
index 50fbeaa..c1f0e36 100644
--- a/python/tests/fake_server/data.py
+++ b/python/tests/fake_server/data.py
@@ -707,11 +707,29 @@ def get_all_messeges_in_chats_response(self) -> dict:
}
def get_list_pinned_messages_in_chat_response(self, chat_id: str) -> dict:
+ print("get_list_pinned_messages_in_chat_response called for chat_id:", chat_id) # Debug print
return {
"value": [
{
- ODATA_TYPE: "#microsoft.graph.pinnedChatMessageInfo",
"id": message.id,
+ "message": {
+ "id": message.id,
+ "etag": message.id,
+ "messageType": "message",
+ "chatId": chat_id,
+ "body": {
+ "content": message.content,
+ "contentType": message.content_type,
+ },
+ "from": {
+ "user": {
+ "id": message.sender.user_id,
+ "displayName": message.sender.display_name,
+ }
+ },
+ "createdDateTime": message.created_date_time,
+ "lastModifiedDateTime": message.created_date_time,
+ }
}
for message in self.chat_messages.get(chat_id, [])
],
From fdf54e2758308b898a8a4183647d0bbb20d0132d Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Sun, 11 Jan 2026 23:58:17 +0100
Subject: [PATCH 84/99] fix: tried to fix resolving id from header
---
python/tests/fake_server/data.py | 36 +++++++++++++++++++++++++------
python/tests/fake_server/setup.py | 6 +-----
2 files changed, 30 insertions(+), 12 deletions(-)
diff --git a/python/tests/fake_server/data.py b/python/tests/fake_server/data.py
index c1f0e36..c667983 100644
--- a/python/tests/fake_server/data.py
+++ b/python/tests/fake_server/data.py
@@ -1,5 +1,9 @@
from dataclasses import dataclass, field
from typing import Optional, List, Dict, Any
+from unittest import result
+from urllib import request
+
+from werkzeug import Response
from teams_lib_pzsp2_z1.model.team import Team
from teams_lib_pzsp2_z1.model.chat import Chat, ChatType
@@ -254,7 +258,8 @@ def get_createTeamViaGroup_response(self, group_id: str) -> dict:
"visibility": team.visibility,
}
- def get_createTeamFromTemplate_response(self, request_json: dict) -> dict:
+ def get_createTeamFromTemplate_response(self, request: request.Request) -> Response:
+ request_json = request.json
new_team = Team(
id=self.newTeamID,
display_name=request_json.get("displayName"),
@@ -267,16 +272,34 @@ def get_createTeamFromTemplate_response(self, request_json: dict) -> dict:
# self.newTeamID == "f47ac10b-58cc-4372-a567-0e02b2c3d479"
op_id = "00000000-0000-0000-0000-000000000000"
- return {
- "status": 202,
- "headers": {
+ return Response(
+ status= 202,
+ headers= {
"Location": f"/teams('{self.newTeamID}')/operations('{op_id}')",
"Content-Location": f"/teams('{self.newTeamID}')",
"Content-Type": "application/json",
"Content-Length": "0"
},
- "body": ""
- }
+ response= ""
+ )
+
+ # def create_team_handler(self, req):
+ # result = self.get_createTeamFromTemplate_response(req.json)
+
+ # base_url = "https://graph.microsoft.com/v1.0"
+ # loc_header = f"/teams/{TEAM_ID}/operations/{OP_ID}"
+ # content_loc_header = f"/teams/{TEAM_ID}"
+
+ # return Response(
+ # response=result["body"],
+ # status=result["status"],
+ # headers={
+ # "Location": full_location,
+ # "Content-Location": full_content_loc,
+ # "Content-Type": result["headers"]["Content-Type"],
+ # "Content-Length": result["headers"]["Content-Length"]
+ # }
+ # )
def get_archiveTeam_response(self, team_id: str) -> dict:
team = self._find_team(team_id)
@@ -707,7 +730,6 @@ def get_all_messeges_in_chats_response(self) -> dict:
}
def get_list_pinned_messages_in_chat_response(self, chat_id: str) -> dict:
- print("get_list_pinned_messages_in_chat_response called for chat_id:", chat_id) # Debug print
return {
"value": [
{
diff --git a/python/tests/fake_server/setup.py b/python/tests/fake_server/setup.py
index f1f2ff2..d4aa92a 100644
--- a/python/tests/fake_server/setup.py
+++ b/python/tests/fake_server/setup.py
@@ -353,11 +353,7 @@ def handle_chats_request(req):
httpserver.expect_request(
"/v1.0/teams",
method="POST"
- ).respond_with_handler(lambda req: Response(
- response=data.get_createTeamFromTemplate_response(req.json)["body"],
- status=data.get_createTeamFromTemplate_response(req.json)["status"],
- headers=data.get_createTeamFromTemplate_response(req.json)["headers"]
- ))
+ ).respond_with_handler(data.get_createTeamFromTemplate_response)
# POST /teams/{team_id}/members
httpserver.expect_request(
From 35967405e2c19f58ebe708fa4ffd2d7a61baaad8 Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Mon, 12 Jan 2026 14:45:14 +0100
Subject: [PATCH 85/99] feat: added searchmessages options structs
---
go/go.mod | 2 +-
go/go.sum | 2 +
.../decoders/decode-searchOptions.go | 79 +++++++++++++++++++
3 files changed, 82 insertions(+), 1 deletion(-)
create mode 100644 go/internal/json-client/decoders/decode-searchOptions.go
diff --git a/go/go.mod b/go/go.mod
index ff058ba..a736666 100644
--- a/go/go.mod
+++ b/go/go.mod
@@ -6,7 +6,7 @@ require (
github.com/microsoft/kiota-abstractions-go v1.9.3
github.com/microsoftgraph/msgraph-sdk-go v1.93.0
github.com/mitchellh/mapstructure v1.5.0
- github.com/pzsp-teams/lib v0.0.0-20260111170324-f66ca4f5d369
+ github.com/pzsp-teams/lib v0.0.0-20260112132712-1335fb93c11f
)
require (
diff --git a/go/go.sum b/go/go.sum
index a25a60c..5a91b58 100644
--- a/go/go.sum
+++ b/go/go.sum
@@ -73,6 +73,8 @@ github.com/pzsp-teams/lib v0.0.0-20260107211439-3d301706e02c h1:ZtUkM66y/jg5g5kE
github.com/pzsp-teams/lib v0.0.0-20260107211439-3d301706e02c/go.mod h1:BQwULf+u8TtbKR0U23MlZXgm2CVcLBJn2cS5gmR7L8M=
github.com/pzsp-teams/lib v0.0.0-20260111170324-f66ca4f5d369 h1:TNB4hQ6u8aH0cekG1xbITdkAxgrY4FmwSWgzfI1e21Q=
github.com/pzsp-teams/lib v0.0.0-20260111170324-f66ca4f5d369/go.mod h1:bpgptBb8PaJV03buetvHnq6plkQHpF0WnY1K5iU31U8=
+github.com/pzsp-teams/lib v0.0.0-20260112132712-1335fb93c11f h1:MOnTg+X51/lukcWWjHyw9OPx9IzC6Q6TJsZXiaH/KnU=
+github.com/pzsp-teams/lib v0.0.0-20260112132712-1335fb93c11f/go.mod h1:bpgptBb8PaJV03buetvHnq6plkQHpF0WnY1K5iU31U8=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/std-uritemplate/std-uritemplate/go/v2 v2.0.8 h1:gMBdYMTHt2mmTdXW8YfvRjRUZ0GhyGV+IqSH9H15bGw=
diff --git a/go/internal/json-client/decoders/decode-searchOptions.go b/go/internal/json-client/decoders/decode-searchOptions.go
new file mode 100644
index 0000000..48c0a0e
--- /dev/null
+++ b/go/internal/json-client/decoders/decode-searchOptions.go
@@ -0,0 +1,79 @@
+package decoders
+
+import (
+ "time"
+
+ "github.com/pzsp-teams/lib/search"
+)
+
+type SearchConfigDTO struct {
+ MaxWorkers int `json:"maxWorkers"`
+}
+
+type SearchPageDTO struct {
+ From *int `json:"from"`
+ Size *int `json:"size"`
+}
+
+type SearchMessageOptionsDTO struct {
+ Query string `json:"query"`
+ SearchPage SearchPageDTO `json:"searchPage"`
+ From []string `json:"from"`
+ NotFrom []string `json:"notFrom"`
+ IsRead bool `json:"isRead"`
+ IsMentioned bool `json:"isMentioned"`
+ To []string `json:"to"`
+ NotTo []string `json:"notTo"`
+ StartTime time.Time `json:"startTime"`
+ EndTime time.Time `json:"endTime"`
+ Interval string `json:"interval"`
+ NotFromMe bool `json:"notFromMe"`
+ NotToMe bool `json:"notToMe"`
+ FromMe bool `json:"fromMe"`
+ ToMe bool `json:"toMe"`
+}
+
+func DecodeSearchMessageOptions(dto *SearchMessageOptionsDTO) *search.SearchMessagesOptions {
+ if dto == nil {
+ return nil
+ }
+
+ interval := search.TimeInterval(dto.Interval)
+
+ options := &search.SearchMessagesOptions{
+ Query: &dto.Query,
+ SearchPage: DecodeSearchPage(&dto.SearchPage),
+ From: dto.From,
+ NotFrom: dto.NotFrom,
+ IsRead: &dto.IsRead,
+ IsMentioned: &dto.IsMentioned,
+ To: dto.To,
+ NotTo: dto.NotTo,
+ StartTime: &dto.StartTime,
+ EndTime: &dto.EndTime,
+ Interval: &interval,
+ NotFromMe: dto.NotFromMe,
+ NotToMe: dto.NotToMe,
+ FromMe: dto.FromMe,
+ ToMe: dto.ToMe,
+ }
+ return options
+}
+
+func DecodeSearchPage(dto *SearchPageDTO) *search.SearchPage {
+ if dto == nil {
+ return nil
+ }
+
+ page := &search.SearchPage{}
+ if dto.From != nil {
+ from := int32(*dto.From)
+ page.From = &from
+ }
+ if dto.Size != nil {
+ size := int32(*dto.Size)
+ page.Size = &size
+ }
+
+ return page
+}
From 354c67c477ab203f289167b92d7332f09793187d Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Mon, 12 Jan 2026 14:54:03 +0100
Subject: [PATCH 86/99] feat: added new functions to go bridge
---
.../json-client/decoders/decode-chatRef.go | 10 +--
go/internal/json-client/request-map.go | 12 +++
go/internal/json-client/requests-channels.go | 19 +++++
go/internal/json-client/requests-chats.go | 76 ++++++++++---------
4 files changed, 74 insertions(+), 43 deletions(-)
diff --git a/go/internal/json-client/decoders/decode-chatRef.go b/go/internal/json-client/decoders/decode-chatRef.go
index c9c9c0c..1bc85e7 100644
--- a/go/internal/json-client/decoders/decode-chatRef.go
+++ b/go/internal/json-client/decoders/decode-chatRef.go
@@ -1,8 +1,6 @@
package decoders
import (
- "fmt"
-
"github.com/pzsp-teams/lib/chats"
)
@@ -16,14 +14,14 @@ const (
ChatTypeOneOnOne = "oneOnOne"
)
-func GetChatRef(dto ChatRefDTO) (chats.ChatRef, error) {
+func GetChatRef(dto ChatRefDTO) chats.ChatRef {
switch dto.Type {
case ChatTypeGroup:
- return chats.GroupChatRef{Ref: dto.Ref}, nil
+ return chats.GroupChatRef{Ref: dto.Ref}
case ChatTypeOneOnOne:
- return chats.OneOnOneChatRef{Ref: dto.Ref}, nil
+ return chats.OneOnOneChatRef{Ref: dto.Ref}
default:
- return nil, fmt.Errorf("unknown chat type: %s", dto.Type)
+ return nil
}
}
diff --git a/go/internal/json-client/request-map.go b/go/internal/json-client/request-map.go
index 68bd08f..9f31f27 100644
--- a/go/internal/json-client/request-map.go
+++ b/go/internal/json-client/request-map.go
@@ -106,6 +106,10 @@ var Handlers = map[string]HandlerFunc{
return c.GetMentionsInChannel(p)
},
+ "searchMessagesInChannel": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ return c.SearchMessagesInChannel(p)
+ },
+
// CHATS
"createOneOnOneChat": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
return c.CreateOneToOneChat(p)
@@ -115,6 +119,10 @@ var Handlers = map[string]HandlerFunc{
return c.CreateGroupChat(p)
},
+ "getChat": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ return c.GetChat(p)
+ },
+
"addMemberToGroupChat": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
return c.AddMemberToGroupChat(p)
},
@@ -170,4 +178,8 @@ var Handlers = map[string]HandlerFunc{
"getMentionsInChat": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
return c.GetMentionsInChat(p)
},
+
+ "searchMessagesInChat": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ return c.SearchMessagesInChat(p)
+ },
}
diff --git a/go/internal/json-client/requests-channels.go b/go/internal/json-client/requests-channels.go
index 81b4f2c..0f0803a 100644
--- a/go/internal/json-client/requests-channels.go
+++ b/go/internal/json-client/requests-channels.go
@@ -5,6 +5,7 @@ import (
"github.com/pzsp-teams/lib-python/internal/json-client/decoders"
"github.com/pzsp-teams/lib/models"
+ "github.com/pzsp-teams/lib/search"
)
type listChannelsParams struct {
@@ -206,3 +207,21 @@ func (c *TeamsJSONClient) GetMentionsInChannel(p map[string]interface{}) (interf
return c.client.Channels.GetMentions(context.TODO(), params.TeamRef, params.ChannelRef, params.RawMentions)
})
}
+
+type searchMessagesInChannelParams struct {
+ TeamRef string `json:"teamRef"`
+ ChannelRef string `json:"channelRef"`
+ SearchOptions decoders.SearchMessageOptionsDTO `json:"searchMessagesOptions"`
+ SearchConfig decoders.SearchConfigDTO `json:"searchConfig"`
+}
+
+func (c *TeamsJSONClient) SearchMessagesInChannel(p map[string]interface{}) (interface{}, error) {
+ return execute(p, func(params searchMessagesInChannelParams) (interface{}, error) {
+ searchOptions := decoders.DecodeSearchMessageOptions(¶ms.SearchOptions)
+ searchConfig, err := decoders.DecodeParams[search.SearchConfig](¶ms.SearchConfig)
+ if err != nil {
+ return nil, err
+ }
+ return c.client.Channels.SearchMessages(context.TODO(), ¶ms.TeamRef, ¶ms.ChannelRef, searchOptions, searchConfig)
+ })
+}
diff --git a/go/internal/json-client/requests-chats.go b/go/internal/json-client/requests-chats.go
index 9a9241b..eda2bb6 100644
--- a/go/internal/json-client/requests-chats.go
+++ b/go/internal/json-client/requests-chats.go
@@ -6,6 +6,7 @@ import (
"github.com/pzsp-teams/lib-python/internal/json-client/decoders"
"github.com/pzsp-teams/lib/chats"
"github.com/pzsp-teams/lib/models"
+ "github.com/pzsp-teams/lib/search"
)
type createOneToOneChatParams struct {
@@ -30,6 +31,17 @@ func (c *TeamsJSONClient) CreateGroupChat(p map[string]interface{}) (interface{}
})
}
+type baseChatParams struct {
+ ChatRef decoders.ChatRefDTO `json:"chatRef"`
+}
+
+func (c *TeamsJSONClient) GetChat(p map[string]interface{}) (interface{}, error) {
+ return execute(p, func(params baseChatParams) (interface{}, error) {
+ chatRef := decoders.GetChatRef(params.ChatRef)
+ return c.client.Chats.GetChat(context.TODO(), chatRef)
+ })
+}
+
type groupChatMemberParams struct {
GroupChatRef string `json:"groupChatRef"`
UserRef string `json:"userRef"`
@@ -77,10 +89,7 @@ type listMessagesInChatParams struct {
func (c *TeamsJSONClient) ListMessagesInChat(p map[string]interface{}) (interface{}, error) {
return execute(p, func(params listMessagesInChatParams) (interface{}, error) {
- chatRef, err := decoders.GetChatRef(params.ChatRef)
- if err != nil {
- return nil, err
- }
+ chatRef := decoders.GetChatRef(params.ChatRef)
nextLink := decoders.GetNextLink(¶ms.NextLink)
return c.client.Chats.ListMessages(context.TODO(), chatRef, params.IncludeSystem, nextLink)
})
@@ -93,10 +102,7 @@ type sendMessageInChatParams struct {
func (c *TeamsJSONClient) SendMessageInChat(p map[string]interface{}) (interface{}, error) {
return execute(p, func(params sendMessageInChatParams) (interface{}, error) {
- chatRef, err := decoders.GetChatRef(params.ChatRef)
- if err != nil {
- return nil, err
- }
+ chatRef := decoders.GetChatRef(params.ChatRef)
body, err := decoders.DecodeParams[models.MessageBody](params.Body)
if err != nil {
return nil, err
@@ -112,21 +118,15 @@ type messageInChatParams struct {
func (c *TeamsJSONClient) DeleteMessageInChat(p map[string]interface{}) (interface{}, error) {
return execute(p, func(params messageInChatParams) (interface{}, error) {
- chatRef, err := decoders.GetChatRef(params.ChatRef)
- if err != nil {
- return nil, err
- }
- err = c.client.Chats.DeleteMessage(context.TODO(), chatRef, params.MessageID)
+ chatRef := decoders.GetChatRef(params.ChatRef)
+ err := c.client.Chats.DeleteMessage(context.TODO(), chatRef, params.MessageID)
return "deleted", err
})
}
func (c *TeamsJSONClient) GetMessageInChat(p map[string]interface{}) (interface{}, error) {
return execute(p, func(params messageInChatParams) (interface{}, error) {
- chatRef, err := decoders.GetChatRef(params.ChatRef)
- if err != nil {
- return nil, err
- }
+ chatRef := decoders.GetChatRef(params.ChatRef)
return c.client.Chats.GetMessage(context.TODO(), chatRef, params.MessageID)
})
}
@@ -161,38 +161,25 @@ func (c *TeamsJSONClient) ListMyChatMessages(p map[string]interface{}) (interfac
})
}
-type baseChatParams struct {
- ChatRef decoders.ChatRefDTO `json:"chatRef"`
-}
-
func (c *TeamsJSONClient) ListPinnedMessagesInChat(p map[string]interface{}) (interface{}, error) {
return execute(p, func(params baseChatParams) (interface{}, error) {
- chatRef, err := decoders.GetChatRef(params.ChatRef)
- if err != nil {
- return nil, err
- }
+ chatRef := decoders.GetChatRef(params.ChatRef)
return c.client.Chats.ListPinnedMessages(context.TODO(), chatRef)
})
}
func (c *TeamsJSONClient) PinMessageInChat(p map[string]interface{}) (interface{}, error) {
return execute(p, func(params messageInChatParams) (interface{}, error) {
- chatRef, err := decoders.GetChatRef(params.ChatRef)
- if err != nil {
- return nil, err
- }
- err = c.client.Chats.PinMessage(context.TODO(), chatRef, params.MessageID)
+ chatRef := decoders.GetChatRef(params.ChatRef)
+ err := c.client.Chats.PinMessage(context.TODO(), chatRef, params.MessageID)
return "pinned", err
})
}
func (c *TeamsJSONClient) UnpinMessageInChat(p map[string]interface{}) (interface{}, error) {
return execute(p, func(params messageInChatParams) (interface{}, error) {
- chatRef, err := decoders.GetChatRef(params.ChatRef)
- if err != nil {
- return nil, err
- }
- err = c.client.Chats.UnpinMessage(context.TODO(), chatRef, params.MessageID)
+ chatRef := decoders.GetChatRef(params.ChatRef)
+ err := c.client.Chats.UnpinMessage(context.TODO(), chatRef, params.MessageID)
return "unpinned", err
})
}
@@ -204,10 +191,25 @@ type mentionInChatParams struct {
func (c *TeamsJSONClient) GetMentionsInChat(p map[string]interface{}) (interface{}, error) {
return execute(p, func(params mentionInChatParams) (interface{}, error) {
- chatRef, err := decoders.GetChatRef(params.ChatRef)
+ chatRef := decoders.GetChatRef(params.ChatRef)
+ return c.client.Chats.GetMentions(context.TODO(), chatRef, params.RawMentions)
+ })
+}
+
+type searchMessagesInChatParams struct {
+ ChatRef decoders.ChatRefDTO `json:"chatRef"`
+ SearchOptions decoders.SearchMessageOptionsDTO `json:"searchMessagesOptions"`
+ SearchConfig decoders.SearchConfigDTO `json:"searchConfig"`
+}
+
+func (c *TeamsJSONClient) SearchMessagesInChat(p map[string]interface{}) (interface{}, error) {
+ return execute(p, func(params searchMessagesInChatParams) (interface{}, error) {
+ chatRef := decoders.GetChatRef(params.ChatRef)
+ searchOptions := decoders.DecodeSearchMessageOptions(¶ms.SearchOptions)
+ searchConfig, err := decoders.DecodeParams[search.SearchConfig](¶ms.SearchConfig)
if err != nil {
return nil, err
}
- return c.client.Chats.GetMentions(context.TODO(), chatRef, params.RawMentions)
+ return c.client.Chats.SearchMessages(context.TODO(), chatRef, searchOptions, searchConfig)
})
}
From cf62781168afdb169628df12575358e6f0290345 Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Mon, 12 Jan 2026 21:07:30 +0100
Subject: [PATCH 87/99] feat: added search messages structures to python
---
.../decoders/decode-searchOptions.go | 55 ++++---
python/teams_lib_pzsp2_z1/model/search.py | 134 ++++++++++++++++++
python/teams_lib_pzsp2_z1/services/chats.py | 28 ++++
3 files changed, 201 insertions(+), 16 deletions(-)
create mode 100644 python/teams_lib_pzsp2_z1/model/search.py
diff --git a/go/internal/json-client/decoders/decode-searchOptions.go b/go/internal/json-client/decoders/decode-searchOptions.go
index 48c0a0e..45ef127 100644
--- a/go/internal/json-client/decoders/decode-searchOptions.go
+++ b/go/internal/json-client/decoders/decode-searchOptions.go
@@ -40,22 +40,45 @@ func DecodeSearchMessageOptions(dto *SearchMessageOptionsDTO) *search.SearchMess
interval := search.TimeInterval(dto.Interval)
- options := &search.SearchMessagesOptions{
- Query: &dto.Query,
- SearchPage: DecodeSearchPage(&dto.SearchPage),
- From: dto.From,
- NotFrom: dto.NotFrom,
- IsRead: &dto.IsRead,
- IsMentioned: &dto.IsMentioned,
- To: dto.To,
- NotTo: dto.NotTo,
- StartTime: &dto.StartTime,
- EndTime: &dto.EndTime,
- Interval: &interval,
- NotFromMe: dto.NotFromMe,
- NotToMe: dto.NotToMe,
- FromMe: dto.FromMe,
- ToMe: dto.ToMe,
+ options := &search.SearchMessagesOptions{}
+ if dto.Query != "" {
+ options.Query = &dto.Query
+ }
+ options.SearchPage = DecodeSearchPage(&dto.SearchPage)
+ if len(dto.From) > 0 {
+ options.From = dto.From
+ }
+ if len(dto.NotFrom) > 0 {
+ options.NotFrom = dto.NotFrom
+ }
+ options.IsRead = &dto.IsRead
+ options.IsMentioned = &dto.IsMentioned
+ if len(dto.To) > 0 {
+ options.To = dto.To
+ }
+ if len(dto.NotTo) > 0 {
+ options.NotTo = dto.NotTo
+ }
+ if !dto.StartTime.IsZero() {
+ options.StartTime = &dto.StartTime
+ }
+ if !dto.EndTime.IsZero() {
+ options.EndTime = &dto.EndTime
+ }
+ if dto.Interval != "" {
+ options.Interval = &interval
+ }
+ if dto.NotFromMe {
+ options.NotFromMe = dto.NotFromMe
+ }
+ if dto.NotToMe {
+ options.NotToMe = dto.NotToMe
+ }
+ if dto.FromMe {
+ options.FromMe = dto.FromMe
+ }
+ if dto.ToMe {
+ options.ToMe = dto.ToMe
}
return options
}
diff --git a/python/teams_lib_pzsp2_z1/model/search.py b/python/teams_lib_pzsp2_z1/model/search.py
new file mode 100644
index 0000000..2c871a0
--- /dev/null
+++ b/python/teams_lib_pzsp2_z1/model/search.py
@@ -0,0 +1,134 @@
+from dataclasses import dataclass
+from enum import Enum
+
+from teams_lib_pzsp2_z1.model.message import Message
+
+
+@dataclass
+class SearchConfig:
+ """
+ Configuration parameters for search operations.
+
+ Attributes:
+ max_workers (int): The maximum number of worker threads to use for concurrent search operations. Defaults to 8.
+ """
+
+ max_workers: int = 8
+
+
+@dataclass
+class SearchPage:
+ """
+ Represents pagination parameters for search results.
+
+ Attributes:
+ from_ (int): The starting index of the search results.
+ size (int): The number of search results to return. Defaults to 25.
+ """
+
+ from_: int = 0
+ size: int = 25
+
+
+class TimeInterval(Enum):
+ """Enumeration of time intervals for filtering search results.
+
+ Attributes:
+ TODAY: Represents the current day.
+ YESTERDAY: Represents the previous day.
+ THIS_WEEK: Represents the current week.
+ THIS_MONTH: Represents the current month.
+ LAST_MONTH: Represents the previous month.
+ THIS_YEAR: Represents the current year.
+ LAST_YEAR: Represents the previous year.
+ """
+
+ TODAY = "today"
+ YESTERDAY = "yesterday"
+ THIS_WEEK = "this week"
+ THIS_MONTH = "this month"
+ LAST_MONTH = "last month"
+ THIS_YEAR = "this year"
+ LAST_YEAR = "last year"
+
+ @dataclass
+ class SearchMessagesOptions:
+ """
+ Configuration options for searching messages.
+
+ Attributes:
+ query (str | None): The search query string.
+ search_page (SearchPage | None): Pagination options.
+ from_ (list[str]): List of sender email addresses to include.
+ not_from (list[str]): List of sender email addresses to exclude.
+ is_read (bool | None): Filter by read status (True for read, False for unread).
+ is_mentioned (bool | None): Filter by mention status (True for mentioned, False for not mentioned).
+ to (list[str]): List of recipient email addresses to include.
+ not_to (list[str]): List of recipient email addresses to exclude.
+ start_time (datetime | None): Start time for the sent time range filter.
+ end_time (datetime | None): End time for the sent time range filter.
+ interval (TimeInterval | None): Predefined time interval for the sent time filter.
+ not_from_me (bool): Exclude messages sent by the current user.
+ not_to_me (bool): Exclude messages sent to the current user.
+ from_me (bool): Include only messages sent by the current user.
+ to_me (bool): Include only messages sent to the current user.
+
+ Note: If interval is set, it takes precedence over start_time and end_time.
+ Note: Using `to` clauses works only in chats, not in team channels.
+ Note: Currently, queries for is_read may not function as expected due to API limitations.
+ """
+
+ query: str | None = None
+ search_page: SearchPage | None = None
+ from_: list[str] = None
+ not_from: list[str] = None
+ is_read: bool | None = None
+ is_mentioned: bool | None = None
+ to: list[str] = None
+ not_to: list[str] = None
+ start_time: object = None
+ end_time: object = None
+ interval: TimeInterval | None = None
+ not_from_me: bool = False
+ not_to_me: bool = False
+ from_me: bool = False
+ to_me: bool = False
+
+ def __post_init__(self):
+ if self.from_ is None:
+ self.from_ = []
+ if self.not_from is None:
+ self.not_from = []
+ if self.to is None:
+ self.to = []
+ if self.not_to is None:
+ self.not_to = []
+
+@dataclass
+class SearchResult:
+ """
+ SearchResult represents a single search result containing a message and its context.
+
+ Attributes:
+ message (Message): The message that matched the search criteria.
+ channel_id (str | None): The ID of the channel where the message was found (if applicable).
+ team_id (str | None): The ID of the team where the message was found (if applicable).
+ chat_id (str | None): The ID of the chat where the message was found (if applicable).
+ """
+ message: Message
+ channel_id: str | None = None
+ team_id: str | None = None
+ chat_id: str | None = None
+
+@dataclass
+class SearchResults:
+ """
+ SearchResults represents the results of a message search.
+
+ Attributes:
+ messages (list[SearchResult]): A list of search results.
+ next_from (int | None): The index to use for the next page of results,
+ or None if there are no more results.
+ """
+ messages: list[SearchResult]
+ next_from: int | None = None
diff --git a/python/teams_lib_pzsp2_z1/services/chats.py b/python/teams_lib_pzsp2_z1/services/chats.py
index 1d072d2..c4a207a 100644
--- a/python/teams_lib_pzsp2_z1/services/chats.py
+++ b/python/teams_lib_pzsp2_z1/services/chats.py
@@ -91,6 +91,34 @@ def create_group_chat(
topic=response["Topic"],
)
+ def get_chat(self, chat_ref: ChatRef) -> Chat:
+ """
+ Retrieves a chat by its reference.
+
+ Args:
+ chat_ref (ChatRef): The chat reference object containing the ID/Name and ChatType.
+
+ Returns:
+ Chat: The requested chat object.
+ """
+ response = self.client.execute(
+ cmd_type="request",
+ method="getChat",
+ params={
+ "chatRef": {
+ "ref": chat_ref.ref,
+ "type": chat_ref.type.value,
+ },
+ },
+ )
+
+ return Chat(
+ id=response["ID"],
+ type=ChatType(response["Type"]),
+ is_hidden=(True if response["IsHidden"] else False),
+ topic=response["Topic"],
+ )
+
def add_member_to_group_chat(self, group_chat_ref: str, user_ref: str) -> Member:
"""Adds a user to an existing Group Chat.
From 35d3db7194b1dccb71333b50d0870bac0d0fbe5b Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Mon, 12 Jan 2026 21:15:46 +0100
Subject: [PATCH 88/99] feat: added new search functions to python lib
---
python/teams_lib_pzsp2_z1/model/search.py | 30 +++++++++++
.../teams_lib_pzsp2_z1/services/channels.py | 52 ++++++++++++++++++
python/teams_lib_pzsp2_z1/services/chats.py | 53 +++++++++++++++++++
3 files changed, 135 insertions(+)
diff --git a/python/teams_lib_pzsp2_z1/model/search.py b/python/teams_lib_pzsp2_z1/model/search.py
index 2c871a0..9983dbc 100644
--- a/python/teams_lib_pzsp2_z1/model/search.py
+++ b/python/teams_lib_pzsp2_z1/model/search.py
@@ -15,6 +15,10 @@ class SearchConfig:
max_workers: int = 8
+ def __dict__(self):
+ """Serializes the object keys to camelCase for the Go backend."""
+ return {"maxWorkers": self.max_workers}
+
@dataclass
class SearchPage:
@@ -29,6 +33,10 @@ class SearchPage:
from_: int = 0
size: int = 25
+ def __dict__(self):
+ """Serializes the object keys to camelCase for the Go backend."""
+ return {"from": self.from_, "size": self.size}
+
class TimeInterval(Enum):
"""Enumeration of time intervals for filtering search results.
@@ -104,6 +112,28 @@ def __post_init__(self):
if self.not_to is None:
self.not_to = []
+ def __dict__(self):
+ """Serializes the object keys to camelCase for the Go backend."""
+
+ result = {
+ "query": self.query,
+ "searchPage": self.search_page,
+ "from": self.from_,
+ "notFrom": self.not_from,
+ "isRead": self.is_read,
+ "isMentioned": self.is_mentioned,
+ "to": self.to,
+ "notTo": self.not_to,
+ "startTime": self.start_time,
+ "endTime": self.end_time,
+ "interval": self.interval.value if self.interval else None,
+ "notFromMe": self.not_from_me,
+ "notToMe": self.not_to_me,
+ "fromMe": self.from_me,
+ "toMe": self.to_me,
+ }
+ return {k: v for k, v in result.items() if v is not None}
+
@dataclass
class SearchResult:
"""
diff --git a/python/teams_lib_pzsp2_z1/services/channels.py b/python/teams_lib_pzsp2_z1/services/channels.py
index 6c39c77..17cd91e 100644
--- a/python/teams_lib_pzsp2_z1/services/channels.py
+++ b/python/teams_lib_pzsp2_z1/services/channels.py
@@ -12,6 +12,12 @@
MessageContentType,
MessageFrom,
)
+from teams_lib_pzsp2_z1.model.search import (
+ SearchConfig,
+ SearchMessagesOptions,
+ SearchResult,
+ SearchResults,
+)
from teams_lib_pzsp2_z1.services.base_service import BaseService
@@ -529,3 +535,49 @@ def get_mentions(
text=mention["Text"],
target_id=mention["TargetID"],
) for mention in response]
+
+ def search_messages(self, team_ref: str, channel_ref: str, options: SearchMessagesOptions, config: SearchConfig) -> SearchResults:
+ """Searches for messages in a channel based on various criteria.
+
+ Args:
+ team_ref (str): The reference to the team.
+ channel_ref (str): The reference to the channel.
+ options (SearchMessagesOptions): The search options and filters.
+ config (SearchConfig): Configuration for the search operation.
+
+ Returns:
+ SearchResults: The results of the search operation.
+ """
+ response = self.client.execute(
+ cmd_type="request",
+ method="searchMessagesInChat",
+ params={
+ "teamRef": team_ref,
+ "channelRef": channel_ref,
+ "searchMessagesOptions": options.__dict__(),
+ "searchConfig": config.__dict__(),
+ },
+ )
+
+ return SearchResults(
+ messages=[
+ SearchResult(
+ message=Message(
+ id=msg["Message"]["ID"],
+ content=msg["Message"]["Content"],
+ content_type=MessageContentType(msg["Message"]["ContentType"]),
+ created_date_time=msg["Message"]["CreatedDateTime"],
+ sender=MessageFrom(
+ user_id=msg["Message"]["From"]["UserID"],
+ display_name=msg["Message"]["From"]["DisplayName"],
+ ),
+ reply_count=msg["Message"]["ReplyCount"],
+ ),
+ channel_id=msg.get("ChannelID"),
+ team_id=msg.get("TeamID"),
+ chat_id=msg.get("ChatID"),
+ )
+ for msg in response["Messages"]
+ ],
+ next_from=response.get("NextFrom"),
+ )
diff --git a/python/teams_lib_pzsp2_z1/services/chats.py b/python/teams_lib_pzsp2_z1/services/chats.py
index c4a207a..48bcbf6 100644
--- a/python/teams_lib_pzsp2_z1/services/chats.py
+++ b/python/teams_lib_pzsp2_z1/services/chats.py
@@ -14,6 +14,12 @@
MessageContentType,
MessageFrom,
)
+from teams_lib_pzsp2_z1.model.search import (
+ SearchConfig,
+ SearchMessagesOptions,
+ SearchResult,
+ SearchResults,
+)
from teams_lib_pzsp2_z1.services.base_service import BaseService
@@ -558,3 +564,50 @@ def get_mentions(self, chat_ref: ChatRef, raw_mentions: list[str]) -> list[Menti
)
for mention in mentions
]
+
+ def search_messages(self, chat_ref: ChatRef, options: SearchMessagesOptions, config: SearchConfig) -> SearchResults:
+ """Searches for messages in a chat based on various criteria.
+
+ Args:
+ chat_ref (ChatRef): The chat reference object.
+ options (SearchMessagesOptions): The search options and filters.
+ config (SearchConfig): Configuration for the search operation.
+
+ Returns:
+ SearchResults: The results of the search operation.
+ """
+ response = self.client.execute(
+ cmd_type="request",
+ method="searchMessagesInChat",
+ params={
+ "chatRef": {
+ "ref": chat_ref.ref,
+ "type": chat_ref.type.value,
+ },
+ "searchMessagesOptions": options.__dict__(),
+ "searchConfig": config.__dict__(),
+ },
+ )
+
+ return SearchResults(
+ messages=[
+ SearchResult(
+ message=Message(
+ id=msg["Message"]["ID"],
+ content=msg["Message"]["Content"],
+ content_type=MessageContentType(msg["Message"]["ContentType"]),
+ created_date_time=msg["Message"]["CreatedDateTime"],
+ sender=MessageFrom(
+ user_id=msg["Message"]["From"]["UserID"],
+ display_name=msg["Message"]["From"]["DisplayName"],
+ ),
+ reply_count=msg["Message"]["ReplyCount"],
+ ),
+ channel_id=msg.get("ChannelID"),
+ team_id=msg.get("TeamID"),
+ chat_id=msg.get("ChatID"),
+ )
+ for msg in response["Messages"]
+ ],
+ next_from=response.get("NextFrom"),
+ )
From 56c466b81c37cbfaccedc2fcd7c26ccda19669aa Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Mon, 12 Jan 2026 21:17:25 +0100
Subject: [PATCH 89/99] docs: added new search structs to docs
---
python/docs/reference/models/channel.md | 2 +-
python/docs/reference/models/chat.md | 2 +-
python/docs/reference/models/member.md | 2 +-
python/docs/reference/models/search.md | 3 +++
python/docs/reference/models/team.md | 2 +-
python/mkdocs.yml | 3 ++-
6 files changed, 9 insertions(+), 5 deletions(-)
create mode 100644 python/docs/reference/models/search.md
diff --git a/python/docs/reference/models/channel.md b/python/docs/reference/models/channel.md
index 483fc6e..8346b97 100644
--- a/python/docs/reference/models/channel.md
+++ b/python/docs/reference/models/channel.md
@@ -1,3 +1,3 @@
-# Channel Models
+# Channel Model
::: teams_lib_pzsp2_z1.model.channel
\ No newline at end of file
diff --git a/python/docs/reference/models/chat.md b/python/docs/reference/models/chat.md
index b1d7ccc..b4eb115 100644
--- a/python/docs/reference/models/chat.md
+++ b/python/docs/reference/models/chat.md
@@ -1,3 +1,3 @@
-# Message Models
+# Chat Model
::: teams_lib_pzsp2_z1.model.chat
\ No newline at end of file
diff --git a/python/docs/reference/models/member.md b/python/docs/reference/models/member.md
index 02b83a1..6056a2c 100644
--- a/python/docs/reference/models/member.md
+++ b/python/docs/reference/models/member.md
@@ -1,3 +1,3 @@
-# Message Models
+# Member Model
::: teams_lib_pzsp2_z1.model.member
\ No newline at end of file
diff --git a/python/docs/reference/models/search.md b/python/docs/reference/models/search.md
new file mode 100644
index 0000000..306e71a
--- /dev/null
+++ b/python/docs/reference/models/search.md
@@ -0,0 +1,3 @@
+# Search messages model
+
+::: teams_lib_pzsp2_z1.model.search
\ No newline at end of file
diff --git a/python/docs/reference/models/team.md b/python/docs/reference/models/team.md
index ef7a4ea..b1176db 100644
--- a/python/docs/reference/models/team.md
+++ b/python/docs/reference/models/team.md
@@ -1,3 +1,3 @@
-# Team Models
+# Team Model
::: teams_lib_pzsp2_z1.model.team
\ No newline at end of file
diff --git a/python/mkdocs.yml b/python/mkdocs.yml
index ee115fa..a861f32 100644
--- a/python/mkdocs.yml
+++ b/python/mkdocs.yml
@@ -56,4 +56,5 @@ nav:
- Channel: reference/models/channel.md
- Message: reference/models/message.md
- Member: reference/models/member.md
- - Chat: reference/models/chat.md
\ No newline at end of file
+ - Chat: reference/models/chat.md
+ - Search: reference/models/search.md
\ No newline at end of file
From 5cc79e942a41ad0015b6d6ee3776f1e328e50f5b Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Mon, 12 Jan 2026 21:56:16 +0100
Subject: [PATCH 90/99] test: added test for get chat
---
python/teams_lib_pzsp2_z1/model/search.py | 6 ++++--
python/tests/fake_server/data.py | 14 ++++++++++++++
python/tests/fake_server/setup.py | 10 ++++++++++
python/tests/test_chats.py | 23 +++++++++++++++++++++++
4 files changed, 51 insertions(+), 2 deletions(-)
diff --git a/python/teams_lib_pzsp2_z1/model/search.py b/python/teams_lib_pzsp2_z1/model/search.py
index 9983dbc..29fb0f5 100644
--- a/python/teams_lib_pzsp2_z1/model/search.py
+++ b/python/teams_lib_pzsp2_z1/model/search.py
@@ -59,8 +59,9 @@ class TimeInterval(Enum):
THIS_YEAR = "this year"
LAST_YEAR = "last year"
- @dataclass
- class SearchMessagesOptions:
+
+@dataclass
+class SearchMessagesOptions:
"""
Configuration options for searching messages.
@@ -134,6 +135,7 @@ def __dict__(self):
}
return {k: v for k, v in result.items() if v is not None}
+
@dataclass
class SearchResult:
"""
diff --git a/python/tests/fake_server/data.py b/python/tests/fake_server/data.py
index c667983..7009c4a 100644
--- a/python/tests/fake_server/data.py
+++ b/python/tests/fake_server/data.py
@@ -562,6 +562,19 @@ def get_remove_member_response(self, team_id: str, channel_id: str, member_id: s
# CHATS
# ==========================================
+ def get_chat_responses(self, chat_id: str) -> dict:
+ chat = next((c for c in self.group_chats + self.oneonone_chats if c.id == chat_id), None)
+ if not chat:
+ return {}
+
+ return {
+ ODATA_CONTEXT: "https://graph.microsoft.com/v1.0/$metadata#chats/$entity",
+ "id": chat.id,
+ "chatType": "oneOnOne" if chat.type == ChatType.ONE_ON_ONE else "group",
+ "isHiddenForAllMembers": chat.is_hidden,
+ "topic": chat.topic,
+ }
+
def get_list_chats_response(self, chat_type: ChatType) -> dict:
chats = self.oneonone_chats if chat_type == ChatType.ONE_ON_ONE else self.group_chats
return {
@@ -641,6 +654,7 @@ def get_remove_member_from_group_chat_response(self, chat_id: str, member_id: st
# CHAT MESSAGES
# ==========================================
+
def get_list_messages_in_chat_response(self, chat_id: str) -> dict:
return {
"value": [
diff --git a/python/tests/fake_server/setup.py b/python/tests/fake_server/setup.py
index d4aa92a..ed68d90 100644
--- a/python/tests/fake_server/setup.py
+++ b/python/tests/fake_server/setup.py
@@ -420,6 +420,16 @@ def handle_chats_request(req):
# CHATS
# ==========================================
+ # GET /chats/{chat_id}
+ httpserver.expect_request(
+ re.compile(r"^/v1.0/chats/([^/]+)"),
+ method="GET"
+ ).respond_with_handler(lambda req: make_log_response(
+ req,
+ data.get_chat_responses(re.search(r"/chats/([^/]+)", req.path).group(1)),
+ "Get Chat"
+ ))
+
# DELETE /chats/{chat_id}/pinnedMessages/{message_id}
httpserver.expect_request(
re.compile(r"^/v1.0/chats/([^/]+)/pinnedMessages/([^/]+)"),
diff --git a/python/tests/test_chats.py b/python/tests/test_chats.py
index a4dfe99..d842dad 100644
--- a/python/tests/test_chats.py
+++ b/python/tests/test_chats.py
@@ -100,6 +100,29 @@ def test_create_one_on_one_chat_integration(httpserver):
finally:
client.close()
+def test_get_chat_integration(httpserver):
+
+ data = setup_fake_server(httpserver)
+
+ client = TeamsClient(auto_init=False)
+ try:
+ init_fake_client(client, httpserver.url_for(""))
+
+ chat = client.chats.get_chat(
+ chat_ref=ChatRef(
+ ref=data.group_chats[1].topic,
+ type=ChatType.GROUP,
+ )
+ )
+
+ assert chat.id == data.group_chats[1].id
+ assert chat.type == data.group_chats[1].type
+ assert chat.topic == data.group_chats[1].topic
+ assert chat.is_hidden == data.group_chats[1].is_hidden
+
+ finally:
+ client.close()
+
def test_list_group_chat_members_integration(httpserver):
From 552cbabd931ef55f231acfd12fc51f5dfb324033 Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Tue, 13 Jan 2026 15:51:11 +0100
Subject: [PATCH 91/99] test: finally added test for search in chats
---
go/go.sum | 12 ++
.../decoders/decode-searchOptions.go | 41 ++---
python/teams_lib_pzsp2_z1/model/search.py | 147 +++++++++---------
.../teams_lib_pzsp2_z1/services/channels.py | 2 +-
python/teams_lib_pzsp2_z1/services/chats.py | 2 +-
python/tests/fake_server/data.py | 132 +++++++++++++---
python/tests/fake_server/setup.py | 46 ++++--
python/tests/test_chats.py | 46 +++++-
8 files changed, 297 insertions(+), 131 deletions(-)
diff --git a/go/go.sum b/go/go.sum
index 5a91b58..a8092f1 100644
--- a/go/go.sum
+++ b/go/go.sum
@@ -75,6 +75,18 @@ github.com/pzsp-teams/lib v0.0.0-20260111170324-f66ca4f5d369 h1:TNB4hQ6u8aH0cekG
github.com/pzsp-teams/lib v0.0.0-20260111170324-f66ca4f5d369/go.mod h1:bpgptBb8PaJV03buetvHnq6plkQHpF0WnY1K5iU31U8=
github.com/pzsp-teams/lib v0.0.0-20260112132712-1335fb93c11f h1:MOnTg+X51/lukcWWjHyw9OPx9IzC6Q6TJsZXiaH/KnU=
github.com/pzsp-teams/lib v0.0.0-20260112132712-1335fb93c11f/go.mod h1:bpgptBb8PaJV03buetvHnq6plkQHpF0WnY1K5iU31U8=
+github.com/pzsp-teams/lib v0.0.0-20260113134422-e5326c621551 h1:w4nwFMJLwLYolig9U5r5HpVL0FhrPTP14NggM5/UX9M=
+github.com/pzsp-teams/lib v0.0.0-20260113134422-e5326c621551/go.mod h1:bpgptBb8PaJV03buetvHnq6plkQHpF0WnY1K5iU31U8=
+github.com/pzsp-teams/lib v0.0.0-20260113134659-c00a30df42d1 h1:ykdfDBsQzNu0DCKA+bm20n3o6EtpVPPhXZNRUbhCsuI=
+github.com/pzsp-teams/lib v0.0.0-20260113134659-c00a30df42d1/go.mod h1:bpgptBb8PaJV03buetvHnq6plkQHpF0WnY1K5iU31U8=
+github.com/pzsp-teams/lib v0.0.0-20260113135020-602a62d68535 h1:fWsO7MUGT+wblfPy0Cubpew9vWblm7+V0UdOUeqbEKU=
+github.com/pzsp-teams/lib v0.0.0-20260113135020-602a62d68535/go.mod h1:bpgptBb8PaJV03buetvHnq6plkQHpF0WnY1K5iU31U8=
+github.com/pzsp-teams/lib v0.0.0-20260113135200-1428146acf2e h1:ir/+PHMWaP4x2hZnQB4L6IDz42//Enf/RDA6Ryvzo70=
+github.com/pzsp-teams/lib v0.0.0-20260113135200-1428146acf2e/go.mod h1:bpgptBb8PaJV03buetvHnq6plkQHpF0WnY1K5iU31U8=
+github.com/pzsp-teams/lib v0.0.0-20260113135504-3f606125f3a8 h1:Ck+6z5keqy6aHZEH7Cpe+ozzQm7rv5dg0tRZf2016Zg=
+github.com/pzsp-teams/lib v0.0.0-20260113135504-3f606125f3a8/go.mod h1:bpgptBb8PaJV03buetvHnq6plkQHpF0WnY1K5iU31U8=
+github.com/pzsp-teams/lib v0.0.0-20260113135852-d15a162b495f h1:iuSQEjA6JrHLPoNJHPhloQlo8lAvqQXFqcxR3CJTn5s=
+github.com/pzsp-teams/lib v0.0.0-20260113135852-d15a162b495f/go.mod h1:bpgptBb8PaJV03buetvHnq6plkQHpF0WnY1K5iU31U8=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/std-uritemplate/std-uritemplate/go/v2 v2.0.8 h1:gMBdYMTHt2mmTdXW8YfvRjRUZ0GhyGV+IqSH9H15bGw=
diff --git a/go/internal/json-client/decoders/decode-searchOptions.go b/go/internal/json-client/decoders/decode-searchOptions.go
index 45ef127..5958b81 100644
--- a/go/internal/json-client/decoders/decode-searchOptions.go
+++ b/go/internal/json-client/decoders/decode-searchOptions.go
@@ -20,17 +20,17 @@ type SearchMessageOptionsDTO struct {
SearchPage SearchPageDTO `json:"searchPage"`
From []string `json:"from"`
NotFrom []string `json:"notFrom"`
- IsRead bool `json:"isRead"`
- IsMentioned bool `json:"isMentioned"`
+ IsRead *bool `json:"isRead"`
+ IsMentioned *bool `json:"isMentioned"`
To []string `json:"to"`
NotTo []string `json:"notTo"`
StartTime time.Time `json:"startTime"`
EndTime time.Time `json:"endTime"`
Interval string `json:"interval"`
- NotFromMe bool `json:"notFromMe"`
- NotToMe bool `json:"notToMe"`
- FromMe bool `json:"fromMe"`
- ToMe bool `json:"toMe"`
+ NotFromMe *bool `json:"notFromMe"`
+ NotToMe *bool `json:"notToMe"`
+ FromMe *bool `json:"fromMe"`
+ ToMe *bool `json:"toMe"`
}
func DecodeSearchMessageOptions(dto *SearchMessageOptionsDTO) *search.SearchMessagesOptions {
@@ -51,8 +51,8 @@ func DecodeSearchMessageOptions(dto *SearchMessageOptionsDTO) *search.SearchMess
if len(dto.NotFrom) > 0 {
options.NotFrom = dto.NotFrom
}
- options.IsRead = &dto.IsRead
- options.IsMentioned = &dto.IsMentioned
+ options.IsRead = dto.IsRead
+ options.IsMentioned = dto.IsMentioned
if len(dto.To) > 0 {
options.To = dto.To
}
@@ -68,18 +68,19 @@ func DecodeSearchMessageOptions(dto *SearchMessageOptionsDTO) *search.SearchMess
if dto.Interval != "" {
options.Interval = &interval
}
- if dto.NotFromMe {
- options.NotFromMe = dto.NotFromMe
- }
- if dto.NotToMe {
- options.NotToMe = dto.NotToMe
- }
- if dto.FromMe {
- options.FromMe = dto.FromMe
- }
- if dto.ToMe {
- options.ToMe = dto.ToMe
- }
+ if dto.NotFromMe != nil {
+ options.NotFromMe = *dto.NotFromMe
+ }
+ if dto.NotToMe != nil {
+ options.NotToMe = *dto.NotToMe
+ }
+ if dto.FromMe != nil {
+ options.FromMe = *dto.FromMe
+ }
+ if dto.ToMe != nil {
+ options.ToMe = *dto.ToMe
+ }
+
return options
}
diff --git a/python/teams_lib_pzsp2_z1/model/search.py b/python/teams_lib_pzsp2_z1/model/search.py
index 29fb0f5..94ee72f 100644
--- a/python/teams_lib_pzsp2_z1/model/search.py
+++ b/python/teams_lib_pzsp2_z1/model/search.py
@@ -62,78 +62,78 @@ class TimeInterval(Enum):
@dataclass
class SearchMessagesOptions:
- """
- Configuration options for searching messages.
-
- Attributes:
- query (str | None): The search query string.
- search_page (SearchPage | None): Pagination options.
- from_ (list[str]): List of sender email addresses to include.
- not_from (list[str]): List of sender email addresses to exclude.
- is_read (bool | None): Filter by read status (True for read, False for unread).
- is_mentioned (bool | None): Filter by mention status (True for mentioned, False for not mentioned).
- to (list[str]): List of recipient email addresses to include.
- not_to (list[str]): List of recipient email addresses to exclude.
- start_time (datetime | None): Start time for the sent time range filter.
- end_time (datetime | None): End time for the sent time range filter.
- interval (TimeInterval | None): Predefined time interval for the sent time filter.
- not_from_me (bool): Exclude messages sent by the current user.
- not_to_me (bool): Exclude messages sent to the current user.
- from_me (bool): Include only messages sent by the current user.
- to_me (bool): Include only messages sent to the current user.
-
- Note: If interval is set, it takes precedence over start_time and end_time.
- Note: Using `to` clauses works only in chats, not in team channels.
- Note: Currently, queries for is_read may not function as expected due to API limitations.
- """
-
- query: str | None = None
- search_page: SearchPage | None = None
- from_: list[str] = None
- not_from: list[str] = None
- is_read: bool | None = None
- is_mentioned: bool | None = None
- to: list[str] = None
- not_to: list[str] = None
- start_time: object = None
- end_time: object = None
- interval: TimeInterval | None = None
- not_from_me: bool = False
- not_to_me: bool = False
- from_me: bool = False
- to_me: bool = False
-
- def __post_init__(self):
- if self.from_ is None:
- self.from_ = []
- if self.not_from is None:
- self.not_from = []
- if self.to is None:
- self.to = []
- if self.not_to is None:
- self.not_to = []
-
- def __dict__(self):
- """Serializes the object keys to camelCase for the Go backend."""
-
- result = {
- "query": self.query,
- "searchPage": self.search_page,
- "from": self.from_,
- "notFrom": self.not_from,
- "isRead": self.is_read,
- "isMentioned": self.is_mentioned,
- "to": self.to,
- "notTo": self.not_to,
- "startTime": self.start_time,
- "endTime": self.end_time,
- "interval": self.interval.value if self.interval else None,
- "notFromMe": self.not_from_me,
- "notToMe": self.not_to_me,
- "fromMe": self.from_me,
- "toMe": self.to_me,
- }
- return {k: v for k, v in result.items() if v is not None}
+ """
+ Configuration options for searching messages.
+
+ Attributes:
+ query (str | None): The search query string.
+ search_page (SearchPage | None): Pagination options.
+ from_ (list[str]): List of sender email addresses to include.
+ not_from (list[str]): List of sender email addresses to exclude.
+ is_read (bool | None): Filter by read status (True for read, False for unread).
+ is_mentioned (bool | None): Filter by mention status (True for mentioned, False for not mentioned).
+ to (list[str]): List of recipient email addresses to include.
+ not_to (list[str]): List of recipient email addresses to exclude.
+ start_time (datetime | None): Start time for the sent time range filter.
+ end_time (datetime | None): End time for the sent time range filter.
+ interval (TimeInterval | None): Predefined time interval for the sent time filter.
+ not_from_me (bool): Exclude messages sent by the current user.
+ not_to_me (bool): Exclude messages sent to the current user.
+ from_me (bool): Include only messages sent by the current user.
+ to_me (bool): Include only messages sent to the current user.
+
+ Note: If interval is set, it takes precedence over start_time and end_time.
+ Note: Using `to` clauses works only in chats, not in team channels.
+ Note: Currently, queries for is_read may not function as expected due to API limitations.
+ """
+
+ query: str | None = None
+ search_page: SearchPage | None = None
+ from_: list[str] = None
+ not_from: list[str] = None
+ is_read: bool | None = None
+ is_mentioned: bool | None = None
+ to: list[str] = None
+ not_to: list[str] = None
+ start_time: object = None
+ end_time: object = None
+ interval: TimeInterval | None = None
+ not_from_me: bool = False
+ not_to_me: bool = False
+ from_me: bool = False
+ to_me: bool = False
+
+ def __post_init__(self):
+ if self.from_ is None:
+ self.from_ = []
+ if self.not_from is None:
+ self.not_from = []
+ if self.to is None:
+ self.to = []
+ if self.not_to is None:
+ self.not_to = []
+
+ def __dict__(self):
+ """Serializes the object keys to camelCase for the Go backend."""
+
+ result = {
+ "query": self.query,
+ "searchPage": self.search_page,
+ "from": self.from_,
+ "notFrom": self.not_from,
+ "isRead": self.is_read,
+ "isMentioned": self.is_mentioned,
+ "to": self.to,
+ "notTo": self.not_to,
+ "startTime": self.start_time,
+ "endTime": self.end_time,
+ "interval": self.interval.value if self.interval else None,
+ "notFromMe": self.not_from_me,
+ "notToMe": self.not_to_me,
+ "fromMe": self.from_me,
+ "toMe": self.to_me,
+ }
+ return {k: v for k, v in result.items() if v is not None and v != []}
@dataclass
@@ -147,11 +147,13 @@ class SearchResult:
team_id (str | None): The ID of the team where the message was found (if applicable).
chat_id (str | None): The ID of the chat where the message was found (if applicable).
"""
+
message: Message
channel_id: str | None = None
team_id: str | None = None
chat_id: str | None = None
+
@dataclass
class SearchResults:
"""
@@ -162,5 +164,6 @@ class SearchResults:
next_from (int | None): The index to use for the next page of results,
or None if there are no more results.
"""
+
messages: list[SearchResult]
next_from: int | None = None
diff --git a/python/teams_lib_pzsp2_z1/services/channels.py b/python/teams_lib_pzsp2_z1/services/channels.py
index 17cd91e..f19fbd1 100644
--- a/python/teams_lib_pzsp2_z1/services/channels.py
+++ b/python/teams_lib_pzsp2_z1/services/channels.py
@@ -578,6 +578,6 @@ def search_messages(self, team_ref: str, channel_ref: str, options: SearchMessag
chat_id=msg.get("ChatID"),
)
for msg in response["Messages"]
- ],
+ ] if response.get("Messages") else [],
next_from=response.get("NextFrom"),
)
diff --git a/python/teams_lib_pzsp2_z1/services/chats.py b/python/teams_lib_pzsp2_z1/services/chats.py
index 48bcbf6..2524569 100644
--- a/python/teams_lib_pzsp2_z1/services/chats.py
+++ b/python/teams_lib_pzsp2_z1/services/chats.py
@@ -608,6 +608,6 @@ def search_messages(self, chat_ref: ChatRef, options: SearchMessagesOptions, con
chat_id=msg.get("ChatID"),
)
for msg in response["Messages"]
- ],
+ ] if response.get("Messages") else [],
next_from=response.get("NextFrom"),
)
diff --git a/python/tests/fake_server/data.py b/python/tests/fake_server/data.py
index 7009c4a..130f8e7 100644
--- a/python/tests/fake_server/data.py
+++ b/python/tests/fake_server/data.py
@@ -1,4 +1,6 @@
from dataclasses import dataclass, field
+import re
+import traceback
from typing import Optional, List, Dict, Any
from unittest import result
from urllib import request
@@ -28,6 +30,7 @@ class FakeServerData:
newGroupMailNickname: str
newTeamVisibility: str
potentialTeams: List[Team] = field(default_factory=list)
+ search_results: List[Message] = field(default_factory=list)
# Initial data containers
messages: Dict[str, List[Message]] = field(default_factory=dict)
@@ -128,8 +131,8 @@ def __init__(self) -> None:
self.chat_messages = {
"chat-123-abc": [
- Message(id="msg-001", content="Hello, team!", content_type="text", sender=MessageFrom(user_id="user-123-abc", display_name="Alice"), created_date_time="2024-01-01T10:00:00Z", reply_count=0),
- Message(id="msg-002", content="Don't forget the meeting at 3 PM.", content_type="text", sender=MessageFrom(user_id="user-456-def", display_name="Bob"), created_date_time="2024-01-01T11:00:00Z", reply_count=2),
+ Message(id="AAMkAGIwMDA5MmY0LWY5ZTgtNGY5YS04NzczLWNhNjc0ZGIyZDBjYgBGAAAAAADm35sgHbzESapJ8_BjBlhEBwDAYtphe7dsRbDrOT-HAHoKAACmqNsoAADAYtphe7dsRbDrOT-HAHoKAAFsBhyEAAA=", content="Hello, team!", content_type="text", sender=MessageFrom(user_id="user-123-abc", display_name="Alice"), created_date_time="2024-01-01T10:00:00Z", reply_count=0),
+ Message(id="BAMkAGIwMDA5MmY0LWY5ZTgtNGY5YS04NzczLWNhNjc0ZGIyZDBjYgBGAAAAAADm35sgHbzESapJ8_BjBlhEBwDAYtphe7dsRbDrOT-HAHoKAACmqNsoAADAYtphe7dsRbDrOT-HAHoKAAFsBhyEAAA=", content="Don't forget the meeting at 3 PM.", content_type="text", sender=MessageFrom(user_id="user-456-def", display_name="Bob"), created_date_time="2024-01-01T11:00:00Z", reply_count=2),
],
}
@@ -749,22 +752,22 @@ def get_list_pinned_messages_in_chat_response(self, chat_id: str) -> dict:
{
"id": message.id,
"message": {
- "id": message.id,
- "etag": message.id,
- "messageType": "message",
- "chatId": chat_id,
- "body": {
- "content": message.content,
- "contentType": message.content_type,
- },
- "from": {
- "user": {
- "id": message.sender.user_id,
- "displayName": message.sender.display_name,
- }
- },
- "createdDateTime": message.created_date_time,
- "lastModifiedDateTime": message.created_date_time,
+ "id": message.id,
+ "etag": message.id,
+ "messageType": "message",
+ "chatId": chat_id,
+ "body": {
+ "content": message.content,
+ "contentType": message.content_type,
+ },
+ "from": {
+ "user": {
+ "id": message.sender.user_id,
+ "displayName": message.sender.display_name,
+ }
+ },
+ "createdDateTime": message.created_date_time,
+ "lastModifiedDateTime": message.created_date_time,
}
}
for message in self.chat_messages.get(chat_id, [])
@@ -780,4 +783,95 @@ def get_me_response(self) -> dict:
"id": self.me.id,
"displayName": self.me.display_name,
"email": self.me.email,
- }
\ No newline at end of file
+ }
+
+ # ==========================================
+ # SEARCH
+ # ==========================================
+
+ def get_search_messages_response(self, request_body) -> dict:
+ hits = []
+ rank = 1
+ messages = []
+
+ # 1. Wyciągamy surowy KQL (to już wiesz że działa)
+ raw_query = ""
+ try:
+ if isinstance(request_body, dict):
+ req_list = request_body.get('requests', [])
+ if req_list:
+ raw_query = req_list[0].get('query', {}).get('queryString', "")
+ except Exception:
+ raw_query = ""
+
+ # 2. CZYSZCZENIE (To jest to, czego Ci brakuje)
+ # Wywalamy wszystko co wygląda jak Klucz:"Wartość"
+ # Zamieniamy 'Hello IsRead:"false"...' -> 'Hello ...'
+ search_term = re.sub(r'\w+:"[^"]+"', '', raw_query)
+
+ # Usuwamy spacje z brzegów -> zostaje samo "Hello"
+ search_term = search_term.strip()
+
+
+ # 3. Pętla szukająca
+ try:
+ for chat_id, messages in self.chat_messages.items():
+ for message in messages:
+ if not message.content:
+ continue
+
+ # Teraz szukamy samego "Hello" w treści wiadomości
+ if search_term and search_term.lower() in message.content.lower():
+
+ # (Bezpieczne pobieranie sendera)
+ sender_id = getattr(message.sender, 'user_id', 'unknown') if message.sender else 'unknown'
+ sender_name = getattr(message.sender, 'display_name', 'Unknown') if message.sender else 'Unknown'
+ created_dt = getattr(message, 'created_date_time', "2024-01-01T12:00:00Z")
+
+ hits.append({
+ "hitId": message.id,
+ "rank": rank,
+ "summary": message.content[:200],
+ "resource": {
+ "id": message.id,
+ "chatId": chat_id,
+ "body": {
+ "content": message.content,
+ "contentType": message.content_type,
+ },
+ "from": {
+ "user": {
+ "id": message.sender.user_id,
+ "displayName": message.sender.display_name,
+ }
+ },
+ "createdDateTime": message.created_date_time,
+ "lastModifiedDateTime": message.created_date_time,
+ }
+ },
+ )
+ rank += 1
+
+ except Exception:
+ traceback.print_exc()
+
+ response = {
+ "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#Collection(microsoft.graph.searchResponse)",
+ "value": [
+ {
+ "searchTerms": [search_term],
+ "hitsContainers": [
+ {
+ "hits": hits,
+ "total": len(hits),
+ "moreResultsAvailable": False,
+ }
+ ],
+ }
+ ]
+ }
+
+ # self.search_results = messages
+
+
+ return response
\ No newline at end of file
diff --git a/python/tests/fake_server/setup.py b/python/tests/fake_server/setup.py
index ed68d90..34fe21c 100644
--- a/python/tests/fake_server/setup.py
+++ b/python/tests/fake_server/setup.py
@@ -14,11 +14,13 @@ def setup_fake_server(httpserver) -> FakeServerData:
data = FakeServerData()
- def make_log_response(request, data_dict: dict, context_msg: str = "") -> Response:
+ def make_log_response(request, data_dict: dict, context_msg: str = "", status_code = None) -> Response:
print(f"\n[MOCK SERVER] 🟢 REQUEST: {request.method} {request.path}", file=sys.stdout)
if context_msg:
print(f"[MOCK SERVER] Context: {context_msg}", file=sys.stdout)
+ print(f"[MOCK SERVER] Returned body: {data_dict}", file=sys.stdout)
+
if request.content_type == "application/json":
try:
req_data = request.json
@@ -27,9 +29,10 @@ def make_log_response(request, data_dict: dict, context_msg: str = "") -> Respon
except Exception as e:
print(f"[MOCK SERVER] ⚠️ Body Error: {e}", file=sys.stdout)
- status_code = 200 if data_dict else 404
- if request.method in ["POST", "PUT"] and data_dict:
- status_code = 201
+ if not status_code:
+ status_code = 200 if data_dict else 404
+ if request.method in ["POST", "PUT"] and data_dict:
+ status_code = 201
print(f"[MOCK SERVER] 👉 Response: {status_code}", file=sys.stdout)
@@ -39,6 +42,21 @@ def make_log_response(request, data_dict: dict, context_msg: str = "") -> Respon
status=status_code
)
+ # ==========================================
+ # SEARCH
+ # ==========================================
+
+ # POST /search/query
+ httpserver.expect_request(
+ "/v1.0/search/query",
+ method="POST"
+ ).respond_with_handler(lambda req: make_log_response(
+ req,
+ data.get_search_messages_response(req.json),
+ "Search Messages",
+ status_code=200
+ ))
+
# ==========================================
# USERS
# ==========================================
@@ -420,15 +438,6 @@ def handle_chats_request(req):
# CHATS
# ==========================================
- # GET /chats/{chat_id}
- httpserver.expect_request(
- re.compile(r"^/v1.0/chats/([^/]+)"),
- method="GET"
- ).respond_with_handler(lambda req: make_log_response(
- req,
- data.get_chat_responses(re.search(r"/chats/([^/]+)", req.path).group(1)),
- "Get Chat"
- ))
# DELETE /chats/{chat_id}/pinnedMessages/{message_id}
httpserver.expect_request(
@@ -557,6 +566,17 @@ def handle_chats_request(req):
"Create Chat"
))
+ # GET /chats/{chat_id}
+ httpserver.expect_request(
+ re.compile(r"^/v1.0/chats/([^/]+)"),
+ method="GET"
+ ).respond_with_handler(lambda req: make_log_response(
+ req,
+ data.get_chat_responses(re.search(r"/chats/([^/]+)", req.path).group(1)),
+ "Get Chat"
+ ))
+
+
# Fallback for unmatched routes
httpserver.expect_request(re.compile(".*")).respond_with_handler(
lambda req: (
diff --git a/python/tests/test_chats.py b/python/tests/test_chats.py
index d842dad..d42dab2 100644
--- a/python/tests/test_chats.py
+++ b/python/tests/test_chats.py
@@ -4,6 +4,7 @@
from teams_lib_pzsp2_z1.model.chat import ChatType, ChatRef
from teams_lib_pzsp2_z1.model.message import MessageContentType, MessageBody
from teams_lib_pzsp2_z1.model.mention import MentionKind
+from teams_lib_pzsp2_z1.model.search import SearchConfig, SearchMessagesOptions, TimeInterval
from datetime import datetime
@@ -110,15 +111,15 @@ def test_get_chat_integration(httpserver):
chat = client.chats.get_chat(
chat_ref=ChatRef(
- ref=data.group_chats[1].topic,
+ ref=data.group_chats[0].topic,
type=ChatType.GROUP,
)
)
- assert chat.id == data.group_chats[1].id
- assert chat.type == data.group_chats[1].type
- assert chat.topic == data.group_chats[1].topic
- assert chat.is_hidden == data.group_chats[1].is_hidden
+ assert chat.id == data.group_chats[0].id
+ assert chat.type == data.group_chats[0].type
+ assert chat.topic == data.group_chats[0].topic
+ assert chat.is_hidden == data.group_chats[0].is_hidden
finally:
client.close()
@@ -473,3 +474,38 @@ def test_get_mention_integration(httpserver):
finally:
client.close()
+def test_search_messages_integration(httpserver):
+
+ data = setup_fake_server(httpserver)
+
+ client = TeamsClient(auto_init=False)
+ try:
+ init_fake_client(client, httpserver.url_for(""))
+
+ results = client.chats.search_messages(
+ chat_ref=ChatRef(
+ ref=data.group_chats[0].topic,
+ type=ChatType.GROUP,
+ ),
+ options=SearchMessagesOptions(
+ query="Hello"
+ ),
+ config=SearchConfig(
+ max_workers=4,
+ )
+ )
+
+ assert len(results.messages) == 1
+
+ assert results.messages[0].chat_id == data.group_chats[0].id
+
+ assert results.messages[0].message.id == data.chat_messages[data.group_chats[0].id][0].id
+ assert results.messages[0].message.content == data.chat_messages[data.group_chats[0].id][0].content
+ assert results.messages[0].message.content_type == MessageContentType(data.chat_messages[data.group_chats[0].id][0].content_type)
+ assert results.messages[0].message.sender.user_id == data.chat_messages[data.group_chats[0].id][0].sender.user_id
+ assert results.messages[0].message.sender.display_name == data.chat_messages[data.group_chats[0].id][0].sender.display_name
+ assert results.messages[0].message.reply_count == data.chat_messages[data.group_chats[0].id][0].reply_count
+ assert results.messages[0].message.created_date_time == data.chat_messages[data.group_chats[0].id][0].created_date_time
+
+ finally:
+ client.close()
From 9e9573076a54f0f2e2ba6d7af62b531b7b557ac1 Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Tue, 13 Jan 2026 16:31:53 +0100
Subject: [PATCH 92/99] test: added test for search in channels
---
go/go.sum | 2 +
.../teams_lib_pzsp2_z1/services/channels.py | 2 +-
python/tests/fake_server/data.py | 72 +++++++++++++------
python/tests/test_channels.py | 39 ++++++++++
python/tests/test_chats.py | 2 +-
5 files changed, 94 insertions(+), 23 deletions(-)
diff --git a/go/go.sum b/go/go.sum
index a8092f1..6790c62 100644
--- a/go/go.sum
+++ b/go/go.sum
@@ -87,6 +87,8 @@ github.com/pzsp-teams/lib v0.0.0-20260113135504-3f606125f3a8 h1:Ck+6z5keqy6aHZEH
github.com/pzsp-teams/lib v0.0.0-20260113135504-3f606125f3a8/go.mod h1:bpgptBb8PaJV03buetvHnq6plkQHpF0WnY1K5iU31U8=
github.com/pzsp-teams/lib v0.0.0-20260113135852-d15a162b495f h1:iuSQEjA6JrHLPoNJHPhloQlo8lAvqQXFqcxR3CJTn5s=
github.com/pzsp-teams/lib v0.0.0-20260113135852-d15a162b495f/go.mod h1:bpgptBb8PaJV03buetvHnq6plkQHpF0WnY1K5iU31U8=
+github.com/pzsp-teams/lib v0.0.0-20260113152600-e9075fa2951a h1:Y/I0MRE60rwsutOcj1FlnyrsrrL6jdJIcxCnL2R80V4=
+github.com/pzsp-teams/lib v0.0.0-20260113152600-e9075fa2951a/go.mod h1:bpgptBb8PaJV03buetvHnq6plkQHpF0WnY1K5iU31U8=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/std-uritemplate/std-uritemplate/go/v2 v2.0.8 h1:gMBdYMTHt2mmTdXW8YfvRjRUZ0GhyGV+IqSH9H15bGw=
diff --git a/python/teams_lib_pzsp2_z1/services/channels.py b/python/teams_lib_pzsp2_z1/services/channels.py
index f19fbd1..2c80e1d 100644
--- a/python/teams_lib_pzsp2_z1/services/channels.py
+++ b/python/teams_lib_pzsp2_z1/services/channels.py
@@ -550,7 +550,7 @@ def search_messages(self, team_ref: str, channel_ref: str, options: SearchMessag
"""
response = self.client.execute(
cmd_type="request",
- method="searchMessagesInChat",
+ method="searchMessagesInChannel",
params={
"teamRef": team_ref,
"channelRef": channel_ref,
diff --git a/python/tests/fake_server/data.py b/python/tests/fake_server/data.py
index 130f8e7..ae7d85b 100644
--- a/python/tests/fake_server/data.py
+++ b/python/tests/fake_server/data.py
@@ -30,7 +30,7 @@ class FakeServerData:
newGroupMailNickname: str
newTeamVisibility: str
potentialTeams: List[Team] = field(default_factory=list)
- search_results: List[Message] = field(default_factory=list)
+ searching: str = "chats"
# Initial data containers
messages: Dict[str, List[Message]] = field(default_factory=dict)
@@ -85,6 +85,7 @@ def __init__(self) -> None:
"19:123123@thread.tacv2": [
Message(id="msg-001", content="Hello, team!", content_type="text", sender=MessageFrom(user_id="user-123-abc", display_name="Alice"), created_date_time="2024-01-01T10:00:00Z", reply_count=0),
Message(id="msg-002", content="Don't forget the meeting at 3 PM.", content_type="text", sender=MessageFrom(user_id="user-456-def", display_name="Bob"), created_date_time="2024-01-01T11:00:00Z", reply_count=2),
+ Message(id="AAMkAGIwMDA5MmY0LWY5ZTgtNGY5YS04NzczLWNhNjc0ZGIyZDBjYgBGAAAAAADm35sgHbzESapJ8_BjBlhEBwDAYtphe7dsRbDrOT-HAHoKAACmqNsoAADAYtphe7dsRbDrOT-HAHoKAAFsBhyEAAA=", content="Hello, team!", content_type="text", sender=MessageFrom(user_id="user-123-abc", display_name="Alice"), created_date_time="2024-01-01T10:00:00Z", reply_count=0),
],
}
@@ -828,28 +829,57 @@ def get_search_messages_response(self, request_body) -> dict:
sender_name = getattr(message.sender, 'display_name', 'Unknown') if message.sender else 'Unknown'
created_dt = getattr(message, 'created_date_time', "2024-01-01T12:00:00Z")
- hits.append({
- "hitId": message.id,
- "rank": rank,
- "summary": message.content[:200],
- "resource": {
- "id": message.id,
- "chatId": chat_id,
- "body": {
- "content": message.content,
- "contentType": message.content_type,
- },
- "from": {
- "user": {
- "id": message.sender.user_id,
- "displayName": message.sender.display_name,
- }
- },
- "createdDateTime": message.created_date_time,
- "lastModifiedDateTime": message.created_date_time,
+ if self.searching == "chats":
+ hits.append({
+ "hitId": message.id,
+ "rank": rank,
+ "summary": message.content[:200],
+ "resource": {
+ "id": message.id,
+ "chatId": chat_id,
+ "body": {
+ "content": message.content,
+ "contentType": message.content_type,
+ },
+ "from": {
+ "user": {
+ "id": sender_id,
+ "displayName": sender_name,
+ }
+ },
+ "createdDateTime": created_dt,
+ "lastModifiedDateTime": created_dt,
+ }
+ },
+ )
+ elif self.searching == "channels":
+ hits.append({
+ "hitId": message.id,
+ "rank": rank,
+ "summary": message.content[:200],
+ "resource": {
+ "id": message.id,
+ "channelIdentity": {
+ "teamId": self.teams[0].id,
+ "channelId": self.channels[self.teams[0].id][0].id,
+ },
+ "teamId": self.teams[0].id,
+ "channelId": self.channels[self.teams[0].id][0].id,
+ "body": {
+ "content": message.content,
+ "contentType": message.content_type,
+ },
+ "from": {
+ "user": {
+ "id": sender_id,
+ "displayName": sender_name,
+ }
+ },
+ "createdDateTime": created_dt,
+ "lastModifiedDateTime": created_dt,
}
},
- )
+ )
rank += 1
except Exception:
diff --git a/python/tests/test_channels.py b/python/tests/test_channels.py
index c8989f8..98323eb 100644
--- a/python/tests/test_channels.py
+++ b/python/tests/test_channels.py
@@ -3,6 +3,7 @@
from tests.fake_server.setup import setup_fake_server
from teams_lib_pzsp2_z1.model.message import MessageContentType, MessageBody
from teams_lib_pzsp2_z1.model.mention import MentionKind
+from teams_lib_pzsp2_z1.model.search import SearchConfig, SearchMessagesOptions
def test_list_channels_integration(httpserver):
@@ -394,6 +395,44 @@ def test_get_mention_integration(httpserver):
client.close()
+def test_search_messages_integration(httpserver):
+
+ data = setup_fake_server(httpserver)
+ data.searching = "channels"
+
+ client = TeamsClient(auto_init=False)
+ try:
+ init_fake_client(client, httpserver.url_for(""))
+
+ results = client.channels.search_messages(
+ team_ref=data.teams[0].display_name,
+ channel_ref=data.channels[data.teams[0].id][0].name,
+ options=SearchMessagesOptions(
+ query="Hello",
+ ),
+ config=SearchConfig(
+ max_workers=4,
+ )
+ )
+
+ assert len(results.messages) == 1
+
+ assert results.messages[0].team_id == data.teams[0].id
+ assert results.messages[0].channel_id == data.channels[data.teams[0].id][0].id
+ assert results.messages[0].chat_id is None
+
+ assert results.messages[0].message.id == data.messages[results.messages[0].channel_id][2].id
+ assert results.messages[0].message.content == data.messages[results.messages[0].channel_id][2].content
+ assert results.messages[0].message.content_type == MessageContentType(data.messages[results.messages[0].channel_id][2].content_type)
+ assert results.messages[0].message.sender.user_id == data.messages[results.messages[0].channel_id][2].sender.user_id
+ assert results.messages[0].message.sender.display_name == data.messages[results.messages[0].channel_id][2].sender.display_name
+ assert results.messages[0].message.reply_count == data.messages[results.messages[0].channel_id][2].reply_count
+ assert results.messages[0].message.created_date_time == data.messages[results.messages[0].channel_id][0].created_date_time
+
+ finally:
+ client.close()
+
+
diff --git a/python/tests/test_chats.py b/python/tests/test_chats.py
index d42dab2..d71275b 100644
--- a/python/tests/test_chats.py
+++ b/python/tests/test_chats.py
@@ -4,7 +4,7 @@
from teams_lib_pzsp2_z1.model.chat import ChatType, ChatRef
from teams_lib_pzsp2_z1.model.message import MessageContentType, MessageBody
from teams_lib_pzsp2_z1.model.mention import MentionKind
-from teams_lib_pzsp2_z1.model.search import SearchConfig, SearchMessagesOptions, TimeInterval
+from teams_lib_pzsp2_z1.model.search import SearchConfig, SearchMessagesOptions
from datetime import datetime
From dec2fae723992c9e132421d6c04f0d222ae011b7 Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Tue, 13 Jan 2026 16:33:50 +0100
Subject: [PATCH 93/99] chore: cleaned tests
---
python/tests/test_teams.py | 20 --------------------
1 file changed, 20 deletions(-)
diff --git a/python/tests/test_teams.py b/python/tests/test_teams.py
index 3c2d7fb..be19e85 100644
--- a/python/tests/test_teams.py
+++ b/python/tests/test_teams.py
@@ -105,26 +105,6 @@ def test_create_team_via_group_integration(httpserver):
client.close()
-def test_create_team_from_template_integration(httpserver):
-
- data = setup_fake_server(httpserver)
-
- client = TeamsClient(auto_init=False)
- try:
- init_fake_client(client, httpserver.url_for(""))
-
- id = client.teams.create_from_template(
- display_name=data.newTeamName,
- description="A team created from a template",
- owners=["user-123-abc"],
- )
-
- assert id == data.newTeamID
-
- finally:
- client.close()
-
-
def test_archive_team_integration(httpserver):
data = setup_fake_server(httpserver)
From 3c16502d4be4adb1442dcd3fcabe117e67a27155 Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Tue, 13 Jan 2026 17:03:34 +0100
Subject: [PATCH 94/99] chore: removed unnecessary comments
---
python/tests/fake_server/data.py | 28 ----------------------------
1 file changed, 28 deletions(-)
diff --git a/python/tests/fake_server/data.py b/python/tests/fake_server/data.py
index ae7d85b..f3cb45d 100644
--- a/python/tests/fake_server/data.py
+++ b/python/tests/fake_server/data.py
@@ -287,24 +287,6 @@ def get_createTeamFromTemplate_response(self, request: request.Request) -> Respo
response= ""
)
- # def create_team_handler(self, req):
- # result = self.get_createTeamFromTemplate_response(req.json)
-
- # base_url = "https://graph.microsoft.com/v1.0"
- # loc_header = f"/teams/{TEAM_ID}/operations/{OP_ID}"
- # content_loc_header = f"/teams/{TEAM_ID}"
-
- # return Response(
- # response=result["body"],
- # status=result["status"],
- # headers={
- # "Location": full_location,
- # "Content-Location": full_content_loc,
- # "Content-Type": result["headers"]["Content-Type"],
- # "Content-Length": result["headers"]["Content-Length"]
- # }
- # )
-
def get_archiveTeam_response(self, team_id: str) -> dict:
team = self._find_team(team_id)
if not team:
@@ -795,7 +777,6 @@ def get_search_messages_response(self, request_body) -> dict:
rank = 1
messages = []
- # 1. Wyciągamy surowy KQL (to już wiesz że działa)
raw_query = ""
try:
if isinstance(request_body, dict):
@@ -805,26 +786,19 @@ def get_search_messages_response(self, request_body) -> dict:
except Exception:
raw_query = ""
- # 2. CZYSZCZENIE (To jest to, czego Ci brakuje)
- # Wywalamy wszystko co wygląda jak Klucz:"Wartość"
- # Zamieniamy 'Hello IsRead:"false"...' -> 'Hello ...'
search_term = re.sub(r'\w+:"[^"]+"', '', raw_query)
- # Usuwamy spacje z brzegów -> zostaje samo "Hello"
search_term = search_term.strip()
- # 3. Pętla szukająca
try:
for chat_id, messages in self.chat_messages.items():
for message in messages:
if not message.content:
continue
- # Teraz szukamy samego "Hello" w treści wiadomości
if search_term and search_term.lower() in message.content.lower():
- # (Bezpieczne pobieranie sendera)
sender_id = getattr(message.sender, 'user_id', 'unknown') if message.sender else 'unknown'
sender_name = getattr(message.sender, 'display_name', 'Unknown') if message.sender else 'Unknown'
created_dt = getattr(message, 'created_date_time', "2024-01-01T12:00:00Z")
@@ -901,7 +875,5 @@ def get_search_messages_response(self, request_body) -> dict:
]
}
- # self.search_results = messages
-
return response
\ No newline at end of file
From 50e9956d30e42a8ff0a12c6c956456e531522d8c Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Tue, 13 Jan 2026 18:24:17 +0100
Subject: [PATCH 95/99] docs: README fixes
---
README.md | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/README.md b/README.md
index cbc32a9..a9913ec 100644
--- a/README.md
+++ b/README.md
@@ -47,8 +47,7 @@ SCOPES=User.Read,Team.ReadBasic.All,Channel.ReadBasic.All
AUTH_METHOD=DEVICE_CODE [or INTERACTIVE]
```
-Scopes needed by all functions could be found in .env.template.
-**//TODO add url to template**
+Scopes needed by all functions could be found in [.env.template](https://github.com/pzsp-teams/lib-python/tree/main/.env.template).
### 2. Basic usage
@@ -120,5 +119,5 @@ client.close()
## 📄 License
This project is licensed under the MIT License - see the [LICENSE](https://github.com/pzsp-teams/lib-python/blob/main/LICENSE) file for details.
-## Porst
+## Port
This lib is also available in [GO](https://github.com/pzsp-teams/lib)
From ed2d7cb1e92a6d750c4fa4ecab013f72e362a916 Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Tue, 13 Jan 2026 18:31:57 +0100
Subject: [PATCH 96/99] chore: replaced interface{} with any
---
go/bridge/responses.go | 4 +-
.../json-client/decoders/decode-params.go | 2 +-
go/internal/json-client/fake-client.go | 2 +-
go/internal/json-client/request-map.go | 90 +++++++++----------
go/internal/json-client/requests-channels.go | 68 +++++++-------
go/internal/json-client/requests-chats.go | 72 +++++++--------
go/internal/json-client/requests-teams.go | 34 +++----
go/internal/json-client/teams-json-client.go | 2 +-
go/internal/json-model/json-model.go | 6 +-
9 files changed, 140 insertions(+), 140 deletions(-)
diff --git a/go/bridge/responses.go b/go/bridge/responses.go
index ff2c4f2..72dcedc 100644
--- a/go/bridge/responses.go
+++ b/go/bridge/responses.go
@@ -6,7 +6,7 @@ import (
)
type response struct {
- Result interface{} `json:"result,omitempty"`
+ Result any `json:"result,omitempty"`
Error string `json:"error,omitempty"`
}
@@ -29,7 +29,7 @@ func respondError(writer *bufio.Writer, err error) {
writer.Flush()
}
-func respondResult(writer *bufio.Writer, result interface{}) {
+func respondResult(writer *bufio.Writer, result any) {
response := response{
Result: result,
}
diff --git a/go/internal/json-client/decoders/decode-params.go b/go/internal/json-client/decoders/decode-params.go
index 6651123..c6a18ef 100644
--- a/go/internal/json-client/decoders/decode-params.go
+++ b/go/internal/json-client/decoders/decode-params.go
@@ -4,7 +4,7 @@ import (
"github.com/mitchellh/mapstructure"
)
-func DecodeParams[T any](p interface{}) (*T, error) {
+func DecodeParams[T any](p any) (*T, error) {
var result T
config := &mapstructure.DecoderConfig{
diff --git a/go/internal/json-client/fake-client.go b/go/internal/json-client/fake-client.go
index 2f131b6..2882bdc 100644
--- a/go/internal/json-client/fake-client.go
+++ b/go/internal/json-client/fake-client.go
@@ -20,7 +20,7 @@ import (
// --- 1. Fake Auth Provider ---
type FakeAuthProvider struct{}
-func (f *FakeAuthProvider) AuthenticateRequest(ctx context.Context, request *azcore.RequestInformation, additionalAuthenticationContext map[string]interface{}) error {
+func (f *FakeAuthProvider) AuthenticateRequest(ctx context.Context, request *azcore.RequestInformation, additionalAuthenticationContext map[string]any) error {
if request.Headers == nil {
request.Headers = azcore.NewRequestHeaders()
}
diff --git a/go/internal/json-client/request-map.go b/go/internal/json-client/request-map.go
index 9f31f27..229b095 100644
--- a/go/internal/json-client/request-map.go
+++ b/go/internal/json-client/request-map.go
@@ -1,185 +1,185 @@
package jsonclient
-type HandlerFunc func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error)
+type HandlerFunc func(c *TeamsJSONClient, p map[string]any) (any, error)
var Handlers = map[string]HandlerFunc{
// TEAMS
- "getTeam": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ "getTeam": func(c *TeamsJSONClient, p map[string]any) (any, error) {
return c.GetTeam(p)
},
- "updateTeam": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ "updateTeam": func(c *TeamsJSONClient, p map[string]any) (any, error) {
return c.UpdateTeam(p)
},
- "listMyJoined": func(c *TeamsJSONClient, _ map[string]interface{}) (interface{}, error) {
+ "listMyJoined": func(c *TeamsJSONClient, _ map[string]any) (any, error) {
return c.ListMyJoined()
},
- "createTeamViaGroup": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ "createTeamViaGroup": func(c *TeamsJSONClient, p map[string]any) (any, error) {
return c.CreateTeamViaGroup(p)
},
- "createTeamFromTemplate": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ "createTeamFromTemplate": func(c *TeamsJSONClient, p map[string]any) (any, error) {
return c.CreateTeamFromTemplate(p)
},
- "archiveTeam": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ "archiveTeam": func(c *TeamsJSONClient, p map[string]any) (any, error) {
return c.ArchiveTeam(p)
},
- "unarchiveTeam": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ "unarchiveTeam": func(c *TeamsJSONClient, p map[string]any) (any, error) {
return c.UnarchiveTeam(p)
},
- "deleteTeam": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ "deleteTeam": func(c *TeamsJSONClient, p map[string]any) (any, error) {
return c.DeleteTeam(p)
},
- "restoreDeletedTeam": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ "restoreDeletedTeam": func(c *TeamsJSONClient, p map[string]any) (any, error) {
return c.RestoreDeletedTeam(p)
},
// CHANNELS
- "listChannels": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ "listChannels": func(c *TeamsJSONClient, p map[string]any) (any, error) {
return c.ListChannels(p)
},
- "getChannel": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ "getChannel": func(c *TeamsJSONClient, p map[string]any) (any, error) {
return c.GetChannel(p)
},
- "createStandardChannel": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ "createStandardChannel": func(c *TeamsJSONClient, p map[string]any) (any, error) {
return c.CreateStandardChannel(p)
},
- "createPrivateChannel": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ "createPrivateChannel": func(c *TeamsJSONClient, p map[string]any) (any, error) {
return c.CreatePrivateChannel(p)
},
- "deleteChannel": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ "deleteChannel": func(c *TeamsJSONClient, p map[string]any) (any, error) {
return c.DeleteChannel(p)
},
- "sendMessageToChannel": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ "sendMessageToChannel": func(c *TeamsJSONClient, p map[string]any) (any, error) {
return c.SendMessageToChannel(p)
},
- "sendReplyToChannel": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ "sendReplyToChannel": func(c *TeamsJSONClient, p map[string]any) (any, error) {
return c.SendReplyToChannel(p)
},
- "listMessagesInChannel": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ "listMessagesInChannel": func(c *TeamsJSONClient, p map[string]any) (any, error) {
return c.ListMessagesInChannel(p)
},
- "getMessageInChannel": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ "getMessageInChannel": func(c *TeamsJSONClient, p map[string]any) (any, error) {
return c.GetMessageInChannel(p)
},
- "listMessageRepliesInChannel": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ "listMessageRepliesInChannel": func(c *TeamsJSONClient, p map[string]any) (any, error) {
return c.ListMessageRepliesInChannel(p)
},
- "getMessageReplyInChannel": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ "getMessageReplyInChannel": func(c *TeamsJSONClient, p map[string]any) (any, error) {
return c.GetMessageReplyInChannel(p)
},
- "listChannelMembers": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ "listChannelMembers": func(c *TeamsJSONClient, p map[string]any) (any, error) {
return c.ListChannelMembers(p)
},
- "addMemberToChannel": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ "addMemberToChannel": func(c *TeamsJSONClient, p map[string]any) (any, error) {
return c.AddMemberToChannel(p)
},
- "removeMemberFromChannel": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ "removeMemberFromChannel": func(c *TeamsJSONClient, p map[string]any) (any, error) {
return c.RemoveMemberFromChannel(p)
},
- "updateMemberRoleInChannel": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ "updateMemberRoleInChannel": func(c *TeamsJSONClient, p map[string]any) (any, error) {
return c.UpdateMemberInChannel(p)
},
- "getMentionsInChannel": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ "getMentionsInChannel": func(c *TeamsJSONClient, p map[string]any) (any, error) {
return c.GetMentionsInChannel(p)
},
- "searchMessagesInChannel": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ "searchMessagesInChannel": func(c *TeamsJSONClient, p map[string]any) (any, error) {
return c.SearchMessagesInChannel(p)
},
// CHATS
- "createOneOnOneChat": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ "createOneOnOneChat": func(c *TeamsJSONClient, p map[string]any) (any, error) {
return c.CreateOneToOneChat(p)
},
- "createGroupChat": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ "createGroupChat": func(c *TeamsJSONClient, p map[string]any) (any, error) {
return c.CreateGroupChat(p)
},
- "getChat": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ "getChat": func(c *TeamsJSONClient, p map[string]any) (any, error) {
return c.GetChat(p)
},
- "addMemberToGroupChat": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ "addMemberToGroupChat": func(c *TeamsJSONClient, p map[string]any) (any, error) {
return c.AddMemberToGroupChat(p)
},
- "removeMemberFromGroupChat": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ "removeMemberFromGroupChat": func(c *TeamsJSONClient, p map[string]any) (any, error) {
return c.RemoveMemberFromGroupChat(p)
},
- "listMembersInGroupChat": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ "listMembersInGroupChat": func(c *TeamsJSONClient, p map[string]any) (any, error) {
return c.ListGroupChatMembers(p)
},
- "updateGroupChatTopic": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ "updateGroupChatTopic": func(c *TeamsJSONClient, p map[string]any) (any, error) {
return c.UpdateGroupChatTopic(p)
},
- "listMessagesInChat": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ "listMessagesInChat": func(c *TeamsJSONClient, p map[string]any) (any, error) {
return c.ListMessagesInChat(p)
},
- "sendMessageInChat": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ "sendMessageInChat": func(c *TeamsJSONClient, p map[string]any) (any, error) {
return c.SendMessageInChat(p)
},
- "deleteMessageInChat": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ "deleteMessageInChat": func(c *TeamsJSONClient, p map[string]any) (any, error) {
return c.DeleteMessageInChat(p)
},
- "getMessageInChat": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ "getMessageInChat": func(c *TeamsJSONClient, p map[string]any) (any, error) {
return c.GetMessageInChat(p)
},
- "listMyChats": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ "listMyChats": func(c *TeamsJSONClient, p map[string]any) (any, error) {
return c.ListMyChats(p)
},
- "listMyChatMessages": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ "listMyChatMessages": func(c *TeamsJSONClient, p map[string]any) (any, error) {
return c.ListMyChatMessages(p)
},
- "listPinnedMessagesInChat": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ "listPinnedMessagesInChat": func(c *TeamsJSONClient, p map[string]any) (any, error) {
return c.ListPinnedMessagesInChat(p)
},
- "pinMessageInChat": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ "pinMessageInChat": func(c *TeamsJSONClient, p map[string]any) (any, error) {
return c.PinMessageInChat(p)
},
- "unpinMessageInChat": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ "unpinMessageInChat": func(c *TeamsJSONClient, p map[string]any) (any, error) {
return c.UnpinMessageInChat(p)
},
- "getMentionsInChat": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ "getMentionsInChat": func(c *TeamsJSONClient, p map[string]any) (any, error) {
return c.GetMentionsInChat(p)
},
- "searchMessagesInChat": func(c *TeamsJSONClient, p map[string]interface{}) (interface{}, error) {
+ "searchMessagesInChat": func(c *TeamsJSONClient, p map[string]any) (any, error) {
return c.SearchMessagesInChat(p)
},
}
diff --git a/go/internal/json-client/requests-channels.go b/go/internal/json-client/requests-channels.go
index 0f0803a..867c323 100644
--- a/go/internal/json-client/requests-channels.go
+++ b/go/internal/json-client/requests-channels.go
@@ -12,8 +12,8 @@ type listChannelsParams struct {
TeamRef string `json:"teamRef"`
}
-func (c *TeamsJSONClient) ListChannels(p map[string]interface{}) (interface{}, error) {
- return execute(p, func(params listChannelsParams) (interface{}, error) {
+func (c *TeamsJSONClient) ListChannels(p map[string]any) (any, error) {
+ return execute(p, func(params listChannelsParams) (any, error) {
return c.client.Channels.ListChannels(context.TODO(), params.TeamRef)
})
}
@@ -23,8 +23,8 @@ type baseChannelParams struct {
ChannelRef string `json:"channelRef"`
}
-func (c *TeamsJSONClient) GetChannel(p map[string]interface{}) (interface{}, error) {
- return execute(p, func(params baseChannelParams) (interface{}, error) {
+func (c *TeamsJSONClient) GetChannel(p map[string]any) (any, error) {
+ return execute(p, func(params baseChannelParams) (any, error) {
return c.client.Channels.Get(context.TODO(), params.TeamRef, params.ChannelRef)
})
}
@@ -34,8 +34,8 @@ type createChannelParams struct {
Name string `json:"name"`
}
-func (c *TeamsJSONClient) CreateStandardChannel(p map[string]interface{}) (interface{}, error) {
- return execute(p, func(params createChannelParams) (interface{}, error) {
+func (c *TeamsJSONClient) CreateStandardChannel(p map[string]any) (any, error) {
+ return execute(p, func(params createChannelParams) (any, error) {
return c.client.Channels.CreateStandardChannel(context.TODO(), params.TeamRef, params.Name)
})
}
@@ -47,14 +47,14 @@ type createPrivateChannelParams struct {
OwnerRefs []string `json:"ownerRefs"`
}
-func (c *TeamsJSONClient) CreatePrivateChannel(p map[string]interface{}) (interface{}, error) {
- return execute(p, func(params createPrivateChannelParams) (interface{}, error) {
+func (c *TeamsJSONClient) CreatePrivateChannel(p map[string]any) (any, error) {
+ return execute(p, func(params createPrivateChannelParams) (any, error) {
return c.client.Channels.CreatePrivateChannel(context.TODO(), params.TeamRef, params.Name, params.MemberRefs, params.OwnerRefs)
})
}
-func (c *TeamsJSONClient) DeleteChannel(p map[string]interface{}) (interface{}, error) {
- return execute(p, func(params baseChannelParams) (interface{}, error) {
+func (c *TeamsJSONClient) DeleteChannel(p map[string]any) (any, error) {
+ return execute(p, func(params baseChannelParams) (any, error) {
err := c.client.Channels.Delete(context.TODO(), params.TeamRef, params.ChannelRef)
return "deleted", err
})
@@ -66,8 +66,8 @@ type sendMessageToChannelParams struct {
Body decoders.MessageBodyDTO `json:"body"`
}
-func (c *TeamsJSONClient) SendMessageToChannel(p map[string]interface{}) (interface{}, error) {
- return execute(p, func(params sendMessageToChannelParams) (interface{}, error) {
+func (c *TeamsJSONClient) SendMessageToChannel(p map[string]any) (any, error) {
+ return execute(p, func(params sendMessageToChannelParams) (any, error) {
body, err := decoders.DecodeParams[models.MessageBody](¶ms.Body)
if err != nil {
return nil, err
@@ -83,8 +83,8 @@ type sendReplyToChannelParams struct {
Body decoders.MessageBodyDTO `json:"body"`
}
-func (c *TeamsJSONClient) SendReplyToChannel(p map[string]interface{}) (interface{}, error) {
- return execute(p, func(params sendReplyToChannelParams) (interface{}, error) {
+func (c *TeamsJSONClient) SendReplyToChannel(p map[string]any) (any, error) {
+ return execute(p, func(params sendReplyToChannelParams) (any, error) {
body, err := decoders.DecodeParams[models.MessageBody](¶ms.Body)
if err != nil {
return nil, err
@@ -106,8 +106,8 @@ type listMessagesParams struct {
NextLink decoders.NextLinkDTO `json:"nextLink"`
}
-func (c *TeamsJSONClient) ListMessagesInChannel(p map[string]interface{}) (interface{}, error) {
- return execute(p, func(params listMessagesParams) (interface{}, error) {
+func (c *TeamsJSONClient) ListMessagesInChannel(p map[string]any) (any, error) {
+ return execute(p, func(params listMessagesParams) (any, error) {
options, err := decoders.DecodeParams[models.ListMessagesOptions](params.Options)
if err != nil {
return nil, err
@@ -123,8 +123,8 @@ type getMessageParams struct {
MessageID string `json:"messageId"`
}
-func (c *TeamsJSONClient) GetMessageInChannel(p map[string]interface{}) (interface{}, error) {
- return execute(p, func(params getMessageParams) (interface{}, error) {
+func (c *TeamsJSONClient) GetMessageInChannel(p map[string]any) (any, error) {
+ return execute(p, func(params getMessageParams) (any, error) {
return c.client.Channels.GetMessage(context.TODO(), params.TeamRef, params.ChannelRef, params.MessageID)
})
}
@@ -138,8 +138,8 @@ type listRepliesParams struct {
NextLink decoders.NextLinkDTO `json:"nextLink"`
}
-func (c *TeamsJSONClient) ListMessageRepliesInChannel(p map[string]interface{}) (interface{}, error) {
- return execute(p, func(params listRepliesParams) (interface{}, error) {
+func (c *TeamsJSONClient) ListMessageRepliesInChannel(p map[string]any) (any, error) {
+ return execute(p, func(params listRepliesParams) (any, error) {
nextLink := decoders.GetNextLink(¶ms.NextLink)
return c.client.Channels.ListReplies(context.TODO(), params.TeamRef, params.ChannelRef, params.MessageID, params.Top, params.IncludeSystem, nextLink)
})
@@ -152,14 +152,14 @@ type getReplyParams struct {
ReplyID string `json:"replyId"`
}
-func (c *TeamsJSONClient) GetMessageReplyInChannel(p map[string]interface{}) (interface{}, error) {
- return execute(p, func(params getReplyParams) (interface{}, error) {
+func (c *TeamsJSONClient) GetMessageReplyInChannel(p map[string]any) (any, error) {
+ return execute(p, func(params getReplyParams) (any, error) {
return c.client.Channels.GetReply(context.TODO(), params.TeamRef, params.ChannelRef, params.MessageID, params.ReplyID)
})
}
-func (c *TeamsJSONClient) ListChannelMembers(p map[string]interface{}) (interface{}, error) {
- return execute(p, func(params baseChannelParams) (interface{}, error) {
+func (c *TeamsJSONClient) ListChannelMembers(p map[string]any) (any, error) {
+ return execute(p, func(params baseChannelParams) (any, error) {
return c.client.Channels.ListMembers(context.TODO(), params.TeamRef, params.ChannelRef)
})
}
@@ -171,14 +171,14 @@ type addOrUpdateMemberToChannelParams struct {
IsOwner bool `json:"isOwner"`
}
-func (c *TeamsJSONClient) AddMemberToChannel(p map[string]interface{}) (interface{}, error) {
- return execute(p, func(params addOrUpdateMemberToChannelParams) (interface{}, error) {
+func (c *TeamsJSONClient) AddMemberToChannel(p map[string]any) (any, error) {
+ return execute(p, func(params addOrUpdateMemberToChannelParams) (any, error) {
return c.client.Channels.AddMember(context.TODO(), params.TeamRef, params.ChannelRef, params.UserRef, params.IsOwner)
})
}
-func (c *TeamsJSONClient) UpdateMemberInChannel(p map[string]interface{}) (interface{}, error) {
- return execute(p, func(params addOrUpdateMemberToChannelParams) (interface{}, error) {
+func (c *TeamsJSONClient) UpdateMemberInChannel(p map[string]any) (any, error) {
+ return execute(p, func(params addOrUpdateMemberToChannelParams) (any, error) {
return c.client.Channels.UpdateMemberRoles(context.TODO(), params.TeamRef, params.ChannelRef, params.UserRef, params.IsOwner)
})
}
@@ -189,8 +189,8 @@ type removeMemberFromChannelParams struct {
UserRef string `json:"userRef"`
}
-func (c *TeamsJSONClient) RemoveMemberFromChannel(p map[string]interface{}) (interface{}, error) {
- return execute(p, func(params removeMemberFromChannelParams) (interface{}, error) {
+func (c *TeamsJSONClient) RemoveMemberFromChannel(p map[string]any) (any, error) {
+ return execute(p, func(params removeMemberFromChannelParams) (any, error) {
err := c.client.Channels.RemoveMember(context.TODO(), params.TeamRef, params.ChannelRef, params.UserRef)
return "removed", err
})
@@ -202,8 +202,8 @@ type getMentionsInChannelParams struct {
RawMentions []string `json:"rawMentions"`
}
-func (c *TeamsJSONClient) GetMentionsInChannel(p map[string]interface{}) (interface{}, error) {
- return execute(p, func(params getMentionsInChannelParams) (interface{}, error) {
+func (c *TeamsJSONClient) GetMentionsInChannel(p map[string]any) (any, error) {
+ return execute(p, func(params getMentionsInChannelParams) (any, error) {
return c.client.Channels.GetMentions(context.TODO(), params.TeamRef, params.ChannelRef, params.RawMentions)
})
}
@@ -215,8 +215,8 @@ type searchMessagesInChannelParams struct {
SearchConfig decoders.SearchConfigDTO `json:"searchConfig"`
}
-func (c *TeamsJSONClient) SearchMessagesInChannel(p map[string]interface{}) (interface{}, error) {
- return execute(p, func(params searchMessagesInChannelParams) (interface{}, error) {
+func (c *TeamsJSONClient) SearchMessagesInChannel(p map[string]any) (any, error) {
+ return execute(p, func(params searchMessagesInChannelParams) (any, error) {
searchOptions := decoders.DecodeSearchMessageOptions(¶ms.SearchOptions)
searchConfig, err := decoders.DecodeParams[search.SearchConfig](¶ms.SearchConfig)
if err != nil {
diff --git a/go/internal/json-client/requests-chats.go b/go/internal/json-client/requests-chats.go
index eda2bb6..34e9e2b 100644
--- a/go/internal/json-client/requests-chats.go
+++ b/go/internal/json-client/requests-chats.go
@@ -13,8 +13,8 @@ type createOneToOneChatParams struct {
RecipientRef string `json:"recipientRef"`
}
-func (c *TeamsJSONClient) CreateOneToOneChat(p map[string]interface{}) (interface{}, error) {
- return execute(p, func(params createOneToOneChatParams) (interface{}, error) {
+func (c *TeamsJSONClient) CreateOneToOneChat(p map[string]any) (any, error) {
+ return execute(p, func(params createOneToOneChatParams) (any, error) {
return c.client.Chats.CreateOneOnOne(context.TODO(), params.RecipientRef)
})
}
@@ -25,8 +25,8 @@ type createGroupChatParams struct {
IncludeMe bool `json:"includeMe"`
}
-func (c *TeamsJSONClient) CreateGroupChat(p map[string]interface{}) (interface{}, error) {
- return execute(p, func(params createGroupChatParams) (interface{}, error) {
+func (c *TeamsJSONClient) CreateGroupChat(p map[string]any) (any, error) {
+ return execute(p, func(params createGroupChatParams) (any, error) {
return c.client.Chats.CreateGroup(context.TODO(), params.RecipientRefs, params.Topic, params.IncludeMe)
})
}
@@ -35,8 +35,8 @@ type baseChatParams struct {
ChatRef decoders.ChatRefDTO `json:"chatRef"`
}
-func (c *TeamsJSONClient) GetChat(p map[string]interface{}) (interface{}, error) {
- return execute(p, func(params baseChatParams) (interface{}, error) {
+func (c *TeamsJSONClient) GetChat(p map[string]any) (any, error) {
+ return execute(p, func(params baseChatParams) (any, error) {
chatRef := decoders.GetChatRef(params.ChatRef)
return c.client.Chats.GetChat(context.TODO(), chatRef)
})
@@ -47,14 +47,14 @@ type groupChatMemberParams struct {
UserRef string `json:"userRef"`
}
-func (c *TeamsJSONClient) AddMemberToGroupChat(p map[string]interface{}) (interface{}, error) {
- return execute(p, func(params groupChatMemberParams) (interface{}, error) {
+func (c *TeamsJSONClient) AddMemberToGroupChat(p map[string]any) (any, error) {
+ return execute(p, func(params groupChatMemberParams) (any, error) {
return c.client.Chats.AddMemberToGroupChat(context.TODO(), chats.GroupChatRef{Ref: params.GroupChatRef}, params.UserRef)
})
}
-func (c *TeamsJSONClient) RemoveMemberFromGroupChat(p map[string]interface{}) (interface{}, error) {
- return execute(p, func(params groupChatMemberParams) (interface{}, error) {
+func (c *TeamsJSONClient) RemoveMemberFromGroupChat(p map[string]any) (any, error) {
+ return execute(p, func(params groupChatMemberParams) (any, error) {
err := c.client.Chats.RemoveMemberFromGroupChat(context.TODO(), chats.GroupChatRef{Ref: params.GroupChatRef}, params.UserRef)
return "removed", err
})
@@ -64,8 +64,8 @@ type listMembersInChatParams struct {
GroupChatRef string `json:"groupChatRef"`
}
-func (c *TeamsJSONClient) ListGroupChatMembers(p map[string]interface{}) (interface{}, error) {
- return execute(p, func(params listMembersInChatParams) (interface{}, error) {
+func (c *TeamsJSONClient) ListGroupChatMembers(p map[string]any) (any, error) {
+ return execute(p, func(params listMembersInChatParams) (any, error) {
return c.client.Chats.ListGroupChatMembers(context.TODO(), chats.GroupChatRef{Ref: params.GroupChatRef})
})
}
@@ -75,8 +75,8 @@ type updateGroupChatTopicParams struct {
Topic string `json:"topic"`
}
-func (c *TeamsJSONClient) UpdateGroupChatTopic(p map[string]interface{}) (interface{}, error) {
- return execute(p, func(params updateGroupChatTopicParams) (interface{}, error) {
+func (c *TeamsJSONClient) UpdateGroupChatTopic(p map[string]any) (any, error) {
+ return execute(p, func(params updateGroupChatTopicParams) (any, error) {
return c.client.Chats.UpdateGroupChatTopic(context.TODO(), chats.GroupChatRef{Ref: params.GroupChatRef}, params.Topic)
})
}
@@ -87,8 +87,8 @@ type listMessagesInChatParams struct {
NextLink decoders.NextLinkDTO `json:"nextLink"`
}
-func (c *TeamsJSONClient) ListMessagesInChat(p map[string]interface{}) (interface{}, error) {
- return execute(p, func(params listMessagesInChatParams) (interface{}, error) {
+func (c *TeamsJSONClient) ListMessagesInChat(p map[string]any) (any, error) {
+ return execute(p, func(params listMessagesInChatParams) (any, error) {
chatRef := decoders.GetChatRef(params.ChatRef)
nextLink := decoders.GetNextLink(¶ms.NextLink)
return c.client.Chats.ListMessages(context.TODO(), chatRef, params.IncludeSystem, nextLink)
@@ -100,8 +100,8 @@ type sendMessageInChatParams struct {
Body decoders.MessageBodyDTO `json:"body"`
}
-func (c *TeamsJSONClient) SendMessageInChat(p map[string]interface{}) (interface{}, error) {
- return execute(p, func(params sendMessageInChatParams) (interface{}, error) {
+func (c *TeamsJSONClient) SendMessageInChat(p map[string]any) (any, error) {
+ return execute(p, func(params sendMessageInChatParams) (any, error) {
chatRef := decoders.GetChatRef(params.ChatRef)
body, err := decoders.DecodeParams[models.MessageBody](params.Body)
if err != nil {
@@ -116,16 +116,16 @@ type messageInChatParams struct {
MessageID string `json:"messageId"`
}
-func (c *TeamsJSONClient) DeleteMessageInChat(p map[string]interface{}) (interface{}, error) {
- return execute(p, func(params messageInChatParams) (interface{}, error) {
+func (c *TeamsJSONClient) DeleteMessageInChat(p map[string]any) (any, error) {
+ return execute(p, func(params messageInChatParams) (any, error) {
chatRef := decoders.GetChatRef(params.ChatRef)
err := c.client.Chats.DeleteMessage(context.TODO(), chatRef, params.MessageID)
return "deleted", err
})
}
-func (c *TeamsJSONClient) GetMessageInChat(p map[string]interface{}) (interface{}, error) {
- return execute(p, func(params messageInChatParams) (interface{}, error) {
+func (c *TeamsJSONClient) GetMessageInChat(p map[string]any) (any, error) {
+ return execute(p, func(params messageInChatParams) (any, error) {
chatRef := decoders.GetChatRef(params.ChatRef)
return c.client.Chats.GetMessage(context.TODO(), chatRef, params.MessageID)
})
@@ -135,8 +135,8 @@ type ListMyChatsParams struct {
ChatType string `json:"chatType"`
}
-func (c *TeamsJSONClient) ListMyChats(p map[string]interface{}) (interface{}, error) {
- return execute(p, func(params ListMyChatsParams) (interface{}, error) {
+func (c *TeamsJSONClient) ListMyChats(p map[string]any) (any, error) {
+ return execute(p, func(params ListMyChatsParams) (any, error) {
var chatType models.ChatType
if params.ChatType != "" {
chatType = models.ChatType(params.ChatType)
@@ -151,8 +151,8 @@ type listChatMessagesParams struct {
Top *int32 `json:"top"`
}
-func (c *TeamsJSONClient) ListMyChatMessages(p map[string]interface{}) (interface{}, error) {
- return execute(p, func(params listChatMessagesParams) (interface{}, error) {
+func (c *TeamsJSONClient) ListMyChatMessages(p map[string]any) (any, error) {
+ return execute(p, func(params listChatMessagesParams) (any, error) {
parsedStartTime, parsedEndTime, err := decoders.DecodeTimeRange(params.StartTime, params.EndTime)
if err != nil {
return nil, err
@@ -161,23 +161,23 @@ func (c *TeamsJSONClient) ListMyChatMessages(p map[string]interface{}) (interfac
})
}
-func (c *TeamsJSONClient) ListPinnedMessagesInChat(p map[string]interface{}) (interface{}, error) {
- return execute(p, func(params baseChatParams) (interface{}, error) {
+func (c *TeamsJSONClient) ListPinnedMessagesInChat(p map[string]any) (any, error) {
+ return execute(p, func(params baseChatParams) (any, error) {
chatRef := decoders.GetChatRef(params.ChatRef)
return c.client.Chats.ListPinnedMessages(context.TODO(), chatRef)
})
}
-func (c *TeamsJSONClient) PinMessageInChat(p map[string]interface{}) (interface{}, error) {
- return execute(p, func(params messageInChatParams) (interface{}, error) {
+func (c *TeamsJSONClient) PinMessageInChat(p map[string]any) (any, error) {
+ return execute(p, func(params messageInChatParams) (any, error) {
chatRef := decoders.GetChatRef(params.ChatRef)
err := c.client.Chats.PinMessage(context.TODO(), chatRef, params.MessageID)
return "pinned", err
})
}
-func (c *TeamsJSONClient) UnpinMessageInChat(p map[string]interface{}) (interface{}, error) {
- return execute(p, func(params messageInChatParams) (interface{}, error) {
+func (c *TeamsJSONClient) UnpinMessageInChat(p map[string]any) (any, error) {
+ return execute(p, func(params messageInChatParams) (any, error) {
chatRef := decoders.GetChatRef(params.ChatRef)
err := c.client.Chats.UnpinMessage(context.TODO(), chatRef, params.MessageID)
return "unpinned", err
@@ -189,8 +189,8 @@ type mentionInChatParams struct {
RawMentions []string `json:"rawMentions"`
}
-func (c *TeamsJSONClient) GetMentionsInChat(p map[string]interface{}) (interface{}, error) {
- return execute(p, func(params mentionInChatParams) (interface{}, error) {
+func (c *TeamsJSONClient) GetMentionsInChat(p map[string]any) (any, error) {
+ return execute(p, func(params mentionInChatParams) (any, error) {
chatRef := decoders.GetChatRef(params.ChatRef)
return c.client.Chats.GetMentions(context.TODO(), chatRef, params.RawMentions)
})
@@ -202,8 +202,8 @@ type searchMessagesInChatParams struct {
SearchConfig decoders.SearchConfigDTO `json:"searchConfig"`
}
-func (c *TeamsJSONClient) SearchMessagesInChat(p map[string]interface{}) (interface{}, error) {
- return execute(p, func(params searchMessagesInChatParams) (interface{}, error) {
+func (c *TeamsJSONClient) SearchMessagesInChat(p map[string]any) (any, error) {
+ return execute(p, func(params searchMessagesInChatParams) (any, error) {
chatRef := decoders.GetChatRef(params.ChatRef)
searchOptions := decoders.DecodeSearchMessageOptions(¶ms.SearchOptions)
searchConfig, err := decoders.DecodeParams[search.SearchConfig](¶ms.SearchConfig)
diff --git a/go/internal/json-client/requests-teams.go b/go/internal/json-client/requests-teams.go
index 49d267c..22022ac 100644
--- a/go/internal/json-client/requests-teams.go
+++ b/go/internal/json-client/requests-teams.go
@@ -10,13 +10,13 @@ type getTeamParams struct {
TeamRef string `json:"teamRef"`
}
-func (c *TeamsJSONClient) GetTeam(p map[string]interface{}) (interface{}, error) {
- return execute(p, func(params getTeamParams) (interface{}, error) {
+func (c *TeamsJSONClient) GetTeam(p map[string]any) (any, error) {
+ return execute(p, func(params getTeamParams) (any, error) {
return c.client.Teams.Get(context.TODO(), params.TeamRef)
})
}
-func (c *TeamsJSONClient) ListMyJoined() (interface{}, error) {
+func (c *TeamsJSONClient) ListMyJoined() (any, error) {
return c.client.Teams.ListMyJoined(context.TODO())
}
@@ -25,8 +25,8 @@ type updateTeamParams struct {
TeamUpdate decoders.UpdateTeamDTO `json:"team"`
}
-func (c *TeamsJSONClient) UpdateTeam(p map[string]interface{}) (interface{}, error) {
- return execute(p, func(params updateTeamParams) (interface{}, error) {
+func (c *TeamsJSONClient) UpdateTeam(p map[string]any) (any, error) {
+ return execute(p, func(params updateTeamParams) (any, error) {
updateTeam := decoders.GetUpdateTeam(¶ms.TeamUpdate)
return c.client.Teams.UpdateTeam(context.TODO(), params.TeamRef, &updateTeam)
})
@@ -38,8 +38,8 @@ type createViaGroupParams struct {
Visibility string `json:"visibility"`
}
-func (c *TeamsJSONClient) CreateTeamViaGroup(p map[string]interface{}) (interface{}, error) {
- return execute(p, func(params createViaGroupParams) (interface{}, error) {
+func (c *TeamsJSONClient) CreateTeamViaGroup(p map[string]any) (any, error) {
+ return execute(p, func(params createViaGroupParams) (any, error) {
return c.client.Teams.CreateViaGroup(context.TODO(), params.DisplayName, params.MailNickname, params.Visibility)
})
}
@@ -53,8 +53,8 @@ type createFromTemplateParams struct {
IncludeMe bool `json:"includeMe"`
}
-func (c *TeamsJSONClient) CreateTeamFromTemplate(p map[string]interface{}) (interface{}, error) {
- return execute(p, func(params createFromTemplateParams) (interface{}, error) {
+func (c *TeamsJSONClient) CreateTeamFromTemplate(p map[string]any) (any, error) {
+ return execute(p, func(params createFromTemplateParams) (any, error) {
return c.client.Teams.CreateFromTemplate(context.TODO(), params.DisplayName, params.Description, params.Owners, params.Members, params.Visibility, params.IncludeMe)
})
}
@@ -64,8 +64,8 @@ type archiveTeamParams struct {
SpoReadOnlyFromMembers *bool `json:"spoReadOnlyFromMembers,omitempty"`
}
-func (c *TeamsJSONClient) ArchiveTeam(p map[string]interface{}) (interface{}, error) {
- return execute(p, func(params archiveTeamParams) (interface{}, error) {
+func (c *TeamsJSONClient) ArchiveTeam(p map[string]any) (any, error) {
+ return execute(p, func(params archiveTeamParams) (any, error) {
err := c.client.Teams.Archive(context.TODO(), params.TeamRef, params.SpoReadOnlyFromMembers)
return "archived", err
})
@@ -75,8 +75,8 @@ type unarchiveTeamParams struct {
TeamRef string `json:"teamRef"`
}
-func (c *TeamsJSONClient) UnarchiveTeam(p map[string]interface{}) (interface{}, error) {
- return execute(p, func(params unarchiveTeamParams) (interface{}, error) {
+func (c *TeamsJSONClient) UnarchiveTeam(p map[string]any) (any, error) {
+ return execute(p, func(params unarchiveTeamParams) (any, error) {
err := c.client.Teams.Unarchive(context.TODO(), params.TeamRef)
return "unarchived", err
})
@@ -86,8 +86,8 @@ type deleteTeamParams struct {
TeamRef string `json:"teamRef"`
}
-func (c *TeamsJSONClient) DeleteTeam(p map[string]interface{}) (interface{}, error) {
- return execute(p, func(params deleteTeamParams) (interface{}, error) {
+func (c *TeamsJSONClient) DeleteTeam(p map[string]any) (any, error) {
+ return execute(p, func(params deleteTeamParams) (any, error) {
err := c.client.Teams.Delete(context.TODO(), params.TeamRef)
return "deleted", err
})
@@ -97,8 +97,8 @@ type restoreTeamParams struct {
DeletedGroupID string `json:"deletedGroupId"`
}
-func (c *TeamsJSONClient) RestoreDeletedTeam(p map[string]interface{}) (interface{}, error) {
- return execute(p, func(params restoreTeamParams) (interface{}, error) {
+func (c *TeamsJSONClient) RestoreDeletedTeam(p map[string]any) (any, error) {
+ return execute(p, func(params restoreTeamParams) (any, error) {
return c.client.Teams.RestoreDeleted(context.TODO(), params.DeletedGroupID)
})
}
diff --git a/go/internal/json-client/teams-json-client.go b/go/internal/json-client/teams-json-client.go
index 20b87a8..7ef7360 100644
--- a/go/internal/json-client/teams-json-client.go
+++ b/go/internal/json-client/teams-json-client.go
@@ -10,7 +10,7 @@ type TeamsJSONClient struct {
client *lib.Client
}
-func execute[T any](p map[string]interface{}, fn func(params T) (interface{}, error)) (interface{}, error) {
+func execute[T any](p map[string]any, fn func(params T) (any, error)) (any, error) {
params, err := decoders.DecodeParams[T](p)
if err != nil {
return nil, err
diff --git a/go/internal/json-model/json-model.go b/go/internal/json-model/json-model.go
index 750604a..05f24bc 100644
--- a/go/internal/json-model/json-model.go
+++ b/go/internal/json-model/json-model.go
@@ -10,7 +10,7 @@ type Request struct {
Type string `json:"type"`
Method string `json:"method,omitempty"`
Config Config `json:"config,omitempty"`
- Params map[string]interface{} `json:"params,omitempty"`
+ Params map[string]any `json:"params,omitempty"`
}
type Config struct {
@@ -83,7 +83,7 @@ func (acm AuthConfigMap) ToAuthConfig() (*config.AuthConfig, error) {
}, nil
}
-func ParseCachePath(value interface{}) (*string, error) {
+func ParseCachePath(value any) (*string, error) {
path, ok := value.(string)
if !ok || path == "" {
return nil, fmt.Errorf("cachePath must be a non-empty string")
@@ -91,7 +91,7 @@ func ParseCachePath(value interface{}) (*string, error) {
return &path, nil
}
-func ParseCacheMode(value interface{}) (config.CacheMode, error) {
+func ParseCacheMode(value any) (config.CacheMode, error) {
switch value {
case "DISABLED":
return config.CacheDisabled, nil
From 4444fb53aa2495556286630354280fec3ec3dbc2 Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Tue, 13 Jan 2026 18:47:11 +0100
Subject: [PATCH 97/99] refactor: passed context.background from main
---
go/bridge/main.go | 3 +-
go/internal/json-client/request-map.go | 182 ++++++++++---------
go/internal/json-client/requests-channels.go | 68 +++----
go/internal/json-client/requests-chats.go | 72 ++++----
go/internal/json-client/requests-teams.go | 36 ++--
5 files changed, 183 insertions(+), 178 deletions(-)
diff --git a/go/bridge/main.go b/go/bridge/main.go
index d598b9e..128be65 100644
--- a/go/bridge/main.go
+++ b/go/bridge/main.go
@@ -5,6 +5,7 @@ import (
"encoding/json"
"fmt"
"os"
+ "context"
jsonClientLib "github.com/pzsp-teams/lib-python/internal/json-client"
jsonModel "github.com/pzsp-teams/lib-python/internal/json-model"
@@ -58,7 +59,7 @@ func main() {
continue
}
- result, err := handler(client, req.Params)
+ result, err := handler(client, context.Background(), req.Params)
if err != nil {
respondError(writer, err)
}
diff --git a/go/internal/json-client/request-map.go b/go/internal/json-client/request-map.go
index 229b095..ffb4a1e 100644
--- a/go/internal/json-client/request-map.go
+++ b/go/internal/json-client/request-map.go
@@ -1,185 +1,189 @@
package jsonclient
-type HandlerFunc func(c *TeamsJSONClient, p map[string]any) (any, error)
+import (
+ "context"
+)
+
+type HandlerFunc func(c *TeamsJSONClient, ctx context.Context, p map[string]any) (any, error)
var Handlers = map[string]HandlerFunc{
// TEAMS
- "getTeam": func(c *TeamsJSONClient, p map[string]any) (any, error) {
- return c.GetTeam(p)
+ "getTeam": func(c *TeamsJSONClient, ctx context.Context, p map[string]any) (any, error) {
+ return c.GetTeam(ctx, p)
},
- "updateTeam": func(c *TeamsJSONClient, p map[string]any) (any, error) {
- return c.UpdateTeam(p)
+ "updateTeam": func(c *TeamsJSONClient, ctx context.Context, p map[string]any) (any, error) {
+ return c.UpdateTeam(ctx, p)
},
- "listMyJoined": func(c *TeamsJSONClient, _ map[string]any) (any, error) {
- return c.ListMyJoined()
+ "listMyJoined": func(c *TeamsJSONClient, ctx context.Context, _ map[string]any) (any, error) {
+ return c.ListMyJoined(ctx)
},
- "createTeamViaGroup": func(c *TeamsJSONClient, p map[string]any) (any, error) {
- return c.CreateTeamViaGroup(p)
+ "createTeamViaGroup": func(c *TeamsJSONClient, ctx context.Context, p map[string]any) (any, error) {
+ return c.CreateTeamViaGroup(ctx, p)
},
- "createTeamFromTemplate": func(c *TeamsJSONClient, p map[string]any) (any, error) {
- return c.CreateTeamFromTemplate(p)
+ "createTeamFromTemplate": func(c *TeamsJSONClient, ctx context.Context, p map[string]any) (any, error) {
+ return c.CreateTeamFromTemplate(ctx, p)
},
- "archiveTeam": func(c *TeamsJSONClient, p map[string]any) (any, error) {
- return c.ArchiveTeam(p)
+ "archiveTeam": func(c *TeamsJSONClient, ctx context.Context, p map[string]any) (any, error) {
+ return c.ArchiveTeam(ctx, p)
},
- "unarchiveTeam": func(c *TeamsJSONClient, p map[string]any) (any, error) {
- return c.UnarchiveTeam(p)
+ "unarchiveTeam": func(c *TeamsJSONClient, ctx context.Context, p map[string]any) (any, error) {
+ return c.UnarchiveTeam(ctx, p)
},
- "deleteTeam": func(c *TeamsJSONClient, p map[string]any) (any, error) {
- return c.DeleteTeam(p)
+ "deleteTeam": func(c *TeamsJSONClient, ctx context.Context, p map[string]any) (any, error) {
+ return c.DeleteTeam(ctx, p)
},
- "restoreDeletedTeam": func(c *TeamsJSONClient, p map[string]any) (any, error) {
- return c.RestoreDeletedTeam(p)
+ "restoreDeletedTeam": func(c *TeamsJSONClient, ctx context.Context, p map[string]any) (any, error) {
+ return c.RestoreDeletedTeam(ctx, p)
},
// CHANNELS
- "listChannels": func(c *TeamsJSONClient, p map[string]any) (any, error) {
- return c.ListChannels(p)
+ "listChannels": func(c *TeamsJSONClient, ctx context.Context, p map[string]any) (any, error) {
+ return c.ListChannels(ctx, p)
},
- "getChannel": func(c *TeamsJSONClient, p map[string]any) (any, error) {
- return c.GetChannel(p)
+ "getChannel": func(c *TeamsJSONClient, ctx context.Context, p map[string]any) (any, error) {
+ return c.GetChannel(ctx, p)
},
- "createStandardChannel": func(c *TeamsJSONClient, p map[string]any) (any, error) {
- return c.CreateStandardChannel(p)
+ "createStandardChannel": func(c *TeamsJSONClient, ctx context.Context, p map[string]any) (any, error) {
+ return c.CreateStandardChannel(ctx, p)
},
- "createPrivateChannel": func(c *TeamsJSONClient, p map[string]any) (any, error) {
- return c.CreatePrivateChannel(p)
+ "createPrivateChannel": func(c *TeamsJSONClient, ctx context.Context, p map[string]any) (any, error) {
+ return c.CreatePrivateChannel(ctx, p)
},
- "deleteChannel": func(c *TeamsJSONClient, p map[string]any) (any, error) {
- return c.DeleteChannel(p)
+ "deleteChannel": func(c *TeamsJSONClient, ctx context.Context, p map[string]any) (any, error) {
+ return c.DeleteChannel(ctx, p)
},
- "sendMessageToChannel": func(c *TeamsJSONClient, p map[string]any) (any, error) {
- return c.SendMessageToChannel(p)
+ "sendMessageToChannel": func(c *TeamsJSONClient, ctx context.Context, p map[string]any) (any, error) {
+ return c.SendMessageToChannel(ctx, p)
},
- "sendReplyToChannel": func(c *TeamsJSONClient, p map[string]any) (any, error) {
- return c.SendReplyToChannel(p)
+ "sendReplyToChannel": func(c *TeamsJSONClient, ctx context.Context, p map[string]any) (any, error) {
+ return c.SendReplyToChannel(ctx, p)
},
- "listMessagesInChannel": func(c *TeamsJSONClient, p map[string]any) (any, error) {
- return c.ListMessagesInChannel(p)
+ "listMessagesInChannel": func(c *TeamsJSONClient, ctx context.Context, p map[string]any) (any, error) {
+ return c.ListMessagesInChannel(ctx, p)
},
- "getMessageInChannel": func(c *TeamsJSONClient, p map[string]any) (any, error) {
- return c.GetMessageInChannel(p)
+ "getMessageInChannel": func(c *TeamsJSONClient, ctx context.Context, p map[string]any) (any, error) {
+ return c.GetMessageInChannel(ctx, p)
},
- "listMessageRepliesInChannel": func(c *TeamsJSONClient, p map[string]any) (any, error) {
- return c.ListMessageRepliesInChannel(p)
+ "listMessageRepliesInChannel": func(c *TeamsJSONClient, ctx context.Context, p map[string]any) (any, error) {
+ return c.ListMessageRepliesInChannel(ctx, p)
},
- "getMessageReplyInChannel": func(c *TeamsJSONClient, p map[string]any) (any, error) {
- return c.GetMessageReplyInChannel(p)
+ "getMessageReplyInChannel": func(c *TeamsJSONClient, ctx context.Context, p map[string]any) (any, error) {
+ return c.GetMessageReplyInChannel(ctx, p)
},
- "listChannelMembers": func(c *TeamsJSONClient, p map[string]any) (any, error) {
- return c.ListChannelMembers(p)
+ "listChannelMembers": func(c *TeamsJSONClient, ctx context.Context, p map[string]any) (any, error) {
+ return c.ListChannelMembers(ctx, p)
},
- "addMemberToChannel": func(c *TeamsJSONClient, p map[string]any) (any, error) {
- return c.AddMemberToChannel(p)
+ "addMemberToChannel": func(c *TeamsJSONClient, ctx context.Context, p map[string]any) (any, error) {
+ return c.AddMemberToChannel(ctx, p)
},
- "removeMemberFromChannel": func(c *TeamsJSONClient, p map[string]any) (any, error) {
- return c.RemoveMemberFromChannel(p)
+ "removeMemberFromChannel": func(c *TeamsJSONClient, ctx context.Context, p map[string]any) (any, error) {
+ return c.RemoveMemberFromChannel(ctx, p)
},
- "updateMemberRoleInChannel": func(c *TeamsJSONClient, p map[string]any) (any, error) {
- return c.UpdateMemberInChannel(p)
+ "updateMemberRoleInChannel": func(c *TeamsJSONClient, ctx context.Context, p map[string]any) (any, error) {
+ return c.UpdateMemberInChannel(ctx, p)
},
- "getMentionsInChannel": func(c *TeamsJSONClient, p map[string]any) (any, error) {
- return c.GetMentionsInChannel(p)
+ "getMentionsInChannel": func(c *TeamsJSONClient, ctx context.Context, p map[string]any) (any, error) {
+ return c.GetMentionsInChannel(ctx, p)
},
- "searchMessagesInChannel": func(c *TeamsJSONClient, p map[string]any) (any, error) {
- return c.SearchMessagesInChannel(p)
+ "searchMessagesInChannel": func(c *TeamsJSONClient, ctx context.Context, p map[string]any) (any, error) {
+ return c.SearchMessagesInChannel(ctx, p)
},
// CHATS
- "createOneOnOneChat": func(c *TeamsJSONClient, p map[string]any) (any, error) {
- return c.CreateOneToOneChat(p)
+ "createOneOnOneChat": func(c *TeamsJSONClient, ctx context.Context, p map[string]any) (any, error) {
+ return c.CreateOneToOneChat(ctx, p)
},
- "createGroupChat": func(c *TeamsJSONClient, p map[string]any) (any, error) {
- return c.CreateGroupChat(p)
+ "createGroupChat": func(c *TeamsJSONClient, ctx context.Context, p map[string]any) (any, error) {
+ return c.CreateGroupChat(ctx, p)
},
- "getChat": func(c *TeamsJSONClient, p map[string]any) (any, error) {
- return c.GetChat(p)
+ "getChat": func(c *TeamsJSONClient, ctx context.Context, p map[string]any) (any, error) {
+ return c.GetChat(ctx, p)
},
- "addMemberToGroupChat": func(c *TeamsJSONClient, p map[string]any) (any, error) {
- return c.AddMemberToGroupChat(p)
+ "addMemberToGroupChat": func(c *TeamsJSONClient, ctx context.Context, p map[string]any) (any, error) {
+ return c.AddMemberToGroupChat(ctx, p)
},
- "removeMemberFromGroupChat": func(c *TeamsJSONClient, p map[string]any) (any, error) {
- return c.RemoveMemberFromGroupChat(p)
+ "removeMemberFromGroupChat": func(c *TeamsJSONClient, ctx context.Context, p map[string]any) (any, error) {
+ return c.RemoveMemberFromGroupChat(ctx, p)
},
- "listMembersInGroupChat": func(c *TeamsJSONClient, p map[string]any) (any, error) {
- return c.ListGroupChatMembers(p)
+ "listMembersInGroupChat": func(c *TeamsJSONClient, ctx context.Context, p map[string]any) (any, error) {
+ return c.ListGroupChatMembers(ctx, p)
},
- "updateGroupChatTopic": func(c *TeamsJSONClient, p map[string]any) (any, error) {
- return c.UpdateGroupChatTopic(p)
+ "updateGroupChatTopic": func(c *TeamsJSONClient, ctx context.Context, p map[string]any) (any, error) {
+ return c.UpdateGroupChatTopic(ctx, p)
},
- "listMessagesInChat": func(c *TeamsJSONClient, p map[string]any) (any, error) {
- return c.ListMessagesInChat(p)
+ "listMessagesInChat": func(c *TeamsJSONClient, ctx context.Context, p map[string]any) (any, error) {
+ return c.ListMessagesInChat(ctx, p)
},
- "sendMessageInChat": func(c *TeamsJSONClient, p map[string]any) (any, error) {
- return c.SendMessageInChat(p)
+ "sendMessageInChat": func(c *TeamsJSONClient, ctx context.Context, p map[string]any) (any, error) {
+ return c.SendMessageInChat(ctx, p)
},
- "deleteMessageInChat": func(c *TeamsJSONClient, p map[string]any) (any, error) {
- return c.DeleteMessageInChat(p)
+ "deleteMessageInChat": func(c *TeamsJSONClient, ctx context.Context, p map[string]any) (any, error) {
+ return c.DeleteMessageInChat(ctx, p)
},
- "getMessageInChat": func(c *TeamsJSONClient, p map[string]any) (any, error) {
- return c.GetMessageInChat(p)
+ "getMessageInChat": func(c *TeamsJSONClient, ctx context.Context, p map[string]any) (any, error) {
+ return c.GetMessageInChat(ctx, p)
},
- "listMyChats": func(c *TeamsJSONClient, p map[string]any) (any, error) {
- return c.ListMyChats(p)
+ "listMyChats": func(c *TeamsJSONClient, ctx context.Context, p map[string]any) (any, error) {
+ return c.ListMyChats(ctx, p)
},
- "listMyChatMessages": func(c *TeamsJSONClient, p map[string]any) (any, error) {
- return c.ListMyChatMessages(p)
+ "listMyChatMessages": func(c *TeamsJSONClient, ctx context.Context, p map[string]any) (any, error) {
+ return c.ListMyChatMessages(ctx, p)
},
- "listPinnedMessagesInChat": func(c *TeamsJSONClient, p map[string]any) (any, error) {
- return c.ListPinnedMessagesInChat(p)
+ "listPinnedMessagesInChat": func(c *TeamsJSONClient, ctx context.Context, p map[string]any) (any, error) {
+ return c.ListPinnedMessagesInChat(ctx, p)
},
- "pinMessageInChat": func(c *TeamsJSONClient, p map[string]any) (any, error) {
- return c.PinMessageInChat(p)
+ "pinMessageInChat": func(c *TeamsJSONClient, ctx context.Context, p map[string]any) (any, error) {
+ return c.PinMessageInChat(ctx, p)
},
- "unpinMessageInChat": func(c *TeamsJSONClient, p map[string]any) (any, error) {
- return c.UnpinMessageInChat(p)
+ "unpinMessageInChat": func(c *TeamsJSONClient, ctx context.Context, p map[string]any) (any, error) {
+ return c.UnpinMessageInChat(ctx, p)
},
- "getMentionsInChat": func(c *TeamsJSONClient, p map[string]any) (any, error) {
- return c.GetMentionsInChat(p)
+ "getMentionsInChat": func(c *TeamsJSONClient, ctx context.Context, p map[string]any) (any, error) {
+ return c.GetMentionsInChat(ctx, p)
},
- "searchMessagesInChat": func(c *TeamsJSONClient, p map[string]any) (any, error) {
- return c.SearchMessagesInChat(p)
+ "searchMessagesInChat": func(c *TeamsJSONClient, ctx context.Context, p map[string]any) (any, error) {
+ return c.SearchMessagesInChat(ctx, p)
},
}
diff --git a/go/internal/json-client/requests-channels.go b/go/internal/json-client/requests-channels.go
index 867c323..1f3a2fa 100644
--- a/go/internal/json-client/requests-channels.go
+++ b/go/internal/json-client/requests-channels.go
@@ -12,9 +12,9 @@ type listChannelsParams struct {
TeamRef string `json:"teamRef"`
}
-func (c *TeamsJSONClient) ListChannels(p map[string]any) (any, error) {
+func (c *TeamsJSONClient) ListChannels(ctx context.Context, p map[string]any) (any, error) {
return execute(p, func(params listChannelsParams) (any, error) {
- return c.client.Channels.ListChannels(context.TODO(), params.TeamRef)
+ return c.client.Channels.ListChannels(ctx, params.TeamRef)
})
}
@@ -23,9 +23,9 @@ type baseChannelParams struct {
ChannelRef string `json:"channelRef"`
}
-func (c *TeamsJSONClient) GetChannel(p map[string]any) (any, error) {
+func (c *TeamsJSONClient) GetChannel(ctx context.Context, p map[string]any) (any, error) {
return execute(p, func(params baseChannelParams) (any, error) {
- return c.client.Channels.Get(context.TODO(), params.TeamRef, params.ChannelRef)
+ return c.client.Channels.Get(ctx, params.TeamRef, params.ChannelRef)
})
}
@@ -34,9 +34,9 @@ type createChannelParams struct {
Name string `json:"name"`
}
-func (c *TeamsJSONClient) CreateStandardChannel(p map[string]any) (any, error) {
+func (c *TeamsJSONClient) CreateStandardChannel(ctx context.Context, p map[string]any) (any, error) {
return execute(p, func(params createChannelParams) (any, error) {
- return c.client.Channels.CreateStandardChannel(context.TODO(), params.TeamRef, params.Name)
+ return c.client.Channels.CreateStandardChannel(ctx, params.TeamRef, params.Name)
})
}
@@ -47,15 +47,15 @@ type createPrivateChannelParams struct {
OwnerRefs []string `json:"ownerRefs"`
}
-func (c *TeamsJSONClient) CreatePrivateChannel(p map[string]any) (any, error) {
+func (c *TeamsJSONClient) CreatePrivateChannel(ctx context.Context, p map[string]any) (any, error) {
return execute(p, func(params createPrivateChannelParams) (any, error) {
- return c.client.Channels.CreatePrivateChannel(context.TODO(), params.TeamRef, params.Name, params.MemberRefs, params.OwnerRefs)
+ return c.client.Channels.CreatePrivateChannel(ctx, params.TeamRef, params.Name, params.MemberRefs, params.OwnerRefs)
})
}
-func (c *TeamsJSONClient) DeleteChannel(p map[string]any) (any, error) {
+func (c *TeamsJSONClient) DeleteChannel(ctx context.Context, p map[string]any) (any, error) {
return execute(p, func(params baseChannelParams) (any, error) {
- err := c.client.Channels.Delete(context.TODO(), params.TeamRef, params.ChannelRef)
+ err := c.client.Channels.Delete(ctx, params.TeamRef, params.ChannelRef)
return "deleted", err
})
}
@@ -66,13 +66,13 @@ type sendMessageToChannelParams struct {
Body decoders.MessageBodyDTO `json:"body"`
}
-func (c *TeamsJSONClient) SendMessageToChannel(p map[string]any) (any, error) {
+func (c *TeamsJSONClient) SendMessageToChannel(ctx context.Context, p map[string]any) (any, error) {
return execute(p, func(params sendMessageToChannelParams) (any, error) {
body, err := decoders.DecodeParams[models.MessageBody](¶ms.Body)
if err != nil {
return nil, err
}
- return c.client.Channels.SendMessage(context.TODO(), params.TeamRef, params.ChannelRef, *body)
+ return c.client.Channels.SendMessage(ctx, params.TeamRef, params.ChannelRef, *body)
})
}
@@ -83,13 +83,13 @@ type sendReplyToChannelParams struct {
Body decoders.MessageBodyDTO `json:"body"`
}
-func (c *TeamsJSONClient) SendReplyToChannel(p map[string]any) (any, error) {
+func (c *TeamsJSONClient) SendReplyToChannel(ctx context.Context, p map[string]any) (any, error) {
return execute(p, func(params sendReplyToChannelParams) (any, error) {
body, err := decoders.DecodeParams[models.MessageBody](¶ms.Body)
if err != nil {
return nil, err
}
- return c.client.Channels.SendReply(context.TODO(), params.TeamRef, params.ChannelRef, params.MessageID, *body)
+ return c.client.Channels.SendReply(ctx, params.TeamRef, params.ChannelRef, params.MessageID, *body)
})
}
@@ -106,14 +106,14 @@ type listMessagesParams struct {
NextLink decoders.NextLinkDTO `json:"nextLink"`
}
-func (c *TeamsJSONClient) ListMessagesInChannel(p map[string]any) (any, error) {
+func (c *TeamsJSONClient) ListMessagesInChannel(ctx context.Context, p map[string]any) (any, error) {
return execute(p, func(params listMessagesParams) (any, error) {
options, err := decoders.DecodeParams[models.ListMessagesOptions](params.Options)
if err != nil {
return nil, err
}
nextLink := decoders.GetNextLink(¶ms.NextLink)
- return c.client.Channels.ListMessages(context.TODO(), params.TeamRef, params.ChannelRef, options, params.IncludeSystem, nextLink)
+ return c.client.Channels.ListMessages(ctx, params.TeamRef, params.ChannelRef, options, params.IncludeSystem, nextLink)
})
}
@@ -123,9 +123,9 @@ type getMessageParams struct {
MessageID string `json:"messageId"`
}
-func (c *TeamsJSONClient) GetMessageInChannel(p map[string]any) (any, error) {
+func (c *TeamsJSONClient) GetMessageInChannel(ctx context.Context, p map[string]any) (any, error) {
return execute(p, func(params getMessageParams) (any, error) {
- return c.client.Channels.GetMessage(context.TODO(), params.TeamRef, params.ChannelRef, params.MessageID)
+ return c.client.Channels.GetMessage(ctx, params.TeamRef, params.ChannelRef, params.MessageID)
})
}
@@ -138,10 +138,10 @@ type listRepliesParams struct {
NextLink decoders.NextLinkDTO `json:"nextLink"`
}
-func (c *TeamsJSONClient) ListMessageRepliesInChannel(p map[string]any) (any, error) {
+func (c *TeamsJSONClient) ListMessageRepliesInChannel(ctx context.Context, p map[string]any) (any, error) {
return execute(p, func(params listRepliesParams) (any, error) {
nextLink := decoders.GetNextLink(¶ms.NextLink)
- return c.client.Channels.ListReplies(context.TODO(), params.TeamRef, params.ChannelRef, params.MessageID, params.Top, params.IncludeSystem, nextLink)
+ return c.client.Channels.ListReplies(ctx, params.TeamRef, params.ChannelRef, params.MessageID, params.Top, params.IncludeSystem, nextLink)
})
}
@@ -152,15 +152,15 @@ type getReplyParams struct {
ReplyID string `json:"replyId"`
}
-func (c *TeamsJSONClient) GetMessageReplyInChannel(p map[string]any) (any, error) {
+func (c *TeamsJSONClient) GetMessageReplyInChannel(ctx context.Context, p map[string]any) (any, error) {
return execute(p, func(params getReplyParams) (any, error) {
- return c.client.Channels.GetReply(context.TODO(), params.TeamRef, params.ChannelRef, params.MessageID, params.ReplyID)
+ return c.client.Channels.GetReply(ctx, params.TeamRef, params.ChannelRef, params.MessageID, params.ReplyID)
})
}
-func (c *TeamsJSONClient) ListChannelMembers(p map[string]any) (any, error) {
+func (c *TeamsJSONClient) ListChannelMembers(ctx context.Context, p map[string]any) (any, error) {
return execute(p, func(params baseChannelParams) (any, error) {
- return c.client.Channels.ListMembers(context.TODO(), params.TeamRef, params.ChannelRef)
+ return c.client.Channels.ListMembers(ctx, params.TeamRef, params.ChannelRef)
})
}
@@ -171,15 +171,15 @@ type addOrUpdateMemberToChannelParams struct {
IsOwner bool `json:"isOwner"`
}
-func (c *TeamsJSONClient) AddMemberToChannel(p map[string]any) (any, error) {
+func (c *TeamsJSONClient) AddMemberToChannel(ctx context.Context, p map[string]any) (any, error) {
return execute(p, func(params addOrUpdateMemberToChannelParams) (any, error) {
- return c.client.Channels.AddMember(context.TODO(), params.TeamRef, params.ChannelRef, params.UserRef, params.IsOwner)
+ return c.client.Channels.AddMember(ctx, params.TeamRef, params.ChannelRef, params.UserRef, params.IsOwner)
})
}
-func (c *TeamsJSONClient) UpdateMemberInChannel(p map[string]any) (any, error) {
+func (c *TeamsJSONClient) UpdateMemberInChannel(ctx context.Context, p map[string]any) (any, error) {
return execute(p, func(params addOrUpdateMemberToChannelParams) (any, error) {
- return c.client.Channels.UpdateMemberRoles(context.TODO(), params.TeamRef, params.ChannelRef, params.UserRef, params.IsOwner)
+ return c.client.Channels.UpdateMemberRoles(ctx, params.TeamRef, params.ChannelRef, params.UserRef, params.IsOwner)
})
}
@@ -189,9 +189,9 @@ type removeMemberFromChannelParams struct {
UserRef string `json:"userRef"`
}
-func (c *TeamsJSONClient) RemoveMemberFromChannel(p map[string]any) (any, error) {
+func (c *TeamsJSONClient) RemoveMemberFromChannel(ctx context.Context, p map[string]any) (any, error) {
return execute(p, func(params removeMemberFromChannelParams) (any, error) {
- err := c.client.Channels.RemoveMember(context.TODO(), params.TeamRef, params.ChannelRef, params.UserRef)
+ err := c.client.Channels.RemoveMember(ctx, params.TeamRef, params.ChannelRef, params.UserRef)
return "removed", err
})
}
@@ -202,9 +202,9 @@ type getMentionsInChannelParams struct {
RawMentions []string `json:"rawMentions"`
}
-func (c *TeamsJSONClient) GetMentionsInChannel(p map[string]any) (any, error) {
+func (c *TeamsJSONClient) GetMentionsInChannel(ctx context.Context, p map[string]any) (any, error) {
return execute(p, func(params getMentionsInChannelParams) (any, error) {
- return c.client.Channels.GetMentions(context.TODO(), params.TeamRef, params.ChannelRef, params.RawMentions)
+ return c.client.Channels.GetMentions(ctx, params.TeamRef, params.ChannelRef, params.RawMentions)
})
}
@@ -215,13 +215,13 @@ type searchMessagesInChannelParams struct {
SearchConfig decoders.SearchConfigDTO `json:"searchConfig"`
}
-func (c *TeamsJSONClient) SearchMessagesInChannel(p map[string]any) (any, error) {
+func (c *TeamsJSONClient) SearchMessagesInChannel(ctx context.Context, p map[string]any) (any, error) {
return execute(p, func(params searchMessagesInChannelParams) (any, error) {
searchOptions := decoders.DecodeSearchMessageOptions(¶ms.SearchOptions)
searchConfig, err := decoders.DecodeParams[search.SearchConfig](¶ms.SearchConfig)
if err != nil {
return nil, err
}
- return c.client.Channels.SearchMessages(context.TODO(), ¶ms.TeamRef, ¶ms.ChannelRef, searchOptions, searchConfig)
+ return c.client.Channels.SearchMessages(ctx, ¶ms.TeamRef, ¶ms.ChannelRef, searchOptions, searchConfig)
})
}
diff --git a/go/internal/json-client/requests-chats.go b/go/internal/json-client/requests-chats.go
index 34e9e2b..4708e76 100644
--- a/go/internal/json-client/requests-chats.go
+++ b/go/internal/json-client/requests-chats.go
@@ -13,9 +13,9 @@ type createOneToOneChatParams struct {
RecipientRef string `json:"recipientRef"`
}
-func (c *TeamsJSONClient) CreateOneToOneChat(p map[string]any) (any, error) {
+func (c *TeamsJSONClient) CreateOneToOneChat(ctx context.Context, p map[string]any) (any, error) {
return execute(p, func(params createOneToOneChatParams) (any, error) {
- return c.client.Chats.CreateOneOnOne(context.TODO(), params.RecipientRef)
+ return c.client.Chats.CreateOneOnOne(ctx, params.RecipientRef)
})
}
@@ -25,9 +25,9 @@ type createGroupChatParams struct {
IncludeMe bool `json:"includeMe"`
}
-func (c *TeamsJSONClient) CreateGroupChat(p map[string]any) (any, error) {
+func (c *TeamsJSONClient) CreateGroupChat(ctx context.Context, p map[string]any) (any, error) {
return execute(p, func(params createGroupChatParams) (any, error) {
- return c.client.Chats.CreateGroup(context.TODO(), params.RecipientRefs, params.Topic, params.IncludeMe)
+ return c.client.Chats.CreateGroup(ctx, params.RecipientRefs, params.Topic, params.IncludeMe)
})
}
@@ -35,10 +35,10 @@ type baseChatParams struct {
ChatRef decoders.ChatRefDTO `json:"chatRef"`
}
-func (c *TeamsJSONClient) GetChat(p map[string]any) (any, error) {
+func (c *TeamsJSONClient) GetChat(ctx context.Context, p map[string]any) (any, error) {
return execute(p, func(params baseChatParams) (any, error) {
chatRef := decoders.GetChatRef(params.ChatRef)
- return c.client.Chats.GetChat(context.TODO(), chatRef)
+ return c.client.Chats.GetChat(ctx, chatRef)
})
}
@@ -47,15 +47,15 @@ type groupChatMemberParams struct {
UserRef string `json:"userRef"`
}
-func (c *TeamsJSONClient) AddMemberToGroupChat(p map[string]any) (any, error) {
+func (c *TeamsJSONClient) AddMemberToGroupChat(ctx context.Context, p map[string]any) (any, error) {
return execute(p, func(params groupChatMemberParams) (any, error) {
- return c.client.Chats.AddMemberToGroupChat(context.TODO(), chats.GroupChatRef{Ref: params.GroupChatRef}, params.UserRef)
+ return c.client.Chats.AddMemberToGroupChat(ctx, chats.GroupChatRef{Ref: params.GroupChatRef}, params.UserRef)
})
}
-func (c *TeamsJSONClient) RemoveMemberFromGroupChat(p map[string]any) (any, error) {
+func (c *TeamsJSONClient) RemoveMemberFromGroupChat(ctx context.Context, p map[string]any) (any, error) {
return execute(p, func(params groupChatMemberParams) (any, error) {
- err := c.client.Chats.RemoveMemberFromGroupChat(context.TODO(), chats.GroupChatRef{Ref: params.GroupChatRef}, params.UserRef)
+ err := c.client.Chats.RemoveMemberFromGroupChat(ctx, chats.GroupChatRef{Ref: params.GroupChatRef}, params.UserRef)
return "removed", err
})
}
@@ -64,9 +64,9 @@ type listMembersInChatParams struct {
GroupChatRef string `json:"groupChatRef"`
}
-func (c *TeamsJSONClient) ListGroupChatMembers(p map[string]any) (any, error) {
+func (c *TeamsJSONClient) ListGroupChatMembers(ctx context.Context, p map[string]any) (any, error) {
return execute(p, func(params listMembersInChatParams) (any, error) {
- return c.client.Chats.ListGroupChatMembers(context.TODO(), chats.GroupChatRef{Ref: params.GroupChatRef})
+ return c.client.Chats.ListGroupChatMembers(ctx, chats.GroupChatRef{Ref: params.GroupChatRef})
})
}
@@ -75,9 +75,9 @@ type updateGroupChatTopicParams struct {
Topic string `json:"topic"`
}
-func (c *TeamsJSONClient) UpdateGroupChatTopic(p map[string]any) (any, error) {
+func (c *TeamsJSONClient) UpdateGroupChatTopic(ctx context.Context, p map[string]any) (any, error) {
return execute(p, func(params updateGroupChatTopicParams) (any, error) {
- return c.client.Chats.UpdateGroupChatTopic(context.TODO(), chats.GroupChatRef{Ref: params.GroupChatRef}, params.Topic)
+ return c.client.Chats.UpdateGroupChatTopic(ctx, chats.GroupChatRef{Ref: params.GroupChatRef}, params.Topic)
})
}
@@ -87,11 +87,11 @@ type listMessagesInChatParams struct {
NextLink decoders.NextLinkDTO `json:"nextLink"`
}
-func (c *TeamsJSONClient) ListMessagesInChat(p map[string]any) (any, error) {
+func (c *TeamsJSONClient) ListMessagesInChat(ctx context.Context, p map[string]any) (any, error) {
return execute(p, func(params listMessagesInChatParams) (any, error) {
chatRef := decoders.GetChatRef(params.ChatRef)
nextLink := decoders.GetNextLink(¶ms.NextLink)
- return c.client.Chats.ListMessages(context.TODO(), chatRef, params.IncludeSystem, nextLink)
+ return c.client.Chats.ListMessages(ctx, chatRef, params.IncludeSystem, nextLink)
})
}
@@ -100,14 +100,14 @@ type sendMessageInChatParams struct {
Body decoders.MessageBodyDTO `json:"body"`
}
-func (c *TeamsJSONClient) SendMessageInChat(p map[string]any) (any, error) {
+func (c *TeamsJSONClient) SendMessageInChat(ctx context.Context, p map[string]any) (any, error) {
return execute(p, func(params sendMessageInChatParams) (any, error) {
chatRef := decoders.GetChatRef(params.ChatRef)
body, err := decoders.DecodeParams[models.MessageBody](params.Body)
if err != nil {
return nil, err
}
- return c.client.Chats.SendMessage(context.TODO(), chatRef, *body)
+ return c.client.Chats.SendMessage(ctx, chatRef, *body)
})
}
@@ -116,18 +116,18 @@ type messageInChatParams struct {
MessageID string `json:"messageId"`
}
-func (c *TeamsJSONClient) DeleteMessageInChat(p map[string]any) (any, error) {
+func (c *TeamsJSONClient) DeleteMessageInChat(ctx context.Context, p map[string]any) (any, error) {
return execute(p, func(params messageInChatParams) (any, error) {
chatRef := decoders.GetChatRef(params.ChatRef)
- err := c.client.Chats.DeleteMessage(context.TODO(), chatRef, params.MessageID)
+ err := c.client.Chats.DeleteMessage(ctx, chatRef, params.MessageID)
return "deleted", err
})
}
-func (c *TeamsJSONClient) GetMessageInChat(p map[string]any) (any, error) {
+func (c *TeamsJSONClient) GetMessageInChat(ctx context.Context, p map[string]any) (any, error) {
return execute(p, func(params messageInChatParams) (any, error) {
chatRef := decoders.GetChatRef(params.ChatRef)
- return c.client.Chats.GetMessage(context.TODO(), chatRef, params.MessageID)
+ return c.client.Chats.GetMessage(ctx, chatRef, params.MessageID)
})
}
@@ -135,13 +135,13 @@ type ListMyChatsParams struct {
ChatType string `json:"chatType"`
}
-func (c *TeamsJSONClient) ListMyChats(p map[string]any) (any, error) {
+func (c *TeamsJSONClient) ListMyChats(ctx context.Context, p map[string]any) (any, error) {
return execute(p, func(params ListMyChatsParams) (any, error) {
var chatType models.ChatType
if params.ChatType != "" {
chatType = models.ChatType(params.ChatType)
}
- return c.client.Chats.ListChats(context.TODO(), &chatType)
+ return c.client.Chats.ListChats(ctx, &chatType)
})
}
@@ -151,35 +151,35 @@ type listChatMessagesParams struct {
Top *int32 `json:"top"`
}
-func (c *TeamsJSONClient) ListMyChatMessages(p map[string]any) (any, error) {
+func (c *TeamsJSONClient) ListMyChatMessages(ctx context.Context, p map[string]any) (any, error) {
return execute(p, func(params listChatMessagesParams) (any, error) {
parsedStartTime, parsedEndTime, err := decoders.DecodeTimeRange(params.StartTime, params.EndTime)
if err != nil {
return nil, err
}
- return c.client.Chats.ListAllMessages(context.TODO(), parsedStartTime, parsedEndTime, params.Top)
+ return c.client.Chats.ListAllMessages(ctx, parsedStartTime, parsedEndTime, params.Top)
})
}
-func (c *TeamsJSONClient) ListPinnedMessagesInChat(p map[string]any) (any, error) {
+func (c *TeamsJSONClient) ListPinnedMessagesInChat(ctx context.Context, p map[string]any) (any, error) {
return execute(p, func(params baseChatParams) (any, error) {
chatRef := decoders.GetChatRef(params.ChatRef)
- return c.client.Chats.ListPinnedMessages(context.TODO(), chatRef)
+ return c.client.Chats.ListPinnedMessages(ctx, chatRef)
})
}
-func (c *TeamsJSONClient) PinMessageInChat(p map[string]any) (any, error) {
+func (c *TeamsJSONClient) PinMessageInChat(ctx context.Context, p map[string]any) (any, error) {
return execute(p, func(params messageInChatParams) (any, error) {
chatRef := decoders.GetChatRef(params.ChatRef)
- err := c.client.Chats.PinMessage(context.TODO(), chatRef, params.MessageID)
+ err := c.client.Chats.PinMessage(ctx, chatRef, params.MessageID)
return "pinned", err
})
}
-func (c *TeamsJSONClient) UnpinMessageInChat(p map[string]any) (any, error) {
+func (c *TeamsJSONClient) UnpinMessageInChat(ctx context.Context, p map[string]any) (any, error) {
return execute(p, func(params messageInChatParams) (any, error) {
chatRef := decoders.GetChatRef(params.ChatRef)
- err := c.client.Chats.UnpinMessage(context.TODO(), chatRef, params.MessageID)
+ err := c.client.Chats.UnpinMessage(ctx, chatRef, params.MessageID)
return "unpinned", err
})
}
@@ -189,10 +189,10 @@ type mentionInChatParams struct {
RawMentions []string `json:"rawMentions"`
}
-func (c *TeamsJSONClient) GetMentionsInChat(p map[string]any) (any, error) {
+func (c *TeamsJSONClient) GetMentionsInChat(ctx context.Context, p map[string]any) (any, error) {
return execute(p, func(params mentionInChatParams) (any, error) {
chatRef := decoders.GetChatRef(params.ChatRef)
- return c.client.Chats.GetMentions(context.TODO(), chatRef, params.RawMentions)
+ return c.client.Chats.GetMentions(ctx, chatRef, params.RawMentions)
})
}
@@ -202,7 +202,7 @@ type searchMessagesInChatParams struct {
SearchConfig decoders.SearchConfigDTO `json:"searchConfig"`
}
-func (c *TeamsJSONClient) SearchMessagesInChat(p map[string]any) (any, error) {
+func (c *TeamsJSONClient) SearchMessagesInChat(ctx context.Context, p map[string]any) (any, error) {
return execute(p, func(params searchMessagesInChatParams) (any, error) {
chatRef := decoders.GetChatRef(params.ChatRef)
searchOptions := decoders.DecodeSearchMessageOptions(¶ms.SearchOptions)
@@ -210,6 +210,6 @@ func (c *TeamsJSONClient) SearchMessagesInChat(p map[string]any) (any, error) {
if err != nil {
return nil, err
}
- return c.client.Chats.SearchMessages(context.TODO(), chatRef, searchOptions, searchConfig)
+ return c.client.Chats.SearchMessages(ctx, chatRef, searchOptions, searchConfig)
})
}
diff --git a/go/internal/json-client/requests-teams.go b/go/internal/json-client/requests-teams.go
index 22022ac..f8608a8 100644
--- a/go/internal/json-client/requests-teams.go
+++ b/go/internal/json-client/requests-teams.go
@@ -10,14 +10,14 @@ type getTeamParams struct {
TeamRef string `json:"teamRef"`
}
-func (c *TeamsJSONClient) GetTeam(p map[string]any) (any, error) {
+func (c *TeamsJSONClient) GetTeam(ctx context.Context, p map[string]any) (any, error) {
return execute(p, func(params getTeamParams) (any, error) {
- return c.client.Teams.Get(context.TODO(), params.TeamRef)
+ return c.client.Teams.Get(ctx, params.TeamRef)
})
}
-func (c *TeamsJSONClient) ListMyJoined() (any, error) {
- return c.client.Teams.ListMyJoined(context.TODO())
+func (c *TeamsJSONClient) ListMyJoined(ctx context.Context) (any, error) {
+ return c.client.Teams.ListMyJoined(ctx)
}
type updateTeamParams struct {
@@ -25,10 +25,10 @@ type updateTeamParams struct {
TeamUpdate decoders.UpdateTeamDTO `json:"team"`
}
-func (c *TeamsJSONClient) UpdateTeam(p map[string]any) (any, error) {
+func (c *TeamsJSONClient) UpdateTeam(ctx context.Context, p map[string]any) (any, error) {
return execute(p, func(params updateTeamParams) (any, error) {
updateTeam := decoders.GetUpdateTeam(¶ms.TeamUpdate)
- return c.client.Teams.UpdateTeam(context.TODO(), params.TeamRef, &updateTeam)
+ return c.client.Teams.UpdateTeam(ctx, params.TeamRef, &updateTeam)
})
}
@@ -38,9 +38,9 @@ type createViaGroupParams struct {
Visibility string `json:"visibility"`
}
-func (c *TeamsJSONClient) CreateTeamViaGroup(p map[string]any) (any, error) {
+func (c *TeamsJSONClient) CreateTeamViaGroup(ctx context.Context, p map[string]any) (any, error) {
return execute(p, func(params createViaGroupParams) (any, error) {
- return c.client.Teams.CreateViaGroup(context.TODO(), params.DisplayName, params.MailNickname, params.Visibility)
+ return c.client.Teams.CreateViaGroup(ctx, params.DisplayName, params.MailNickname, params.Visibility)
})
}
@@ -53,9 +53,9 @@ type createFromTemplateParams struct {
IncludeMe bool `json:"includeMe"`
}
-func (c *TeamsJSONClient) CreateTeamFromTemplate(p map[string]any) (any, error) {
+func (c *TeamsJSONClient) CreateTeamFromTemplate(ctx context.Context, p map[string]any) (any, error) {
return execute(p, func(params createFromTemplateParams) (any, error) {
- return c.client.Teams.CreateFromTemplate(context.TODO(), params.DisplayName, params.Description, params.Owners, params.Members, params.Visibility, params.IncludeMe)
+ return c.client.Teams.CreateFromTemplate(ctx, params.DisplayName, params.Description, params.Owners, params.Members, params.Visibility, params.IncludeMe)
})
}
@@ -64,9 +64,9 @@ type archiveTeamParams struct {
SpoReadOnlyFromMembers *bool `json:"spoReadOnlyFromMembers,omitempty"`
}
-func (c *TeamsJSONClient) ArchiveTeam(p map[string]any) (any, error) {
+func (c *TeamsJSONClient) ArchiveTeam(ctx context.Context, p map[string]any) (any, error) {
return execute(p, func(params archiveTeamParams) (any, error) {
- err := c.client.Teams.Archive(context.TODO(), params.TeamRef, params.SpoReadOnlyFromMembers)
+ err := c.client.Teams.Archive(ctx, params.TeamRef, params.SpoReadOnlyFromMembers)
return "archived", err
})
}
@@ -75,9 +75,9 @@ type unarchiveTeamParams struct {
TeamRef string `json:"teamRef"`
}
-func (c *TeamsJSONClient) UnarchiveTeam(p map[string]any) (any, error) {
+func (c *TeamsJSONClient) UnarchiveTeam(ctx context.Context, p map[string]any) (any, error) {
return execute(p, func(params unarchiveTeamParams) (any, error) {
- err := c.client.Teams.Unarchive(context.TODO(), params.TeamRef)
+ err := c.client.Teams.Unarchive(ctx, params.TeamRef)
return "unarchived", err
})
}
@@ -86,9 +86,9 @@ type deleteTeamParams struct {
TeamRef string `json:"teamRef"`
}
-func (c *TeamsJSONClient) DeleteTeam(p map[string]any) (any, error) {
+func (c *TeamsJSONClient) DeleteTeam(ctx context.Context, p map[string]any) (any, error) {
return execute(p, func(params deleteTeamParams) (any, error) {
- err := c.client.Teams.Delete(context.TODO(), params.TeamRef)
+ err := c.client.Teams.Delete(ctx, params.TeamRef)
return "deleted", err
})
}
@@ -97,8 +97,8 @@ type restoreTeamParams struct {
DeletedGroupID string `json:"deletedGroupId"`
}
-func (c *TeamsJSONClient) RestoreDeletedTeam(p map[string]any) (any, error) {
+func (c *TeamsJSONClient) RestoreDeletedTeam(ctx context.Context, p map[string]any) (any, error) {
return execute(p, func(params restoreTeamParams) (any, error) {
- return c.client.Teams.RestoreDeleted(context.TODO(), params.DeletedGroupID)
+ return c.client.Teams.RestoreDeleted(ctx, params.DeletedGroupID)
})
}
From 44eaca4086455ca4b632b1fa465c46cab9ae4a54 Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Tue, 13 Jan 2026 18:50:32 +0100
Subject: [PATCH 98/99] docs: solved todo in python docs readme
---
python/README.md | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/python/README.md b/python/README.md
index 8e66add..1ccd0ae 100644
--- a/python/README.md
+++ b/python/README.md
@@ -47,8 +47,7 @@ SCOPES=User.Read,Team.ReadBasic.All,Channel.ReadBasic.All
AUTH_METHOD=DEVICE_CODE [or INTERACTIVE]
```
-Scopes needed by all functions could be found in .env.template.
-**//TODO add url to template**
+Scopes needed by all functions could be found in [.env.template](https://github.com/pzsp-teams/lib-python/tree/main/.env.template).
### 2. Basic usage
From 386a2319b373dbabeaaa8d32f439cd8910208085 Mon Sep 17 00:00:00 2001
From: Michal <01187288@pw.edu.pl>
Date: Tue, 13 Jan 2026 18:55:07 +0100
Subject: [PATCH 99/99] docs: fixed index.md
---
python/docs/index.md | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/python/docs/index.md b/python/docs/index.md
index c92817b..dd570f2 100644
--- a/python/docs/index.md
+++ b/python/docs/index.md
@@ -50,8 +50,7 @@ SCOPES=User.Read,Team.ReadBasic.All,Channel.ReadBasic.All
AUTH_METHOD=DEVICE_CODE [or INTERACTIVE]
```
-Scopes needed by all functions could be found in .env.template.
-**//TODO add url to template**
+Scopes needed by all functions could be found in [.env.template](https://github.com/pzsp-teams/lib-python/tree/main/.env.template).
### 2. Basic usage