tangled
alpha
login
or
join now
tangled.org
/
core
732
fork
atom
Monorepo for Tangled
tangled.org
732
fork
atom
overview
issues
175
pulls
82
pipelines
appview/pulls: add functionality to edit pull title and description
#1131
open
opened by
murex.tngl.sh
6 days ago
targeting
master
from
murex.tngl.sh/tangled
:
feat/pull-edit
add a pencil icon in UI
add corresponding route to edit PR title and description
closes:
#175
👍
👎
😆
🎉
🫤
❤️
🚀
👀
👍
0
👎
0
😆
0
🎉
0
🫤
0
❤️
0
🚀
0
👀
0
Labels
None yet.
assignee
None yet.
Participants
1
AT URI
at://did:plc:owyua2lvxbs55wyhs22dqu2s/sh.tangled.repo.pull/3mgoby3czel22
+227
-2
7 changed files
Diff
round
#0
expand all
collapse all
unified
split
appview
db
pulls.go
pages
pages.go
templates
repo
pulls
fragments
pullHeader.html
putPull.html
pulls
pulls.go
router.go
validator
pull.go
+19
appview/db/pulls.go
···
644
644
return err
645
645
}
646
646
647
647
+
func UpdatePullTitleBody(e Execer, repoAt syntax.ATURI, pullId int, title, body string) error {
648
648
+
_, err := e.Exec(
649
649
+
`update pulls set title = ?, body = ? where repo_at = ? and pull_id = ?`,
650
650
+
title, body, repoAt, pullId,
651
651
+
)
652
652
+
return err
653
653
+
}
654
654
+
655
655
+
func UpdatePullTitleBodyAndRefs(tx *sql.Tx, pull *models.Pull) error {
656
656
+
_, err := tx.Exec(
657
657
+
`update pulls set title = ?, body = ? where repo_at = ? and pull_id = ?`,
658
658
+
pull.Title, pull.Body, pull.RepoAt, pull.PullId,
659
659
+
)
660
660
+
if err != nil {
661
661
+
return err
662
662
+
}
663
663
+
return putReferences(tx, pull.AtUri(), pull.References)
664
664
+
}
665
665
+
647
666
func ResubmitPull(e Execer, pullAt syntax.ATURI, newRoundNumber int, newPatch string, combinedPatch string, newSourceRev string) error {
648
667
_, err := e.Exec(`
649
668
insert into pull_submissions (pull_at, round_number, patch, combined, source_rev)
+10
appview/pages/pages.go
···
1202
1202
return p.executeRepo("repo/pulls/pull", w, params)
1203
1203
}
1204
1204
1205
1205
+
type EditPullParams struct {
1206
1206
+
LoggedInUser *oauth.MultiAccountUser
1207
1207
+
RepoInfo repoinfo.RepoInfo
1208
1208
+
Pull *models.Pull
1209
1209
+
}
1210
1210
+
1211
1211
+
func (p *Pages) EditPullFragment(w io.Writer, params EditPullParams) error {
1212
1212
+
return p.executePlain("repo/pulls/fragments/putPull", w, params)
1213
1213
+
}
1214
1214
+
1205
1215
type RepoPullPatchParams struct {
1206
1216
LoggedInUser *oauth.MultiAccountUser
1207
1217
RepoInfo repoinfo.RepoInfo
+12
-2
appview/pages/templates/repo/pulls/fragments/pullHeader.html
···
1
1
{{ define "repo/pulls/fragments/pullHeader" }}
2
2
+
<div id="pull-{{ .Pull.PullId }}">
2
3
<header class="pb-2">
3
4
<h1 class="text-2xl dark:text-white">
4
5
{{ .Pull.Title | description }}
···
55
56
</span>
56
57
{{ end }}
57
58
</span>
59
59
+
{{ if and .LoggedInUser (eq .LoggedInUser.Did .Pull.OwnerDid) }}
60
60
+
<a
61
61
+
class="text-gray-500 dark:text-gray-400 flex gap-1 items-center group cursor-pointer"
62
62
+
hx-get="/{{ .RepoInfo.FullName }}/pulls/{{ .Pull.PullId }}/edit"
63
63
+
hx-swap="innerHTML"
64
64
+
hx-target="#pull-{{ .Pull.PullId }}"
65
65
+
title="Edit pull request">
66
66
+
{{ i "pencil" "size-3" }}
67
67
+
</a>
68
68
+
{{ end }}
58
69
</div>
59
70
60
71
{{ if .Pull.Body }}
···
70
81
"ThreadAt" .Pull.AtUri) }}
71
82
</div>
72
83
</section>
73
73
-
74
74
-
84
84
+
</div>
75
85
{{ end }}
+44
appview/pages/templates/repo/pulls/fragments/putPull.html
···
1
1
+
{{ define "repo/pulls/fragments/putPull" }}
2
2
+
<form
3
3
+
hx-post="/{{ .RepoInfo.FullName }}/pulls/{{ .Pull.PullId }}/edit"
4
4
+
hx-trigger="submit, keydown[(ctrlKey || metaKey) && key=='Enter'] from:(#title,#body)"
5
5
+
hx-swap="none"
6
6
+
hx-indicator="#pull-edit-spinner">
7
7
+
<div class="flex flex-col gap-2">
8
8
+
<div>
9
9
+
<label for="title">title</label>
10
10
+
<input type="text" name="title" id="title" class="w-full" value="{{ .Pull.Title }}" />
11
11
+
</div>
12
12
+
<div>
13
13
+
<label for="body">body</label>
14
14
+
<textarea
15
15
+
name="body"
16
16
+
id="body"
17
17
+
rows="15"
18
18
+
class="w-full resize-y"
19
19
+
placeholder="Describe your pull request. Markdown is supported."
20
20
+
>{{ .Pull.Body }}</textarea>
21
21
+
</div>
22
22
+
<div class="flex justify-between">
23
23
+
<div id="pull-edit-error" class="error"></div>
24
24
+
<div class="flex gap-2 items-center">
25
25
+
<a
26
26
+
class="btn flex items-center gap-2 no-underline hover:no-underline"
27
27
+
type="button"
28
28
+
href="/{{ .RepoInfo.FullName }}/pulls/{{ .Pull.PullId }}"
29
29
+
>
30
30
+
{{ i "x" "w-4 h-4" }}
31
31
+
cancel
32
32
+
</a>
33
33
+
<button type="submit" class="btn-create flex items-center gap-2">
34
34
+
{{ i "pencil" "w-4 h-4" }}
35
35
+
edit pull request
36
36
+
<span id="pull-edit-spinner" class="group">
37
37
+
{{ i "loader-circle" "w-4 h-4 animate-spin hidden group-[.htmx-request]:inline" }}
38
38
+
</span>
39
39
+
</button>
40
40
+
</div>
41
41
+
</div>
42
42
+
</div>
43
43
+
</form>
44
44
+
{{ end }}
+124
appview/pulls/pulls.go
···
2343
2343
s.pages.HxLocation(w, fmt.Sprintf("/%s/pulls/%d", ownerSlashRepo, pull.PullId))
2344
2344
}
2345
2345
2346
2346
+
func (s *Pulls) EditPull(w http.ResponseWriter, r *http.Request) {
2347
2347
+
l := s.logger.With("handler", "EditPull")
2348
2348
+
user := s.oauth.GetMultiAccountUser(r)
2349
2349
+
noticeId := "pull-edit-error"
2350
2350
+
2351
2351
+
_, err := s.repoResolver.Resolve(r)
2352
2352
+
if err != nil {
2353
2353
+
l.Error("failed to resolve repo", "err", err)
2354
2354
+
s.pages.Notice(w, noticeId, "Failed to edit pull.")
2355
2355
+
return
2356
2356
+
}
2357
2357
+
2358
2358
+
pull, ok := r.Context().Value("pull").(*models.Pull)
2359
2359
+
if !ok {
2360
2360
+
l.Error("failed to get pull")
2361
2361
+
s.pages.Notice(w, noticeId, "Failed to edit pull.")
2362
2362
+
return
2363
2363
+
}
2364
2364
+
2365
2365
+
if user.Active.Did != pull.OwnerDid {
2366
2366
+
s.pages.Notice(w, noticeId, "Only the pull request author can edit it.")
2367
2367
+
return
2368
2368
+
}
2369
2369
+
2370
2370
+
switch r.Method {
2371
2371
+
case http.MethodGet:
2372
2372
+
s.pages.EditPullFragment(w, pages.EditPullParams{
2373
2373
+
LoggedInUser: user,
2374
2374
+
RepoInfo: s.repoResolver.GetRepoInfo(r, user),
2375
2375
+
Pull: pull,
2376
2376
+
})
2377
2377
+
case http.MethodPost:
2378
2378
+
updatedPull := *pull
2379
2379
+
updatedPull.Title = r.FormValue("title")
2380
2380
+
updatedPull.Body = r.FormValue("body")
2381
2381
+
updatedPull.Mentions, updatedPull.References = s.mentionsResolver.Resolve(r.Context(), updatedPull.Body)
2382
2382
+
2383
2383
+
if err := s.validator.ValidatePull(&updatedPull); err != nil {
2384
2384
+
l.Error("validation error", "err", err)
2385
2385
+
s.pages.Notice(w, noticeId, fmt.Sprintf("Failed to edit pull: %s", err))
2386
2386
+
return
2387
2387
+
}
2388
2388
+
2389
2389
+
client, err := s.oauth.AuthorizedClient(r)
2390
2390
+
if err != nil {
2391
2391
+
l.Error("failed to get authorized client", "err", err)
2392
2392
+
s.pages.Notice(w, noticeId, "Failed to edit pull.")
2393
2393
+
return
2394
2394
+
}
2395
2395
+
2396
2396
+
ex, err := comatproto.RepoGetRecord(r.Context(), client, "", tangled.RepoPullNSID, pull.OwnerDid, pull.Rkey)
2397
2397
+
if err != nil {
2398
2398
+
l.Error("failed to get record", "err", err)
2399
2399
+
s.pages.Notice(w, noticeId, "Failed to edit pull, no record found on PDS.")
2400
2400
+
return
2401
2401
+
}
2402
2402
+
2403
2403
+
var existingRecord tangled.RepoPull
2404
2404
+
bytes, err := ex.Value.MarshalJSON()
2405
2405
+
if err != nil {
2406
2406
+
l.Error("failed to marshal existing record", "err", err)
2407
2407
+
s.pages.Notice(w, noticeId, "Failed to edit pull.")
2408
2408
+
return
2409
2409
+
}
2410
2410
+
if err := json.Unmarshal(bytes, &existingRecord); err != nil {
2411
2411
+
l.Error("failed to decode existing record", "err", err)
2412
2412
+
s.pages.Notice(w, noticeId, "Failed to edit pull.")
2413
2413
+
return
2414
2414
+
}
2415
2415
+
2416
2416
+
existingRecord.Title = updatedPull.Title
2417
2417
+
if updatedPull.Body != "" {
2418
2418
+
existingRecord.Body = &updatedPull.Body
2419
2419
+
} else {
2420
2420
+
existingRecord.Body = nil
2421
2421
+
}
2422
2422
+
existingRecord.Mentions = make([]string, len(updatedPull.Mentions))
2423
2423
+
for i, did := range updatedPull.Mentions {
2424
2424
+
existingRecord.Mentions[i] = string(did)
2425
2425
+
}
2426
2426
+
existingRecord.References = make([]string, len(updatedPull.References))
2427
2427
+
for i, uri := range updatedPull.References {
2428
2428
+
existingRecord.References[i] = string(uri)
2429
2429
+
}
2430
2430
+
2431
2431
+
_, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{
2432
2432
+
Collection: tangled.RepoPullNSID,
2433
2433
+
Repo: pull.OwnerDid,
2434
2434
+
Rkey: pull.Rkey,
2435
2435
+
SwapRecord: ex.Cid,
2436
2436
+
Record: &lexutil.LexiconTypeDecoder{
2437
2437
+
Val: &existingRecord,
2438
2438
+
},
2439
2439
+
})
2440
2440
+
if err != nil {
2441
2441
+
l.Error("failed to edit record on PDS", "err", err)
2442
2442
+
s.pages.Notice(w, noticeId, "Failed to edit pull on PDS.")
2443
2443
+
return
2444
2444
+
}
2445
2445
+
2446
2446
+
tx, err := s.db.BeginTx(r.Context(), nil)
2447
2447
+
if err != nil {
2448
2448
+
l.Error("failed to begin transaction", "err", err)
2449
2449
+
s.pages.Notice(w, noticeId, "Failed to edit pull.")
2450
2450
+
return
2451
2451
+
}
2452
2452
+
defer tx.Rollback()
2453
2453
+
2454
2454
+
if err := db.UpdatePullTitleBodyAndRefs(tx, &updatedPull); err != nil {
2455
2455
+
l.Error("failed to update pull in DB", "err", err)
2456
2456
+
s.pages.Notice(w, noticeId, "Failed to edit pull.")
2457
2457
+
return
2458
2458
+
}
2459
2459
+
2460
2460
+
if err = tx.Commit(); err != nil {
2461
2461
+
l.Error("failed to commit", "err", err)
2462
2462
+
s.pages.Notice(w, noticeId, "Failed to edit pull.")
2463
2463
+
return
2464
2464
+
}
2465
2465
+
2466
2466
+
s.pages.HxRefresh(w)
2467
2467
+
}
2468
2468
+
}
2469
2469
+
2346
2470
func (s *Pulls) ClosePull(w http.ResponseWriter, r *http.Request) {
2347
2471
user := s.oauth.GetMultiAccountUser(r)
2348
2472
+2
appview/pulls/router.go
···
41
41
42
42
r.Group(func(r chi.Router) {
43
43
r.Use(middleware.AuthMiddleware(s.oauth))
44
44
+
r.Get("/edit", s.EditPull)
45
45
+
r.Post("/edit", s.EditPull)
44
46
r.Route("/resubmit", func(r chi.Router) {
45
47
r.Get("/", s.ResubmitPull)
46
48
r.Post("/", s.ResubmitPull)
+16
appview/validator/pull.go
···
1
1
+
package validator
2
2
+
3
3
+
import (
4
4
+
"fmt"
5
5
+
"strings"
6
6
+
7
7
+
"tangled.org/core/appview/models"
8
8
+
)
9
9
+
10
10
+
func (v *Validator) ValidatePull(pull *models.Pull) error {
11
11
+
if pull.Title == "" || strings.TrimSpace(v.sanitizer.SanitizeDescription(pull.Title)) == "" {
12
12
+
return fmt.Errorf("pull title is empty")
13
13
+
}
14
14
+
15
15
+
return nil
16
16
+
}
History
1 round
0 comments
sign up
or
login
to add to the discussion
murex.tngl.sh
submitted
#0
6d
1 commit
expand
collapse
1b4f2a0f
appview/pulls: add functionality to edit pull title and description
Signed-off-by: Nupur Agrawal <nupur202000@gmail.com>
no conflicts, ready to merge
expand 0 comments