feat: accept browser file captures
This commit is contained in:
parent
2cbf542b13
commit
df4538532f
|
|
@ -47,6 +47,7 @@ type CapturePayload struct {
|
||||||
Page CapturePage `json:"page"`
|
Page CapturePage `json:"page"`
|
||||||
Selection *CaptureSelection `json:"selection,omitempty"`
|
Selection *CaptureSelection `json:"selection,omitempty"`
|
||||||
Link *CaptureLink `json:"link,omitempty"`
|
Link *CaptureLink `json:"link,omitempty"`
|
||||||
|
File *CaptureFile `json:"file,omitempty"`
|
||||||
Browser *CaptureBrowser `json:"browser,omitempty"`
|
Browser *CaptureBrowser `json:"browser,omitempty"`
|
||||||
Context interface{} `json:"context,omitempty"`
|
Context interface{} `json:"context,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
@ -66,6 +67,13 @@ type CaptureLink struct {
|
||||||
Text string `json:"text"`
|
Text string `json:"text"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CaptureFile struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Mime string `json:"mime"`
|
||||||
|
Size int64 `json:"size"`
|
||||||
|
Text string `json:"text"`
|
||||||
|
}
|
||||||
|
|
||||||
type CaptureBrowser struct {
|
type CaptureBrowser struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
}
|
}
|
||||||
|
|
@ -209,7 +217,7 @@ func (p CapturePayload) Validate() error {
|
||||||
if strings.TrimSpace(p.CapturedAt) == "" {
|
if strings.TrimSpace(p.CapturedAt) == "" {
|
||||||
return fmt.Errorf("capturedAt is required")
|
return fmt.Errorf("capturedAt is required")
|
||||||
}
|
}
|
||||||
if p.Kind != "page" && p.Kind != "selection" && p.Kind != "link" {
|
if p.Kind != "page" && p.Kind != "selection" && p.Kind != "link" && p.Kind != "file" {
|
||||||
return fmt.Errorf("unsupported kind")
|
return fmt.Errorf("unsupported kind")
|
||||||
}
|
}
|
||||||
if strings.TrimSpace(p.Page.URL) == "" {
|
if strings.TrimSpace(p.Page.URL) == "" {
|
||||||
|
|
@ -221,6 +229,12 @@ func (p CapturePayload) Validate() error {
|
||||||
if p.Kind == "link" && (p.Link == nil || strings.TrimSpace(p.Link.URL) == "") {
|
if p.Kind == "link" && (p.Link == nil || strings.TrimSpace(p.Link.URL) == "") {
|
||||||
return fmt.Errorf("link.url is required")
|
return fmt.Errorf("link.url is required")
|
||||||
}
|
}
|
||||||
|
if p.Kind == "file" && (p.File == nil || strings.TrimSpace(p.File.Name) == "") {
|
||||||
|
return fmt.Errorf("file.name is required")
|
||||||
|
}
|
||||||
|
if p.Kind == "file" && (p.File == nil || p.File.Text == "") {
|
||||||
|
return fmt.Errorf("file.text is required")
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -250,6 +264,11 @@ func (p CapturePayload) EventPayload() map[string]interface{} {
|
||||||
result["url"] = linkURL
|
result["url"] = linkURL
|
||||||
result["title"] = strings.TrimSpace(p.Link.Text)
|
result["title"] = strings.TrimSpace(p.Link.Text)
|
||||||
result["domain"] = captureDomain(linkURL, "")
|
result["domain"] = captureDomain(linkURL, "")
|
||||||
|
case "file":
|
||||||
|
result["fileName"] = strings.TrimSpace(p.File.Name)
|
||||||
|
result["fileMime"] = strings.TrimSpace(p.File.Mime)
|
||||||
|
result["fileSize"] = p.File.Size
|
||||||
|
result["fileText"] = p.File.Text
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -85,6 +85,69 @@ func TestReceiverAcceptsSelectionCaptureAndPublishesEvent(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestReceiverAcceptsFileCaptureAndPublishesEvent(t *testing.T) {
|
||||||
|
bus := events.NewBus()
|
||||||
|
received := make(chan events.Event, 1)
|
||||||
|
bus.Subscribe("browser.capture.file", func(event events.Event) {
|
||||||
|
received <- event
|
||||||
|
})
|
||||||
|
|
||||||
|
receiver := New(bus)
|
||||||
|
body := `{
|
||||||
|
"schemaVersion": 1,
|
||||||
|
"captureId": "capture-file",
|
||||||
|
"capturedAt": "2026-06-29T02:00:00.000Z",
|
||||||
|
"source": "verstak-browser-extension",
|
||||||
|
"kind": "file",
|
||||||
|
"page": {
|
||||||
|
"url": "https://example.com/files",
|
||||||
|
"title": "Example Files",
|
||||||
|
"domain": "example.com"
|
||||||
|
},
|
||||||
|
"file": {
|
||||||
|
"name": "notes.txt",
|
||||||
|
"mime": "text/plain",
|
||||||
|
"size": 11,
|
||||||
|
"text": "hello file"
|
||||||
|
},
|
||||||
|
"browser": {
|
||||||
|
"name": "Firefox"
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
|
||||||
|
req := httptest.NewRequest(http.MethodPost, "/api/browser-inbox/v1/captures", bytes.NewBufferString(body))
|
||||||
|
rec := httptest.NewRecorder()
|
||||||
|
|
||||||
|
receiver.ServeHTTP(rec, req)
|
||||||
|
|
||||||
|
if rec.Code != http.StatusAccepted {
|
||||||
|
t.Fatalf("status = %d, want %d; body=%s", rec.Code, http.StatusAccepted, rec.Body.String())
|
||||||
|
}
|
||||||
|
event := <-received
|
||||||
|
if event.Name != "browser.capture.file" {
|
||||||
|
t.Fatalf("event name = %q, want browser.capture.file", event.Name)
|
||||||
|
}
|
||||||
|
payload, ok := event.Payload.(map[string]interface{})
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("event payload type = %T, want map[string]interface{}", event.Payload)
|
||||||
|
}
|
||||||
|
if payload["kind"] != "file" {
|
||||||
|
t.Fatalf("payload kind = %v, want file", payload["kind"])
|
||||||
|
}
|
||||||
|
if payload["fileName"] != "notes.txt" {
|
||||||
|
t.Fatalf("payload fileName = %v, want notes.txt", payload["fileName"])
|
||||||
|
}
|
||||||
|
if payload["fileMime"] != "text/plain" {
|
||||||
|
t.Fatalf("payload fileMime = %v, want text/plain", payload["fileMime"])
|
||||||
|
}
|
||||||
|
if payload["fileSize"] != int64(11) {
|
||||||
|
t.Fatalf("payload fileSize = %v, want 11", payload["fileSize"])
|
||||||
|
}
|
||||||
|
if payload["fileText"] != "hello file" {
|
||||||
|
t.Fatalf("payload fileText = %v, want hello file", payload["fileText"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestReceiverAnnotatesCaptureWithCurrentWorkspace(t *testing.T) {
|
func TestReceiverAnnotatesCaptureWithCurrentWorkspace(t *testing.T) {
|
||||||
bus := events.NewBus()
|
bus := events.NewBus()
|
||||||
received := make(chan events.Event, 1)
|
received := make(chan events.Event, 1)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue