Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1876,6 +1876,21 @@ if err == nil && exists {
// IP is in the list
}

// Add text items to a text list
// The list must be of type "texts". Duplicates are automatically ignored.
err := descopeClient.Management.List().AddTexts(context.Background(), "list-id", []string{"item1", "item2"})

// Remove text items from a text list
// The list must be of type "texts". Non-existent texts are silently ignored.
err := descopeClient.Management.List().RemoveTexts(context.Background(), "list-id", []string{"item1"})

// Check if a text exists in a text list
// The list must be of type "texts".
exists, err := descopeClient.Management.List().CheckText(context.Background(), "list-id", "item1")
if err == nil && exists {
// Text is in the list
}

// Clear all data from a list
// The list metadata (name, description, type) is preserved.
err := descopeClient.Management.List().Clear(context.Background(), "list-id")
Expand Down
40 changes: 29 additions & 11 deletions descope/api/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,9 @@ var (
listAddIPs: "mgmt/list/ip/add",
listRemoveIPs: "mgmt/list/ip/remove",
listCheckIP: "mgmt/list/ip/check",
listAddTexts: "mgmt/list/text/add",
listRemoveTexts: "mgmt/list/text/remove",
listCheckText: "mgmt/list/text/check",
listClear: "mgmt/list/clear",
},
logout: "auth/logout",
Expand Down Expand Up @@ -559,17 +562,20 @@ type mgmtEndpoints struct {
descoperDelete string
descoperSearch string

listCreate string
listUpdate string
listDelete string
listLoad string
listLoadByName string
listLoadAll string
listImport string
listAddIPs string
listRemoveIPs string
listCheckIP string
listClear string
listCreate string
listUpdate string
listDelete string
listLoad string
listLoadByName string
listLoadAll string
listImport string
listAddIPs string
listRemoveIPs string
listCheckIP string
listAddTexts string
listRemoveTexts string
listCheckText string
listClear string
}

func (e *endpoints) SignInOTP() string {
Expand Down Expand Up @@ -1547,6 +1553,18 @@ func (e *endpoints) ManagementListCheckIP() string {
return path.Join(e.version, e.mgmt.listCheckIP)
}

func (e *endpoints) ManagementListAddTexts() string {
return path.Join(e.version, e.mgmt.listAddTexts)
}

func (e *endpoints) ManagementListRemoveTexts() string {
return path.Join(e.version, e.mgmt.listRemoveTexts)
}

func (e *endpoints) ManagementListCheckText() string {
return path.Join(e.version, e.mgmt.listCheckText)
}

func (e *endpoints) ManagementListClear() string {
return path.Join(e.version, e.mgmt.listClear)
}
Expand Down
55 changes: 55 additions & 0 deletions descope/internal/mgmt/lists.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,61 @@ func (l *lists) CheckIP(ctx context.Context, id string, ip string) (bool, error)
return checkRes.Exists, nil
}

func (l *lists) AddTexts(ctx context.Context, id string, texts []string) error {
if id == "" {
return utils.NewInvalidArgumentError("id")
}
if texts == nil {
return utils.NewInvalidArgumentError("texts")
}
req := &descope.ListTextsRequest{
ID: id,
Texts: texts,
}
_, err := l.client.DoPostRequest(ctx, api.Routes.ManagementListAddTexts(), req, nil, "")
return err
}

func (l *lists) RemoveTexts(ctx context.Context, id string, texts []string) error {
if id == "" {
return utils.NewInvalidArgumentError("id")
}
if texts == nil {
return utils.NewInvalidArgumentError("texts")
}
req := &descope.ListTextsRequest{
ID: id,
Texts: texts,
}
_, err := l.client.DoPostRequest(ctx, api.Routes.ManagementListRemoveTexts(), req, nil, "")
return err
}

func (l *lists) CheckText(ctx context.Context, id string, text string) (bool, error) {
if id == "" {
return false, utils.NewInvalidArgumentError("id")
}
if text == "" {
return false, utils.NewInvalidArgumentError("text")
}
req := &descope.ListCheckTextRequest{
ID: id,
Text: text,
}
res, err := l.client.DoPostRequest(ctx, api.Routes.ManagementListCheckText(), req, nil, "")
if err != nil {
return false, err
}

var checkRes struct {
Exists bool `json:"exists"`
}
if err := utils.Unmarshal([]byte(res.BodyStr), &checkRes); err != nil {
return false, err
}
return checkRes.Exists, nil
}

func (l *lists) Clear(ctx context.Context, id string) error {
if id == "" {
return utils.NewInvalidArgumentError("id")
Expand Down
105 changes: 105 additions & 0 deletions descope/internal/mgmt/lists_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -488,3 +488,108 @@ func TestListClearEmptyID(t *testing.T) {
require.Error(t, err)
require.Contains(t, err.Error(), "id")
}

func TestListAddTextsSuccess(t *testing.T) {
mgmt := newTestMgmt(nil, helpers.DoOkWithBody(func(r *http.Request) {
require.Equal(t, r.Header.Get("Authorization"), "Bearer a:key")
req := map[string]any{}
require.NoError(t, helpers.ReadBody(r, &req))
require.Equal(t, "list-123", req["id"])
texts := req["texts"].([]any)
require.Len(t, texts, 2)
require.Equal(t, "item1", texts[0])
require.Equal(t, "item2", texts[1])
}, nil))

err := mgmt.List().AddTexts(context.Background(), "list-123", []string{"item1", "item2"})
require.NoError(t, err)
}

func TestListRemoveTextsSuccess(t *testing.T) {
mgmt := newTestMgmt(nil, helpers.DoOkWithBody(func(r *http.Request) {
require.Equal(t, r.Header.Get("Authorization"), "Bearer a:key")
req := map[string]any{}
require.NoError(t, helpers.ReadBody(r, &req))
require.Equal(t, "list-123", req["id"])
texts := req["texts"].([]any)
require.Len(t, texts, 1)
require.Equal(t, "item1", texts[0])
}, nil))

err := mgmt.List().RemoveTexts(context.Background(), "list-123", []string{"item1"})
require.NoError(t, err)
}

func TestListCheckTextSuccess(t *testing.T) {
response := map[string]any{
"exists": true,
}
mgmt := newTestMgmt(nil, helpers.DoOkWithBody(func(r *http.Request) {
require.Equal(t, r.Header.Get("Authorization"), "Bearer a:key")
req := map[string]any{}
require.NoError(t, helpers.ReadBody(r, &req))
require.Equal(t, "list-123", req["id"])
require.Equal(t, "item1", req["text"])
}, response))

exists, err := mgmt.List().CheckText(context.Background(), "list-123", "item1")
require.NoError(t, err)
require.True(t, exists)
}

func TestListCheckTextNotExists(t *testing.T) {
response := map[string]any{
"exists": false,
}
mgmt := newTestMgmt(nil, helpers.DoOkWithBody(func(r *http.Request) {
require.Equal(t, r.Header.Get("Authorization"), "Bearer a:key")
}, response))

exists, err := mgmt.List().CheckText(context.Background(), "list-123", "item999")
require.NoError(t, err)
require.False(t, exists)
}

func TestListAddTextsEmptyID(t *testing.T) {
mgmt := newTestMgmt(nil, helpers.DoOk(nil))
err := mgmt.List().AddTexts(context.Background(), "", []string{"item1"})
require.Error(t, err)
require.Contains(t, err.Error(), "id")
}

func TestListAddTextsNilTexts(t *testing.T) {
mgmt := newTestMgmt(nil, helpers.DoOk(nil))
err := mgmt.List().AddTexts(context.Background(), "list-123", nil)
require.Error(t, err)
require.Contains(t, err.Error(), "texts")
}

func TestListRemoveTextsEmptyID(t *testing.T) {
mgmt := newTestMgmt(nil, helpers.DoOk(nil))
err := mgmt.List().RemoveTexts(context.Background(), "", []string{"item1"})
require.Error(t, err)
require.Contains(t, err.Error(), "id")
}

func TestListRemoveTextsNilTexts(t *testing.T) {
mgmt := newTestMgmt(nil, helpers.DoOk(nil))
err := mgmt.List().RemoveTexts(context.Background(), "list-123", nil)
require.Error(t, err)
require.Contains(t, err.Error(), "texts")
}

func TestListCheckTextEmptyID(t *testing.T) {
mgmt := newTestMgmt(nil, helpers.DoOk(nil))
exists, err := mgmt.List().CheckText(context.Background(), "", "item1")
require.Error(t, err)
require.False(t, exists)
require.Contains(t, err.Error(), "id")
}

func TestListCheckTextEmptyText(t *testing.T) {
mgmt := newTestMgmt(nil, helpers.DoOk(nil))
exists, err := mgmt.List().CheckText(context.Background(), "list-123", "")
require.Error(t, err)
require.False(t, exists)
require.Contains(t, err.Error(), "text")
}
17 changes: 17 additions & 0 deletions descope/sdk/mgmt.go
Original file line number Diff line number Diff line change
Expand Up @@ -1170,6 +1170,23 @@ type List interface {
// Returns true if the IP exists in the list, false otherwise.
CheckIP(ctx context.Context, id string, ip string) (bool, error)

// Add text items to a text list.
//
// The list must be of type "texts". Duplicate texts are automatically ignored.
// The order of existing texts is preserved and new texts are appended.
AddTexts(ctx context.Context, id string, texts []string) error

// Remove text items from a text list.
//
// The list must be of type "texts". Non-existent texts are silently ignored.
RemoveTexts(ctx context.Context, id string, texts []string) error

// Check if a text exists in a text list.
//
// The list must be of type "texts".
// Returns true if the text exists in the list, false otherwise.
CheckText(ctx context.Context, id string, text string) (bool, error)

// Clear all data from a list.
//
// The list metadata (name, description, type) is preserved.
Expand Down
31 changes: 31 additions & 0 deletions descope/tests/mocks/mgmt/managementmock.go
Original file line number Diff line number Diff line change
Expand Up @@ -2234,6 +2234,16 @@ type MockList struct {
CheckIPResponse bool
CheckIPError error

AddTextsAssert func(id string, texts []string)
AddTextsError error

RemoveTextsAssert func(id string, texts []string)
RemoveTextsError error

CheckTextAssert func(id string, text string)
CheckTextResponse bool
CheckTextError error

ClearAssert func(id string)
ClearError error
}
Expand Down Expand Up @@ -2305,6 +2315,27 @@ func (m *MockList) CheckIP(_ context.Context, id string, ip string) (bool, error
return m.CheckIPResponse, m.CheckIPError
}

func (m *MockList) AddTexts(_ context.Context, id string, texts []string) error {
if m.AddTextsAssert != nil {
m.AddTextsAssert(id, texts)
}
return m.AddTextsError
}

func (m *MockList) RemoveTexts(_ context.Context, id string, texts []string) error {
if m.RemoveTextsAssert != nil {
m.RemoveTextsAssert(id, texts)
}
return m.RemoveTextsError
}

func (m *MockList) CheckText(_ context.Context, id string, text string) (bool, error) {
if m.CheckTextAssert != nil {
m.CheckTextAssert(id, text)
}
return m.CheckTextResponse, m.CheckTextError
}

func (m *MockList) Clear(_ context.Context, id string) error {
if m.ClearAssert != nil {
m.ClearAssert(id)
Expand Down
10 changes: 10 additions & 0 deletions descope/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -1609,3 +1609,13 @@ type ListCheckIPRequest struct {
ID string `json:"id"`
IP string `json:"ip"`
}

type ListTextsRequest struct {
ID string `json:"id"`
Texts []string `json:"texts"`
}

type ListCheckTextRequest struct {
ID string `json:"id"`
Text string `json:"text"`
}