perf(markdown): preload export path metadata
Some checks failed
Validation / validate (push) Has been cancelled
Some checks failed
Validation / validate (push) Has been cancelled
This commit is contained in:
parent
010ca24c49
commit
446cccad81
@ -39,10 +39,14 @@ func (e Exporter) Export(ctx context.Context) (Summary, error) {
|
||||
if err != nil {
|
||||
return Summary{}, err
|
||||
}
|
||||
paths, err := newPathResolver(ctx, e.Store)
|
||||
if err != nil {
|
||||
return Summary{}, err
|
||||
}
|
||||
var s Summary
|
||||
keep := map[string]bool{}
|
||||
for _, page := range pages {
|
||||
path, err := e.writePage(ctx, page)
|
||||
path, err := e.writePage(ctx, paths, page)
|
||||
if err != nil {
|
||||
return s, err
|
||||
}
|
||||
@ -56,19 +60,10 @@ func (e Exporter) Export(ctx context.Context) (Summary, error) {
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func (e Exporter) writePage(ctx context.Context, page store.Page) (string, error) {
|
||||
spaceName, err := e.Store.SpaceName(ctx, page.SpaceID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
teamID, err := e.Store.PageTeamID(ctx, page)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
teamName, err := e.Store.TeamName(ctx, teamID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
func (e Exporter) writePage(ctx context.Context, paths pathResolver, page store.Page) (string, error) {
|
||||
spaceName := paths.spaceName(page.SpaceID)
|
||||
teamID := paths.pageTeamID(page)
|
||||
teamName := paths.teamName(teamID)
|
||||
blocks, err := e.Store.PageBlocks(ctx, page.ID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
@ -112,6 +107,80 @@ func (e Exporter) writePage(ctx context.Context, page store.Page) (string, error
|
||||
return path, os.WriteFile(path, []byte(out), 0o644)
|
||||
}
|
||||
|
||||
type pathResolver struct {
|
||||
spaces map[string]string
|
||||
teams map[string]string
|
||||
blocks map[string]store.ParentRef
|
||||
collections map[string]store.ParentRef
|
||||
}
|
||||
|
||||
func newPathResolver(ctx context.Context, st *store.Store) (pathResolver, error) {
|
||||
spaces, err := st.SpaceNames(ctx)
|
||||
if err != nil {
|
||||
return pathResolver{}, err
|
||||
}
|
||||
teams, err := st.TeamNames(ctx)
|
||||
if err != nil {
|
||||
return pathResolver{}, err
|
||||
}
|
||||
blocks, err := st.BlockParents(ctx)
|
||||
if err != nil {
|
||||
return pathResolver{}, err
|
||||
}
|
||||
collections, err := st.CollectionParents(ctx)
|
||||
if err != nil {
|
||||
return pathResolver{}, err
|
||||
}
|
||||
return pathResolver{spaces: spaces, teams: teams, blocks: blocks, collections: collections}, nil
|
||||
}
|
||||
|
||||
func (r pathResolver) spaceName(id string) string {
|
||||
if id == "" {
|
||||
return "default"
|
||||
}
|
||||
if name := r.spaces[id]; name != "" {
|
||||
return name
|
||||
}
|
||||
return "space-" + notiontext.ShortID(id)
|
||||
}
|
||||
|
||||
func (r pathResolver) teamName(id string) string {
|
||||
if id == "" {
|
||||
return ""
|
||||
}
|
||||
if name := r.teams[id]; name != "" {
|
||||
return name
|
||||
}
|
||||
return "team-" + notiontext.ShortID(id)
|
||||
}
|
||||
|
||||
func (r pathResolver) pageTeamID(page store.Page) string {
|
||||
return r.resolveTeamID(page.ParentTable, page.ParentID, page.CollectionID, map[string]bool{page.ID: true})
|
||||
}
|
||||
|
||||
func (r pathResolver) resolveTeamID(table, id, collectionID string, seen map[string]bool) string {
|
||||
if table == "team" {
|
||||
return id
|
||||
}
|
||||
if table == "collection" && id == "" {
|
||||
id = collectionID
|
||||
}
|
||||
if id == "" || seen[table+":"+id] {
|
||||
return ""
|
||||
}
|
||||
seen[table+":"+id] = true
|
||||
switch table {
|
||||
case "block":
|
||||
parent := r.blocks[id]
|
||||
return r.resolveTeamID(parent.Table, parent.ID, "", seen)
|
||||
case "collection", "database", "data_source":
|
||||
parent := r.collections[id]
|
||||
return r.resolveTeamID(parent.Table, parent.ID, "", seen)
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func writeFrontMatter(b *strings.Builder, page store.Page, spaceName, teamID, teamName string) {
|
||||
b.WriteString("---\n")
|
||||
writeKV(b, "id", page.ID)
|
||||
|
||||
@ -142,6 +142,38 @@ func TestExporterUsesWorkspaceAndTeamspacePath(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestExporterResolvesTeamspaceThroughCollectionParent(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
st, err := store.Open(filepath.Join(t.TempDir(), "notcrawl.db"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer st.Close()
|
||||
now := store.NowMS()
|
||||
if err := st.UpsertSpace(ctx, store.Space{ID: "space1", Name: "Acme Org", Source: "test", SyncedAt: now}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := st.UpsertTeam(ctx, store.Team{ID: "team1", SpaceID: "space1", Name: "Research Lab", Source: "test", SyncedAt: now}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := st.UpsertCollection(ctx, store.Collection{ID: "collection1", SpaceID: "space1", ParentID: "team1", ParentTable: "team", Name: "Roadmap", Source: "test", SyncedAt: now}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := st.UpsertPage(ctx, store.Page{ID: "page1", SpaceID: "space1", ParentID: "collection1", ParentTable: "collection", CollectionID: "collection1", Title: "Row", Alive: true, Source: "test", SyncedAt: now}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
dir := t.TempDir()
|
||||
s, err := Exporter{Store: st, Dir: dir}.Export(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
want := filepath.Join(dir, "acme-org", "research-lab", "row-page1.md")
|
||||
if len(s.Files) != 1 || s.Files[0] != want {
|
||||
t.Fatalf("unexpected export path: %+v, want %s", s.Files, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExporterUsesReadableMissingSpaceFallback(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
st, err := store.Open(filepath.Join(t.TempDir(), "notcrawl.db"))
|
||||
|
||||
@ -119,6 +119,76 @@ func (s *Store) PageComments(ctx context.Context, pageID string) ([]Comment, err
|
||||
return comments, rows.Err()
|
||||
}
|
||||
|
||||
func (s *Store) SpaceNames(ctx context.Context) (map[string]string, error) {
|
||||
rows, err := s.queryContext(ctx, `select id, name from spaces`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
out := map[string]string{}
|
||||
for rows.Next() {
|
||||
var id, name string
|
||||
if err := rows.Scan(&id, &name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out[id] = name
|
||||
}
|
||||
return out, rows.Err()
|
||||
}
|
||||
|
||||
func (s *Store) TeamNames(ctx context.Context) (map[string]string, error) {
|
||||
rows, err := s.queryContext(ctx, `select id, name from teams`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
out := map[string]string{}
|
||||
for rows.Next() {
|
||||
var id, name string
|
||||
if err := rows.Scan(&id, &name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out[id] = name
|
||||
}
|
||||
return out, rows.Err()
|
||||
}
|
||||
|
||||
func (s *Store) BlockParents(ctx context.Context) (map[string]ParentRef, error) {
|
||||
rows, err := s.queryContext(ctx, `select id, parent_id, parent_table from blocks`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
out := map[string]ParentRef{}
|
||||
for rows.Next() {
|
||||
var id string
|
||||
var parentID, parentTable sql.NullString
|
||||
if err := rows.Scan(&id, &parentID, &parentTable); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out[id] = ParentRef{ID: parentID.String, Table: parentTable.String}
|
||||
}
|
||||
return out, rows.Err()
|
||||
}
|
||||
|
||||
func (s *Store) CollectionParents(ctx context.Context) (map[string]ParentRef, error) {
|
||||
rows, err := s.queryContext(ctx, `select id, parent_id, parent_table from collections`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
out := map[string]ParentRef{}
|
||||
for rows.Next() {
|
||||
var id string
|
||||
var parentID, parentTable sql.NullString
|
||||
if err := rows.Scan(&id, &parentID, &parentTable); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out[id] = ParentRef{ID: parentID.String, Table: parentTable.String}
|
||||
}
|
||||
return out, rows.Err()
|
||||
}
|
||||
|
||||
func (s *Store) SpaceName(ctx context.Context, id string) (string, error) {
|
||||
if id == "" {
|
||||
return "default", nil
|
||||
|
||||
@ -105,6 +105,11 @@ type RawRecord struct {
|
||||
SyncedAt int64
|
||||
}
|
||||
|
||||
type ParentRef struct {
|
||||
ID string
|
||||
Table string
|
||||
}
|
||||
|
||||
type SearchResult struct {
|
||||
Kind string
|
||||
ID string
|
||||
|
||||
Loading…
Reference in New Issue
Block a user