From 0da21f7cf1908b60fa1bbaf8f59a7c9f1f1190c9 Mon Sep 17 00:00:00 2001
From: Andrei Varabyeu <andrei_varabyeu@epam.com>
Date: Tue, 8 Aug 2023 10:57:34 +0200
Subject: [PATCH 1/2] Generalizes activity Handler interface. Adds
 HandlerFuncsMap struct that allows mapping handlers for any action type

---
 core/activity/handler.go           |  39 +++++++----
 core/bot_framework_adapter_test.go | 100 +++++++++++++++++++++++++++++
 2 files changed, 127 insertions(+), 12 deletions(-)

diff --git a/core/activity/handler.go b/core/activity/handler.go
index 897562e..7b2e600 100644
--- a/core/activity/handler.go
+++ b/core/activity/handler.go
@@ -28,9 +28,20 @@ import (
 
 // Handler acts as the interface for the client program to define actions on various events from connector service.
 type Handler interface {
-	OnMessage(context *TurnContext) (schema.Activity, error)
-	OnInvoke(context *TurnContext) (schema.Activity, error)
-	OnConversationUpdate(context *TurnContext) (schema.Activity, error)
+	On(context *TurnContext) (schema.Activity, error)
+}
+
+// HandlerFuncsMap is an adaptor to let client program handler functions
+// for all activity types, including not supported by HandlerFuncs adaptor
+type HandlerFuncsMap struct {
+	Funcs map[schema.ActivityTypes]func(turn *TurnContext) (schema.Activity, error)
+}
+
+func (hf HandlerFuncsMap) On(context *TurnContext) (schema.Activity, error) {
+	if handler, ok := hf.Funcs[context.Activity.Type]; ok {
+		return handler(context)
+	}
+	return schema.Activity{}, fmt.Errorf("Activity type %s not supported", context.Activity.Type)
 }
 
 // HandlerFuncs is an adaptor to let client program specify as many or
@@ -42,6 +53,18 @@ type HandlerFuncs struct {
 	OnConversationUpdateFunc func(turn *TurnContext) (schema.Activity, error)
 }
 
+func (r HandlerFuncs) On(context *TurnContext) (schema.Activity, error) {
+	switch context.Activity.Type {
+	case schema.Message:
+		return r.OnMessage(context)
+	case schema.Invoke:
+		return r.OnInvoke(context)
+	case schema.ConversationUpdate:
+		return r.OnConversationUpdate(context)
+	}
+	return schema.Activity{}, fmt.Errorf("Activity type %s not supported yet", context.Activity.Type)
+}
+
 // OnMessage handles a 'message' event from connector service.
 func (r HandlerFuncs) OnMessage(turn *TurnContext) (schema.Activity, error) {
 	if r.OnMessageFunc != nil {
@@ -69,13 +92,5 @@ func (r HandlerFuncs) OnInvoke(turn *TurnContext) (schema.Activity, error) {
 // PrepareActivityContext routes the received Activity to respective handler function.
 // Returns the result of the handler function.
 func PrepareActivityContext(handler Handler, context *TurnContext) (schema.Activity, error) {
-	switch context.Activity.Type {
-	case schema.Message:
-		return handler.OnMessage(context)
-	case schema.Invoke:
-		return handler.OnInvoke(context)
-	case schema.ConversationUpdate:
-		return handler.OnConversationUpdate(context)
-	}
-	return schema.Activity{}, fmt.Errorf("Activity type %s not supported yet", context.Activity.Type)
+	return handler.On(context)
 }
diff --git a/core/bot_framework_adapter_test.go b/core/bot_framework_adapter_test.go
index 6e80657..5838287 100644
--- a/core/bot_framework_adapter_test.go
+++ b/core/bot_framework_adapter_test.go
@@ -177,3 +177,103 @@ func TestActivityUpdate(t *testing.T) {
 	handler.ServeHTTP(rr, req)
 	assert.Equal(t, rr.Code, 200, "Expect 200 response status")
 }
+
+func TestActivityCustom(t *testing.T) {
+	srv := serverMock(t)
+
+	type args struct {
+		Activity schema.Activity
+		Handler  activity.Handler
+	}
+
+	tests := []struct {
+		name    string
+		args    args
+		wantErr bool
+	}{
+		{
+			args: args{
+				Activity: schema.Activity{
+					Type: schema.InstallationUpdate,
+					Conversation: schema.ConversationAccount{
+						ID:   "abcd1234",
+						Name: "Convo1",
+					},
+					MembersAdded: []schema.ChannelAccount{
+						{
+							ID:   "123456",
+							Name: "Some member name",
+							Role: schema.BOT,
+						},
+					},
+					Text:       "Message from Teams Client",
+					ReplyToID:  "5d5cdc723",
+					ServiceURL: srv.URL,
+				},
+				Handler: activity.HandlerFuncsMap{
+					Funcs: map[schema.ActivityTypes]func(turn *activity.TurnContext) (schema.Activity, error){
+						schema.InstallationUpdate: func(turn *activity.TurnContext) (schema.Activity, error) {
+							return turn.SendActivity(activity.MsgOptionText("supported"))
+						},
+					},
+				},
+			},
+		},
+		{
+			args: args{
+				Activity: schema.Activity{
+					Type: schema.InstallationUpdate,
+					Conversation: schema.ConversationAccount{
+						ID:   "abcd1234",
+						Name: "Convo1",
+					},
+					MembersAdded: []schema.ChannelAccount{
+						{
+							ID:   "123456",
+							Name: "Some member name",
+							Role: schema.BOT,
+						},
+					},
+					Text:       "Message from Teams Client",
+					ReplyToID:  "5d5cdc723",
+					ServiceURL: srv.URL,
+				},
+				Handler: activity.HandlerFuncs{},
+			},
+			wantErr: true,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			handler := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
+				ctx := context.Background()
+				setting := core.AdapterSetting{
+					AppID:       "asdasd",
+					AppPassword: "cfg.MicrosoftTeams.AppPassword",
+				}
+				setting.CredentialProvider = auth.SimpleCredentialProvider{
+					AppID:    setting.AppID,
+					Password: setting.AppPassword,
+				}
+				clientConfig, err := client.NewClientConfig(setting.CredentialProvider, auth.ToChannelFromBotLoginURL[0])
+				assert.Nil(t, err, fmt.Sprintf("Failed with error %s", err))
+				connectorClient, err := client.NewClient(clientConfig)
+				assert.Nil(t, err, fmt.Sprintf("Failed with error %s", err))
+				adapter := core.BotFrameworkAdapter{setting, &core.MockTokenValidator{}, connectorClient}
+				act, err := adapter.ParseRequest(ctx, req)
+
+				err = adapter.ProcessActivity(ctx, act, tt.args.Handler)
+				assert.True(t, (err != nil) == tt.wantErr, fmt.Sprintf("Failed with error %s", err))
+			})
+			rr := httptest.NewRecorder()
+			bodyJSON, err := json.Marshal(tt.args.Activity)
+			assert.Nil(t, err, fmt.Sprintf("Failed with error %s", err))
+			bodyBytes := bytes.NewReader(bodyJSON)
+			req, err := http.NewRequest(http.MethodPost, "/api/messages", bodyBytes)
+			assert.Nil(t, err, fmt.Sprintf("Failed with error %s", err))
+			req.Header.Set("Authorization", "Bearer abc123")
+			handler.ServeHTTP(rr, req)
+			assert.Equal(t, rr.Code, 200, "Expect 200 response status")
+		})
+	}
+}

From 503ace3d3e8143a14b4d09a9444a68784ab5c9c3 Mon Sep 17 00:00:00 2001
From: Andrei Varabyeu <andrei_varabyeu@epam.com>
Date: Tue, 22 Aug 2023 11:59:43 +0200
Subject: [PATCH 2/2] make HandlerFuncsMap as map type instead of struct

---
 core/activity/handler.go           | 6 ++----
 core/bot_framework_adapter_test.go | 6 ++----
 2 files changed, 4 insertions(+), 8 deletions(-)

diff --git a/core/activity/handler.go b/core/activity/handler.go
index 7b2e600..593c4ca 100644
--- a/core/activity/handler.go
+++ b/core/activity/handler.go
@@ -33,12 +33,10 @@ type Handler interface {
 
 // HandlerFuncsMap is an adaptor to let client program handler functions
 // for all activity types, including not supported by HandlerFuncs adaptor
-type HandlerFuncsMap struct {
-	Funcs map[schema.ActivityTypes]func(turn *TurnContext) (schema.Activity, error)
-}
+type HandlerFuncsMap map[schema.ActivityTypes]func(turn *TurnContext) (schema.Activity, error)
 
 func (hf HandlerFuncsMap) On(context *TurnContext) (schema.Activity, error) {
-	if handler, ok := hf.Funcs[context.Activity.Type]; ok {
+	if handler, ok := hf[context.Activity.Type]; ok {
 		return handler(context)
 	}
 	return schema.Activity{}, fmt.Errorf("Activity type %s not supported", context.Activity.Type)
diff --git a/core/bot_framework_adapter_test.go b/core/bot_framework_adapter_test.go
index 5838287..749564f 100644
--- a/core/bot_framework_adapter_test.go
+++ b/core/bot_framework_adapter_test.go
@@ -211,10 +211,8 @@ func TestActivityCustom(t *testing.T) {
 					ServiceURL: srv.URL,
 				},
 				Handler: activity.HandlerFuncsMap{
-					Funcs: map[schema.ActivityTypes]func(turn *activity.TurnContext) (schema.Activity, error){
-						schema.InstallationUpdate: func(turn *activity.TurnContext) (schema.Activity, error) {
-							return turn.SendActivity(activity.MsgOptionText("supported"))
-						},
+					schema.InstallationUpdate: func(turn *activity.TurnContext) (schema.Activity, error) {
+						return turn.SendActivity(activity.MsgOptionText("supported"))
 					},
 				},
 			},