@@ -239,14 +239,16 @@ func (a *app) handleAIFallback(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
type integrationStagingRequest struct {
|
||||
Source string `json:"source"`
|
||||
Query string `json:"query"`
|
||||
Title string `json:"title"`
|
||||
Text string `json:"text"`
|
||||
Answer string `json:"answer"`
|
||||
Categories []string `json:"categories"`
|
||||
Keywords []string `json:"keywords"`
|
||||
MinScore *float64 `json:"min_score,omitempty"`
|
||||
Source string `json:"source"`
|
||||
Query string `json:"query"`
|
||||
Title string `json:"title"`
|
||||
Text string `json:"text"`
|
||||
Answer string `json:"answer"`
|
||||
Categories []string `json:"categories"`
|
||||
Keywords []string `json:"keywords"`
|
||||
MinScore *float64 `json:"min_score,omitempty"`
|
||||
IntegrationKey string `json:"integration_key,omitempty"`
|
||||
Metadata map[string]any `json:"metadata,omitempty"`
|
||||
}
|
||||
|
||||
func integrationBearerAuthorized(r *http.Request) (bool, bool) {
|
||||
@@ -296,9 +298,9 @@ func (a *app) handleIntegrationStaging(w http.ResponseWriter, r *http.Request) {
|
||||
if req.MinScore != nil {
|
||||
minScore = *req.MinScore
|
||||
}
|
||||
result, err := a.staging.SaveFromSource(req.Query, req.Source, staging.Draft{
|
||||
result, err := a.staging.SaveFromIntegration(req.Query, req.Source, staging.Draft{
|
||||
Title: req.Title, Text: req.Text, Answer: req.Answer, Categories: req.Categories, Keywords: req.Keywords,
|
||||
}, false, minScore)
|
||||
}, false, minScore, staging.IntegrationOptions{IntegrationKey: req.IntegrationKey, Metadata: req.Metadata})
|
||||
if err != nil {
|
||||
writeError(w, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
|
||||
@@ -424,3 +424,38 @@ func TestEditorBasicAuthDoesNotLeakCredentialsToIntegrationClient(t *testing.T)
|
||||
t.Fatalf("editor API unexpectedly bypassed basic auth: %d", itemsRR.Code)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIntegrationDraftWithStableKeyUpdatesInsteadOfDuplicating(t *testing.T) {
|
||||
t.Setenv("KB_INTEGRATION_TOKEN", "integration-secret")
|
||||
s, err := store.New(t.TempDir())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
st, err := staging.New(t.TempDir())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
web, _ := fs.Sub(webFS, "web")
|
||||
h := newApp(s, web).withStaging(st).routes()
|
||||
post := func(answer string) {
|
||||
payload := fmt.Sprintf(`{"source":"NeuroForge Research","query":"NVIDIA","title":"NVIDIA","answer":%q,"integration_key":"neuroforge-goal:g1","metadata":{"research_goal_id":"g1"}}`, answer)
|
||||
req := httptest.NewRequest(http.MethodPost, "/api/integrations/staging", bytes.NewBufferString(payload))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Authorization", "Bearer integration-secret")
|
||||
rr := httptest.NewRecorder()
|
||||
h.ServeHTTP(rr, req)
|
||||
if rr.Code != http.StatusCreated {
|
||||
t.Fatalf("status=%d body=%s", rr.Code, rr.Body.String())
|
||||
}
|
||||
}
|
||||
post("erste Fassung")
|
||||
post("zweite Fassung")
|
||||
if st.Count() != 1 {
|
||||
t.Fatalf("expected one active draft, got %d", st.Count())
|
||||
}
|
||||
items, _ := st.List(staging.Query{Page: 1, PageSize: 10})
|
||||
got, _ := st.Get(items.Items[0].Key)
|
||||
if got.Document["answer"] != "zweite Fassung" {
|
||||
t.Fatalf("draft not refreshed: %#v", got.Document)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user