feat: add CSV import and export functionality for module entries. Implement endpoints for exporting and importing module entries in CSV format, supporting types AS_PREFIXES, DOMAINS, and IP_RANGES. Enhance UI with buttons for CSV operations, improving user experience in managing module data.
CI / changes (push) Successful in 6s
CI / openapi (push) Successful in 22s
CI / go (push) Successful in 50s
CI / docker-web (deploy/docker/evobgp-web/Dockerfile, , evobgp-web) (push) Successful in 1m4s
CI / docker-web (deploy/docker/evobgp-web/Dockerfile, evobgp-all, evobgp-web-all) (push) Successful in 1m5s
CI / docker-bird (push) Has been skipped
CI / bird2 (push) Successful in 15s
CI / docker-go-prime (push) Successful in 24s
CI / docker-go (deploy/docker/evobgp-agent/Dockerfile, , evobgp-agent) (push) Successful in 1m1s
CI / docker-go (evobgp-all, 1, deploy/docker/gobinary/Dockerfile, , evobgp-all) (push) Successful in 2m7s
CI / docker-go (evobgp-api, 1, deploy/docker/gobinary/Dockerfile, , evobgp-api) (push) Successful in 1m22s
CI / docker-go (evobgp-deploy, 0, deploy/docker/gobinary/Dockerfile, , evobgp-deploy) (push) Successful in 1m26s
CI / docker-go (evobgp-ingest, 0, deploy/docker/gobinary/Dockerfile, , evobgp-ingest) (push) Successful in 1m23s
CI / docker-go (evobgp-node, 0, deploy/docker/gobinary/Dockerfile, , evobgp-node) (push) Successful in 1m6s
CI / docker-go (evobgp-render, 0, deploy/docker/gobinary/Dockerfile, , evobgp-render) (push) Successful in 1m28s
CI / docker-go (evobgp-scheduler, 0, deploy/docker/gobinary/Dockerfile, , evobgp-scheduler) (push) Successful in 1m21s
CI / changes (push) Successful in 6s
CI / openapi (push) Successful in 22s
CI / go (push) Successful in 50s
CI / docker-web (deploy/docker/evobgp-web/Dockerfile, , evobgp-web) (push) Successful in 1m4s
CI / docker-web (deploy/docker/evobgp-web/Dockerfile, evobgp-all, evobgp-web-all) (push) Successful in 1m5s
CI / docker-bird (push) Has been skipped
CI / bird2 (push) Successful in 15s
CI / docker-go-prime (push) Successful in 24s
CI / docker-go (deploy/docker/evobgp-agent/Dockerfile, , evobgp-agent) (push) Successful in 1m1s
CI / docker-go (evobgp-all, 1, deploy/docker/gobinary/Dockerfile, , evobgp-all) (push) Successful in 2m7s
CI / docker-go (evobgp-api, 1, deploy/docker/gobinary/Dockerfile, , evobgp-api) (push) Successful in 1m22s
CI / docker-go (evobgp-deploy, 0, deploy/docker/gobinary/Dockerfile, , evobgp-deploy) (push) Successful in 1m26s
CI / docker-go (evobgp-ingest, 0, deploy/docker/gobinary/Dockerfile, , evobgp-ingest) (push) Successful in 1m23s
CI / docker-go (evobgp-node, 0, deploy/docker/gobinary/Dockerfile, , evobgp-node) (push) Successful in 1m6s
CI / docker-go (evobgp-render, 0, deploy/docker/gobinary/Dockerfile, , evobgp-render) (push) Successful in 1m28s
CI / docker-go (evobgp-scheduler, 0, deploy/docker/gobinary/Dockerfile, , evobgp-scheduler) (push) Successful in 1m21s
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
package httpapi
|
||||
|
||||
import (
|
||||
"encoding/csv"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestModuleEntriesCSVImportExportIPRanges(t *testing.T) {
|
||||
srv, err := New(Options{
|
||||
InsecureDev: true,
|
||||
SeedDemo: true,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer srv.Close()
|
||||
|
||||
tenant, _, modIP, _, _ := srv.Store().DemoIDs()
|
||||
srv.apiKeys = parseAPIKeysSpec("opkey|" + tenant + "|operator")
|
||||
|
||||
ts := httptest.NewServer(srv.Handler())
|
||||
defer ts.Close()
|
||||
client := ts.Client()
|
||||
base := ts.URL
|
||||
|
||||
reqList, _ := http.NewRequest(http.MethodGet, base+"/v1/communities?limit=10", nil)
|
||||
reqList.Header.Set("Authorization", "Bearer opkey")
|
||||
respList, err := client.Do(reqList)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer respList.Body.Close()
|
||||
if respList.StatusCode != http.StatusOK {
|
||||
b, _ := io.ReadAll(respList.Body)
|
||||
t.Fatalf("communities status %d: %s", respList.StatusCode, b)
|
||||
}
|
||||
var listBody struct {
|
||||
Items []struct {
|
||||
Community string `json:"community"`
|
||||
} `json:"items"`
|
||||
}
|
||||
if err := json.NewDecoder(respList.Body).Decode(&listBody); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(listBody.Items) == 0 {
|
||||
t.Fatal("expected seeded community")
|
||||
}
|
||||
|
||||
csvBody := "\"ipRange\",\"community\"\n\"10.254.1.254/32\",\"" + listBody.Items[0].Community + "\"\n\"160.79.104.0/23\",\"" + listBody.Items[0].Community + "\"\n"
|
||||
reqImport, _ := http.NewRequest(http.MethodPost, base+"/v1/modules/"+modIP+"/entries.csv", strings.NewReader(csvBody))
|
||||
reqImport.Header.Set("Authorization", "Bearer opkey")
|
||||
reqImport.Header.Set("Content-Type", "text/csv")
|
||||
respImport, err := client.Do(reqImport)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer respImport.Body.Close()
|
||||
if respImport.StatusCode != http.StatusOK {
|
||||
b, _ := io.ReadAll(respImport.Body)
|
||||
t.Fatalf("import status %d: %s", respImport.StatusCode, b)
|
||||
}
|
||||
var importBody struct {
|
||||
Imported int `json:"imported"`
|
||||
}
|
||||
if err := json.NewDecoder(respImport.Body).Decode(&importBody); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if importBody.Imported != 2 {
|
||||
t.Fatalf("expected imported=2, got %d", importBody.Imported)
|
||||
}
|
||||
|
||||
reqExport, _ := http.NewRequest(http.MethodGet, base+"/v1/modules/"+modIP+"/entries.csv", nil)
|
||||
reqExport.Header.Set("Authorization", "Bearer opkey")
|
||||
respExport, err := client.Do(reqExport)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer respExport.Body.Close()
|
||||
if respExport.StatusCode != http.StatusOK {
|
||||
b, _ := io.ReadAll(respExport.Body)
|
||||
t.Fatalf("export status %d: %s", respExport.StatusCode, b)
|
||||
}
|
||||
if got := respExport.Header.Get("Content-Type"); !strings.Contains(got, "text/csv") {
|
||||
t.Fatalf("unexpected content-type: %q", got)
|
||||
}
|
||||
raw, err := io.ReadAll(respExport.Body)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
records, err := csv.NewReader(strings.NewReader(string(raw))).ReadAll()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(records) < 3 {
|
||||
t.Fatalf("expected at least 3 csv rows, got %d", len(records))
|
||||
}
|
||||
if records[0][0] != "ipRange" || records[0][1] != "community" {
|
||||
t.Fatalf("unexpected header: %#v", records[0])
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user