Skip to content

Commit 99b63b1

Browse files
committed
fix: restore accidentally deleted mirror data scripts
1 parent eb01a55 commit 99b63b1

File tree

2 files changed

+231
-0
lines changed

2 files changed

+231
-0
lines changed
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
// This tool was created by Claude Code as a simple way to kick the tires on data migrations
2+
// by fetching production data from the public registry API.
3+
// It is not intended for production use.
4+
//
5+
//go:build ignore
6+
7+
package main
8+
9+
import (
10+
"encoding/json"
11+
"fmt"
12+
"io"
13+
"net/http"
14+
"os"
15+
"time"
16+
)
17+
18+
type ServerResponse struct {
19+
Servers []json.RawMessage `json:"servers"`
20+
Metadata struct {
21+
NextCursor string `json:"next_cursor,omitempty"`
22+
Count int `json:"count"`
23+
} `json:"metadata"`
24+
}
25+
26+
func main() {
27+
baseURL := "https://registry.modelcontextprotocol.io/v0/servers"
28+
var allServers []json.RawMessage
29+
cursor := ""
30+
pageCount := 0
31+
32+
for {
33+
pageCount++
34+
url := baseURL
35+
if cursor != "" {
36+
url = fmt.Sprintf("%s?cursor=%s", baseURL, cursor)
37+
}
38+
39+
fmt.Printf("Fetching page %d: %s\n", pageCount, url)
40+
41+
resp, err := http.Get(url)
42+
if err != nil {
43+
panic(fmt.Sprintf("Failed to fetch: %v", err))
44+
}
45+
defer resp.Body.Close()
46+
47+
body, err := io.ReadAll(resp.Body)
48+
if err != nil {
49+
panic(fmt.Sprintf("Failed to read body: %v", err))
50+
}
51+
52+
var serverResp ServerResponse
53+
if err := json.Unmarshal(body, &serverResp); err != nil {
54+
panic(fmt.Sprintf("Failed to parse JSON: %v", err))
55+
}
56+
57+
allServers = append(allServers, serverResp.Servers...)
58+
fmt.Printf(" Got %d servers (total: %d)\n", len(serverResp.Servers), len(allServers))
59+
60+
if serverResp.Metadata.NextCursor == "" {
61+
break
62+
}
63+
cursor = serverResp.Metadata.NextCursor
64+
65+
// Be nice to the API
66+
time.Sleep(100 * time.Millisecond)
67+
}
68+
69+
// Save all servers to a file
70+
output := map[string]interface{}{
71+
"servers": allServers,
72+
"count": len(allServers),
73+
"fetched": time.Now().Format(time.RFC3339),
74+
}
75+
76+
data, err := json.MarshalIndent(output, "", " ")
77+
if err != nil {
78+
panic(fmt.Sprintf("Failed to marshal output: %v", err))
79+
}
80+
81+
outputFile := "production_servers.json"
82+
if err := os.WriteFile(outputFile, data, 0644); err != nil {
83+
panic(fmt.Sprintf("Failed to write file: %v", err))
84+
}
85+
86+
fmt.Printf("\nDone! Saved %d servers to %s\n", len(allServers), outputFile)
87+
}
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
// This tool was created by Claude Code as a simple way to kick the tires on data migrations
2+
// by loading production data into a test database for migration testing.
3+
// It is not intended for production use.
4+
//
5+
//go:build ignore
6+
7+
package main
8+
9+
import (
10+
"database/sql"
11+
"encoding/json"
12+
"fmt"
13+
"io/ioutil"
14+
"log"
15+
"path/filepath"
16+
17+
"github.com/google/uuid"
18+
_ "github.com/lib/pq"
19+
)
20+
21+
func main() {
22+
// Database connection
23+
db, err := sql.Open("postgres", "postgres://postgres:testpass@localhost:5433/registry_test?sslmode=disable")
24+
if err != nil {
25+
log.Fatal("Failed to connect to database:", err)
26+
}
27+
defer db.Close()
28+
29+
// Run migrations up to a specific point (configure as needed)
30+
maxMigration := 7 // Change this to test different migration states
31+
fmt.Printf("Running migrations 001-%03d...\n", maxMigration)
32+
migrationsDir := "internal/database/migrations"
33+
for i := 1; i <= maxMigration; i++ {
34+
migrationFile := filepath.Join(migrationsDir, fmt.Sprintf("%03d_*.sql", i))
35+
files, err := filepath.Glob(migrationFile)
36+
if err != nil || len(files) == 0 {
37+
log.Fatalf("Migration file %d not found", i)
38+
}
39+
40+
content, err := ioutil.ReadFile(files[0])
41+
if err != nil {
42+
log.Fatalf("Failed to read migration %d: %v", i, err)
43+
}
44+
45+
fmt.Printf(" Applying migration %d: %s\n", i, filepath.Base(files[0]))
46+
if _, err := db.Exec(string(content)); err != nil {
47+
log.Fatalf("Failed to apply migration %d: %v", i, err)
48+
}
49+
}
50+
51+
// Load production data
52+
fmt.Println("\nLoading production data...")
53+
data, err := ioutil.ReadFile("scripts/mirror_data/production_servers.json")
54+
if err != nil {
55+
log.Fatal("Failed to read production data:", err)
56+
}
57+
58+
var prodData struct {
59+
Servers []json.RawMessage `json:"servers"`
60+
}
61+
if err := json.Unmarshal(data, &prodData); err != nil {
62+
log.Fatal("Failed to parse production data:", err)
63+
}
64+
65+
fmt.Printf("Loading %d servers...\n", len(prodData.Servers))
66+
67+
// Prepare insert statement
68+
stmt, err := db.Prepare("INSERT INTO servers (version_id, value) VALUES ($1, $2)")
69+
if err != nil {
70+
log.Fatal("Failed to prepare statement:", err)
71+
}
72+
defer stmt.Close()
73+
74+
// Insert each server
75+
for i, server := range prodData.Servers {
76+
// Generate a unique version_id
77+
versionID := uuid.New().String()
78+
79+
// The server data is already JSON, just insert it
80+
if _, err := stmt.Exec(versionID, server); err != nil {
81+
log.Printf("Failed to insert server %d: %v", i, err)
82+
continue
83+
}
84+
}
85+
86+
fmt.Println("Data loaded successfully!")
87+
88+
// Verify the data
89+
var count int
90+
db.QueryRow("SELECT COUNT(*) FROM servers").Scan(&count)
91+
fmt.Printf("\nTotal servers in database: %d\n", count)
92+
93+
// Check for NULL status values in the JSON
94+
fmt.Println("\nAnalyzing status field in JSON data...")
95+
96+
rows, err := db.Query(`
97+
SELECT
98+
COUNT(*) as total,
99+
COUNT(CASE WHEN value->>'status' IS NULL THEN 1 END) as null_status,
100+
COUNT(CASE WHEN value->>'status' = '' THEN 1 END) as empty_status,
101+
COUNT(CASE WHEN value->>'status' = 'null' THEN 1 END) as string_null_status,
102+
COUNT(CASE WHEN value->>'status' = 'active' THEN 1 END) as active_status,
103+
COUNT(CASE WHEN value->>'status' = 'deprecated' THEN 1 END) as deprecated_status,
104+
COUNT(CASE WHEN value->>'status' = 'deleted' THEN 1 END) as deleted_status
105+
FROM servers
106+
`)
107+
if err != nil {
108+
log.Fatal("Failed to analyze data:", err)
109+
}
110+
defer rows.Close()
111+
112+
if rows.Next() {
113+
var total, nullStatus, emptyStatus, stringNullStatus, activeStatus, deprecatedStatus, deletedStatus int
114+
rows.Scan(&total, &nullStatus, &emptyStatus, &stringNullStatus, &activeStatus, &deprecatedStatus, &deletedStatus)
115+
116+
fmt.Printf(" Total servers: %d\n", total)
117+
fmt.Printf(" NULL status: %d\n", nullStatus)
118+
fmt.Printf(" Empty status: %d\n", emptyStatus)
119+
fmt.Printf(" 'null' string status: %d\n", stringNullStatus)
120+
fmt.Printf(" 'active' status: %d\n", activeStatus)
121+
fmt.Printf(" 'deprecated' status: %d\n", deprecatedStatus)
122+
fmt.Printf(" 'deleted' status: %d\n", deletedStatus)
123+
fmt.Printf(" Other/Invalid: %d\n", total-nullStatus-emptyStatus-stringNullStatus-activeStatus-deprecatedStatus-deletedStatus)
124+
}
125+
126+
// Print sample servers with no status
127+
fmt.Println("\nSample servers with NULL status:")
128+
rows, err = db.Query(`
129+
SELECT value->>'name', value->>'version'
130+
FROM servers
131+
WHERE value->>'status' IS NULL
132+
LIMIT 5
133+
`)
134+
if err == nil {
135+
defer rows.Close()
136+
for rows.Next() {
137+
var name, version string
138+
rows.Scan(&name, &version)
139+
fmt.Printf(" - %s@%s\n", name, version)
140+
}
141+
}
142+
143+
fmt.Printf("\nDatabase is ready for testing migration %03d!\n", maxMigration+1)
144+
}

0 commit comments

Comments
 (0)