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 01bca8de b4ada4d6

verified
+487 -6
api/tangled/cbor_gen.go
··· 6881 6881 } 6882 6882 6883 6883 cw := cbg.NewCborWriter(w) 6884 - fieldCount := 5 6884 + fieldCount := 7 6885 6885 6886 6886 if t.Body == nil { 6887 + fieldCount-- 6888 + } 6889 + 6890 + if t.Mentions == nil { 6891 + fieldCount-- 6892 + } 6893 + 6894 + if t.References == nil { 6887 6895 fieldCount-- 6888 6896 } 6889 6897 ··· 6988 6996 return err 6989 6997 } 6990 6998 6999 + // t.Mentions ([]string) (slice) 7000 + if t.Mentions != nil { 7001 + 7002 + if len("mentions") > 1000000 { 7003 + return xerrors.Errorf("Value in field \"mentions\" was too long") 7004 + } 7005 + 7006 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("mentions"))); err != nil { 7007 + return err 7008 + } 7009 + if _, err := cw.WriteString(string("mentions")); err != nil { 7010 + return err 7011 + } 7012 + 7013 + if len(t.Mentions) > 8192 { 7014 + return xerrors.Errorf("Slice value in field t.Mentions was too long") 7015 + } 7016 + 7017 + if err := cw.WriteMajorTypeHeader(cbg.MajArray, uint64(len(t.Mentions))); err != nil { 7018 + return err 7019 + } 7020 + for _, v := range t.Mentions { 7021 + if len(v) > 1000000 { 7022 + return xerrors.Errorf("Value in field v was too long") 7023 + } 7024 + 7025 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(v))); err != nil { 7026 + return err 7027 + } 7028 + if _, err := cw.WriteString(string(v)); err != nil { 7029 + return err 7030 + } 7031 + 7032 + } 7033 + } 7034 + 6991 7035 // t.CreatedAt (string) (string) 6992 7036 if len("createdAt") > 1000000 { 6993 7037 return xerrors.Errorf("Value in field \"createdAt\" was too long") ··· 7010 7054 if _, err := cw.WriteString(string(t.CreatedAt)); err != nil { 7011 7055 return err 7012 7056 } 7057 + 7058 + // t.References ([]string) (slice) 7059 + if t.References != nil { 7060 + 7061 + if len("references") > 1000000 { 7062 + return xerrors.Errorf("Value in field \"references\" was too long") 7063 + } 7064 + 7065 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("references"))); err != nil { 7066 + return err 7067 + } 7068 + if _, err := cw.WriteString(string("references")); err != nil { 7069 + return err 7070 + } 7071 + 7072 + if len(t.References) > 8192 { 7073 + return xerrors.Errorf("Slice value in field t.References was too long") 7074 + } 7075 + 7076 + if err := cw.WriteMajorTypeHeader(cbg.MajArray, uint64(len(t.References))); err != nil { 7077 + return err 7078 + } 7079 + for _, v := range t.References { 7080 + if len(v) > 1000000 { 7081 + return xerrors.Errorf("Value in field v was too long") 7082 + } 7083 + 7084 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(v))); err != nil { 7085 + return err 7086 + } 7087 + if _, err := cw.WriteString(string(v)); err != nil { 7088 + return err 7089 + } 7090 + 7091 + } 7092 + } 7013 7093 return nil 7014 7094 } 7015 7095 ··· 7038 7118 7039 7119 n := extra 7040 7120 7041 - nameBuf := make([]byte, 9) 7121 + nameBuf := make([]byte, 10) 7042 7122 for i := uint64(0); i < n; i++ { 7043 7123 nameLen, ok, err := cbg.ReadFullStringIntoBuf(cr, nameBuf, 1000000) 7044 7124 if err != nil { ··· 7107 7187 } 7108 7188 7109 7189 t.Title = string(sval) 7190 + } 7191 + // t.Mentions ([]string) (slice) 7192 + case "mentions": 7193 + 7194 + maj, extra, err = cr.ReadHeader() 7195 + if err != nil { 7196 + return err 7197 + } 7198 + 7199 + if extra > 8192 { 7200 + return fmt.Errorf("t.Mentions: array too large (%d)", extra) 7201 + } 7202 + 7203 + if maj != cbg.MajArray { 7204 + return fmt.Errorf("expected cbor array") 7205 + } 7206 + 7207 + if extra > 0 { 7208 + t.Mentions = make([]string, extra) 7209 + } 7210 + 7211 + for i := 0; i < int(extra); i++ { 7212 + { 7213 + var maj byte 7214 + var extra uint64 7215 + var err error 7216 + _ = maj 7217 + _ = extra 7218 + _ = err 7219 + 7220 + { 7221 + sval, err := cbg.ReadStringWithMax(cr, 1000000) 7222 + if err != nil { 7223 + return err 7224 + } 7225 + 7226 + t.Mentions[i] = string(sval) 7227 + } 7228 + 7229 + } 7110 7230 } 7111 7231 // t.CreatedAt (string) (string) 7112 7232 case "createdAt": ··· 7119 7239 7120 7240 t.CreatedAt = string(sval) 7121 7241 } 7242 + // t.References ([]string) (slice) 7243 + case "references": 7244 + 7245 + maj, extra, err = cr.ReadHeader() 7246 + if err != nil { 7247 + return err 7248 + } 7249 + 7250 + if extra > 8192 { 7251 + return fmt.Errorf("t.References: array too large (%d)", extra) 7252 + } 7253 + 7254 + if maj != cbg.MajArray { 7255 + return fmt.Errorf("expected cbor array") 7256 + } 7257 + 7258 + if extra > 0 { 7259 + t.References = make([]string, extra) 7260 + } 7261 + 7262 + for i := 0; i < int(extra); i++ { 7263 + { 7264 + var maj byte 7265 + var extra uint64 7266 + var err error 7267 + _ = maj 7268 + _ = extra 7269 + _ = err 7270 + 7271 + { 7272 + sval, err := cbg.ReadStringWithMax(cr, 1000000) 7273 + if err != nil { 7274 + return err 7275 + } 7276 + 7277 + t.References[i] = string(sval) 7278 + } 7279 + 7280 + } 7281 + } 7122 7282 7123 7283 default: 7124 7284 // Field doesn't exist on this type, so ignore it ··· 7137 7297 } 7138 7298 7139 7299 cw := cbg.NewCborWriter(w) 7140 - fieldCount := 5 7300 + fieldCount := 7 7301 + 7302 + if t.Mentions == nil { 7303 + fieldCount-- 7304 + } 7305 + 7306 + if t.References == nil { 7307 + fieldCount-- 7308 + } 7141 7309 7142 7310 if t.ReplyTo == nil { 7143 7311 fieldCount-- ··· 7244 7412 } 7245 7413 } 7246 7414 7415 + // t.Mentions ([]string) (slice) 7416 + if t.Mentions != nil { 7417 + 7418 + if len("mentions") > 1000000 { 7419 + return xerrors.Errorf("Value in field \"mentions\" was too long") 7420 + } 7421 + 7422 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("mentions"))); err != nil { 7423 + return err 7424 + } 7425 + if _, err := cw.WriteString(string("mentions")); err != nil { 7426 + return err 7427 + } 7428 + 7429 + if len(t.Mentions) > 8192 { 7430 + return xerrors.Errorf("Slice value in field t.Mentions was too long") 7431 + } 7432 + 7433 + if err := cw.WriteMajorTypeHeader(cbg.MajArray, uint64(len(t.Mentions))); err != nil { 7434 + return err 7435 + } 7436 + for _, v := range t.Mentions { 7437 + if len(v) > 1000000 { 7438 + return xerrors.Errorf("Value in field v was too long") 7439 + } 7440 + 7441 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(v))); err != nil { 7442 + return err 7443 + } 7444 + if _, err := cw.WriteString(string(v)); err != nil { 7445 + return err 7446 + } 7447 + 7448 + } 7449 + } 7450 + 7247 7451 // t.CreatedAt (string) (string) 7248 7452 if len("createdAt") > 1000000 { 7249 7453 return xerrors.Errorf("Value in field \"createdAt\" was too long") ··· 7266 7470 if _, err := cw.WriteString(string(t.CreatedAt)); err != nil { 7267 7471 return err 7268 7472 } 7473 + 7474 + // t.References ([]string) (slice) 7475 + if t.References != nil { 7476 + 7477 + if len("references") > 1000000 { 7478 + return xerrors.Errorf("Value in field \"references\" was too long") 7479 + } 7480 + 7481 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("references"))); err != nil { 7482 + return err 7483 + } 7484 + if _, err := cw.WriteString(string("references")); err != nil { 7485 + return err 7486 + } 7487 + 7488 + if len(t.References) > 8192 { 7489 + return xerrors.Errorf("Slice value in field t.References was too long") 7490 + } 7491 + 7492 + if err := cw.WriteMajorTypeHeader(cbg.MajArray, uint64(len(t.References))); err != nil { 7493 + return err 7494 + } 7495 + for _, v := range t.References { 7496 + if len(v) > 1000000 { 7497 + return xerrors.Errorf("Value in field v was too long") 7498 + } 7499 + 7500 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(v))); err != nil { 7501 + return err 7502 + } 7503 + if _, err := cw.WriteString(string(v)); err != nil { 7504 + return err 7505 + } 7506 + 7507 + } 7508 + } 7269 7509 return nil 7270 7510 } 7271 7511 ··· 7294 7534 7295 7535 n := extra 7296 7536 7297 - nameBuf := make([]byte, 9) 7537 + nameBuf := make([]byte, 10) 7298 7538 for i := uint64(0); i < n; i++ { 7299 7539 nameLen, ok, err := cbg.ReadFullStringIntoBuf(cr, nameBuf, 1000000) 7300 7540 if err != nil { ··· 7362 7602 } 7363 7603 7364 7604 t.ReplyTo = (*string)(&sval) 7605 + } 7606 + } 7607 + // t.Mentions ([]string) (slice) 7608 + case "mentions": 7609 + 7610 + maj, extra, err = cr.ReadHeader() 7611 + if err != nil { 7612 + return err 7613 + } 7614 + 7615 + if extra > 8192 { 7616 + return fmt.Errorf("t.Mentions: array too large (%d)", extra) 7617 + } 7618 + 7619 + if maj != cbg.MajArray { 7620 + return fmt.Errorf("expected cbor array") 7621 + } 7622 + 7623 + if extra > 0 { 7624 + t.Mentions = make([]string, extra) 7625 + } 7626 + 7627 + for i := 0; i < int(extra); i++ { 7628 + { 7629 + var maj byte 7630 + var extra uint64 7631 + var err error 7632 + _ = maj 7633 + _ = extra 7634 + _ = err 7635 + 7636 + { 7637 + sval, err := cbg.ReadStringWithMax(cr, 1000000) 7638 + if err != nil { 7639 + return err 7640 + } 7641 + 7642 + t.Mentions[i] = string(sval) 7643 + } 7644 + 7365 7645 } 7366 7646 } 7367 7647 // t.CreatedAt (string) (string) ··· 7374 7654 } 7375 7655 7376 7656 t.CreatedAt = string(sval) 7657 + } 7658 + // t.References ([]string) (slice) 7659 + case "references": 7660 + 7661 + maj, extra, err = cr.ReadHeader() 7662 + if err != nil { 7663 + return err 7664 + } 7665 + 7666 + if extra > 8192 { 7667 + return fmt.Errorf("t.References: array too large (%d)", extra) 7668 + } 7669 + 7670 + if maj != cbg.MajArray { 7671 + return fmt.Errorf("expected cbor array") 7672 + } 7673 + 7674 + if extra > 0 { 7675 + t.References = make([]string, extra) 7676 + } 7677 + 7678 + for i := 0; i < int(extra); i++ { 7679 + { 7680 + var maj byte 7681 + var extra uint64 7682 + var err error 7683 + _ = maj 7684 + _ = extra 7685 + _ = err 7686 + 7687 + { 7688 + sval, err := cbg.ReadStringWithMax(cr, 1000000) 7689 + if err != nil { 7690 + return err 7691 + } 7692 + 7693 + t.References[i] = string(sval) 7694 + } 7695 + 7696 + } 7377 7697 } 7378 7698 7379 7699 default: ··· 7892 8212 } 7893 8213 7894 8214 cw := cbg.NewCborWriter(w) 8215 + fieldCount := 6 7895 8216 7896 - if _, err := cw.Write([]byte{164}); err != nil { 8217 + if t.Mentions == nil { 8218 + fieldCount-- 8219 + } 8220 + 8221 + if t.References == nil { 8222 + fieldCount-- 8223 + } 8224 + 8225 + if _, err := cw.Write(cbg.CborEncodeMajorType(cbg.MajMap, uint64(fieldCount))); err != nil { 7897 8226 return err 7898 8227 } 7899 8228 ··· 7962 8291 return err 7963 8292 } 7964 8293 8294 + // t.Mentions ([]string) (slice) 8295 + if t.Mentions != nil { 8296 + 8297 + if len("mentions") > 1000000 { 8298 + return xerrors.Errorf("Value in field \"mentions\" was too long") 8299 + } 8300 + 8301 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("mentions"))); err != nil { 8302 + return err 8303 + } 8304 + if _, err := cw.WriteString(string("mentions")); err != nil { 8305 + return err 8306 + } 8307 + 8308 + if len(t.Mentions) > 8192 { 8309 + return xerrors.Errorf("Slice value in field t.Mentions was too long") 8310 + } 8311 + 8312 + if err := cw.WriteMajorTypeHeader(cbg.MajArray, uint64(len(t.Mentions))); err != nil { 8313 + return err 8314 + } 8315 + for _, v := range t.Mentions { 8316 + if len(v) > 1000000 { 8317 + return xerrors.Errorf("Value in field v was too long") 8318 + } 8319 + 8320 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(v))); err != nil { 8321 + return err 8322 + } 8323 + if _, err := cw.WriteString(string(v)); err != nil { 8324 + return err 8325 + } 8326 + 8327 + } 8328 + } 8329 + 7965 8330 // t.CreatedAt (string) (string) 7966 8331 if len("createdAt") > 1000000 { 7967 8332 return xerrors.Errorf("Value in field \"createdAt\" was too long") ··· 7983 8348 } 7984 8349 if _, err := cw.WriteString(string(t.CreatedAt)); err != nil { 7985 8350 return err 8351 + } 8352 + 8353 + // t.References ([]string) (slice) 8354 + if t.References != nil { 8355 + 8356 + if len("references") > 1000000 { 8357 + return xerrors.Errorf("Value in field \"references\" was too long") 8358 + } 8359 + 8360 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("references"))); err != nil { 8361 + return err 8362 + } 8363 + if _, err := cw.WriteString(string("references")); err != nil { 8364 + return err 8365 + } 8366 + 8367 + if len(t.References) > 8192 { 8368 + return xerrors.Errorf("Slice value in field t.References was too long") 8369 + } 8370 + 8371 + if err := cw.WriteMajorTypeHeader(cbg.MajArray, uint64(len(t.References))); err != nil { 8372 + return err 8373 + } 8374 + for _, v := range t.References { 8375 + if len(v) > 1000000 { 8376 + return xerrors.Errorf("Value in field v was too long") 8377 + } 8378 + 8379 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(v))); err != nil { 8380 + return err 8381 + } 8382 + if _, err := cw.WriteString(string(v)); err != nil { 8383 + return err 8384 + } 8385 + 8386 + } 7986 8387 } 7987 8388 return nil 7988 8389 } ··· 8012 8413 8013 8414 n := extra 8014 8415 8015 - nameBuf := make([]byte, 9) 8416 + nameBuf := make([]byte, 10) 8016 8417 for i := uint64(0); i < n; i++ { 8017 8418 nameLen, ok, err := cbg.ReadFullStringIntoBuf(cr, nameBuf, 1000000) 8018 8419 if err != nil { ··· 8061 8462 8062 8463 t.LexiconTypeID = string(sval) 8063 8464 } 8465 + // t.Mentions ([]string) (slice) 8466 + case "mentions": 8467 + 8468 + maj, extra, err = cr.ReadHeader() 8469 + if err != nil { 8470 + return err 8471 + } 8472 + 8473 + if extra > 8192 { 8474 + return fmt.Errorf("t.Mentions: array too large (%d)", extra) 8475 + } 8476 + 8477 + if maj != cbg.MajArray { 8478 + return fmt.Errorf("expected cbor array") 8479 + } 8480 + 8481 + if extra > 0 { 8482 + t.Mentions = make([]string, extra) 8483 + } 8484 + 8485 + for i := 0; i < int(extra); i++ { 8486 + { 8487 + var maj byte 8488 + var extra uint64 8489 + var err error 8490 + _ = maj 8491 + _ = extra 8492 + _ = err 8493 + 8494 + { 8495 + sval, err := cbg.ReadStringWithMax(cr, 1000000) 8496 + if err != nil { 8497 + return err 8498 + } 8499 + 8500 + t.Mentions[i] = string(sval) 8501 + } 8502 + 8503 + } 8504 + } 8064 8505 // t.CreatedAt (string) (string) 8065 8506 case "createdAt": 8066 8507 ··· 8071 8512 } 8072 8513 8073 8514 t.CreatedAt = string(sval) 8515 + } 8516 + // t.References ([]string) (slice) 8517 + case "references": 8518 + 8519 + maj, extra, err = cr.ReadHeader() 8520 + if err != nil { 8521 + return err 8522 + } 8523 + 8524 + if extra > 8192 { 8525 + return fmt.Errorf("t.References: array too large (%d)", extra) 8526 + } 8527 + 8528 + if maj != cbg.MajArray { 8529 + return fmt.Errorf("expected cbor array") 8530 + } 8531 + 8532 + if extra > 0 { 8533 + t.References = make([]string, extra) 8534 + } 8535 + 8536 + for i := 0; i < int(extra); i++ { 8537 + { 8538 + var maj byte 8539 + var extra uint64 8540 + var err error 8541 + _ = maj 8542 + _ = extra 8543 + _ = err 8544 + 8545 + { 8546 + sval, err := cbg.ReadStringWithMax(cr, 1000000) 8547 + if err != nil { 8548 + return err 8549 + } 8550 + 8551 + t.References[i] = string(sval) 8552 + } 8553 + 8554 + } 8074 8555 } 8075 8556 8076 8557 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 }