Skip to content

Commit 59d5c2e

Browse files
Davphlamoul
andauthored
chore(govdao): improve error handling in Render (#4910)
Fix: #4629 Add more checks, fix interface call on nil, and enhance error messages in GovDAO to create a more user-friendly experience. I didn't add second check on `GetStatus` return value, as it should never be triggered. I'm not sure the `getPropStatus` UNKNOWN status can be executed, but it's always great to have. ## Tests #### Prerequisites ``` // Install gno gnodev . // In any directory ``` ### DAO not initialized - http://127.0.0.1:8888/r/gov/dao/v3/impl:1000 <img width="1190" height="618" alt="image" src="https://github.com/user-attachments/assets/aef433fa-f3eb-4948-9d34-dd20aaebc3c2" /> ### Invalid proposal For the next tests, you should load http://127.0.0.1:8888/r/gov/dao/v3/loader before executing the test to initialize the DAO. - "No proposals yet." - http://127.0.0.1:8888/r/gov/dao/v3/impl <img width="1324" height="956" alt="image" src="https://github.com/user-attachments/assets/d8f6135a-a190-4518-8bf8-b1e092fa5dbf" /> - "Error: Invalid proposal ID format." - http://127.0.0.1:8888/r/gov/dao/v3/impl:100a <img width="1390" height="524" alt="image" src="https://github.com/user-attachments/assets/3a37eeed-def0-446e-aad5-3b389eecb23c" /> - "Proposal not found" - http://127.0.0.1:8888/r/gov/dao/v3/impl:1000 <img width="1222" height="536" alt="image" src="https://github.com/user-attachments/assets/b4031c5c-8713-41ec-ac55-c90034ac0893" /> --------- Co-authored-by: Manfred Touron <[email protected]>
1 parent a2c8e1d commit 59d5c2e

File tree

3 files changed

+49
-9
lines changed

3 files changed

+49
-9
lines changed

examples/gno.land/r/gov/dao/proxy.gno

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@ package dao
33
import (
44
"chain"
55
"chain/runtime"
6+
"errors"
67
"strconv"
8+
9+
"gno.land/p/nt/ufmt"
710
)
811

912
// dao is the actual govDAO implementation, having all the needed business logic
@@ -43,6 +46,9 @@ func MustCreateProposal(cur realm, r ProposalRequest) ProposalID {
4346
// If the proposal was denied, it will return false. If the proposal is correctly
4447
// executed, it will return true. If something happens this function will panic.
4548
func ExecuteProposal(cur realm, pid ProposalID) bool {
49+
if dao == nil {
50+
return false
51+
}
4652
execute, err := dao.PreExecuteProposal(pid)
4753
if err != nil {
4854
panic(err.Error())
@@ -64,6 +70,9 @@ func ExecuteProposal(cur realm, pid ProposalID) bool {
6470
// CreateProposal will try to create a new proposal, that will be validated by the actual
6571
// govDAO implementation. If the proposal cannot be created, an error will be returned.
6672
func CreateProposal(cur realm, r ProposalRequest) (ProposalID, error) {
73+
if dao == nil {
74+
return -1, errors.New("DAO not initialized")
75+
}
6776
author, err := dao.PreCreateProposal(r)
6877
if err != nil {
6978
return -1, err
@@ -97,6 +106,9 @@ func MustVoteOnProposal(cur realm, r VoteRequest) {
97106
// If the voter cannot vote the specified proposal, this method will return an error
98107
// with the explanation of why.
99108
func VoteOnProposal(cur realm, r VoteRequest) error {
109+
if dao == nil {
110+
return errors.New("DAO not initialized")
111+
}
100112
return dao.VoteOnProposal(r)
101113
}
102114

@@ -119,11 +131,17 @@ func MustGetProposal(cur realm, pid ProposalID) *Proposal {
119131

120132
// GetProposal gets created proposal by its ID
121133
func GetProposal(cur realm, pid ProposalID) (*Proposal, error) {
134+
if dao == nil {
135+
return nil, errors.New("DAO not initialized")
136+
}
122137
if err := dao.PreGetProposal(pid); err != nil {
123138
return nil, err
124139
}
125140

126141
prop := proposals.GetProposal(pid)
142+
if prop == nil {
143+
return nil, errors.New(ufmt.Sprintf("Proposal %v does not exist.", int64(pid)))
144+
}
127145

128146
if err := dao.PostGetProposal(pid, prop); err != nil {
129147
return nil, err

examples/gno.land/r/gov/dao/proxy_test.gno

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,9 @@ func TestProxy_Functions(cur realm, t *testing.T) {
7979
pid = MustCreateProposal(cross, NewProposalRequest("Proposal Title", "Description", e))
8080
})
8181

82-
p := MustGetProposal(cross, 1000)
83-
if p != nil {
84-
panic("proposal must be nil")
82+
p, err := GetProposal(cross, 1000)
83+
if p != nil || err == nil {
84+
panic("proposal should not exist and should return an error")
8585
}
8686
p = MustGetProposal(cross, pid)
8787
urequire.Equal(t, "Proposal Title", p.Title())

examples/gno.land/r/gov/dao/v3/impl/render.gno

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,15 @@ func (ren *render) renderActiveProposals(url string, d *GovDAO) string {
6161
out += "[> Go to Memberstore <](/r/gov/dao/v3/memberstore)\n"
6262
out += "## Proposals\n"
6363
page := ren.pssPager.MustGetPageByPath(url)
64+
if len(page.Items) == 0 {
65+
out += "\nNo proposals yet.\n\n"
66+
return out
67+
}
68+
6469
for _, item := range page.Items {
6570
seqpid, err := seqid.FromString(item.Key)
6671
if err != nil {
67-
panic(err.Error())
72+
continue
6873
}
6974
out += ren.renderProposalListItem(ufmt.Sprintf("%v", int64(seqpid)), d)
7075
out += "---\n\n"
@@ -78,10 +83,15 @@ func (ren *render) renderActiveProposals(url string, d *GovDAO) string {
7883
func (ren *render) renderProposalPage(sPid string, d *GovDAO) string {
7984
pid, err := strconv.ParseInt(sPid, 10, 64)
8085
if err != nil {
81-
panic(err.Error())
86+
return ufmt.Sprintf("# Error: Invalid proposal ID format.\n\n\n%s\n\n", err.Error())
8287
}
88+
89+
p, err := dao.GetProposal(cross, dao.ProposalID(pid))
90+
if err != nil {
91+
return ufmt.Sprintf("# Proposal not found\n\n%s", err.Error())
92+
}
93+
8394
ps := d.pss.GetStatus(dao.ProposalID(pid))
84-
p := dao.MustGetProposal(cross, dao.ProposalID(pid))
8595
out := ufmt.Sprintf("## Prop #%v - %v\n", pid, p.Title())
8696
out += "Author: " + tryResolveAddr(p.Author()) + "\n\n"
8797

@@ -102,10 +112,15 @@ func (ren *render) renderProposalPage(sPid string, d *GovDAO) string {
102112
func (ren *render) renderProposalListItem(sPid string, d *GovDAO) string {
103113
pid, err := strconv.ParseInt(sPid, 10, 64)
104114
if err != nil {
105-
panic(err.Error())
115+
return ufmt.Sprintf("# Error: Invalid proposal ID format.\n\n\n%s\n\n", err.Error())
106116
}
117+
118+
p, err := dao.GetProposal(cross, dao.ProposalID(pid))
119+
if err != nil {
120+
return ufmt.Sprintf("# Proposal not found\n\n%s\n\n", err.Error())
121+
}
122+
107123
ps := d.pss.GetStatus(dao.ProposalID(pid))
108-
p := dao.MustGetProposal(cross, dao.ProposalID(pid))
109124
out := ufmt.Sprintf("### [Prop #%v - %v](%v:%v)\n", pid, p.Title(), ren.relativeRealmPath, pid)
110125
out += ufmt.Sprintf("Author: %s\n\n", tryResolveAddr(p.Author()))
111126

@@ -122,9 +137,13 @@ func (ren *render) renderProposalListItem(sPid string, d *GovDAO) string {
122137
func (ren *render) renderVotesForProposal(sPid string, d *GovDAO) string {
123138
pid, err := strconv.ParseInt(sPid, 10, 64)
124139
if err != nil {
125-
panic(err.Error())
140+
return ufmt.Sprintf("# Error: Invalid proposal ID format.\n\n\n%s\n\n", err.Error())
126141
}
142+
127143
ps := d.pss.GetStatus(dao.ProposalID(pid))
144+
if ps == nil {
145+
return ufmt.Sprintf("# Proposal not found\n\nProposal %v does not exist.", pid)
146+
}
128147

129148
out := ""
130149
out += ufmt.Sprintf("# Proposal #%v - Vote List\n\n", pid)
@@ -138,6 +157,9 @@ func isPropActive(ps *proposalStatus) bool {
138157
}
139158

140159
func getPropStatus(ps *proposalStatus) string {
160+
if ps == nil {
161+
return "UNKNOWN"
162+
}
141163
if ps.Accepted {
142164
return "ACCEPTED"
143165
} else if ps.Denied {

0 commit comments

Comments
 (0)