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>

authored by boltless.me and committed by Tangled 3b2409e9 d2dcc711

+649 -8
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: ··· 7614 7934 } 7615 7935 7616 7936 cw := cbg.NewCborWriter(w) 7617 - fieldCount := 7 7937 + fieldCount := 9 7618 7938 7619 7939 if t.Body == nil { 7940 + fieldCount-- 7941 + } 7942 + 7943 + if t.Mentions == nil { 7944 + fieldCount-- 7945 + } 7946 + 7947 + if t.References == nil { 7620 7948 fieldCount-- 7621 7949 } 7622 7950 ··· 7760 8088 return err 7761 8089 } 7762 8090 8091 + // t.Mentions ([]string) (slice) 8092 + if t.Mentions != nil { 8093 + 8094 + if len("mentions") > 1000000 { 8095 + return xerrors.Errorf("Value in field \"mentions\" was too long") 8096 + } 8097 + 8098 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("mentions"))); err != nil { 8099 + return err 8100 + } 8101 + if _, err := cw.WriteString(string("mentions")); err != nil { 8102 + return err 8103 + } 8104 + 8105 + if len(t.Mentions) > 8192 { 8106 + return xerrors.Errorf("Slice value in field t.Mentions was too long") 8107 + } 8108 + 8109 + if err := cw.WriteMajorTypeHeader(cbg.MajArray, uint64(len(t.Mentions))); err != nil { 8110 + return err 8111 + } 8112 + for _, v := range t.Mentions { 8113 + if len(v) > 1000000 { 8114 + return xerrors.Errorf("Value in field v was too long") 8115 + } 8116 + 8117 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(v))); err != nil { 8118 + return err 8119 + } 8120 + if _, err := cw.WriteString(string(v)); err != nil { 8121 + return err 8122 + } 8123 + 8124 + } 8125 + } 8126 + 7763 8127 // t.CreatedAt (string) (string) 7764 8128 if len("createdAt") > 1000000 { 7765 8129 return xerrors.Errorf("Value in field \"createdAt\" was too long") ··· 7782 8146 if _, err := cw.WriteString(string(t.CreatedAt)); err != nil { 7783 8147 return err 7784 8148 } 8149 + 8150 + // t.References ([]string) (slice) 8151 + if t.References != nil { 8152 + 8153 + if len("references") > 1000000 { 8154 + return xerrors.Errorf("Value in field \"references\" was too long") 8155 + } 8156 + 8157 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("references"))); err != nil { 8158 + return err 8159 + } 8160 + if _, err := cw.WriteString(string("references")); err != nil { 8161 + return err 8162 + } 8163 + 8164 + if len(t.References) > 8192 { 8165 + return xerrors.Errorf("Slice value in field t.References was too long") 8166 + } 8167 + 8168 + if err := cw.WriteMajorTypeHeader(cbg.MajArray, uint64(len(t.References))); err != nil { 8169 + return err 8170 + } 8171 + for _, v := range t.References { 8172 + if len(v) > 1000000 { 8173 + return xerrors.Errorf("Value in field v was too long") 8174 + } 8175 + 8176 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(v))); err != nil { 8177 + return err 8178 + } 8179 + if _, err := cw.WriteString(string(v)); err != nil { 8180 + return err 8181 + } 8182 + 8183 + } 8184 + } 7785 8185 return nil 7786 8186 } 7787 8187 ··· 7810 8210 7811 8211 n := extra 7812 8212 7813 - nameBuf := make([]byte, 9) 8213 + nameBuf := make([]byte, 10) 7814 8214 for i := uint64(0); i < n; i++ { 7815 8215 nameLen, ok, err := cbg.ReadFullStringIntoBuf(cr, nameBuf, 1000000) 7816 8216 if err != nil { ··· 7919 8319 } 7920 8320 } 7921 8321 8322 + } 8323 + // t.Mentions ([]string) (slice) 8324 + case "mentions": 8325 + 8326 + maj, extra, err = cr.ReadHeader() 8327 + if err != nil { 8328 + return err 8329 + } 8330 + 8331 + if extra > 8192 { 8332 + return fmt.Errorf("t.Mentions: array too large (%d)", extra) 8333 + } 8334 + 8335 + if maj != cbg.MajArray { 8336 + return fmt.Errorf("expected cbor array") 8337 + } 8338 + 8339 + if extra > 0 { 8340 + t.Mentions = make([]string, extra) 8341 + } 8342 + 8343 + for i := 0; i < int(extra); i++ { 8344 + { 8345 + var maj byte 8346 + var extra uint64 8347 + var err error 8348 + _ = maj 8349 + _ = extra 8350 + _ = err 8351 + 8352 + { 8353 + sval, err := cbg.ReadStringWithMax(cr, 1000000) 8354 + if err != nil { 8355 + return err 8356 + } 8357 + 8358 + t.Mentions[i] = string(sval) 8359 + } 8360 + 8361 + } 7922 8362 } 7923 8363 // t.CreatedAt (string) (string) 7924 8364 case "createdAt": ··· 7931 8371 7932 8372 t.CreatedAt = string(sval) 7933 8373 } 8374 + // t.References ([]string) (slice) 8375 + case "references": 8376 + 8377 + maj, extra, err = cr.ReadHeader() 8378 + if err != nil { 8379 + return err 8380 + } 8381 + 8382 + if extra > 8192 { 8383 + return fmt.Errorf("t.References: array too large (%d)", extra) 8384 + } 8385 + 8386 + if maj != cbg.MajArray { 8387 + return fmt.Errorf("expected cbor array") 8388 + } 8389 + 8390 + if extra > 0 { 8391 + t.References = make([]string, extra) 8392 + } 8393 + 8394 + for i := 0; i < int(extra); i++ { 8395 + { 8396 + var maj byte 8397 + var extra uint64 8398 + var err error 8399 + _ = maj 8400 + _ = extra 8401 + _ = err 8402 + 8403 + { 8404 + sval, err := cbg.ReadStringWithMax(cr, 1000000) 8405 + if err != nil { 8406 + return err 8407 + } 8408 + 8409 + t.References[i] = string(sval) 8410 + } 8411 + 8412 + } 8413 + } 7934 8414 7935 8415 default: 7936 8416 // Field doesn't exist on this type, so ignore it ··· 7949 8429 } 7950 8430 7951 8431 cw := cbg.NewCborWriter(w) 8432 + fieldCount := 6 7952 8433 7953 - if _, err := cw.Write([]byte{164}); err != nil { 8434 + if t.Mentions == nil { 8435 + fieldCount-- 8436 + } 8437 + 8438 + if t.References == nil { 8439 + fieldCount-- 8440 + } 8441 + 8442 + if _, err := cw.Write(cbg.CborEncodeMajorType(cbg.MajMap, uint64(fieldCount))); err != nil { 7954 8443 return err 7955 8444 } 7956 8445 ··· 8019 8508 return err 8020 8509 } 8021 8510 8511 + // t.Mentions ([]string) (slice) 8512 + if t.Mentions != nil { 8513 + 8514 + if len("mentions") > 1000000 { 8515 + return xerrors.Errorf("Value in field \"mentions\" was too long") 8516 + } 8517 + 8518 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("mentions"))); err != nil { 8519 + return err 8520 + } 8521 + if _, err := cw.WriteString(string("mentions")); err != nil { 8522 + return err 8523 + } 8524 + 8525 + if len(t.Mentions) > 8192 { 8526 + return xerrors.Errorf("Slice value in field t.Mentions was too long") 8527 + } 8528 + 8529 + if err := cw.WriteMajorTypeHeader(cbg.MajArray, uint64(len(t.Mentions))); err != nil { 8530 + return err 8531 + } 8532 + for _, v := range t.Mentions { 8533 + if len(v) > 1000000 { 8534 + return xerrors.Errorf("Value in field v was too long") 8535 + } 8536 + 8537 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(v))); err != nil { 8538 + return err 8539 + } 8540 + if _, err := cw.WriteString(string(v)); err != nil { 8541 + return err 8542 + } 8543 + 8544 + } 8545 + } 8546 + 8022 8547 // t.CreatedAt (string) (string) 8023 8548 if len("createdAt") > 1000000 { 8024 8549 return xerrors.Errorf("Value in field \"createdAt\" was too long") ··· 8040 8565 } 8041 8566 if _, err := cw.WriteString(string(t.CreatedAt)); err != nil { 8042 8567 return err 8568 + } 8569 + 8570 + // t.References ([]string) (slice) 8571 + if t.References != nil { 8572 + 8573 + if len("references") > 1000000 { 8574 + return xerrors.Errorf("Value in field \"references\" was too long") 8575 + } 8576 + 8577 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("references"))); err != nil { 8578 + return err 8579 + } 8580 + if _, err := cw.WriteString(string("references")); err != nil { 8581 + return err 8582 + } 8583 + 8584 + if len(t.References) > 8192 { 8585 + return xerrors.Errorf("Slice value in field t.References was too long") 8586 + } 8587 + 8588 + if err := cw.WriteMajorTypeHeader(cbg.MajArray, uint64(len(t.References))); err != nil { 8589 + return err 8590 + } 8591 + for _, v := range t.References { 8592 + if len(v) > 1000000 { 8593 + return xerrors.Errorf("Value in field v was too long") 8594 + } 8595 + 8596 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(v))); err != nil { 8597 + return err 8598 + } 8599 + if _, err := cw.WriteString(string(v)); err != nil { 8600 + return err 8601 + } 8602 + 8603 + } 8043 8604 } 8044 8605 return nil 8045 8606 } ··· 8069 8630 8070 8631 n := extra 8071 8632 8072 - nameBuf := make([]byte, 9) 8633 + nameBuf := make([]byte, 10) 8073 8634 for i := uint64(0); i < n; i++ { 8074 8635 nameLen, ok, err := cbg.ReadFullStringIntoBuf(cr, nameBuf, 1000000) 8075 8636 if err != nil { ··· 8118 8679 8119 8680 t.LexiconTypeID = string(sval) 8120 8681 } 8682 + // t.Mentions ([]string) (slice) 8683 + case "mentions": 8684 + 8685 + maj, extra, err = cr.ReadHeader() 8686 + if err != nil { 8687 + return err 8688 + } 8689 + 8690 + if extra > 8192 { 8691 + return fmt.Errorf("t.Mentions: array too large (%d)", extra) 8692 + } 8693 + 8694 + if maj != cbg.MajArray { 8695 + return fmt.Errorf("expected cbor array") 8696 + } 8697 + 8698 + if extra > 0 { 8699 + t.Mentions = make([]string, extra) 8700 + } 8701 + 8702 + for i := 0; i < int(extra); i++ { 8703 + { 8704 + var maj byte 8705 + var extra uint64 8706 + var err error 8707 + _ = maj 8708 + _ = extra 8709 + _ = err 8710 + 8711 + { 8712 + sval, err := cbg.ReadStringWithMax(cr, 1000000) 8713 + if err != nil { 8714 + return err 8715 + } 8716 + 8717 + t.Mentions[i] = string(sval) 8718 + } 8719 + 8720 + } 8721 + } 8121 8722 // t.CreatedAt (string) (string) 8122 8723 case "createdAt": 8123 8724 ··· 8128 8729 } 8129 8730 8130 8731 t.CreatedAt = string(sval) 8732 + } 8733 + // t.References ([]string) (slice) 8734 + case "references": 8735 + 8736 + maj, extra, err = cr.ReadHeader() 8737 + if err != nil { 8738 + return err 8739 + } 8740 + 8741 + if extra > 8192 { 8742 + return fmt.Errorf("t.References: array too large (%d)", extra) 8743 + } 8744 + 8745 + if maj != cbg.MajArray { 8746 + return fmt.Errorf("expected cbor array") 8747 + } 8748 + 8749 + if extra > 0 { 8750 + t.References = make([]string, extra) 8751 + } 8752 + 8753 + for i := 0; i < int(extra); i++ { 8754 + { 8755 + var maj byte 8756 + var extra uint64 8757 + var err error 8758 + _ = maj 8759 + _ = extra 8760 + _ = err 8761 + 8762 + { 8763 + sval, err := cbg.ReadStringWithMax(cr, 1000000) 8764 + if err != nil { 8765 + return err 8766 + } 8767 + 8768 + t.References[i] = string(sval) 8769 + } 8770 + 8771 + } 8131 8772 } 8132 8773 8133 8774 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 }
+2
api/tangled/repopull.go
··· 20 20 LexiconTypeID string `json:"$type,const=sh.tangled.repo.pull" cborgen:"$type,const=sh.tangled.repo.pull"` 21 21 Body *string `json:"body,omitempty" cborgen:"body,omitempty"` 22 22 CreatedAt string `json:"createdAt" cborgen:"createdAt"` 23 + Mentions []string `json:"mentions,omitempty" cborgen:"mentions,omitempty"` 23 24 Patch string `json:"patch" cborgen:"patch"` 25 + References []string `json:"references,omitempty" cborgen:"references,omitempty"` 24 26 Source *RepoPull_Source `json:"source,omitempty" cborgen:"source,omitempty"` 25 27 Target *RepoPull_Target `json:"target" cborgen:"target"` 26 28 Title string `json:"title" cborgen:"title"`
+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 ··· 569 576 -- indexes for better performance 570 577 create index if not exists idx_notifications_recipient_created on notifications(recipient_did, created desc); 571 578 create index if not exists idx_notifications_recipient_read on notifications(recipient_did, read); 579 + create index if not exists idx_references_from_at on reference_links(from_at); 580 + create index if not exists idx_references_to_at on reference_links(to_at); 572 581 `) 573 582 if err != nil { 574 583 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
+92 -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 + err := deleteReferences(tx, fromAt) 176 + if err != nil { 177 + return fmt.Errorf("delete old reference_links: %w", err) 178 + } 179 + if len(references) == 0 { 180 + return nil 181 + } 182 + 183 + values := make([]string, 0, len(references)) 184 + args := make([]any, 0, len(references)*2) 185 + for _, ref := range references { 186 + values = append(values, "(?, ?)") 187 + args = append(args, fromAt, ref) 188 + } 189 + _, err = tx.Exec( 190 + fmt.Sprintf( 191 + `insert into reference_links (from_at, to_at) 192 + values %s`, 193 + strings.Join(values, ","), 194 + ), 195 + args..., 196 + ) 197 + if err != nil { 198 + return fmt.Errorf("insert new reference_links: %w", err) 199 + } 200 + return nil 201 + } 202 + 203 + func deleteReferences(tx *sql.Tx, fromAt syntax.ATURI) error { 204 + _, err := tx.Exec(`delete from reference_links where from_at = ?`, fromAt) 205 + return err 206 + } 207 + 208 + func GetReferencesAll(e Execer, filters ...filter) (map[syntax.ATURI][]syntax.ATURI, error) { 209 + var ( 210 + conditions []string 211 + args []any 212 + ) 213 + for _, filter := range filters { 214 + conditions = append(conditions, filter.Condition()) 215 + args = append(args, filter.Arg()...) 216 + } 217 + 218 + whereClause := "" 219 + if conditions != nil { 220 + whereClause = " where " + strings.Join(conditions, " and ") 221 + } 222 + 223 + rows, err := e.Query( 224 + fmt.Sprintf( 225 + `select from_at, to_at from reference_links %s`, 226 + whereClause, 227 + ), 228 + args..., 229 + ) 230 + if err != nil { 231 + return nil, fmt.Errorf("query reference_links: %w", err) 232 + } 233 + defer rows.Close() 234 + 235 + result := make(map[syntax.ATURI][]syntax.ATURI) 236 + 237 + for rows.Next() { 238 + var from, to syntax.ATURI 239 + if err := rows.Scan(&from, &to); err != nil { 240 + return nil, fmt.Errorf("scan row: %w", err) 241 + } 242 + 243 + result[from] = append(result[from], to) 244 + } 245 + if err := rows.Err(); err != nil { 246 + return nil, fmt.Errorf("iterate rows: %w", err) 247 + } 248 + 249 + return result, nil 250 + }
+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
··· 238 238 } 239 239 l = l.With("did", issue.Did, "rkey", issue.Rkey) 240 240 241 + tx, err := rp.db.Begin() 242 + if err != nil { 243 + l.Error("failed to start transaction", "err", err) 244 + rp.pages.Notice(w, "issue-comment", "Failed to create comment, try again later.") 245 + return 246 + } 247 + defer tx.Rollback() 248 + 241 249 // delete from PDS 242 250 client, err := rp.oauth.AuthorizedClient(r) 243 251 if err != nil { ··· 258 266 } 259 267 260 268 // delete from db 261 - if err := db.DeleteIssues(rp.db, db.FilterEq("id", issue.Id)); err != nil { 269 + if err := db.DeleteIssues(tx, issue.Did, issue.Rkey); err != nil { 262 270 l.Error("failed to delete issue", "err", err) 263 271 rp.pages.Notice(w, noticeId, "Failed to delete issue.") 264 272 return 265 273 } 274 + tx.Commit() 266 275 267 276 rp.notifier.DeleteIssue(r.Context(), issue) 268 277 ··· 394 403 replyTo = &replyToUri 395 404 } 396 405 397 - mentions, _ := rp.refResolver.Resolve(r.Context(), body) 406 + mentions, references := rp.refResolver.Resolve(r.Context(), body) 398 407 399 408 comment := models.IssueComment{ 400 - Did: user.Did, 401 - Rkey: tid.TID(), 402 - IssueAt: issue.AtUri().String(), 403 - ReplyTo: replyTo, 404 - Body: body, 405 - Created: time.Now(), 409 + Did: user.Did, 410 + Rkey: tid.TID(), 411 + IssueAt: issue.AtUri().String(), 412 + ReplyTo: replyTo, 413 + Body: body, 414 + Created: time.Now(), 415 + Mentions: mentions, 416 + References: references, 406 417 } 407 418 if err = rp.validator.ValidateIssueComment(&comment); err != nil { 408 419 l.Error("failed to validate comment", "err", err) ··· 439 450 } 440 451 }() 441 452 442 - commentId, err := db.AddIssueComment(rp.db, comment) 453 + tx, err := rp.db.Begin() 454 + if err != nil { 455 + l.Error("failed to start transaction", "err", err) 456 + rp.pages.Notice(w, "issue-comment", "Failed to create comment, try again later.") 457 + return 458 + } 459 + defer tx.Rollback() 460 + 461 + commentId, err := db.AddIssueComment(tx, comment) 443 462 if err != nil { 444 463 l.Error("failed to create comment", "err", err) 445 464 rp.pages.Notice(w, "issue-comment", "Failed to create comment.") 446 465 return 447 466 } 467 + err = tx.Commit() 468 + if err != nil { 469 + l.Error("failed to commit transaction", "err", err) 470 + rp.pages.Notice(w, "issue-comment", "Failed to create comment, try again later.") 471 + return 472 + } 448 473 449 474 // reset atUri to make rollback a no-op 450 475 atUri = "" ··· 552 577 newComment.Edited = &now 553 578 record := newComment.AsRecord() 554 579 555 - _, err = db.AddIssueComment(rp.db, newComment) 580 + tx, err := rp.db.Begin() 581 + if err != nil { 582 + l.Error("failed to start transaction", "err", err) 583 + rp.pages.Notice(w, "repo-notice", "Failed to update description, try again later.") 584 + return 585 + } 586 + defer tx.Rollback() 587 + 588 + _, err = db.AddIssueComment(tx, newComment) 556 589 if err != nil { 557 590 l.Error("failed to perferom update-description query", "err", err) 558 591 rp.pages.Notice(w, "repo-notice", "Failed to update description, try again later.") 559 592 return 560 593 } 594 + tx.Commit() 561 595 562 596 // rkey is optional, it was introduced later 563 597 if newComment.Rkey != "" { ··· 867 901 }) 868 902 case http.MethodPost: 869 903 body := r.FormValue("body") 870 - mentions, _ := rp.refResolver.Resolve(r.Context(), body) 904 + mentions, references := rp.refResolver.Resolve(r.Context(), body) 871 905 872 906 issue := &models.Issue{ 873 - RepoAt: f.RepoAt(), 874 - Rkey: tid.TID(), 875 - Title: r.FormValue("title"), 876 - Body: body, 877 - Open: true, 878 - Did: user.Did, 879 - Created: time.Now(), 880 - Repo: f, 907 + RepoAt: f.RepoAt(), 908 + Rkey: tid.TID(), 909 + Title: r.FormValue("title"), 910 + Body: body, 911 + Open: true, 912 + Did: user.Did, 913 + Created: time.Now(), 914 + Mentions: mentions, 915 + References: references, 916 + Repo: f, 881 917 } 882 918 883 919 if err := rp.validator.ValidateIssue(issue); err != nil {
+70 -34
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 - Did: ownerDid, 210 - Rkey: rkey, 211 - Body: record.Body, 212 - IssueAt: record.Issue, 213 - ReplyTo: record.ReplyTo, 214 - Created: created, 243 + Did: ownerDid, 244 + Rkey: rkey, 245 + Body: record.Body, 246 + IssueAt: record.Issue, 247 + ReplyTo: record.ReplyTo, 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
··· 720 720 return 721 721 } 722 722 723 - mentions, _ := s.refResolver.Resolve(r.Context(), body) 723 + mentions, references := s.refResolver.Resolve(r.Context(), body) 724 724 725 725 // Start a transaction 726 726 tx, err := s.db.BeginTx(r.Context(), nil) ··· 764 764 Body: body, 765 765 CommentAt: atResp.Uri, 766 766 SubmissionId: pull.Submissions[roundNumber].ID, 767 + Mentions: mentions, 768 + References: references, 767 769 } 768 770 769 771 // 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 }
+14
lexicons/pulls/pull.json
··· 36 36 "createdAt": { 37 37 "type": "string", 38 38 "format": "datetime" 39 + }, 40 + "mentions": { 41 + "type": "array", 42 + "items": { 43 + "type": "string", 44 + "format": "did" 45 + } 46 + }, 47 + "references": { 48 + "type": "array", 49 + "items": { 50 + "type": "string", 51 + "format": "at-uri" 52 + } 39 53 } 40 54 } 41 55 }