From be3326b5fcd8303fb781567b90d8779d3f129594 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Larivi=C3=A8re?= Date: Wed, 25 Feb 2026 16:44:59 -0500 Subject: [PATCH] feat(entry_folder): add GetByName and move GetByNameOptions to shared scope --- VERSION | 2 +- entries.go | 6 +++++ entry_credential.go | 6 ----- entry_folder.go | 22 +++++++++++++++++ entry_folder_test.go | 56 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 85 insertions(+), 7 deletions(-) diff --git a/VERSION b/VERSION index 503a21d..1cf0537 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.18.2 +0.19.0 diff --git a/entries.go b/entries.go index dbf6579..62f09ac 100644 --- a/entries.go +++ b/entries.go @@ -172,6 +172,12 @@ type entryListRawResponse struct { TotalPage int `json:"totalPage"` } +// GetByNameOptions contains optional filters for GetByName. +// A nil field means the filter is not applied. +type GetByNameOptions struct { + Path *string +} + // getEntriesOptions contains optional filters for listing entries. // A nil value means the filter is not applied. type GetEntriesOptions struct { diff --git a/entry_credential.go b/entry_credential.go index 954cb6b..b7713c5 100644 --- a/entry_credential.go +++ b/entry_credential.go @@ -481,12 +481,6 @@ func (c *EntryCredentialService) GetEntriesWithContext(ctx context.Context, vaul return credentials, nil } -// GetByNameOptions contains optional filters for GetByName. -// A nil field means the filter is not applied. -type GetByNameOptions struct { - Path *string -} - // GetByName retrieves a single credential entry by name, subType, and optional filters. // Returns ErrEntryNotFound if no match exists. // Returns ErrMultipleEntriesFound if more than one match exists. diff --git a/entry_folder.go b/entry_folder.go index 5c084a6..180ecc0 100644 --- a/entry_folder.go +++ b/entry_folder.go @@ -266,6 +266,28 @@ func (c *EntryFolderService) DeleteByIdWithContext(ctx context.Context, vaultId return nil } +// GetByName retrieves a single folder entry by name and optional filters. +// Returns ErrEntryNotFound if no match exists. +func (c *EntryFolderService) GetByName(vaultId, name string, opts GetByNameOptions) (Entry, error) { + return c.GetByNameWithContext(context.Background(), vaultId, name, opts) +} + +// GetByNameWithContext retrieves a single folder entry by name and optional filters. +// Returns ErrEntryNotFound if no match exists. +// The provided context can be used to cancel the request. +func (c *EntryFolderService) GetByNameWithContext(ctx context.Context, vaultId, name string, opts GetByNameOptions) (Entry, error) { + entries, err := c.GetEntriesWithContext(ctx, vaultId, GetEntriesOptions{Name: &name, Path: opts.Path}) + if err != nil { + return Entry{}, err + } + + if len(entries) == 0 { + return Entry{}, ErrEntryNotFound + } + + return c.GetByIdWithContext(ctx, vaultId, entries[0].Id) +} + // GetEntries returns a list of folder entries from a vault with optional filters. // Note: The API does not support filtering by entry type, so all entries are fetched and filtered client-side. func (c *EntryFolderService) GetEntries(vaultId string, opts GetEntriesOptions) ([]Entry, error) { diff --git a/entry_folder_test.go b/entry_folder_test.go index 2967776..5c2e37c 100644 --- a/entry_folder_test.go +++ b/entry_folder_test.go @@ -196,6 +196,62 @@ func Test_NestedFolders(t *testing.T) { require.NoError(t, err, "Failed to delete parent folder") } +func Test_GetFolderByName(t *testing.T) { + vault := createTestVault(t, "folder-getbyname") + testPath := "go-dvls\\folder-getbyname" + + entry := Entry{ + VaultId: vault.Id, + Name: "MyFolder", + Path: testPath, + Type: EntryFolderType, + SubType: EntryFolderSubTypeFolder, + Data: &EntryFolderData{Domain: "test.local", Username: "testuser"}, + } + + id, err := testClient.Entries.Folder.New(entry) + require.NoError(t, err, "Failed to create folder entry") + t.Cleanup(func() { + _ = testClient.Entries.Folder.DeleteById(vault.Id, id) + }) + + // GetByName with name only + got, err := testClient.Entries.Folder.GetByName(vault.Id, "MyFolder", GetByNameOptions{Path: &testPath}) + require.NoError(t, err) + assert.Equal(t, id, got.Id) + assert.Equal(t, "MyFolder", got.Name) + assert.Equal(t, testPath+`\MyFolder`, got.Path) + + // GetByName with non-existent name returns ErrEntryNotFound + _, err = testClient.Entries.Folder.GetByName(vault.Id, "NonExistentFolder", GetByNameOptions{Path: &testPath}) + assert.ErrorIs(t, err, ErrEntryNotFound) + + // GetByName without path filter also finds the entry + got, err = testClient.Entries.Folder.GetByName(vault.Id, "MyFolder", GetByNameOptions{}) + require.NoError(t, err) + assert.Equal(t, id, got.Id) + + // Root-level folder: path returned by API is the folder name itself + rootEntry := Entry{ + VaultId: vault.Id, + Name: "MyRootFolder", + Path: "", + Type: EntryFolderType, + SubType: EntryFolderSubTypeFolder, + Data: &EntryFolderData{}, + } + rootId, err := testClient.Entries.Folder.New(rootEntry) + require.NoError(t, err) + t.Cleanup(func() { + _ = testClient.Entries.Folder.DeleteById(vault.Id, rootId) + }) + + root, err := testClient.Entries.Folder.GetByName(vault.Id, "MyRootFolder", GetByNameOptions{}) + require.NoError(t, err) + assert.Equal(t, rootId, root.Id) + assert.Equal(t, "MyRootFolder", root.Path) +} + func Test_GetFolderEntries_Filters(t *testing.T) { vault := createTestVault(t, "folder-getentries") testPath := "go-dvls\\folder-getentries"