Monorepo for Tangled tangled.org

appview,lexicons: atprotate the mentions & references

Storing references parsed from the markdown body in atproto record and
DB. There can be lots of reference types considering the from/to types
so storing both as AT-URIs

Using `sql.Tx` more to combine multiple DB query to single recoverable
operation.

Note: Pulls don't have mentinos/references yet

Signed-off-by: Seongmin Lee <git@boltless.me>

boltless.me da6ba16d e874a6b3

verified
+487 -6
api/tangled/cbor_gen.go
··· 6938 6938 } 6939 6939 6940 6940 cw := cbg.NewCborWriter(w) 6941 - fieldCount := 5 6941 + fieldCount := 7 6942 6942 6943 6943 if t.Body == nil { 6944 + fieldCount-- 6945 + } 6946 + 6947 + if t.Mentions == nil { 6948 + fieldCount-- 6949 + } 6950 + 6951 + if t.References == nil { 6944 6952 fieldCount-- 6945 6953 } 6946 6954 ··· 7045 7053 return err 7046 7054 } 7047 7055 7056 + // t.Mentions ([]string) (slice) 7057 + if t.Mentions != nil { 7058 + 7059 + if len("mentions") > 1000000 { 7060 + return xerrors.Errorf("Value in field \"mentions\" was too long") 7061 + } 7062 + 7063 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("mentions"))); err != nil { 7064 + return err 7065 + } 7066 + if _, err := cw.WriteString(string("mentions")); err != nil { 7067 + return err 7068 + } 7069 + 7070 + if len(t.Mentions) > 8192 { 7071 + return xerrors.Errorf("Slice value in field t.Mentions was too long") 7072 + } 7073 + 7074 + if err := cw.WriteMajorTypeHeader(cbg.MajArray, uint64(len(t.Mentions))); err != nil { 7075 + return err 7076 + } 7077 + for _, v := range t.Mentions { 7078 + if len(v) > 1000000 { 7079 + return xerrors.Errorf("Value in field v was too long") 7080 + } 7081 + 7082 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(v))); err != nil { 7083 + return err 7084 + } 7085 + if _, err := cw.WriteString(string(v)); err != nil { 7086 + return err 7087 + } 7088 + 7089 + } 7090 + } 7091 + 7048 7092 // t.CreatedAt (string) (string) 7049 7093 if len("createdAt") > 1000000 { 7050 7094 return xerrors.Errorf("Value in field \"createdAt\" was too long") ··· 7067 7111 if _, err := cw.WriteString(string(t.CreatedAt)); err != nil { 7068 7112 return err 7069 7113 } 7114 + 7115 + // t.References ([]string) (slice) 7116 + if t.References != nil { 7117 + 7118 + if len("references") > 1000000 { 7119 + return xerrors.Errorf("Value in field \"references\" was too long") 7120 + } 7121 + 7122 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("references"))); err != nil { 7123 + return err 7124 + } 7125 + if _, err := cw.WriteString(string("references")); err != nil { 7126 + return err 7127 + } 7128 + 7129 + if len(t.References) > 8192 { 7130 + return xerrors.Errorf("Slice value in field t.References was too long") 7131 + } 7132 + 7133 + if err := cw.WriteMajorTypeHeader(cbg.MajArray, uint64(len(t.References))); err != nil { 7134 + return err 7135 + } 7136 + for _, v := range t.References { 7137 + if len(v) > 1000000 { 7138 + return xerrors.Errorf("Value in field v was too long") 7139 + } 7140 + 7141 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(v))); err != nil { 7142 + return err 7143 + } 7144 + if _, err := cw.WriteString(string(v)); err != nil { 7145 + return err 7146 + } 7147 + 7148 + } 7149 + } 7070 7150 return nil 7071 7151 } 7072 7152 ··· 7095 7175 7096 7176 n := extra 7097 7177 7098 - nameBuf := make([]byte, 9) 7178 + nameBuf := make([]byte, 10) 7099 7179 for i := uint64(0); i < n; i++ { 7100 7180 nameLen, ok, err := cbg.ReadFullStringIntoBuf(cr, nameBuf, 1000000) 7101 7181 if err != nil { ··· 7164 7244 } 7165 7245 7166 7246 t.Title = string(sval) 7247 + } 7248 + // t.Mentions ([]string) (slice) 7249 + case "mentions": 7250 + 7251 + maj, extra, err = cr.ReadHeader() 7252 + if err != nil { 7253 + return err 7254 + } 7255 + 7256 + if extra > 8192 { 7257 + return fmt.Errorf("t.Mentions: array too large (%d)", extra) 7258 + } 7259 + 7260 + if maj != cbg.MajArray { 7261 + return fmt.Errorf("expected cbor array") 7262 + } 7263 + 7264 + if extra > 0 { 7265 + t.Mentions = make([]string, extra) 7266 + } 7267 + 7268 + for i := 0; i < int(extra); i++ { 7269 + { 7270 + var maj byte 7271 + var extra uint64 7272 + var err error 7273 + _ = maj 7274 + _ = extra 7275 + _ = err 7276 + 7277 + { 7278 + sval, err := cbg.ReadStringWithMax(cr, 1000000) 7279 + if err != nil { 7280 + return err 7281 + } 7282 + 7283 + t.Mentions[i] = string(sval) 7284 + } 7285 + 7286 + } 7167 7287 } 7168 7288 // t.CreatedAt (string) (string) 7169 7289 case "createdAt": ··· 7176 7296 7177 7297 t.CreatedAt = string(sval) 7178 7298 } 7299 + // t.References ([]string) (slice) 7300 + case "references": 7301 + 7302 + maj, extra, err = cr.ReadHeader() 7303 + if err != nil { 7304 + return err 7305 + } 7306 + 7307 + if extra > 8192 { 7308 + return fmt.Errorf("t.References: array too large (%d)", extra) 7309 + } 7310 + 7311 + if maj != cbg.MajArray { 7312 + return fmt.Errorf("expected cbor array") 7313 + } 7314 + 7315 + if extra > 0 { 7316 + t.References = make([]string, extra) 7317 + } 7318 + 7319 + for i := 0; i < int(extra); i++ { 7320 + { 7321 + var maj byte 7322 + var extra uint64 7323 + var err error 7324 + _ = maj 7325 + _ = extra 7326 + _ = err 7327 + 7328 + { 7329 + sval, err := cbg.ReadStringWithMax(cr, 1000000) 7330 + if err != nil { 7331 + return err 7332 + } 7333 + 7334 + t.References[i] = string(sval) 7335 + } 7336 + 7337 + } 7338 + } 7179 7339 7180 7340 default: 7181 7341 // Field doesn't exist on this type, so ignore it ··· 7194 7354 } 7195 7355 7196 7356 cw := cbg.NewCborWriter(w) 7197 - fieldCount := 5 7357 + fieldCount := 7 7358 + 7359 + if t.Mentions == nil { 7360 + fieldCount-- 7361 + } 7362 + 7363 + if t.References == nil { 7364 + fieldCount-- 7365 + } 7198 7366 7199 7367 if t.ReplyTo == nil { 7200 7368 fieldCount-- ··· 7301 7469 } 7302 7470 } 7303 7471 7472 + // t.Mentions ([]string) (slice) 7473 + if t.Mentions != nil { 7474 + 7475 + if len("mentions") > 1000000 { 7476 + return xerrors.Errorf("Value in field \"mentions\" was too long") 7477 + } 7478 + 7479 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("mentions"))); err != nil { 7480 + return err 7481 + } 7482 + if _, err := cw.WriteString(string("mentions")); err != nil { 7483 + return err 7484 + } 7485 + 7486 + if len(t.Mentions) > 8192 { 7487 + return xerrors.Errorf("Slice value in field t.Mentions was too long") 7488 + } 7489 + 7490 + if err := cw.WriteMajorTypeHeader(cbg.MajArray, uint64(len(t.Mentions))); err != nil { 7491 + return err 7492 + } 7493 + for _, v := range t.Mentions { 7494 + if len(v) > 1000000 { 7495 + return xerrors.Errorf("Value in field v was too long") 7496 + } 7497 + 7498 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(v))); err != nil { 7499 + return err 7500 + } 7501 + if _, err := cw.WriteString(string(v)); err != nil { 7502 + return err 7503 + } 7504 + 7505 + } 7506 + } 7507 + 7304 7508 // t.CreatedAt (string) (string) 7305 7509 if len("createdAt") > 1000000 { 7306 7510 return xerrors.Errorf("Value in field \"createdAt\" was too long") ··· 7323 7527 if _, err := cw.WriteString(string(t.CreatedAt)); err != nil { 7324 7528 return err 7325 7529 } 7530 + 7531 + // t.References ([]string) (slice) 7532 + if t.References != nil { 7533 + 7534 + if len("references") > 1000000 { 7535 + return xerrors.Errorf("Value in field \"references\" was too long") 7536 + } 7537 + 7538 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("references"))); err != nil { 7539 + return err 7540 + } 7541 + if _, err := cw.WriteString(string("references")); err != nil { 7542 + return err 7543 + } 7544 + 7545 + if len(t.References) > 8192 { 7546 + return xerrors.Errorf("Slice value in field t.References was too long") 7547 + } 7548 + 7549 + if err := cw.WriteMajorTypeHeader(cbg.MajArray, uint64(len(t.References))); err != nil { 7550 + return err 7551 + } 7552 + for _, v := range t.References { 7553 + if len(v) > 1000000 { 7554 + return xerrors.Errorf("Value in field v was too long") 7555 + } 7556 + 7557 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(v))); err != nil { 7558 + return err 7559 + } 7560 + if _, err := cw.WriteString(string(v)); err != nil { 7561 + return err 7562 + } 7563 + 7564 + } 7565 + } 7326 7566 return nil 7327 7567 } 7328 7568 ··· 7351 7591 7352 7592 n := extra 7353 7593 7354 - nameBuf := make([]byte, 9) 7594 + nameBuf := make([]byte, 10) 7355 7595 for i := uint64(0); i < n; i++ { 7356 7596 nameLen, ok, err := cbg.ReadFullStringIntoBuf(cr, nameBuf, 1000000) 7357 7597 if err != nil { ··· 7419 7659 } 7420 7660 7421 7661 t.ReplyTo = (*string)(&sval) 7662 + } 7663 + } 7664 + // t.Mentions ([]string) (slice) 7665 + case "mentions": 7666 + 7667 + maj, extra, err = cr.ReadHeader() 7668 + if err != nil { 7669 + return err 7670 + } 7671 + 7672 + if extra > 8192 { 7673 + return fmt.Errorf("t.Mentions: array too large (%d)", extra) 7674 + } 7675 + 7676 + if maj != cbg.MajArray { 7677 + return fmt.Errorf("expected cbor array") 7678 + } 7679 + 7680 + if extra > 0 { 7681 + t.Mentions = make([]string, extra) 7682 + } 7683 + 7684 + for i := 0; i < int(extra); i++ { 7685 + { 7686 + var maj byte 7687 + var extra uint64 7688 + var err error 7689 + _ = maj 7690 + _ = extra 7691 + _ = err 7692 + 7693 + { 7694 + sval, err := cbg.ReadStringWithMax(cr, 1000000) 7695 + if err != nil { 7696 + return err 7697 + } 7698 + 7699 + t.Mentions[i] = string(sval) 7700 + } 7701 + 7422 7702 } 7423 7703 } 7424 7704 // t.CreatedAt (string) (string) ··· 7431 7711 } 7432 7712 7433 7713 t.CreatedAt = string(sval) 7714 + } 7715 + // t.References ([]string) (slice) 7716 + case "references": 7717 + 7718 + maj, extra, err = cr.ReadHeader() 7719 + if err != nil { 7720 + return err 7721 + } 7722 + 7723 + if extra > 8192 { 7724 + return fmt.Errorf("t.References: array too large (%d)", extra) 7725 + } 7726 + 7727 + if maj != cbg.MajArray { 7728 + return fmt.Errorf("expected cbor array") 7729 + } 7730 + 7731 + if extra > 0 { 7732 + t.References = make([]string, extra) 7733 + } 7734 + 7735 + for i := 0; i < int(extra); i++ { 7736 + { 7737 + var maj byte 7738 + var extra uint64 7739 + var err error 7740 + _ = maj 7741 + _ = extra 7742 + _ = err 7743 + 7744 + { 7745 + sval, err := cbg.ReadStringWithMax(cr, 1000000) 7746 + if err != nil { 7747 + return err 7748 + } 7749 + 7750 + t.References[i] = string(sval) 7751 + } 7752 + 7753 + } 7434 7754 } 7435 7755 7436 7756 default: ··· 7949 8269 } 7950 8270 7951 8271 cw := cbg.NewCborWriter(w) 8272 + fieldCount := 6 7952 8273 7953 - if _, err := cw.Write([]byte{164}); err != nil { 8274 + if t.Mentions == nil { 8275 + fieldCount-- 8276 + } 8277 + 8278 + if t.References == nil { 8279 + fieldCount-- 8280 + } 8281 + 8282 + if _, err := cw.Write(cbg.CborEncodeMajorType(cbg.MajMap, uint64(fieldCount))); err != nil { 7954 8283 return err 7955 8284 } 7956 8285 ··· 8019 8348 return err 8020 8349 } 8021 8350 8351 + // t.Mentions ([]string) (slice) 8352 + if t.Mentions != nil { 8353 + 8354 + if len("mentions") > 1000000 { 8355 + return xerrors.Errorf("Value in field \"mentions\" was too long") 8356 + } 8357 + 8358 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("mentions"))); err != nil { 8359 + return err 8360 + } 8361 + if _, err := cw.WriteString(string("mentions")); err != nil { 8362 + return err 8363 + } 8364 + 8365 + if len(t.Mentions) > 8192 { 8366 + return xerrors.Errorf("Slice value in field t.Mentions was too long") 8367 + } 8368 + 8369 + if err := cw.WriteMajorTypeHeader(cbg.MajArray, uint64(len(t.Mentions))); err != nil { 8370 + return err 8371 + } 8372 + for _, v := range t.Mentions { 8373 + if len(v) > 1000000 { 8374 + return xerrors.Errorf("Value in field v was too long") 8375 + } 8376 + 8377 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(v))); err != nil { 8378 + return err 8379 + } 8380 + if _, err := cw.WriteString(string(v)); err != nil { 8381 + return err 8382 + } 8383 + 8384 + } 8385 + } 8386 + 8022 8387 // t.CreatedAt (string) (string) 8023 8388 if len("createdAt") > 1000000 { 8024 8389 return xerrors.Errorf("Value in field \"createdAt\" was too long") ··· 8040 8405 } 8041 8406 if _, err := cw.WriteString(string(t.CreatedAt)); err != nil { 8042 8407 return err 8408 + } 8409 + 8410 + // t.References ([]string) (slice) 8411 + if t.References != nil { 8412 + 8413 + if len("references") > 1000000 { 8414 + return xerrors.Errorf("Value in field \"references\" was too long") 8415 + } 8416 + 8417 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("references"))); err != nil { 8418 + return err 8419 + } 8420 + if _, err := cw.WriteString(string("references")); err != nil { 8421 + return err 8422 + } 8423 + 8424 + if len(t.References) > 8192 { 8425 + return xerrors.Errorf("Slice value in field t.References was too long") 8426 + } 8427 + 8428 + if err := cw.WriteMajorTypeHeader(cbg.MajArray, uint64(len(t.References))); err != nil { 8429 + return err 8430 + } 8431 + for _, v := range t.References { 8432 + if len(v) > 1000000 { 8433 + return xerrors.Errorf("Value in field v was too long") 8434 + } 8435 + 8436 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(v))); err != nil { 8437 + return err 8438 + } 8439 + if _, err := cw.WriteString(string(v)); err != nil { 8440 + return err 8441 + } 8442 + 8443 + } 8043 8444 } 8044 8445 return nil 8045 8446 } ··· 8069 8470 8070 8471 n := extra 8071 8472 8072 - nameBuf := make([]byte, 9) 8473 + nameBuf := make([]byte, 10) 8073 8474 for i := uint64(0); i < n; i++ { 8074 8475 nameLen, ok, err := cbg.ReadFullStringIntoBuf(cr, nameBuf, 1000000) 8075 8476 if err != nil { ··· 8118 8519 8119 8520 t.LexiconTypeID = string(sval) 8120 8521 } 8522 + // t.Mentions ([]string) (slice) 8523 + case "mentions": 8524 + 8525 + maj, extra, err = cr.ReadHeader() 8526 + if err != nil { 8527 + return err 8528 + } 8529 + 8530 + if extra > 8192 { 8531 + return fmt.Errorf("t.Mentions: array too large (%d)", extra) 8532 + } 8533 + 8534 + if maj != cbg.MajArray { 8535 + return fmt.Errorf("expected cbor array") 8536 + } 8537 + 8538 + if extra > 0 { 8539 + t.Mentions = make([]string, extra) 8540 + } 8541 + 8542 + for i := 0; i < int(extra); i++ { 8543 + { 8544 + var maj byte 8545 + var extra uint64 8546 + var err error 8547 + _ = maj 8548 + _ = extra 8549 + _ = err 8550 + 8551 + { 8552 + sval, err := cbg.ReadStringWithMax(cr, 1000000) 8553 + if err != nil { 8554 + return err 8555 + } 8556 + 8557 + t.Mentions[i] = string(sval) 8558 + } 8559 + 8560 + } 8561 + } 8121 8562 // t.CreatedAt (string) (string) 8122 8563 case "createdAt": 8123 8564 ··· 8128 8569 } 8129 8570 8130 8571 t.CreatedAt = string(sval) 8572 + } 8573 + // t.References ([]string) (slice) 8574 + case "references": 8575 + 8576 + maj, extra, err = cr.ReadHeader() 8577 + if err != nil { 8578 + return err 8579 + } 8580 + 8581 + if extra > 8192 { 8582 + return fmt.Errorf("t.References: array too large (%d)", extra) 8583 + } 8584 + 8585 + if maj != cbg.MajArray { 8586 + return fmt.Errorf("expected cbor array") 8587 + } 8588 + 8589 + if extra > 0 { 8590 + t.References = make([]string, extra) 8591 + } 8592 + 8593 + for i := 0; i < int(extra); i++ { 8594 + { 8595 + var maj byte 8596 + var extra uint64 8597 + var err error 8598 + _ = maj 8599 + _ = extra 8600 + _ = err 8601 + 8602 + { 8603 + sval, err := cbg.ReadStringWithMax(cr, 1000000) 8604 + if err != nil { 8605 + return err 8606 + } 8607 + 8608 + t.References[i] = string(sval) 8609 + } 8610 + 8611 + } 8131 8612 } 8132 8613 8133 8614 default:
+7 -5
api/tangled/issuecomment.go
··· 17 17 } // 18 18 // RECORDTYPE: RepoIssueComment 19 19 type RepoIssueComment struct { 20 - LexiconTypeID string `json:"$type,const=sh.tangled.repo.issue.comment" cborgen:"$type,const=sh.tangled.repo.issue.comment"` 21 - Body string `json:"body" cborgen:"body"` 22 - CreatedAt string `json:"createdAt" cborgen:"createdAt"` 23 - Issue string `json:"issue" cborgen:"issue"` 24 - ReplyTo *string `json:"replyTo,omitempty" cborgen:"replyTo,omitempty"` 20 + LexiconTypeID string `json:"$type,const=sh.tangled.repo.issue.comment" cborgen:"$type,const=sh.tangled.repo.issue.comment"` 21 + Body string `json:"body" cborgen:"body"` 22 + CreatedAt string `json:"createdAt" cborgen:"createdAt"` 23 + Issue string `json:"issue" cborgen:"issue"` 24 + Mentions []string `json:"mentions,omitempty" cborgen:"mentions,omitempty"` 25 + References []string `json:"references,omitempty" cborgen:"references,omitempty"` 26 + ReplyTo *string `json:"replyTo,omitempty" cborgen:"replyTo,omitempty"` 25 27 }
+6 -4
api/tangled/pullcomment.go
··· 17 17 } // 18 18 // RECORDTYPE: RepoPullComment 19 19 type RepoPullComment struct { 20 - LexiconTypeID string `json:"$type,const=sh.tangled.repo.pull.comment" cborgen:"$type,const=sh.tangled.repo.pull.comment"` 21 - Body string `json:"body" cborgen:"body"` 22 - CreatedAt string `json:"createdAt" cborgen:"createdAt"` 23 - Pull string `json:"pull" cborgen:"pull"` 20 + LexiconTypeID string `json:"$type,const=sh.tangled.repo.pull.comment" cborgen:"$type,const=sh.tangled.repo.pull.comment"` 21 + Body string `json:"body" cborgen:"body"` 22 + CreatedAt string `json:"createdAt" cborgen:"createdAt"` 23 + Mentions []string `json:"mentions,omitempty" cborgen:"mentions,omitempty"` 24 + Pull string `json:"pull" cborgen:"pull"` 25 + References []string `json:"references,omitempty" cborgen:"references,omitempty"` 24 26 }
+7 -5
api/tangled/repoissue.go
··· 17 17 } // 18 18 // RECORDTYPE: RepoIssue 19 19 type RepoIssue struct { 20 - LexiconTypeID string `json:"$type,const=sh.tangled.repo.issue" cborgen:"$type,const=sh.tangled.repo.issue"` 21 - Body *string `json:"body,omitempty" cborgen:"body,omitempty"` 22 - CreatedAt string `json:"createdAt" cborgen:"createdAt"` 23 - Repo string `json:"repo" cborgen:"repo"` 24 - Title string `json:"title" cborgen:"title"` 20 + LexiconTypeID string `json:"$type,const=sh.tangled.repo.issue" cborgen:"$type,const=sh.tangled.repo.issue"` 21 + Body *string `json:"body,omitempty" cborgen:"body,omitempty"` 22 + CreatedAt string `json:"createdAt" cborgen:"createdAt"` 23 + Mentions []string `json:"mentions,omitempty" cborgen:"mentions,omitempty"` 24 + References []string `json:"references,omitempty" cborgen:"references,omitempty"` 25 + Repo string `json:"repo" cborgen:"repo"` 26 + Title string `json:"title" cborgen:"title"` 25 27 }
+9
appview/db/db.go
··· 561 561 email_notifications integer not null default 0 562 562 ); 563 563 564 + create table if not exists reference_links ( 565 + id integer primary key autoincrement, 566 + from_at text not null, 567 + to_at text not null, 568 + unique (from_at, to_at) 569 + ); 570 + 564 571 create table if not exists migrations ( 565 572 id integer primary key autoincrement, 566 573 name text unique ··· 571 578 create index if not exists idx_notifications_recipient_read on notifications(recipient_did, read); 572 579 create index if not exists idx_stars_created on stars(created); 573 580 create index if not exists idx_stars_repo_at_created on stars(repo_at, created); 581 + create index if not exists idx_references_from_at on reference_links(from_at); 582 + create index if not exists idx_references_to_at on reference_links(to_at); 574 583 `) 575 584 if err != nil { 576 585 return nil, err
+73 -18
appview/db/issues.go
··· 10 10 "time" 11 11 12 12 "github.com/bluesky-social/indigo/atproto/syntax" 13 + "tangled.org/core/api/tangled" 13 14 "tangled.org/core/appview/models" 14 15 "tangled.org/core/appview/pagination" 15 16 ) ··· 69 70 returning rowid, issue_id 70 71 `, issue.RepoAt, issue.Did, issue.Rkey, newIssueId, issue.Title, issue.Body) 71 72 72 - return row.Scan(&issue.Id, &issue.IssueId) 73 + err = row.Scan(&issue.Id, &issue.IssueId) 74 + if err != nil { 75 + return fmt.Errorf("scan row: %w", err) 76 + } 77 + 78 + if err := putReferences(tx, issue.AtUri(), issue.References); err != nil { 79 + return fmt.Errorf("put reference_links: %w", err) 80 + } 81 + return nil 73 82 } 74 83 75 84 func updateIssue(tx *sql.Tx, issue *models.Issue) error { ··· 79 88 set title = ?, body = ?, edited = ? 80 89 where did = ? and rkey = ? 81 90 `, issue.Title, issue.Body, time.Now().Format(time.RFC3339), issue.Did, issue.Rkey) 82 - return err 91 + if err != nil { 92 + return err 93 + } 94 + 95 + if err := putReferences(tx, issue.AtUri(), issue.References); err != nil { 96 + return fmt.Errorf("put reference_links: %w", err) 97 + } 98 + return nil 83 99 } 84 100 85 101 func GetIssuesPaginated(e Execer, page pagination.Page, filters ...filter) ([]models.Issue, error) { ··· 234 250 } 235 251 } 236 252 253 + // collect references for each issue 254 + allReferencs, err := GetReferencesAll(e, FilterIn("from_at", issueAts)) 255 + if err != nil { 256 + return nil, fmt.Errorf("failed to query reference_links: %w", err) 257 + } 258 + for issueAt, references := range allReferencs { 259 + if issue, ok := issueMap[issueAt.String()]; ok { 260 + issue.References = references 261 + } 262 + } 263 + 237 264 var issues []models.Issue 238 265 for _, i := range issueMap { 239 266 issues = append(issues, *i) ··· 323 350 return ids, nil 324 351 } 325 352 326 - func AddIssueComment(e Execer, c models.IssueComment) (int64, error) { 327 - result, err := e.Exec( 353 + func AddIssueComment(tx *sql.Tx, c models.IssueComment) (int64, error) { 354 + result, err := tx.Exec( 328 355 `insert into issue_comments ( 329 356 did, 330 357 rkey, ··· 363 390 return 0, err 364 391 } 365 392 393 + if err := putReferences(tx, c.AtUri(), c.References); err != nil { 394 + return 0, fmt.Errorf("put reference_links: %w", err) 395 + } 396 + 366 397 return id, nil 367 398 } 368 399 ··· 386 417 } 387 418 388 419 func GetIssueComments(e Execer, filters ...filter) ([]models.IssueComment, error) { 389 - var comments []models.IssueComment 420 + commentMap := make(map[string]*models.IssueComment) 390 421 391 422 var conditions []string 392 423 var args []any ··· 465 496 comment.ReplyTo = &replyTo.V 466 497 } 467 498 468 - comments = append(comments, comment) 499 + atUri := comment.AtUri().String() 500 + commentMap[atUri] = &comment 469 501 } 470 502 471 503 if err = rows.Err(); err != nil { 472 504 return nil, err 473 505 } 474 506 507 + // collect references for each comments 508 + commentAts := slices.Collect(maps.Keys(commentMap)) 509 + allReferencs, err := GetReferencesAll(e, FilterIn("from_at", commentAts)) 510 + if err != nil { 511 + return nil, fmt.Errorf("failed to query reference_links: %w", err) 512 + } 513 + for commentAt, references := range allReferencs { 514 + if comment, ok := commentMap[commentAt.String()]; ok { 515 + comment.References = references 516 + } 517 + } 518 + 519 + var comments []models.IssueComment 520 + for _, c := range commentMap { 521 + comments = append(comments, *c) 522 + } 523 + 524 + sort.Slice(comments, func(i, j int) bool { 525 + return comments[i].Created.After(comments[j].Created) 526 + }) 527 + 475 528 return comments, nil 476 529 } 477 530 478 - func DeleteIssues(e Execer, filters ...filter) error { 479 - var conditions []string 480 - var args []any 481 - for _, filter := range filters { 482 - conditions = append(conditions, filter.Condition()) 483 - args = append(args, filter.Arg()...) 531 + func DeleteIssues(tx *sql.Tx, did, rkey string) error { 532 + _, err := tx.Exec( 533 + `delete from issues 534 + where did = ? and rkey = ?`, 535 + did, 536 + rkey, 537 + ) 538 + if err != nil { 539 + return fmt.Errorf("delete issue: %w", err) 484 540 } 485 541 486 - whereClause := "" 487 - if conditions != nil { 488 - whereClause = " where " + strings.Join(conditions, " and ") 542 + uri := syntax.ATURI(fmt.Sprintf("at://%s/%s/%s", did, tangled.RepoIssueNSID, rkey)) 543 + err = deleteReferences(tx, uri) 544 + if err != nil { 545 + return fmt.Errorf("delete reference_links: %w", err) 489 546 } 490 547 491 - query := fmt.Sprintf(`delete from issues %s`, whereClause) 492 - _, err := e.Exec(query, args...) 493 - return err 548 + return nil 494 549 } 495 550 496 551 func CloseIssues(e Execer, filters ...filter) error {
+31 -5
appview/db/pulls.go
··· 432 432 submissionIds := slices.Collect(maps.Keys(submissionMap)) 433 433 comments, err := GetPullComments(e, FilterIn("submission_id", submissionIds)) 434 434 if err != nil { 435 - return nil, err 435 + return nil, fmt.Errorf("failed to get pull comments: %w", err) 436 436 } 437 437 for _, comment := range comments { 438 438 if submission, ok := submissionMap[comment.SubmissionId]; ok { ··· 492 492 } 493 493 defer rows.Close() 494 494 495 - var comments []models.PullComment 495 + commentMap := make(map[string]*models.PullComment) 496 496 for rows.Next() { 497 497 var comment models.PullComment 498 498 var createdAt string ··· 514 514 comment.Created = t 515 515 } 516 516 517 - comments = append(comments, comment) 517 + atUri := comment.AtUri().String() 518 + commentMap[atUri] = &comment 518 519 } 519 520 520 521 if err := rows.Err(); err != nil { 521 522 return nil, err 522 523 } 523 524 525 + // collect references for each comments 526 + commentAts := slices.Collect(maps.Keys(commentMap)) 527 + allReferencs, err := GetReferencesAll(e, FilterIn("from_at", commentAts)) 528 + if err != nil { 529 + return nil, fmt.Errorf("failed to query reference_links: %w", err) 530 + } 531 + for commentAt, references := range allReferencs { 532 + if comment, ok := commentMap[commentAt.String()]; ok { 533 + comment.References = references 534 + } 535 + } 536 + 537 + var comments []models.PullComment 538 + for _, c := range commentMap { 539 + comments = append(comments, *c) 540 + } 541 + 542 + sort.Slice(comments, func(i, j int) bool { 543 + return comments[i].Created.Before(comments[j].Created) 544 + }) 545 + 524 546 return comments, nil 525 547 } 526 548 ··· 600 622 return pulls, nil 601 623 } 602 624 603 - func NewPullComment(e Execer, comment *models.PullComment) (int64, error) { 625 + func NewPullComment(tx *sql.Tx, comment *models.PullComment) (int64, error) { 604 626 query := `insert into pull_comments (owner_did, repo_at, submission_id, comment_at, pull_id, body) values (?, ?, ?, ?, ?, ?)` 605 - res, err := e.Exec( 627 + res, err := tx.Exec( 606 628 query, 607 629 comment.OwnerDid, 608 630 comment.RepoAt, ··· 618 640 i, err := res.LastInsertId() 619 641 if err != nil { 620 642 return 0, err 643 + } 644 + 645 + if err := putReferences(tx, comment.AtUri(), comment.References); err != nil { 646 + return 0, fmt.Errorf("put reference_links: %w", err) 621 647 } 622 648 623 649 return i, nil
+93 -14
appview/db/reference.go
··· 10 10 "tangled.org/core/appview/models" 11 11 ) 12 12 13 - // FindReferences resolves refLinks to Issue/PR/IssueComment/PullComment ATURIs. 13 + // ValidateReferenceLinks resolves refLinks to Issue/PR/IssueComment/PullComment ATURIs. 14 14 // It will ignore missing refLinks. 15 - func FindReferences(e Execer, refLinks []models.ReferenceLink) ([]syntax.ATURI, error) { 15 + func ValidateReferenceLinks(e Execer, refLinks []models.ReferenceLink) ([]syntax.ATURI, error) { 16 16 var ( 17 17 issueRefs []models.ReferenceLink 18 18 pullRefs []models.ReferenceLink ··· 27 27 } 28 28 issueUris, err := findIssueReferences(e, issueRefs) 29 29 if err != nil { 30 - return nil, err 30 + return nil, fmt.Errorf("find issue references: %w", err) 31 31 } 32 32 pullUris, err := findPullReferences(e, pullRefs) 33 33 if err != nil { 34 - return nil, err 34 + return nil, fmt.Errorf("find pull references: %w", err) 35 35 } 36 36 37 37 return append(issueUris, pullUris...), nil ··· 101 101 } 102 102 uris = append(uris, uri) 103 103 } 104 + if err := rows.Err(); err != nil { 105 + return nil, fmt.Errorf("iterate rows: %w", err) 106 + } 107 + 104 108 return uris, nil 105 109 } 106 110 ··· 120 124 ) 121 125 select 122 126 p.owner_did, p.rkey, 123 - c.owner_did, c.rkey 127 + c.comment_at 124 128 from input inp 125 129 join repos r 126 130 on r.did = inp.owner_did ··· 146 150 for rows.Next() { 147 151 // Scan rows 148 152 var pullOwner, pullRkey string 149 - var commentOwner, commentRkey sql.NullString 153 + var commentUri sql.NullString 150 154 var uri syntax.ATURI 151 - if err := rows.Scan(&pullOwner, &pullRkey, &commentOwner, &commentRkey); err != nil { 155 + if err := rows.Scan(&pullOwner, &pullRkey, &commentUri); err != nil { 152 156 return nil, err 153 157 } 154 - if commentOwner.Valid && commentRkey.Valid { 155 - uri = syntax.ATURI(fmt.Sprintf( 156 - "at://%s/%s/%s", 157 - commentOwner.String, 158 - tangled.RepoPullCommentNSID, 159 - commentRkey.String, 160 - )) 158 + if commentUri.Valid { 159 + // no-op 160 + uri = syntax.ATURI(commentUri.String) 161 161 } else { 162 162 uri = syntax.ATURI(fmt.Sprintf( 163 163 "at://%s/%s/%s", ··· 170 170 } 171 171 return uris, nil 172 172 } 173 + 174 + func putReferences(tx *sql.Tx, fromAt syntax.ATURI, references []syntax.ATURI) error { 175 + fmt.Println("insering references", references) 176 + err := deleteReferences(tx, fromAt) 177 + if err != nil { 178 + return fmt.Errorf("delete old reference_links: %w", err) 179 + } 180 + if len(references) == 0 { 181 + return nil 182 + } 183 + 184 + values := make([]string, 0, len(references)) 185 + args := make([]any, 0, len(references)*2) 186 + for _, ref := range references { 187 + values = append(values, "(?, ?)") 188 + args = append(args, fromAt, ref) 189 + } 190 + _, err = tx.Exec( 191 + fmt.Sprintf( 192 + `insert into reference_links (from_at, to_at) 193 + values %s`, 194 + strings.Join(values, ","), 195 + ), 196 + args..., 197 + ) 198 + if err != nil { 199 + return fmt.Errorf("insert new reference_links: %w", err) 200 + } 201 + return nil 202 + } 203 + 204 + func deleteReferences(tx *sql.Tx, fromAt syntax.ATURI) error { 205 + _, err := tx.Exec(`delete from reference_links where from_at = ?`, fromAt) 206 + return err 207 + } 208 + 209 + func GetReferencesAll(e Execer, filters ...filter) (map[syntax.ATURI][]syntax.ATURI, error) { 210 + var ( 211 + conditions []string 212 + args []any 213 + ) 214 + for _, filter := range filters { 215 + conditions = append(conditions, filter.Condition()) 216 + args = append(args, filter.Arg()...) 217 + } 218 + 219 + whereClause := "" 220 + if conditions != nil { 221 + whereClause = " where " + strings.Join(conditions, " and ") 222 + } 223 + 224 + rows, err := e.Query( 225 + fmt.Sprintf( 226 + `select from_at, to_at from reference_links %s`, 227 + whereClause, 228 + ), 229 + args..., 230 + ) 231 + if err != nil { 232 + return nil, fmt.Errorf("query reference_links: %w", err) 233 + } 234 + defer rows.Close() 235 + 236 + result := make(map[syntax.ATURI][]syntax.ATURI) 237 + 238 + for rows.Next() { 239 + var from, to syntax.ATURI 240 + if err := rows.Scan(&from, &to); err != nil { 241 + return nil, fmt.Errorf("scan row: %w", err) 242 + } 243 + 244 + result[from] = append(result[from], to) 245 + } 246 + if err := rows.Err(); err != nil { 247 + return nil, fmt.Errorf("iterate rows: %w", err) 248 + } 249 + 250 + return result, nil 251 + }
+22 -5
appview/ingester.go
··· 841 841 return nil 842 842 843 843 case jmodels.CommitOperationDelete: 844 + tx, err := ddb.BeginTx(ctx, nil) 845 + if err != nil { 846 + l.Error("failed to begin transaction", "err", err) 847 + return err 848 + } 849 + defer tx.Rollback() 850 + 844 851 if err := db.DeleteIssues( 845 - ddb, 846 - db.FilterEq("did", did), 847 - db.FilterEq("rkey", rkey), 852 + tx, 853 + did, 854 + rkey, 848 855 ); err != nil { 849 856 l.Error("failed to delete", "err", err) 850 857 return fmt.Errorf("failed to delete issue record: %w", err) 858 + } 859 + if err := tx.Commit(); err != nil { 860 + l.Error("failed to commit txn", "err", err) 861 + return err 851 862 } 852 863 853 864 return nil ··· 888 899 return fmt.Errorf("failed to validate comment: %w", err) 889 900 } 890 901 891 - _, err = db.AddIssueComment(ddb, *comment) 902 + tx, err := ddb.Begin() 903 + if err != nil { 904 + return fmt.Errorf("failed to start transaction: %w", err) 905 + } 906 + defer tx.Rollback() 907 + 908 + _, err = db.AddIssueComment(tx, *comment) 892 909 if err != nil { 893 910 return fmt.Errorf("failed to create issue comment: %w", err) 894 911 } 895 912 896 - return nil 913 + return tx.Commit() 897 914 898 915 case jmodels.CommitOperationDelete: 899 916 if err := db.DeleteIssueComments(
+55 -19
appview/issues/issues.go
··· 241 241 } 242 242 l = l.With("did", issue.Did, "rkey", issue.Rkey) 243 243 244 + tx, err := rp.db.Begin() 245 + if err != nil { 246 + l.Error("failed to start transaction", "err", err) 247 + rp.pages.Notice(w, "issue-comment", "Failed to create comment, try again later.") 248 + return 249 + } 250 + defer tx.Rollback() 251 + 244 252 // delete from PDS 245 253 client, err := rp.oauth.AuthorizedClient(r) 246 254 if err != nil { ··· 261 269 } 262 270 263 271 // delete from db 264 - if err := db.DeleteIssues(rp.db, db.FilterEq("id", issue.Id)); err != nil { 272 + if err := db.DeleteIssues(tx, issue.Did, issue.Rkey); err != nil { 265 273 l.Error("failed to delete issue", "err", err) 266 274 rp.pages.Notice(w, noticeId, "Failed to delete issue.") 267 275 return 268 276 } 277 + tx.Commit() 269 278 270 279 rp.notifier.DeleteIssue(r.Context(), issue) 271 280 ··· 402 411 replyTo = &replyToUri 403 412 } 404 413 405 - mentions, _ := rp.refResolver.Resolve(r.Context(), body) 414 + mentions, references := rp.refResolver.Resolve(r.Context(), body) 406 415 407 416 comment := models.IssueComment{ 408 - Did: user.Did, 409 - Rkey: tid.TID(), 410 - IssueAt: issue.AtUri().String(), 411 - ReplyTo: replyTo, 412 - Body: body, 413 - Created: time.Now(), 417 + Did: user.Did, 418 + Rkey: tid.TID(), 419 + IssueAt: issue.AtUri().String(), 420 + ReplyTo: replyTo, 421 + Body: body, 422 + Created: time.Now(), 423 + Mentions: mentions, 424 + References: references, 414 425 } 415 426 if err = rp.validator.ValidateIssueComment(&comment); err != nil { 416 427 l.Error("failed to validate comment", "err", err) ··· 447 458 } 448 459 }() 449 460 450 - commentId, err := db.AddIssueComment(rp.db, comment) 461 + tx, err := rp.db.Begin() 462 + if err != nil { 463 + l.Error("failed to start transaction", "err", err) 464 + rp.pages.Notice(w, "issue-comment", "Failed to create comment, try again later.") 465 + return 466 + } 467 + defer tx.Rollback() 468 + 469 + commentId, err := db.AddIssueComment(tx, comment) 451 470 if err != nil { 452 471 l.Error("failed to create comment", "err", err) 453 472 rp.pages.Notice(w, "issue-comment", "Failed to create comment.") 454 473 return 455 474 } 475 + err = tx.Commit() 476 + if err != nil { 477 + l.Error("failed to commit transaction", "err", err) 478 + rp.pages.Notice(w, "issue-comment", "Failed to create comment, try again later.") 479 + return 480 + } 456 481 457 482 // reset atUri to make rollback a no-op 458 483 atUri = "" ··· 569 594 newComment.Edited = &now 570 595 record := newComment.AsRecord() 571 596 572 - _, err = db.AddIssueComment(rp.db, newComment) 597 + tx, err := rp.db.Begin() 598 + if err != nil { 599 + l.Error("failed to start transaction", "err", err) 600 + rp.pages.Notice(w, "repo-notice", "Failed to update description, try again later.") 601 + return 602 + } 603 + defer tx.Rollback() 604 + 605 + _, err = db.AddIssueComment(tx, newComment) 573 606 if err != nil { 574 607 l.Error("failed to perferom update-description query", "err", err) 575 608 rp.pages.Notice(w, "repo-notice", "Failed to update description, try again later.") 576 609 return 577 610 } 611 + tx.Commit() 578 612 579 613 // rkey is optional, it was introduced later 580 614 if newComment.Rkey != "" { ··· 881 915 }) 882 916 case http.MethodPost: 883 917 body := r.FormValue("body") 884 - mentions, _ := rp.refResolver.Resolve(r.Context(), body) 918 + mentions, references := rp.refResolver.Resolve(r.Context(), body) 885 919 886 920 issue := &models.Issue{ 887 - RepoAt: f.RepoAt(), 888 - Rkey: tid.TID(), 889 - Title: r.FormValue("title"), 890 - Body: body, 891 - Open: true, 892 - Did: user.Did, 893 - Created: time.Now(), 894 - Repo: &f.Repo, 921 + RepoAt: f.RepoAt(), 922 + Rkey: tid.TID(), 923 + Title: r.FormValue("title"), 924 + Body: body, 925 + Open: true, 926 + Did: user.Did, 927 + Created: time.Now(), 928 + Mentions: mentions, 929 + References: references, 930 + Repo: &f.Repo, 895 931 } 896 932 897 933 if err := rp.validator.ValidateIssue(issue); err != nil {
+64 -28
appview/models/issue.go
··· 10 10 ) 11 11 12 12 type Issue struct { 13 - Id int64 14 - Did string 15 - Rkey string 16 - RepoAt syntax.ATURI 17 - IssueId int 18 - Created time.Time 19 - Edited *time.Time 20 - Deleted *time.Time 21 - Title string 22 - Body string 23 - Open bool 13 + Id int64 14 + Did string 15 + Rkey string 16 + RepoAt syntax.ATURI 17 + IssueId int 18 + Created time.Time 19 + Edited *time.Time 20 + Deleted *time.Time 21 + Title string 22 + Body string 23 + Open bool 24 + Mentions []syntax.DID 25 + References []syntax.ATURI 24 26 25 27 // optionally, populate this when querying for reverse mappings 26 28 // like comment counts, parent repo etc. ··· 34 36 } 35 37 36 38 func (i *Issue) AsRecord() tangled.RepoIssue { 39 + mentions := make([]string, len(i.Mentions)) 40 + for i, did := range i.Mentions { 41 + mentions[i] = string(did) 42 + } 43 + references := make([]string, len(i.References)) 44 + for i, uri := range i.References { 45 + references[i] = string(uri) 46 + } 37 47 return tangled.RepoIssue{ 38 - Repo: i.RepoAt.String(), 39 - Title: i.Title, 40 - Body: &i.Body, 41 - CreatedAt: i.Created.Format(time.RFC3339), 48 + Repo: i.RepoAt.String(), 49 + Title: i.Title, 50 + Body: &i.Body, 51 + Mentions: mentions, 52 + References: references, 53 + CreatedAt: i.Created.Format(time.RFC3339), 42 54 } 43 55 } 44 56 ··· 161 173 } 162 174 163 175 type IssueComment struct { 164 - Id int64 165 - Did string 166 - Rkey string 167 - IssueAt string 168 - ReplyTo *string 169 - Body string 170 - Created time.Time 171 - Edited *time.Time 172 - Deleted *time.Time 176 + Id int64 177 + Did string 178 + Rkey string 179 + IssueAt string 180 + ReplyTo *string 181 + Body string 182 + Created time.Time 183 + Edited *time.Time 184 + Deleted *time.Time 185 + Mentions []syntax.DID 186 + References []syntax.ATURI 173 187 } 174 188 175 189 func (i *IssueComment) AtUri() syntax.ATURI { ··· 177 191 } 178 192 179 193 func (i *IssueComment) AsRecord() tangled.RepoIssueComment { 194 + mentions := make([]string, len(i.Mentions)) 195 + for i, did := range i.Mentions { 196 + mentions[i] = string(did) 197 + } 198 + references := make([]string, len(i.References)) 199 + for i, uri := range i.References { 200 + references[i] = string(uri) 201 + } 180 202 return tangled.RepoIssueComment{ 181 - Body: i.Body, 182 - Issue: i.IssueAt, 183 - CreatedAt: i.Created.Format(time.RFC3339), 184 - ReplyTo: i.ReplyTo, 203 + Body: i.Body, 204 + Issue: i.IssueAt, 205 + CreatedAt: i.Created.Format(time.RFC3339), 206 + ReplyTo: i.ReplyTo, 207 + Mentions: mentions, 208 + References: references, 185 209 } 186 210 } 187 211 ··· 205 229 return nil, err 206 230 } 207 231 232 + i := record 233 + mentions := make([]syntax.DID, len(record.Mentions)) 234 + for i, did := range record.Mentions { 235 + mentions[i] = syntax.DID(did) 236 + } 237 + references := make([]syntax.ATURI, len(record.References)) 238 + for i, uri := range i.References { 239 + references[i] = syntax.ATURI(uri) 240 + } 241 + 208 242 comment := IssueComment{ 209 243 Did: ownerDid, 210 244 Rkey: rkey, ··· 212 246 IssueAt: record.Issue, 213 247 ReplyTo: record.ReplyTo, 214 248 Created: created, 249 + Mentions: mentions, 250 + References: references, 215 251 } 216 252 217 253 return &comment, nil
+26
appview/models/pull.go
··· 148 148 Body string 149 149 150 150 // meta 151 + Mentions []syntax.DID 152 + References []syntax.ATURI 153 + 154 + // meta 151 155 Created time.Time 152 156 } 157 + 158 + func (p *PullComment) AtUri() syntax.ATURI { 159 + return syntax.ATURI(p.CommentAt) 160 + } 161 + 162 + // func (p *PullComment) AsRecord() tangled.RepoPullComment { 163 + // mentions := make([]string, len(p.Mentions)) 164 + // for i, did := range p.Mentions { 165 + // mentions[i] = string(did) 166 + // } 167 + // references := make([]string, len(p.References)) 168 + // for i, uri := range p.References { 169 + // references[i] = string(uri) 170 + // } 171 + // return tangled.RepoPullComment{ 172 + // Pull: p.PullAt, 173 + // Body: p.Body, 174 + // Mentions: mentions, 175 + // References: references, 176 + // CreatedAt: p.Created.Format(time.RFC3339), 177 + // } 178 + // } 153 179 154 180 func (p *Pull) LastRoundNumber() int { 155 181 return len(p.Submissions) - 1
+3 -1
appview/pulls/pulls.go
··· 733 733 return 734 734 } 735 735 736 - mentions, _ := s.refResolver.Resolve(r.Context(), body) 736 + mentions, references := s.refResolver.Resolve(r.Context(), body) 737 737 738 738 // Start a transaction 739 739 tx, err := s.db.BeginTx(r.Context(), nil) ··· 777 777 Body: body, 778 778 CommentAt: atResp.Uri, 779 779 SubmissionId: pull.Submissions[roundNumber].ID, 780 + Mentions: mentions, 781 + References: references, 780 782 } 781 783 782 784 // Create the pull comment in the database with the commentAt field
+2 -2
appview/refresolver/resolver.go
··· 34 34 } 35 35 36 36 func (r *Resolver) Resolve(ctx context.Context, source string) ([]syntax.DID, []syntax.ATURI) { 37 - l := r.logger.With("method", "find_references") 37 + l := r.logger.With("method", "Resolve") 38 38 rawMentions, rawRefs := markup.FindReferences(r.config.Core.AppviewHost, source) 39 39 l.Debug("found possible references", "mentions", rawMentions, "refs", rawRefs) 40 40 idents := r.idResolver.ResolveIdents(ctx, rawMentions) ··· 55 55 rawRef.Handle = string(ident.DID) 56 56 resolvedRefs = append(resolvedRefs, rawRef) 57 57 } 58 - aturiRefs, err := db.FindReferences(r.execer, resolvedRefs) 58 + aturiRefs, err := db.ValidateReferenceLinks(r.execer, resolvedRefs) 59 59 if err != nil { 60 60 l.Error("failed running query", "err", err) 61 61 }
+14
lexicons/issue/comment.json
··· 29 29 "replyTo": { 30 30 "type": "string", 31 31 "format": "at-uri" 32 + }, 33 + "mentions": { 34 + "type": "array", 35 + "items": { 36 + "type": "string", 37 + "format": "did" 38 + } 39 + }, 40 + "references": { 41 + "type": "array", 42 + "items": { 43 + "type": "string", 44 + "format": "at-uri" 45 + } 32 46 } 33 47 } 34 48 }
+14
lexicons/issue/issue.json
··· 24 24 "createdAt": { 25 25 "type": "string", 26 26 "format": "datetime" 27 + }, 28 + "mentions": { 29 + "type": "array", 30 + "items": { 31 + "type": "string", 32 + "format": "did" 33 + } 34 + }, 35 + "references": { 36 + "type": "array", 37 + "items": { 38 + "type": "string", 39 + "format": "at-uri" 40 + } 27 41 } 28 42 } 29 43 }
+14
lexicons/pulls/comment.json
··· 25 25 "createdAt": { 26 26 "type": "string", 27 27 "format": "datetime" 28 + }, 29 + "mentions": { 30 + "type": "array", 31 + "items": { 32 + "type": "string", 33 + "format": "did" 34 + } 35 + }, 36 + "references": { 37 + "type": "array", 38 + "items": { 39 + "type": "string", 40 + "format": "at-uri" 41 + } 28 42 } 29 43 } 30 44 }