Skip to content

Commit 9120126

Browse files
committed
add mail to search index
fix Remove data from the search index when no longer needed
1 parent e56ef78 commit 9120126

11 files changed

+348
-16
lines changed

acceptance/mail_test.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@ package acceptance
33
import (
44
"crypto/tls"
55
"encoding/json"
6+
"fmt"
67
"github.com/stretchr/testify/require"
78
"mokapi/config/static"
89
"mokapi/server/cert"
910
"mokapi/smtp/smtptest"
1011
"mokapi/try"
1112
"net"
13+
"net/http"
1214
"os"
1315
"path"
1416
"testing"
@@ -25,6 +27,7 @@ func (suite *MailSuite) SetupSuite() {
2527
cfg.Certificates.Static = []static.Certificate{
2628
{Cert: "./mail/mail.mokapi.local.pem"},
2729
}
30+
cfg.Api.Search.Enabled = true
2831
suite.initCmd(cfg)
2932
}
3033

@@ -151,6 +154,21 @@ func (suite *MailSuite) TestSendMail() {
151154
//require.NoError(suite.T(), err)
152155
}
153156

157+
func (suite *MailSuite) TestSearch() {
158+
try.GetRequest(suite.T(), fmt.Sprintf("http://127.0.0.1:%v/api/search/query?q=Mokapi%%20MailServer", suite.cfg.Api.Port),
159+
nil,
160+
try.HasStatusCode(http.StatusOK),
161+
try.AssertBody(func(t *testing.T, body string) {
162+
var data map[string]any
163+
err := json.Unmarshal([]byte(body), &data)
164+
require.NoError(t, err)
165+
require.NotNil(t, data)
166+
167+
require.Equal(t, float64(5), data["total"])
168+
}),
169+
)
170+
}
171+
154172
func (suite *MailSuite) TestSendMail_OldFormat() {
155173
err := smtptest.SendMail("[email protected]",
156174

runtime/index.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,8 @@ func (a *App) Search(r search.Request) (search.Result, error) {
151151
item, err = events.GetSearchResult(fields, discriminators)
152152
case "kafka":
153153
item, err = getKafkaSearchResult(fields, discriminators)
154+
case "mail":
155+
item, err = getMailSearchResult(fields, discriminators)
154156
default:
155157
log.Errorf("unknown discriminator: %s", strings.Join(discriminators, "_"))
156158
continue

runtime/runtime.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ func New(cfg *static.Config) *App {
5252
Kafka: &KafkaStore{monitor: m, cfg: cfg, index: index, events: em},
5353
Mqtt: &MqttStore{monitor: m, cfg: cfg, sm: em},
5454
Ldap: &LdapStore{cfg: cfg, events: em},
55-
Mail: &MailStore{cfg: cfg, sm: em},
55+
Mail: &MailStore{cfg: cfg, sm: em, index: index},
5656
cfg: cfg,
5757
index: index,
5858
}

runtime/runtime_http_search.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -194,12 +194,12 @@ func getHttpSearchResult(fields map[string]string, discriminator []string) (sear
194194
}
195195

196196
func (s *HttpStore) removeFromIndex(cfg *openapi.Config) {
197-
_ = s.index.Delete(cfg.Info.Name)
197+
_ = s.index.Delete(fmt.Sprintf("http_%s", cfg.Info.Name))
198198

199199
for path, p := range cfg.Paths {
200-
_ = s.index.Delete(fmt.Sprintf("%s_%s", cfg.Info.Name, path))
200+
_ = s.index.Delete(fmt.Sprintf("http_%s_%s", cfg.Info.Name, path))
201201
for method := range p.Value.Operations() {
202-
_ = s.index.Delete(fmt.Sprintf("%s_%s_%s", cfg.Info.Name, path, method))
202+
_ = s.index.Delete(fmt.Sprintf("http_%s_%s_%s", cfg.Info.Name, path, method))
203203
}
204204
}
205205
}

runtime/runtime_http_search_test.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,19 @@ func TestIndex_Http(t *testing.T) {
4646
r.Results[0])
4747
},
4848
},
49+
{
50+
name: "config should be remove from index",
51+
test: func(t *testing.T, app *runtime.App) {
52+
cfg := openapitest.NewConfig("3.0", openapitest.WithInfo("foo", "", ""))
53+
app.AddHttp(toConfig(cfg))
54+
r, err := app.Search(search.Request{QueryText: "foo", Limit: 10})
55+
require.NoError(t, err)
56+
require.Len(t, r.Results, 1)
57+
app.RemoveHttp(toConfig(cfg))
58+
r, err = app.Search(search.Request{QueryText: "foo", Limit: 10})
59+
require.Len(t, r.Results, 0)
60+
},
61+
},
4962
{
5063
name: "Search by substring",
5164
test: func(t *testing.T, app *runtime.App) {

runtime/runtime_kafka.go

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,13 @@ func (s *KafkaStore) Remove(c *dynamic.Config) {
130130

131131
name := cfg.Info.Name
132132
ki := s.infos[name]
133-
ki.Remove(c)
133+
134+
if s.cfg.Api.Search.Enabled {
135+
s.removeFromIndex(ki.Config)
136+
}
137+
delete(ki.configs, c.Info.Url.String())
138+
ki.update()
139+
134140
if len(ki.configs) == 0 {
135141
s.m.RUnlock()
136142
s.m.Lock()
@@ -222,11 +228,6 @@ func IsAsyncApiConfig(c *dynamic.Config) (*asyncapi3.Config, bool) {
222228
return cfg, true
223229
}
224230

225-
func (c *KafkaInfo) Remove(cfg *dynamic.Config) {
226-
delete(c.configs, cfg.Info.Url.String())
227-
c.update()
228-
}
229-
230231
func getKafkaConfig(c *dynamic.Config) (*asyncapi3.Config, error) {
231232
if _, ok := c.Data.(*asyncapi3.Config); ok {
232233
return c.Data.(*asyncapi3.Config), nil

runtime/runtime_kafka_search.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,3 +171,11 @@ func getSchema(s *asyncapi3.SchemaRef) (*schema.IndexData, error) {
171171
return nil, fmt.Errorf("unsupported schema type: %T", v)
172172
}
173173
}
174+
175+
func (s *KafkaStore) removeFromIndex(cfg *asyncapi3.Config) {
176+
_ = s.index.Delete(fmt.Sprintf("kafka_%s", cfg.Info.Name))
177+
178+
for name := range cfg.Channels {
179+
_ = s.index.Delete(fmt.Sprintf("kafka_%s_%s", cfg.Info.Name, name))
180+
}
181+
}

runtime/runtime_kafka_search_test.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,23 @@ func TestIndex_Kafka(t *testing.T) {
4949
r.Results[0])
5050
},
5151
},
52+
{
53+
name: "config should be removed from index",
54+
test: func(t *testing.T, app *runtime.App) {
55+
cfg := asyncapi3test.NewConfig(asyncapi3test.WithInfo("Kafka Test server", "", ""))
56+
_, err := app.Kafka.Add(toConfig(cfg), enginetest.NewEngine())
57+
require.NoError(t, err)
58+
59+
r, err := app.Search(search.Request{QueryText: "Test", Limit: 10})
60+
require.NoError(t, err)
61+
require.Len(t, r.Results, 1)
62+
63+
app.Kafka.Remove(toConfig(cfg))
64+
r, err = app.Search(search.Request{QueryText: "Test", Limit: 10})
65+
require.NoError(t, err)
66+
require.Len(t, r.Results, 0)
67+
},
68+
},
5269
{
5370
name: "Search topic",
5471
test: func(t *testing.T, app *runtime.App) {

runtime/runtime_mail.go

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package runtime
22

33
import (
44
"context"
5+
"github.com/blevesearch/bleve/v2"
56
log "github.com/sirupsen/logrus"
67
"mokapi/config/dynamic"
78
"mokapi/config/static"
@@ -27,6 +28,7 @@ type MailStore struct {
2728
m sync.RWMutex
2829
cfg *static.Config
2930
sm *events.StoreManager
31+
index bleve.Index
3032
}
3133

3234
type MailInfo struct {
@@ -83,6 +85,10 @@ func (s *MailStore) Add(c *dynamic.Config) *MailInfo {
8385
mi.AddConfig(c)
8486
}
8587

88+
if s.cfg.Api.Search.Enabled {
89+
s.addToIndex(mi.Config)
90+
}
91+
8692
return mi
8793
}
8894

@@ -103,7 +109,13 @@ func (s *MailStore) Remove(c *dynamic.Config) {
103109
cfg := c.Data.(*mail.Config)
104110
name := cfg.Info.Name
105111
mi := s.infos[name]
106-
mi.Remove(c)
112+
if s.cfg.Api.Search.Enabled {
113+
s.removeFromIndex(mi.Config)
114+
}
115+
116+
delete(mi.configs, c.Info.Url.String())
117+
mi.update()
118+
107119
if len(mi.configs) == 0 {
108120
s.m.RUnlock()
109121
s.m.Lock()
@@ -177,11 +189,6 @@ func (c *MailInfo) Configs() []*dynamic.Config {
177189
return r
178190
}
179191

180-
func (c *MailInfo) Remove(cfg *dynamic.Config) {
181-
delete(c.configs, cfg.Info.Url.String())
182-
c.update()
183-
}
184-
185192
type mailHandler struct {
186193
monitor *monitor.Mail
187194
next *mail.Handler

runtime/runtime_mail_search.go

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
package runtime
2+
3+
import (
4+
"fmt"
5+
"mokapi/providers/mail"
6+
"mokapi/runtime/search"
7+
"path"
8+
"strings"
9+
)
10+
11+
type mailSearchIndexData struct {
12+
Type string `json:"type"`
13+
Discriminator string `json:"discriminator"`
14+
Api string `json:"api"`
15+
Name string `json:"name"`
16+
Version string `json:"version"`
17+
Description string `json:"description"`
18+
Contact *mail.Contact `json:"contact"`
19+
Servers []mailSearchIndexServer `json:"servers"`
20+
}
21+
22+
type mailSearchIndexServer struct {
23+
mail.Server
24+
Name string `json:"name"`
25+
}
26+
27+
type mailSearchIndexMailbox struct {
28+
Type string `json:"type"`
29+
Discriminator string `json:"discriminator"`
30+
Api string `json:"api"`
31+
Name string `json:"name"`
32+
Username string `json:"username"`
33+
Password string `json:"password"`
34+
Description string `json:"description"`
35+
Folders []mailSearchIndexFolder `json:"folders"`
36+
}
37+
38+
type mailSearchIndexFolder struct {
39+
Name string `json:"name"`
40+
Flags []string `json:"flags"`
41+
}
42+
43+
func (s *MailStore) addToIndex(cfg *mail.Config) {
44+
if cfg == nil || cfg.Info.Name == "" {
45+
return
46+
}
47+
48+
c := mailSearchIndexData{
49+
Type: "mail",
50+
Discriminator: "mail",
51+
Api: cfg.Info.Name,
52+
Name: cfg.Info.Name,
53+
Version: cfg.Info.Version,
54+
Description: cfg.Info.Description,
55+
Contact: cfg.Info.Contact,
56+
}
57+
for name, server := range cfg.Servers {
58+
if server == nil {
59+
continue
60+
}
61+
c.Servers = append(c.Servers, mailSearchIndexServer{
62+
Name: name,
63+
Server: *server,
64+
})
65+
}
66+
67+
add(s.index, fmt.Sprintf("mail_%s", cfg.Info.Name), c)
68+
69+
for name, mb := range cfg.Mailboxes {
70+
mbi := mailSearchIndexMailbox{
71+
Type: "mail",
72+
Discriminator: "mail_mailbox",
73+
Api: cfg.Info.Name,
74+
Name: name,
75+
Username: mb.Username,
76+
Password: mb.Password,
77+
Description: mb.Description,
78+
}
79+
for n, f := range mb.Folders {
80+
mbi.Folders = append(mbi.Folders, getMailboxFolders(f, n)...)
81+
}
82+
add(s.index, fmt.Sprintf("mail_%s_%s", cfg.Info.Name, name), mbi)
83+
}
84+
}
85+
86+
func getMailSearchResult(fields map[string]string, discriminator []string) (search.ResultItem, error) {
87+
result := search.ResultItem{
88+
Type: "MAIL",
89+
}
90+
91+
if len(discriminator) == 1 {
92+
result.Title = fields["name"]
93+
result.Params = map[string]string{
94+
"type": strings.ToLower(result.Type),
95+
"service": result.Title,
96+
}
97+
return result, nil
98+
}
99+
100+
switch discriminator[1] {
101+
case "mailbox":
102+
result.Domain = fields["api"]
103+
result.Title = fields["name"]
104+
result.Params = map[string]string{
105+
"type": strings.ToLower(result.Type),
106+
"service": result.Domain,
107+
"mailbox": fields["name"],
108+
}
109+
default:
110+
return result, fmt.Errorf("unsupported search result: %s", strings.Join(discriminator, "_"))
111+
}
112+
return result, nil
113+
}
114+
115+
func (s *MailStore) removeFromIndex(cfg *mail.Config) {
116+
_ = s.index.Delete(fmt.Sprintf("mail_%s", cfg.Info.Name))
117+
}
118+
119+
func getMailboxFolders(f *mail.FolderConfig, name string) []mailSearchIndexFolder {
120+
var result []mailSearchIndexFolder
121+
result = append(result, mailSearchIndexFolder{
122+
Name: name,
123+
Flags: f.Flags,
124+
})
125+
126+
for childName, child := range f.Folders {
127+
children := getMailboxFolders(child, path.Join(name, childName))
128+
result = append(result, children...)
129+
}
130+
131+
return result
132+
}

0 commit comments

Comments
 (0)