Improve autofill status feedback
This commit is contained in:
+30
-11
@@ -28,10 +28,29 @@ type File struct {
|
||||
Entries []Entry `json:"entries"`
|
||||
}
|
||||
|
||||
type MatchStatus string
|
||||
|
||||
const (
|
||||
MatchStatusNone MatchStatus = ""
|
||||
MatchStatusFound MatchStatus = "found"
|
||||
MatchStatusAmbiguous MatchStatus = "ambiguous"
|
||||
MatchStatusMissing MatchStatus = "missing"
|
||||
)
|
||||
|
||||
type MatchResult struct {
|
||||
Status MatchStatus `json:"status"`
|
||||
Entry Entry `json:"entry,omitempty"`
|
||||
}
|
||||
|
||||
func Match(cache File, webURL string) (Entry, bool) {
|
||||
result := Resolve(cache, webURL)
|
||||
return result.Entry, result.Status == MatchStatusFound
|
||||
}
|
||||
|
||||
func Resolve(cache File, webURL string) MatchResult {
|
||||
target := normalizeURL(webURL)
|
||||
if target.host == "" {
|
||||
return Entry{}, false
|
||||
return MatchResult{Status: MatchStatusMissing}
|
||||
}
|
||||
|
||||
exactHost := make([]Entry, 0)
|
||||
@@ -46,8 +65,8 @@ func Match(cache File, webURL string) (Entry, bool) {
|
||||
}
|
||||
}
|
||||
|
||||
if matched, ok := chooseEntry(target, exactHost); ok {
|
||||
return matched, true
|
||||
if result := chooseEntry(target, exactHost); result.Status != MatchStatusMissing {
|
||||
return result
|
||||
}
|
||||
return chooseEntry(target, parentHost)
|
||||
}
|
||||
@@ -152,12 +171,12 @@ func cleanPath(path string) string {
|
||||
return path
|
||||
}
|
||||
|
||||
func chooseEntry(target normalizedTarget, entries []Entry) (Entry, bool) {
|
||||
func chooseEntry(target normalizedTarget, entries []Entry) MatchResult {
|
||||
switch len(entries) {
|
||||
case 0:
|
||||
return Entry{}, false
|
||||
return MatchResult{Status: MatchStatusMissing}
|
||||
case 1:
|
||||
return entries[0], true
|
||||
return MatchResult{Status: MatchStatusFound, Entry: entries[0]}
|
||||
}
|
||||
|
||||
exact := make([]Entry, 0)
|
||||
@@ -181,18 +200,18 @@ func chooseEntry(target normalizedTarget, entries []Entry) (Entry, bool) {
|
||||
}
|
||||
}
|
||||
if len(exact) == 1 {
|
||||
return exact[0], true
|
||||
return MatchResult{Status: MatchStatusFound, Entry: exact[0]}
|
||||
}
|
||||
if len(exact) > 1 {
|
||||
return Entry{}, false
|
||||
return MatchResult{Status: MatchStatusAmbiguous}
|
||||
}
|
||||
if len(bestPrefix) == 1 {
|
||||
return bestPrefix[0], true
|
||||
return MatchResult{Status: MatchStatusFound, Entry: bestPrefix[0]}
|
||||
}
|
||||
if len(bestPrefix) == 0 {
|
||||
return Entry{}, false
|
||||
return MatchResult{Status: MatchStatusMissing}
|
||||
}
|
||||
return Entry{}, false
|
||||
return MatchResult{Status: MatchStatusAmbiguous}
|
||||
}
|
||||
|
||||
func collectTargets(item vault.Entry) []string {
|
||||
|
||||
@@ -156,6 +156,49 @@ func TestMatchRejectsAmbiguousSharedHost(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveReportsFoundAmbiguousAndMissingStatuses(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cache := File{
|
||||
Entries: []Entry{
|
||||
{
|
||||
ID: "one",
|
||||
Title: "Admin Login",
|
||||
Username: "admin",
|
||||
Password: "secret1",
|
||||
URL: "https://example.com/admin",
|
||||
Host: "example.com",
|
||||
},
|
||||
{
|
||||
ID: "two",
|
||||
Title: "Shared Login A",
|
||||
Username: "shared-a",
|
||||
Password: "secret2",
|
||||
URL: "https://shared.example.com",
|
||||
Host: "shared.example.com",
|
||||
},
|
||||
{
|
||||
ID: "three",
|
||||
Title: "Shared Login B",
|
||||
Username: "shared-b",
|
||||
Password: "secret3",
|
||||
URL: "https://shared.example.com",
|
||||
Host: "shared.example.com",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if got := Resolve(cache, "https://example.com/admin/login"); got.Status != MatchStatusFound || got.Entry.ID != "one" {
|
||||
t.Fatalf("Resolve(found) = %#v, want found entry one", got)
|
||||
}
|
||||
if got := Resolve(cache, "https://shared.example.com"); got.Status != MatchStatusAmbiguous {
|
||||
t.Fatalf("Resolve(ambiguous) = %#v, want ambiguous", got)
|
||||
}
|
||||
if got := Resolve(cache, "https://nowhere.invalid"); got.Status != MatchStatusMissing {
|
||||
t.Fatalf("Resolve(missing) = %#v, want missing", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMatchChoosesLongestPathPrefix(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user