history_reads.gno
6.65 Kb · 135 lines
1package memba_notes_v1
2
3import "strconv"
4
5func PublicHistoryInfoJSON(idHex string) string {
6 id := historyID(idHex)
7 h := historyGet(id)
8 if h == nil {
9 return "null"
10 }
11 n := h.Latest
12 return `{"schema":"memba.notes.public-history-info/v1","id":` + q(idHex) + `,"start_state_revision":` + u(h.StartRevision) + `,"start_body_revision":` + u(h.StartBodyRevision) + `,"head_seq":` + u(h.Head) + `,"latest_state_revision":` + u(n.Revision) + `,"retention":"public-moderator-redactable","coverage":"public-since-publication-v1","deleted":` + strconv.FormatBool(n.Deleted) + `,"moderator_hidden":` + strconv.FormatBool(n.ModeratorHidden) + `}`
13}
14func historyEntryFields(e *historyEntry) string {
15 proposal := "null"
16 if e.Proposal != 0 {
17 proposal = u(e.Proposal)
18 }
19 return `"seq":` + u(e.Seq) + `,"action":` + q(e.Action) + `,"height":` + q(strconv.FormatInt(e.Height, 10)) + `,"state_revision":` + u(e.Revision) + `,"actor":` + q(string(e.Actor)) + `,"executor":` + historyNullableText(string(e.Executor)) + `,"op_id":` + historyNullableRaw(e.Operation) + `,"proposal_id":` + proposal
20}
21func PublicHistoryJSON(idHex string, afterSeq, throughSeq uint64, limit int) string {
22 id := historyID(idHex)
23 if limit < 1 || limit > 20 {
24 panic("notes: history page limit")
25 }
26 h := historyGet(id)
27 if h == nil {
28 return "null"
29 }
30 if throughSeq == 0 {
31 if afterSeq != 0 {
32 panic("notes: history first page cursor")
33 }
34 throughSeq = h.Head
35 }
36 if afterSeq > throughSeq || throughSeq > h.Head {
37 panic("notes: history cursor")
38 }
39 items, next := "", "null"
40 last, count := afterSeq, 0
41 // Direct keys preserve contiguity without a scan or after+limit overflow.
42 for last < throughSeq && count < limit {
43 last++
44 e := historyEntryAt(id, last)
45 if count > 0 {
46 items += ","
47 }
48 items += "{" + historyEntryFields(e) + `,"comment_id":` + historyNullableRaw(e.Comment) + "}"
49 count++
50 }
51 if last < throughSeq {
52 next = u(last)
53 }
54 return `{"schema":"memba.notes.public-history-page/v1","id":` + q(idHex) + `,"through_seq":` + u(throughSeq) + `,"items":[` + items + `],"next_cursor":` + next + `}`
55}
56func PublicHistoryEntryJSON(idHex string, seq uint64) string {
57 v, ok := historyEntries.Get(historyKey(historyID(idHex), seq))
58 if !ok {
59 return "null"
60 }
61 e := v.(*historyEntry)
62 return `{"schema":"memba.notes.public-history-entry/v1","id":` + q(idHex) + "," + historyEntryFields(e) + `,"governance_action":` + historyNullableText(e.GovernanceAction) + `,"detail":` + e.Detail + `}`
63}
64
65// PublicHistoryVersionJSON reads the public state recorded at a state revision.
66// recorded_seq is the event that recorded it; PublicHistoryBodyChunkJSON and
67// RedactPublicContentAtSeq address that version by this seq.
68func PublicHistoryVersionJSON(idHex string, stateRevision uint64) string {
69 id := historyID(idHex)
70 h := historyGet(id)
71 if h == nil {
72 return "null"
73 }
74 e := historyRevisionEntry(id, h, stateRevision)
75 if e == nil {
76 return "null"
77 }
78 v := e.Version
79 titleRevision, title, bodyRevision, bodyHash, bodyBytes := "null", "null", "null", "null", "0"
80 titleRedacted, bodyRedacted := false, false
81 if v.Title != nil {
82 titleRevision, titleRedacted = u(v.TitleRevision), v.Title.Redacted
83 if !titleRedacted {
84 title = blob(v.Title.Bytes)
85 }
86 }
87 if v.Body != nil {
88 bodyRevision, bodyHash, bodyBytes, bodyRedacted = u(v.BodyRevision), q(v.Body.Hash), strconv.Itoa(len(v.Body.Bytes)), v.Body.Redacted
89 }
90 writers := ""
91 for i, who := range v.Writers {
92 if i > 0 {
93 writers += ","
94 }
95 writers += q(string(who))
96 }
97 return `{"schema":"memba.notes.public-history-version/v1","id":` + q(idHex) + `,"state_revision":` + u(v.Revision) + `,"recorded_seq":` + u(e.Seq) + `,"owner":` + q(string(v.Owner)) + `,"pending_owner":` + historyNullableText(string(v.PendingOwner)) + `,"owner_is_realm":` + strconv.FormatBool(v.OwnerIsRealm) + `,"owner_generation":` + u(v.OwnerGeneration) + `,"mode":` + strconv.Itoa(int(v.Mode)) + `,"epoch":` + u(uint64(v.Epoch)) + `,"title_revision":` + titleRevision + `,"title_blob":` + title + `,"title_redacted":` + strconv.FormatBool(titleRedacted) + `,"body_revision":` + bodyRevision + `,"body_bytes":` + q(bodyBytes) + `,"body_sha256":` + bodyHash + `,"body_redacted":` + strconv.FormatBool(bodyRedacted) + `,"writers":[` + writers + `],"policies":{"read":` + q(v.PolicyRead) + `,"write":` + q(v.PolicyWrite) + `,"comment":` + q(v.PolicyComment) + `},"gov_writers":` + strconv.FormatBool(v.GovWriters) + `,"allow_public_writes":` + strconv.FormatBool(v.PublicWrites) + `,"owner_listed":` + strconv.FormatBool(v.Listed) + `,"moderator_hidden":` + strconv.FormatBool(v.ModeratorHidden) + `,"deleted":` + strconv.FormatBool(v.Deleted) + `}`
98}
99
100// PublicHistoryBodyChunkJSON reads the body of the version recorded by the
101// note-level event seq (a version's recorded_seq). A later note-level event
102// that kept the body returns the same body; comment and cleanup events, deletion
103// versions, unknown seqs and redacted bodies return null.
104func PublicHistoryBodyChunkJSON(idHex string, seq uint64, offset, limit int) string {
105 id := historyID(idHex)
106 if limit < 1 || limit > MaxBodyChunkBytes || offset < 0 {
107 panic("notes: history chunk bounds")
108 }
109 v := historyVersionAt(id, seq)
110 if v == nil || v.Body == nil {
111 return "null"
112 }
113 b := v.Body
114 if b.Redacted {
115 return "null"
116 }
117 if offset > len(b.Bytes) {
118 panic("notes: history chunk bounds")
119 }
120 end := offset + limit
121 if end > len(b.Bytes) {
122 end = len(b.Bytes)
123 }
124 return `{"schema":"memba.notes.public-history-body/v2","id":` + q(idHex) + `,"seq":` + u(seq) + `,"body_revision":` + u(v.BodyRevision) + `,"sha256":` + q(b.Hash) + `,"offset":` + q(strconv.Itoa(offset)) + `,"total":` + q(strconv.Itoa(len(b.Bytes))) + `,"next_offset":` + q(strconv.Itoa(end)) + `,"chunk_blob":` + blob(b.Bytes[offset:end]) + `}`
125}
126func PublicHistoryCommentJSON(idHex, cidHex string) string {
127 id, cid := historyID(idHex), historyID(cidHex)
128 v, ok := historyComments.Get(commentKey(id, cid))
129 if !ok {
130 return "null"
131 }
132 h := v.(*historyComment)
133 // The reference describes the first version: revision 1, not yet deleted, hidden or resolved.
134 return `{"schema":"memba.notes.public-history-comment/v1","id":` + q(idHex) + `,"comment_id":` + q(cidHex) + `,"content":{"author":` + q(string(h.Author)) + `,"created_height":` + q(strconv.FormatInt(h.CreatedHeight, 10)) + `,"epoch":` + u(uint64(h.Epoch)) + `,"anchor_blob":` + blob(h.Anchor) + `,"body_blob":` + blob(h.Body) + `,"reference":` + historyCommentDetail(cidHex, h.Parent, 1, h.BodyRevision, false, false, false, h) + `},"presentation":{"latest_seq":` + u(h.LatestSeq) + `,"deleted":` + strconv.FormatBool(h.Deleted) + `,"hidden":` + strconv.FormatBool(h.Hidden) + `,"resolved":` + strconv.FormatBool(h.Resolved) + `,"redacted":` + strconv.FormatBool(h.Redacted) + `}}`
135}