Skip to content

Commit 6a5e8a5

Browse files
committed
Add routes to get aliases list
1 parent 4b40cb9 commit 6a5e8a5

5 files changed

Lines changed: 123 additions & 32 deletions

File tree

internal/routes/alias.go

Lines changed: 70 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,14 @@ import (
99
"github.com/swaggest/usecase/status"
1010
)
1111

12+
type aliasLister interface {
13+
ListAliases() []spellchecker.ListItem
14+
}
15+
16+
type aliasGetter interface {
17+
GetCodeByAlias(alias string) (string, error)
18+
}
19+
1220
type aliasSetter interface {
1321
SetAlias(alias string, code string) error
1422
}
@@ -17,15 +25,73 @@ type aliasDeleter interface {
1725
DeleteAlias(alias string) error
1826
}
1927

28+
type AliasListResponse struct {
29+
Items []ListItem `json:"items"`
30+
}
31+
32+
func aliasList(registry aliasLister) usecase.Interactor {
33+
u := usecase.NewInteractor(func(ctx context.Context, input Empty, output *AliasListResponse) error {
34+
items := registry.ListAliases()
35+
36+
result := make([]ListItem, 0, len(items))
37+
38+
for _, item := range items {
39+
result = append(result, ListItem{
40+
Code: item.Code,
41+
Aliases: item.Aliases,
42+
})
43+
}
44+
45+
output.Items = result
46+
47+
return nil
48+
})
49+
50+
u.SetTitle("List all aliases")
51+
u.SetDescription("With their aliasesdictionaries")
52+
u.SetExpectedErrors(status.Internal, status.AlreadyExists, status.InvalidArgument)
53+
54+
return u
55+
}
56+
57+
type AliasGetRequest struct {
58+
Alias string `path:"alias" minLength:"1" description:"Alias to set to the dictionary"`
59+
}
60+
61+
type AliasGetResponse struct {
62+
Dictionary string `json:"dictionary"`
63+
}
64+
65+
func aliasGet(registry aliasGetter) usecase.Interactor {
66+
u := usecase.NewInteractor(func(ctx context.Context, input AliasGetRequest, output *AliasGetResponse) error {
67+
code, err := registry.GetCodeByAlias(input.Alias)
68+
if errors.Is(spellchecker.ErrNotFound, err) {
69+
return status.Wrap(err, status.NotFound)
70+
} else if err != nil {
71+
return status.Wrap(err, status.Internal)
72+
}
73+
74+
output.Dictionary = code
75+
76+
return nil
77+
})
78+
79+
u.SetTitle("Get dictionary alias")
80+
u.SetDescription("Returns dictionary code assigned to the provided alias")
81+
u.SetExpectedErrors(status.Internal, status.NotFound)
82+
83+
return u
84+
}
85+
2086
type AliasSetRequest struct {
2187
Alias string `path:"alias" minLength:"1" description:"Alias to set to the dictionary"`
2288
Dictionary string `json:"dictionary"`
2389
}
2490

2591
func aliasSet(registry aliasSetter) usecase.Interactor {
26-
u := usecase.NewInteractor(func(ctx context.Context, input AliasSetRequest, output *EmptyResponse) error {
92+
u := usecase.NewInteractor(func(ctx context.Context, input AliasSetRequest, output *Empty) error {
2793
err := registry.SetAlias(input.Alias, input.Dictionary)
28-
if errors.Is(spellchecker.ErrNotFound, err) {
94+
if errors.Is(spellchecker.ErrAliasNotFound, err) {
2995
return status.Wrap(err, status.NotFound)
3096
} else if err != nil {
3197
return status.Wrap(err, status.Internal)
@@ -46,9 +112,9 @@ type AliasDeleteRequest struct {
46112
}
47113

48114
func aliasDelete(registry aliasDeleter) usecase.Interactor {
49-
u := usecase.NewInteractor(func(ctx context.Context, input AliasDeleteRequest, output *EmptyResponse) error {
115+
u := usecase.NewInteractor(func(ctx context.Context, input AliasDeleteRequest, output *Empty) error {
50116
err := registry.DeleteAlias(input.Alias)
51-
if errors.Is(spellchecker.ErrNotFound, err) {
117+
if errors.Is(spellchecker.ErrAliasNotFound, err) {
52118
return status.Wrap(err, status.NotFound)
53119
} else if err != nil {
54120
return status.Wrap(err, status.Internal)

internal/routes/dictionary.go

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@ import (
99
"github.com/swaggest/usecase/status"
1010
)
1111

12-
type DictionaryListRequest struct{}
13-
1412
type DictionaryListResponse struct {
1513
Items []ListItem `json:"items"`
1614
}
@@ -21,7 +19,7 @@ type ListItem struct {
2119
}
2220

2321
func dictionaryList(registry *spellchecker.Registry) usecase.Interactor {
24-
u := usecase.NewInteractor(func(ctx context.Context, input DictionaryListRequest, output *DictionaryListResponse) error {
22+
u := usecase.NewInteractor(func(ctx context.Context, input Empty, output *DictionaryListResponse) error {
2523
items := registry.List()
2624

2725
result := make([]ListItem, 0, len(items))
@@ -53,7 +51,7 @@ type DictionaryCreateRequest struct {
5351
}
5452

5553
func dictionaryCreate(registry *spellchecker.Registry) usecase.Interactor {
56-
u := usecase.NewInteractor(func(ctx context.Context, input DictionaryCreateRequest, output *EmptyResponse) error {
54+
u := usecase.NewInteractor(func(ctx context.Context, input DictionaryCreateRequest, output *Empty) error {
5755
_, err := registry.Add(input.Code, spellchecker.Options{
5856
Alphabet: input.Alphabet,
5957
MaxErrors: input.MaxErrors,
@@ -79,7 +77,7 @@ type DictionaryDeleteRequest struct {
7977
}
8078

8179
func dictionaryDelete(registry *spellchecker.Registry) usecase.Interactor {
82-
u := usecase.NewInteractor(func(ctx context.Context, input DictionaryDeleteRequest, output *EmptyResponse) error {
80+
u := usecase.NewInteractor(func(ctx context.Context, input DictionaryDeleteRequest, output *Empty) error {
8381
err := registry.Delete(input.Code)
8482
if errors.Is(spellchecker.ErrNotFound, err) {
8583
return status.Wrap(err, status.NotFound)
@@ -102,7 +100,7 @@ type DictionarySaveRequest struct {
102100
}
103101

104102
func dictionarySave(registry *spellchecker.Registry) usecase.Interactor {
105-
u := usecase.NewInteractor(func(ctx context.Context, input DictionarySaveRequest, output *EmptyResponse) error {
103+
u := usecase.NewInteractor(func(ctx context.Context, input DictionarySaveRequest, output *Empty) error {
106104
err := registry.Save(input.Code)
107105
if errors.Is(spellchecker.ErrNotFound, err) {
108106
return status.Wrap(err, status.NotFound)

internal/routes/routes.go

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import (
99
"github.com/swaggest/rest/nethttp"
1010
)
1111

12-
type EmptyResponse struct{}
12+
type Empty struct{}
1313

1414
func Routes(registry *spellchecker.Registry, splitter *regexp.Regexp) func(r chi.Router) {
1515
return func(r chi.Router) {
@@ -43,20 +43,24 @@ func dictionaryRoutes(registry *spellchecker.Registry, splitter *regexp.Regexp)
4343
r.Method(http.MethodPost, "/{code}/fix", nethttp.NewHandler(
4444
dictionaryFix(registry, splitter),
4545
))
46-
47-
r.Method(http.MethodPost, "/{code}/alias", nethttp.NewHandler(
48-
aliasSet(registry),
49-
))
5046
}
5147
}
5248

5349
func aliasRoutes(registry *spellchecker.Registry) func(r chi.Router) {
5450
return func(r chi.Router) {
55-
r.Method(http.MethodPut, "/alias/{alias}", nethttp.NewHandler(
51+
r.Method(http.MethodGet, "/", nethttp.NewHandler(
52+
aliasList(registry),
53+
))
54+
55+
r.Method(http.MethodGet, "/{alias}", nethttp.NewHandler(
56+
aliasGet(registry),
57+
))
58+
59+
r.Method(http.MethodPut, "/{alias}", nethttp.NewHandler(
5660
aliasSet(registry),
5761
))
5862

59-
r.Method(http.MethodDelete, "/alias/{alias}", nethttp.NewHandler(
63+
r.Method(http.MethodDelete, "/{alias}", nethttp.NewHandler(
6064
aliasDelete(registry),
6165
))
6266
}

internal/spellchecker/metadata.go

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
11
package spellchecker
22

3-
import "slices"
3+
import (
4+
"fmt"
5+
"slices"
6+
)
47

58
const metadataFile = "metadata"
69

10+
var (
11+
ErrAliasNotFound = fmt.Errorf("alias not found")
12+
)
13+
714
type Metadata struct {
815
Aliases map[string]string `json:"aliases"` // alias => dict
916
InvertedAliases map[string][]string `json:"invertedAliases"` // dict => aliases
@@ -16,20 +23,20 @@ func newMetadata() Metadata {
1623
}
1724
}
1825

19-
func (r *Registry) DeleteAlias(alias string) error {
20-
r.mu.Lock()
21-
defer r.mu.Unlock()
26+
func (r *Registry) ListAliases() []ListItem {
27+
return r.List()
28+
}
2229

23-
err := r.doDeleteAlias(alias)
24-
if err != nil {
25-
return err
26-
}
30+
func (r *Registry) GetCodeByAlias(alias string) (string, error) {
31+
r.mu.RLock()
32+
defer r.mu.RUnlock()
2733

28-
if err := r.doSaveMetadata(); err != nil {
29-
return err
34+
v, ok := r.metadata.Aliases[alias]
35+
if !ok {
36+
return "", ErrAliasNotFound
3037
}
3138

32-
return nil
39+
return v, nil
3340
}
3441

3542
func (r *Registry) SetAlias(alias string, to string) error {
@@ -38,7 +45,7 @@ func (r *Registry) SetAlias(alias string, to string) error {
3845

3946
_, ok := r.items[to]
4047
if !ok {
41-
return ErrNotFound
48+
return ErrAliasNotFound
4249
}
4350

4451
existing, ok := r.metadata.Aliases[alias]
@@ -60,10 +67,26 @@ func (r *Registry) SetAlias(alias string, to string) error {
6067
return nil
6168
}
6269

70+
func (r *Registry) DeleteAlias(alias string) error {
71+
r.mu.Lock()
72+
defer r.mu.Unlock()
73+
74+
err := r.doDeleteAlias(alias)
75+
if err != nil {
76+
return err
77+
}
78+
79+
if err := r.doSaveMetadata(); err != nil {
80+
return err
81+
}
82+
83+
return nil
84+
}
85+
6386
func (r *Registry) doDeleteAlias(alias string) error {
6487
existing, ok := r.metadata.Aliases[alias]
6588
if !ok {
66-
return ErrNotFound
89+
return ErrAliasNotFound
6790
}
6891

6992
delete(r.metadata.Aliases, alias)

internal/spellchecker/metadata_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ func Test_Registry_SetAlias(t *testing.T) {
1515
require.NoError(t, err)
1616

1717
err = r.SetAlias("code2", "code")
18-
require.ErrorIs(t, err, ErrNotFound)
18+
require.ErrorIs(t, err, ErrAliasNotFound)
1919
})
2020

2121
t.Run("success", func(t *testing.T) {
@@ -82,7 +82,7 @@ func Test_Registry_DeleteAlias(t *testing.T) {
8282
require.NoError(t, err)
8383

8484
err = r.DeleteAlias("code")
85-
require.ErrorIs(t, err, ErrNotFound)
85+
require.ErrorIs(t, err, ErrAliasNotFound)
8686
})
8787

8888
t.Run("success", func(t *testing.T) {

0 commit comments

Comments
 (0)