Eventually-decentralized project hosting and management platform
Clone
HTTPS:
darcs clone https://vervis.peers.community/repos/WvWbo
SSH:
darcs clone USERNAME@vervis.peers.community:WvWbo
Tags
TODO
Ticket.hs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 | {- This file is part of Vervis.
-
- Written in 2019, 2020, 2021, 2022, 2023
- by fr33domlover <fr33domlover@riseup.net>.
-
- ♡ Copying is an act of love. Please copy, reuse and share.
-
- The author(s) have dedicated all copyright and related and neighboring
- rights to this software to the public domain worldwide. This software is
- distributed without any warranty.
-
- You should have received a copy of the CC0 Public Domain Dedication along
- with this software. If not, see
- <http://creativecommons.org/publicdomain/zero/1.0/>.
-}
{-# LANGUAGE RankNTypes #-}
module Vervis.Federation.Ticket
( --personOfferTicketF
deckOfferTicketF
, loomOfferTicketF
--, repoAddBundleF
, loomApplyF
--, deckOfferDepF
--, repoOfferDepF
, deckResolveF
, loomResolveF
)
where
import Control.Applicative
import Control.Exception hiding (Handler)
import Control.Monad
import Control.Monad.Logger.CallStack
import Control.Monad.Trans.Class
import Control.Monad.Trans.Except
import Control.Monad.Trans.Maybe
import Control.Monad.Trans.Reader
import Data.Aeson
import Data.Align
import Data.Bifoldable
import Data.Bifunctor
import Data.Bitraversable
import Data.ByteString (ByteString)
import Data.Either
import Data.Foldable
import Data.Function
import Data.List (nub, union)
import Data.List.NonEmpty (NonEmpty (..))
import Data.Maybe
import Data.Text (Text)
import Data.These
import Data.Time.Calendar
import Data.Time.Clock
import Data.Traversable
import Database.Persist
import Database.Persist.Sql
import System.Exit
import System.Process.Typed
import Text.Blaze.Html (preEscapedToHtml)
import Text.Blaze.Html.Renderer.Text
import Yesod.Core hiding (logError, logWarn, logInfo, logDebug)
import Yesod.Core.Handler
import Yesod.Persist.Core
import qualified Data.ByteString.Lazy as BL
import qualified Data.List.NonEmpty as NE
import qualified Data.List.Ordered as LO
import qualified Data.Text as T
import qualified Data.Text.Encoding as TE
import qualified Data.Text.Lazy as TL
import Database.Persist.JSON
import Network.FedURI
import Web.ActivityPub hiding (Patch, Ticket (..), Repo (..), Project (..), ActorLocal (..))
import Yesod.ActivityPub
import Yesod.FedURI
import Yesod.Hashids
import Yesod.MonadSite
import qualified Web.ActivityPub as AP
import Control.Monad.Trans.Except.Local
import Data.Tuple.Local
import Database.Persist.Local
import Yesod.Persist.Local
import qualified Data.Text.UTF8.Local as TU
import Development.PatchMediaType
import Vervis.Access
import Vervis.ActivityPub
import Vervis.Actor
import Vervis.Cloth
import Vervis.Data.Actor
import Vervis.Data.Ticket
import Vervis.Darcs
import Vervis.Web.Delivery
import Vervis.Federation.Auth
import Vervis.Federation.Util
import Vervis.FedURI
import Vervis.Fetch
import Vervis.Foundation
import Vervis.Git
import Vervis.Model
import Vervis.Model.Role
import Vervis.Model.Ticket
import Vervis.Path
import Vervis.Persist.Actor
import Vervis.Persist.Ticket
import Vervis.Query
import Vervis.Recipient
import Vervis.Ticket
import Vervis.Web.Repo
{-
checkBranch
:: Host
-> LocalURI
-> ExceptT Text Handler (Either (ShrIdent, RpIdent, Maybe Text) FedURI)
checkBranch h lu = do
hl <- hostIsLocal h
if hl
then Left <$> do
route <-
fromMaybeE
(decodeRouteLocal lu)
"MR target is local but isn't a valid route"
case route of
RepoR shr rp -> return (shr, rp, Nothing)
RepoBranchR shr rp b -> return (shr, rp, Just b)
_ ->
throwE
"MR target is a valid local route, but isn't a repo \
\or branch route"
else return $ Right $ ObjURI h lu
checkBranch' (ObjURI h lu) = checkBranch h lu
checkOfferTicket
:: RemoteAuthor
-> AP.Ticket URIMode
-> FedURI
-> ExceptT
Text
Handler
( Either WorkItemTarget (Host, LocalURI, Maybe (Maybe LocalURI, PatchMediaType, NonEmpty Text))
, TextHtml
, TextHtml
, TextPandocMarkdown
)
checkOfferTicket author ticket uTarget = do
target <- parseTarget uTarget
(muContext, summary, content, source, mmr) <- checkTicket ticket
for_ muContext $
\ u -> unless (u == uTarget) $ throwE "Offer target != Ticket context"
target' <- matchTargetAndMR target mmr
return (target', summary, content, source)
where
parseTarget u@(ObjURI h lu) = do
hl <- hostIsLocal h
if hl
then Left <$> do
route <-
fromMaybeE
(decodeRouteLocal lu)
"Offer target is local but isn't a valid route"
case route of
ProjectR shr prj -> return $ Left (shr, prj)
RepoR shr rp -> return $ Right (shr, rp)
_ ->
throwE
"Offer target is a valid local route, but isn't a \
\project or repo route"
else return $ Right u
checkTicket (AP.Ticket mlocal attrib mpublished mupdated muContext summary
content source muAssigned mresolved mmr) = do
verifyNothingE mlocal "Ticket with 'id'"
unless (attrib == objUriLocal (remoteAuthorURI author)) $
throwE "Author created ticket attibuted to someone else"
verifyNothingE mpublished "Ticket has 'published'"
verifyNothingE mupdated "Ticket has 'updated'"
verifyNothingE muAssigned "Ticket has 'assignedTo'"
when (isJust mresolved) $ throwE "Ticket is resolved"
mmr' <- traverse (uncurry checkMR) mmr
return (muContext, summary, content, source, mmr')
where
checkMR h (MergeRequest muOrigin luTarget ebundle) = do
verifyNothingE muOrigin "MR with 'origin'"
branch <- checkBranch h luTarget
(typ, diffs) <-
case ebundle of
Left _ -> throwE "MR bundle specified as a URI"
Right (hBundle, bundle) -> checkBundle hBundle bundle
case (typ, diffs) of
(PatchMediaTypeDarcs, _ :| _ : _) ->
throwE "More than one Darcs patch bundle provided"
_ -> return ()
return (branch, typ, diffs)
where
checkBundle _ (AP.BundleHosted _ _) =
throwE "Patches specified as URIs"
checkBundle h (AP.BundleOffer mlocal patches) = do
verifyNothingE mlocal "Bundle with 'id'"
(typ:|typs, diffs) <- NE.unzip <$> traverse (checkPatch h) patches
unless (all (== typ) typs) $ throwE "Different patch types"
return (typ, diffs)
where
checkPatch h (AP.Patch mlocal attrib mpub typ content) = do
verifyNothingE mlocal "Patch with 'id'"
unless (ObjURI h attrib == remoteAuthorURI author) $
throwE "Ticket and Patch attrib mismatch"
verifyNothingE mpub "Patch has 'published'"
return (typ, content)
matchTargetAndMR (Left (Left (shr, prj))) Nothing = return $ Left $ WITProject shr prj
matchTargetAndMR (Left (Left (shr, prj))) (Just _) = throwE "Patch offered to project"
matchTargetAndMR (Left (Right (shr, rp))) Nothing = throwE "Issue offered to repo"
matchTargetAndMR (Left (Right (shr, rp))) (Just (branch, typ, diffs)) = do
branch' <-
case branch of
Left (shr', rp', mb) | shr == shr' && rp == rp' -> return mb
_ -> throwE "MR target repo/branch and Offer target repo mismatch"
case patchMediaTypeVCS typ of
VCSDarcs ->
unless (isNothing branch') $
throwE "Darcs MR specifies a branch"
VCSGit ->
unless (isJust branch') $
throwE "Git MR doesn't specify the branch"
return $ Left $ WITRepo shr rp branch' typ diffs
matchTargetAndMR (Right (ObjURI h lu)) Nothing = return $ Right (h, lu, Nothing)
matchTargetAndMR (Right (ObjURI h lu)) (Just (branch, typ, diffs)) = do
luBranch <-
case branch of
Right (ObjURI h' lu') | h == h' -> return lu
_ -> throwE "MR target repo/branch and Offer target repo mismatch"
let bundle =
( if lu == luBranch then Nothing else Just luBranch
, typ
, diffs
)
return $ Right (h, lu, Just bundle)
-}
personOfferTicketF
:: UTCTime
-> KeyHashid Person
-> RemoteAuthor
-> ActivityBody
-> Maybe (RecipientRoutes, ByteString)
-> LocalURI
-> AP.Ticket URIMode
-> FedURI
-> ExceptT Text Handler Text
personOfferTicketF now recipHash author body mfwd luOffer ticket uTarget = do
error "sharerOfferTicketF temporarily disabled"
{-
(target, _, _, _) <- checkOfferTicket author ticket uTarget
mractid <- runDBExcept $ do
ibidRecip <- lift $ do
sid <- getKeyBy404 $ UniqueSharer shrRecip
personInbox <$> getValBy404 (UniquePersonIdent sid)
case target of
Left (WITProject shr prj) -> do
mjid <- lift $ runMaybeT $ do
sid <- MaybeT $ getKeyBy $ UniqueSharer shr
MaybeT $ getKeyBy $ UniqueProject prj sid
void $ fromMaybeE mjid "Offer target: No such local project"
Left (WITRepo shr rp _ _ _) -> do
mrid <- lift $ runMaybeT $ do
sid <- MaybeT $ getKeyBy $ UniqueSharer shr
MaybeT $ getKeyBy $ UniqueRepo rp sid
void $ fromMaybeE mrid "Offer target: No such local repo"
Right _ -> return ()
lift $ insertToInbox now author body ibidRecip luOffer True
return $
case mractid of
Nothing -> "Activity already exists in my inbox"
Just _ -> "Activity inserted to my inbox"
-}
{-
insertLocalTicket now author txl summary content source ractidOffer obiidAccept = do
did <- insert Discussion
fsid <- insert FollowerSet
tid <- insert Ticket
{ ticketNumber = Nothing
, ticketCreated = now
, ticketTitle = unTextHtml summary
, ticketSource = unTextPandocMarkdown source
, ticketDescription = unTextHtml content
, ticketAssignee = Nothing
, ticketStatus = TSNew
}
ltid <- insert LocalTicket
{ localTicketTicket = tid
, localTicketDiscuss = did
, localTicketFollowers = fsid
}
tclid <- insert TicketContextLocal
{ ticketContextLocalTicket = tid
, ticketContextLocalAccept = obiidAccept
}
insert_ $ txl tclid
insert_ TicketAuthorRemote
{ ticketAuthorRemoteTicket = tclid
, ticketAuthorRemoteAuthor = remoteAuthorId author
, ticketAuthorRemoteOpen = ractidOffer
}
return (tid, ltid)
-}
deckOfferTicketF
:: UTCTime
-> KeyHashid Deck
-> RemoteAuthor
-> ActivityBody
-> Maybe (RecipientRoutes, ByteString)
-> LocalURI
-> AP.Ticket URIMode
-> FedURI
-> ExceptT Text Handler Text
deckOfferTicketF now recipDeckHash author body mfwd luOffer ticket uTarget = do
-- Check input
recipDeckID <- decodeKeyHashid404 recipDeckHash
(title, desc, source) <- do
let uAuthor@(ObjURI hAuthor _) = remoteAuthorURI author
WorkItemOffer {..} <- checkOfferTicket hAuthor ticket uTarget
unless (wioAuthor == Right (remoteAuthorURI author)) $
throwE "Offering a Ticket attributed to someone else"
case wioRest of
TAM_Task deckID ->
if deckID == recipDeckID
then return ()
else throwE
"Offer target is some other local deck, so I have \
\no use for this Offer. Was I supposed to receive \
\it?"
TAM_Merge _ _ ->
throwE
"Offer target is some local loom, so I have no use for \
\this Offer. Was I supposed to receive it?"
TAM_Remote _ _ ->
throwE
"Offer target is some remote tracker, so I have no use \
\for this Offer. Was I supposed to receive it?"
return (wioTitle, wioDesc, wioSource)
-- Find recipient deck in DB, returning 404 if doesn't exist because we're
-- in the deck's inbox post handler
maybeHttp <- runDBExcept $ do
(recipDeckActorID, recipDeckActor) <- lift $ do
deck <- get404 recipDeckID
let actorID = deckActor deck
(actorID,) <$> getJust actorID
-- Insert the Offer to deck's inbox
mractid <- lift $ insertToInbox now author body (actorInbox recipDeckActor) luOffer False
for mractid $ \ offerID -> do
-- Forward the Offer activity to relevant local stages, and
-- schedule delivery for unavailable remote members of them
maybeHttpFwdOffer <- lift $ for mfwd $ \ (localRecips, sig) -> do
let sieve =
makeRecipientSet
[]
[LocalStageDeckFollowers recipDeckHash]
forwardActivityDB
(actbBL body) localRecips sig recipDeckActorID
(LocalActorDeck recipDeckHash) sieve offerID
-- Insert the new ticket to our DB
acceptID <- lift $ insertEmptyOutboxItem (actorOutbox recipDeckActor) now
taskID <- lift $ insertTask now title desc source recipDeckID offerID acceptID
-- Prepare an Accept activity and insert to deck's outbox
(actionAccept, localRecipsAccept, remoteRecipsAccept, fwdHostsAccept) <-
lift $ prepareAccept taskID
_luAccept <- lift $ updateOutboxItem (LocalActorDeck recipDeckID) acceptID actionAccept
-- Deliver the Accept to local recipients, and schedule delivery
-- for unavailable remote recipients
deliverHttpAccept <-
deliverActivityDB
(LocalActorDeck recipDeckHash) recipDeckActorID
localRecipsAccept remoteRecipsAccept fwdHostsAccept
acceptID actionAccept
-- Return instructions for HTTP inbox-forwarding of the Offer
-- activity, and for HTTP delivery of the Accept activity to
-- remote recipients
return (maybeHttpFwdOffer, deliverHttpAccept)
-- Launch asynchronous HTTP forwarding of the Offer activity and HTTP
-- delivery of the Accept activity
case maybeHttp of
Nothing -> return "I already have this activity in my inbox, doing nothing"
Just (maybeHttpFwdOffer, deliverHttpAccept) -> do
forkWorker "deckOfferTicketF Accept HTTP delivery" deliverHttpAccept
case maybeHttpFwdOffer of
Nothing -> return "Opened a ticket, no inbox-forwarding to do"
Just forwardHttpOffer -> do
forkWorker "deckOfferTicketF inbox-forwarding" forwardHttpOffer
return "Opened a ticket and ran inbox-forwarding of the Offer"
where
insertTask now title desc source deckID offerID acceptID = do
did <- insert Discussion
fsid <- insert FollowerSet
tid <- insert Ticket
{ ticketNumber = Nothing
, ticketCreated = now
, ticketTitle = title
, ticketSource = source
, ticketDescription = desc
, ticketDiscuss = did
, ticketFollowers = fsid
, ticketAccept = acceptID
}
insert_ TicketAuthorRemote
{ ticketAuthorRemoteTicket = tid
, ticketAuthorRemoteAuthor = remoteAuthorId author
, ticketAuthorRemoteOpen = offerID
}
insert $ TicketDeck tid deckID
prepareAccept taskID = do
encodeRouteLocal <- getEncodeRouteLocal
encodeRouteHome <- getEncodeRouteHome
taskHash <- encodeKeyHashid taskID
ra <- getJust $ remoteAuthorId author
let ObjURI hAuthor luAuthor = remoteAuthorURI author
audSender =
AudRemote hAuthor
[luAuthor]
(maybeToList $ remoteActorFollowers ra)
audTracker = AudLocal [] [LocalStageDeckFollowers recipDeckHash]
(recipientSet, remoteActors, fwdHosts, audLocal, audRemote) =
collectAudience [audSender, audTracker]
recips = map encodeRouteHome audLocal ++ audRemote
action = AP.Action
{ AP.actionCapability = Nothing
, AP.actionSummary = Nothing
, AP.actionAudience = AP.Audience recips [] [] [] [] []
, AP.actionFulfills = []
, AP.actionSpecific = AP.AcceptActivity AP.Accept
{ AP.acceptObject = ObjURI hAuthor luOffer
, AP.acceptResult =
Just $ encodeRouteLocal $
TicketR recipDeckHash taskHash
}
}
return (action, recipientSet, remoteActors, fwdHosts)
activityAlreadyInInbox hAct luAct inboxID = fmap isJust . runMaybeT $ do
instanceID <- MaybeT $ getKeyBy $ UniqueInstance hAct
remoteObjectID <- MaybeT $ getKeyBy $ UniqueRemoteObject instanceID luAct
remoteActivityID <- MaybeT $ getKeyBy $ UniqueRemoteActivity remoteObjectID
MaybeT $ getBy $ UniqueInboxItemRemote inboxID remoteActivityID
loomOfferTicketF
:: UTCTime
-> KeyHashid Loom
-> RemoteAuthor
-> ActivityBody
-> Maybe (RecipientRoutes, ByteString)
-> LocalURI
-> AP.Ticket URIMode
-> FedURI
-> ExceptT Text Handler (Text, Maybe (ExceptT Text Worker Text))
loomOfferTicketF now recipLoomHash author body mfwd luOffer ticket uTarget = do
-- Check input
recipLoomID <- decodeKeyHashid404 recipLoomHash
(title, desc, source, originTipOrBundle, targetRepoID, maybeTargetBranch) <- do
let uAuthor@(ObjURI hAuthor _) = remoteAuthorURI author
WorkItemOffer {..} <- checkOfferTicket hAuthor ticket uTarget
unless (wioAuthor == Right (remoteAuthorURI author)) $
throwE "Offering a Ticket attributed to someone else"
Merge maybeOriginTip maybeBundle targetTip <- case wioRest of
TAM_Task _ ->
throwE
"Offer target is some local deck, so I have no use for \
\this Offer. Was I supposed to receive it?"
TAM_Merge loomID merge ->
if loomID == recipLoomID
then return merge
else throwE
"Offer target is some other local loom, so I have \
\no use for this Offer. Was I supposed to receive \
\it?"
TAM_Remote _ _ ->
throwE
"Offer target is some remote tracker, so I have no use \
\for this Offer. Was I supposed to receive it?"
originTipOrBundle <-
fromMaybeE
(align maybeOriginTip maybeBundle)
"MR provides neither origin nor patches"
(targetRepoID, maybeTargetBranch) <-
case targetTip of
TipLocalRepo repoID -> pure (repoID, Nothing)
TipLocalBranch repoID branch -> pure (repoID, Just branch)
_ -> throwE "MR target is a remote repo (this tracker serves only local repos)"
return (wioTitle, wioDesc, wioSource, originTipOrBundle, targetRepoID, maybeTargetBranch)
-- Soon we're going to proceed asynchronously to be able to HTTP GET the
-- origin repo AP object, because:
--
-- * No support for providing a signed repo object directly in the
-- Offer activity
-- * It may be nice to make sure a remote origin repo's VCS type
-- matches the target repo's VCS, even if patches are provided too
-- + However there's no support for caching VCS type when
-- remembering remote repo in our DB, so we'd have to check this
-- every time
-- * If origin is remote and no patches are provided, we'll need to
-- know the clone URL to generate the patches ourselves
-- + However the code here, for some simplicity, doesn't have a
-- way to skip that and do the whole handler synchronously in
-- case patches are provided or the origin is a local repo
-- + And no support for caching the clone URI in DB when
-- remembering the remote repo, so we'd need to do this every
-- time
--
-- So first let's do some checks using the DB, on the loom, on the target
-- repo (which is always local), and on the origin repo if it's local
(recipLoomRepoID, Entity recipLoomActorID recipLoomActor, alreadyInInbox) <- lift $ runDB $ do
-- Find recipient loom in DB, returning 404 if doesn't exist because
-- we're in the loom's inbox post handler
(recipLoomRepoID, recipLoomActor@(Entity _ actor)) <- do
loom <- get404 recipLoomID
let actorID = loomActor loom
(loomRepo loom,) . Entity actorID <$> getJust actorID
-- Has the loom already received this activity to its inbox? If yes, we
-- won't process it again
alreadyInInbox <- do
let hOffer = objUriAuthority $ remoteAuthorURI author
activityAlreadyInInbox hOffer luOffer (actorInbox actor)
return (recipLoomRepoID, recipLoomActor, alreadyInInbox)
if alreadyInInbox
then return ("I already have this activity in my inbox, ignoring", Nothing)
else do
(targetRepoVCS, originOrBundle) <- runDBExcept $ do
-- Grab loom's repo from DB and verify that it consents to be served by
-- the loom, otherwise this loom doesn't accept tickets
unless (targetRepoID == recipLoomRepoID) $
throwE "MR target repo isn't the one served by the Offer target loom"
targetRepo <- lift $ getJust targetRepoID
unless (repoLoom targetRepo == Just recipLoomID) $
throwE "Offer target loom doesn't have repo's consent to serve it"
-- Verify VCS type match between patch bundle and target repo
for_ (justThere originTipOrBundle) $ \ (Material typ diffs) -> do
unless (repoVcs targetRepo == patchMediaTypeVCS typ) $
throwE "Patch type and local target repo VCS mismatch"
case (typ, diffs) of
(PatchMediaTypeDarcs, _ :| _ : _) ->
throwE "More than one Darcs dpatch file provided"
_ -> pure ()
-- If origin repo is local, find it in our DB and verify its VCS type
-- matches the target repo
originOrBundle <- flip (bifor originTipOrBundle) pure $ \ originTip -> do
let origin =
case originTip of
TipLocalRepo repoID -> Left (repoID, Nothing)
TipLocalBranch repoID branch -> Left (repoID, Just branch)
TipRemote uOrigin -> Right (uOrigin, Nothing)
TipRemoteBranch uRepo branch -> Right (uRepo, Just branch)
bitraverse_
(\ (repoID, maybeBranch) -> do
repo <- getE repoID "MR origin local repo not found in DB"
unless (repoVcs repo == repoVcs targetRepo) $
throwE "Local origin repo VCS differs from target repo VCS"
)
pure
origin
return origin
return (repoVcs targetRepo, originOrBundle)
return $ (,) "Ran initial checks, doing the rest asynchronously" $ Just $ do
-- If origin repo is remote, HTTP GET its AP representation and
-- remember it in our DB
originOrBundle' <-
bitraverse
(bitraverse
pure
(\ (uOrigin, maybeOriginBranch) -> do
(vcs, remoteOrigin) <-
case maybeOriginBranch of
Nothing -> do
(vcs, raid, uClone, mb) <- withExceptT (T.pack . show) $ httpGetRemoteTip uOrigin
return (vcs, (raid, uClone, first Just <$> mb))
Just branch -> do
(vcs, raid, uClone) <- withExceptT (T.pack . show) $ httpGetRemoteRepo uOrigin
return (vcs, (raid, uClone, Just (Nothing, branch)))
unless (vcs == targetRepoVCS) $
throwE "Remote origin repo VCS differs from target repo VCS"
return remoteOrigin
)
)
pure
originOrBundle
-- Verify that branches are specified for Git and aren't specified for
-- Darcs
-- Also, produce a data structure separating by VCS rather than by
-- local/remote origin, which we'll need for generating patches
tipInfo <- case targetRepoVCS of
VCSGit -> do
targetBranch <- fromMaybeE maybeTargetBranch "Local target repo is Git but no target branch specified"
maybeOrigin <- for (justHere originOrBundle') $ \case
Left (originRepoID, maybeOriginBranch) -> do
originBranch <- fromMaybeE maybeOriginBranch "Local origin repo is Git but no origin branch specified"
return (Left originRepoID, originBranch)
Right (_remoteActorID, uClone, maybeOriginBranch) -> do
(_maybeURI, originBranch) <- fromMaybeE maybeOriginBranch "Remote origin repo is Git but no origin branch specified"
return (Right uClone, originBranch)
return $ Left (targetBranch, maybeOrigin)
VCSDarcs -> do
verifyNothingE maybeTargetBranch "Local target repo is Darcs but target branch specified"
maybeOriginRepo <- for (justHere originOrBundle') $ \case
Left (originRepoID, maybeOriginBranch) -> do
verifyNothingE maybeOriginBranch "Local origin repo is Darcs but origin branch specified"
return $ Left originRepoID
Right (_remoteActorID, uClone, maybeOriginBranch) -> do
verifyNothingE maybeOriginBranch "Remote origin repo is Darcs but origin branch specified"
return $ Right uClone
return $ Right $ maybeOriginRepo
maybeHttp <- runSiteDBExcept $ do
-- Insert the Offer to loom's inbox
mractid <- lift $ insertToInbox now author body (actorInbox recipLoomActor) luOffer False
for mractid $ \ offerID -> do
-- Forward the Offer activity to relevant local stages, and
-- schedule delivery for unavailable remote members of them
maybeHttpFwdOffer <- lift $ for mfwd $ \ (localRecips, sig) -> do
let sieve =
makeRecipientSet
[]
[LocalStageLoomFollowers recipLoomHash]
forwardActivityDB
(actbBL body) localRecips sig
recipLoomActorID (LocalActorLoom recipLoomHash)
sieve offerID
-- Insert the new ticket to our DB
acceptID <- lift $ insertEmptyOutboxItem (actorOutbox recipLoomActor) now
ticketID <- lift $ insertTicket now title desc source offerID acceptID
clothID <- lift $ insertMerge recipLoomID ticketID maybeTargetBranch originOrBundle'
let maybePull =
let maybeTipInfo =
case tipInfo of
Left (b, mo) -> Left . (b,) <$> mo
Right mo -> Right <$> mo
hasBundle = isJust $ justThere originOrBundle'
in (clothID, targetRepoID, hasBundle,) <$> maybeTipInfo
-- Prepare an Accept activity and insert to loom's outbox
(actionAccept, localRecipsAccept, remoteRecipsAccept, fwdHostsAccept) <-
lift $ prepareAccept clothID
_luAccept <- lift $ updateOutboxItem (LocalActorLoom recipLoomID) acceptID actionAccept
-- Deliver the Accept to local recipients, and schedule delivery
-- for unavailable remote recipients
deliverHttpAccept <-
deliverActivityDB
(LocalActorLoom recipLoomHash) recipLoomActorID
localRecipsAccept remoteRecipsAccept
fwdHostsAccept acceptID actionAccept
-- Return instructions for HTTP inbox-forwarding of the Offer
-- activity, and for HTTP delivery of the Accept activity to
-- remote recipients, and for generating patches from
-- the origin repo
return
(maybeHttpFwdOffer, deliverHttpAccept, maybePull)
-- Launch asynchronous HTTP forwarding of the Offer activity and HTTP
-- delivery of the Accept activity, and generate patches if we opened
-- a local MR that mentions just an origin
case maybeHttp of
Nothing ->
return
"When I started serving this activity, I didn't have it in my inbox, \
\but now suddenly it seems I already do, so ignoring"
Just (maybeHttpFwdOffer, deliverHttpAccept, maybePull) -> do
forkWorker "loomOfferTicketF Accept HTTP delivery" deliverHttpAccept
traverse generatePatches maybePull
case maybeHttpFwdOffer of
Nothing -> return "Opened a merge request, no inbox-forwarding to do"
Just forwardHttpOffer -> do
forkWorker "loomOfferTicketF inbox-forwarding" forwardHttpOffer
return "Opened a merge request and ran inbox-forwarding of the Offer"
where
insertTicket now title desc source offerID acceptID = do
did <- insert Discussion
fsid <- insert FollowerSet
tid <- insert Ticket
{ ticketNumber = Nothing
, ticketCreated = now
, ticketTitle = title
, ticketSource = source
, ticketDescription = desc
, ticketDiscuss = did
, ticketFollowers = fsid
, ticketAccept = acceptID
}
insert_ TicketAuthorRemote
{ ticketAuthorRemoteTicket = tid
, ticketAuthorRemoteAuthor = remoteAuthorId author
, ticketAuthorRemoteOpen = offerID
}
return tid
insertMerge
:: LoomId
-> TicketId
-> Maybe Text
-> These
(Either
(RepoId, Maybe Text)
(RemoteActorId, FedURI, Maybe (Maybe LocalURI, Text))
)
Material
-> WorkerDB TicketLoomId
insertMerge loomID ticketID maybeTargetBranch originOrBundle = do
clothID <- insert $ TicketLoom ticketID loomID maybeTargetBranch
for_ (justHere originOrBundle) $ \case
Left (repoID, maybeOriginBranch) ->
insert_ $ MergeOriginLocal clothID repoID maybeOriginBranch
Right (remoteActorID, _uClone, maybeOriginBranch) -> do
originID <- insert $ MergeOriginRemote clothID remoteActorID
for_ maybeOriginBranch $ \ (mlu, b) ->
insert_ $ MergeOriginRemoteBranch originID mlu b
for_ (justThere originOrBundle) $ \ (Material typ diffs) -> do
bundleID <- insert $ Bundle clothID False
insertMany_ $ NE.toList $ NE.reverse $
NE.map (Patch bundleID now typ) diffs
return clothID
prepareAccept clothID = do
encodeRouteLocal <- getEncodeRouteLocal
encodeRouteHome <- getEncodeRouteHome
clothHash <- encodeKeyHashid clothID
ra <- getJust $ remoteAuthorId author
let ObjURI hAuthor luAuthor = remoteAuthorURI author
audSender =
AudRemote hAuthor
[luAuthor]
(maybeToList $ remoteActorFollowers ra)
audTracker = AudLocal [] [LocalStageLoomFollowers recipLoomHash]
(recipientSet, remoteActors, fwdHosts, audLocal, audRemote) =
collectAudience [audSender, audTracker]
recips = map encodeRouteHome audLocal ++ audRemote
action = AP.Action
{ AP.actionCapability = Nothing
, AP.actionSummary = Nothing
, AP.actionAudience = AP.Audience recips [] [] [] [] []
, AP.actionFulfills = []
, AP.actionSpecific = AP.AcceptActivity AP.Accept
{ AP.acceptObject = ObjURI hAuthor luOffer
, AP.acceptResult =
Just $ encodeRouteLocal $
ClothR recipLoomHash clothHash
}
}
return (action, recipientSet, remoteActors, fwdHosts)
repoOfferTicketF
:: UTCTime
-> KeyHashid Repo
-> RemoteAuthor
-> ActivityBody
-> Maybe (RecipientRoutes, ByteString)
-> LocalURI
-> AP.Ticket URIMode
-> FedURI
-> ExceptT Text Handler Text
repoOfferTicketF now recipHash author body mfwd luOffer ticket uTarget = do
error "repoOfferTicketF temporarily disabled"
{-
(target, summary, content, source) <- checkOfferTicket author ticket uTarget
mmhttp <- for (targetRelevance target) $ \ (mb, typ, diffs) -> runDBExcept $ do
Entity rid r <- lift $ do
sid <- getKeyBy404 $ UniqueSharer shrRecip
getBy404 $ UniqueRepo rpRecip sid
unless (repoVcs r == patchMediaTypeVCS typ) $
throwE "Patch type and repo VCS mismatch"
mractid <- lift $ insertToInbox now author body (repoInbox r) luOffer False
lift $ for mractid $ \ ractid -> do
mremotesHttpFwd <- for mfwd $ \ (localRecips, sig) -> do
let sieve =
makeRecipientSet
[]
[ LocalPersonCollectionRepoTeam shrRecip rpRecip
, LocalPersonCollectionRepoFollowers shrRecip rpRecip
]
remoteRecips <-
insertRemoteActivityToLocalInboxes
False ractid $
localRecipSieve'
sieve False False localRecips
(sig,) <$> deliverRemoteDB_R (actbBL body) ractid rid sig remoteRecips
(obiidAccept, docAccept, fwdHostsAccept, recipsAccept) <- do
obiidAccept <- insertEmptyOutboxItem (repoOutbox r) now
let makeTRL tclid = TicketRepoLocal tclid rid mb
(tid, ltid) <- insertLocalTicket now author makeTRL summary content source ractid obiidAccept
bnid <- insert $ Bundle tid
insertMany_ $ NE.toList $ NE.map (Patch bnid now typ) diffs
(docAccept, localRecipsAccept, remoteRecipsAccept, fwdHostsAccept) <-
insertAccept shrRecip rpRecip author luOffer ltid obiidAccept
knownRemoteRecipsAccept <-
deliverLocal'
False
(LocalActorRepo shrRecip rpRecip)
(repoInbox r)
obiidAccept
localRecipsAccept
(obiidAccept,docAccept,fwdHostsAccept,) <$>
deliverRemoteDB fwdHostsAccept obiidAccept remoteRecipsAccept knownRemoteRecipsAccept
return (mremotesHttpFwd, obiidAccept, docAccept, fwdHostsAccept, recipsAccept)
case mmhttp of
Nothing -> return "Offer target isn't me, not using"
Just mhttp ->
case mhttp of
Nothing -> return "Activity already in my inbox, doing nothing"
Just (mremotesHttpFwd, obiid, doc, fwdHosts, remotes) -> do
for_ mremotesHttpFwd $ \ (sig, remotes) ->
forkWorker "repoOfferTicketF inbox-forwarding" $
deliverRemoteHTTP_R now shrRecip rpRecip (actbBL body) sig remotes
forkWorker "repoOfferTicketF Accept HTTP delivery" $
deliverRemoteHttp' fwdHosts obiid doc remotes
return $
case mremotesHttpFwd of
Nothing -> "Accepted new patch, no inbox-forwarding to do"
Just _ -> "Accepted new patch and ran inbox-forwarding of the Offer"
where
targetRelevance (Left (WITRepo shr rp mb vcs diffs))
| shr == shrRecip && rp == rpRecip = Just (mb, vcs, diffs)
targetRelevance _ = Nothing
insertAccept shr rp author luOffer ltid obiidAccept = do
encodeRouteLocal <- getEncodeRouteLocal
encodeRouteHome <- getEncodeRouteHome
hLocal <- asksSite siteInstanceHost
obikhidAccept <- encodeKeyHashid obiidAccept
ltkhid <- encodeKeyHashid ltid
ra <- getJust $ remoteAuthorId author
let ObjURI hAuthor luAuthor = remoteAuthorURI author
audAuthor =
AudRemote hAuthor [luAuthor] (maybeToList $ remoteActorFollowers ra)
audProject =
AudLocal []
[ LocalPersonCollectionRepoTeam shr rp
, LocalPersonCollectionRepoFollowers shr rp
]
(recipientSet, remoteActors, fwdHosts, audLocal, audRemote) =
collectAudience [audAuthor, audProject]
recips = map encodeRouteHome audLocal ++ audRemote
doc = Doc hLocal Activity
{ activityId =
Just $ encodeRouteLocal $
RepoOutboxItemR shr rp obikhidAccept
, activityActor = encodeRouteLocal $ RepoR shr rp
, activityCapability = Nothing
, activitySummary = Nothing
, activityAudience = Audience recips [] [] [] [] []
, activitySpecific = AcceptActivity Accept
{ acceptObject = ObjURI hAuthor luOffer
, acceptResult =
Just $ encodeRouteLocal $ RepoProposalR shr rp ltkhid
}
}
update obiidAccept [OutboxItemActivity =. persistJSONObjectFromDoc doc]
return (doc, recipientSet, remoteActors, fwdHosts)
-}
{-
data RemoteBundle = RemoteBundle
{ rpBranch :: Maybe LocalURI
, rpType :: PatchMediaType
, rpDiffs :: NonEmpty Text
}
data RemoteWorkItem = RemoteWorkItem
{ rwiHost :: Host
, rwiTarget :: Maybe LocalURI
, rwiContext :: LocalURI
, rwiBundle :: Maybe RemoteBundle
}
data RemoteWorkItem' = RemoteWorkItem'
{ rwiHost' :: Host
, rwiContext' :: LocalURI
, rwiBundle' :: Maybe RemoteBundle
}
data ParsedCreateTicket = ParsedCreateTicket
{ pctItem :: Either (Bool, WorkItemTarget) RemoteWorkItem
, pctLocal :: TicketLocal
, pctPublished :: UTCTime
, pctTitle :: TextHtml
, pctDesc :: TextHtml
, pctSource :: TextPandocMarkdown
}
-}
repoAddBundleF
:: UTCTime
-> KeyHashid Repo
-> RemoteAuthor
-> ActivityBody
-> Maybe (RecipientRoutes, ByteString)
-> LocalURI
-> NonEmpty (AP.Patch URIMode)
-> FedURI
-> ExceptT Text Handler (Text, Maybe (ExceptT Text Worker Text))
repoAddBundleF now recipHash author body mfwd luAdd patches uTarget = do
error "repoAddBundleF temporarily disabled"
{-
ticket <- parseWorkItem "Target" uTarget
(typ, diffs) <- do
((typ, diff) :| rest) <-
for patches $ \ (AP.Patch mlocal attrib mpub typ content) -> do
verifyNothingE mlocal "Patch with 'id'"
unless (attrib == objUriLocal (remoteAuthorURI author)) $
throwE "Add and Patch attrib mismatch"
verifyNothingE mpub "Patch has 'published'"
return (typ, content)
let (typs, diffs) = unzip rest
unless (all (== typ) typs) $ throwE "Patches of different media types"
return (typ, diff :| diffs)
Entity ridRecip repoRecip <- lift $ runDB $ do
sid <- getKeyBy404 $ UniqueSharer shrRecip
getBy404 $ UniqueRepo rpRecip sid
unless (repoVcs repoRecip == patchMediaTypeVCS typ) $
throwE "Patch type and repo VCS mismatch"
return $ (,) "Ran initial checks, doing the rest asynchronously" $ Just $ do
relevantTicket <-
for (ticketRelevance shrRecip rpRecip ticket) $ \ ltid -> do
author <- runSiteDBExcept $ do
(_, _, _, _, _, _, author, _, _) <- do
mticket <- lift $ getRepoProposal shrRecip rpRecip ltid
fromMaybeE mticket $ "Target" <> ": No such repo-patch"
lift $ getWorkItemAuthorDetail author
return (ltid, author)
mhttp <- runSiteDBExcept $ do
mractid <- lift $ insertToInbox now author body (repoInbox repoRecip) luAdd False
for mractid $ \ ractid -> do
mremotesHttpFwd <- lift $ for mfwd $ \ (localRecips, sig) -> do
relevantFollowers <- askRelevantFollowers
let rf = relevantFollowers shrRecip rpRecip
sieve =
makeRecipientSet [] $ catMaybes
[ rf ticket
]
remoteRecips <-
insertRemoteActivityToLocalInboxes False ractid $
localRecipSieve'
sieve False False localRecips
(sig,) <$> deliverRemoteDB_R (actbBL body) ractid ridRecip sig remoteRecips
mremotesHttpAccept <- lift $ for relevantTicket $ \ ticketData@(ltid, _author) -> do
obiidAccept <- insertEmptyOutboxItem (repoOutbox repoRecip) now
tid <- localTicketTicket <$> getJust ltid
bnid <- insert $ Bundle tid
insertMany_ $ NE.toList $ NE.map (Patch bnid now typ) diffs
(docAccept, localRecipsAccept, remoteRecipsAccept, fwdHostsAccept) <-
insertAccept luAdd obiidAccept bnid ticketData
knownRemoteRecipsAccept <-
deliverLocal'
False
(LocalActorRepo shrRecip rpRecip)
(repoInbox repoRecip)
obiidAccept
localRecipsAccept
(obiidAccept,docAccept,fwdHostsAccept,) <$>
deliverRemoteDB fwdHostsAccept obiidAccept remoteRecipsAccept knownRemoteRecipsAccept
return (mremotesHttpFwd, mremotesHttpAccept)
case mhttp of
Nothing -> return "I already have this activity in my inbox, doing nothing"
Just (mremotesHttpFwd, mremotesHttpAccept) -> do
for_ mremotesHttpFwd $ \ (sig, remotes) ->
forkWorker "repoAddBundleF inbox-forwarding" $
deliverRemoteHTTP_R now shrRecip rpRecip (actbBL body) sig remotes
for_ mremotesHttpAccept $ \ (obiid, doc, fwdHosts, remotes) ->
forkWorker "repoAddBundleF Accept HTTP delivery" $
deliverRemoteHttp' fwdHosts obiid doc remotes
return $
case (mremotesHttpAccept, mremotesHttpFwd) of
(Nothing, Nothing) -> "Ticket not mine, just stored in inbox and no inbox-forwarding to do"
(Nothing, Just _) -> "Ticket not mine, just stored in inbox and ran inbox-forwarding"
(Just _, Nothing) -> "Accepted new bundle, no inbox-forwarding to do"
(Just _, Just _) -> "Accepted new bundle and ran inbox-forwarding of the Add"
where
ticketRelevance shr rp (Left (WorkItemRepoProposal shr' rp' ltid))
| shr == shr' && rp == rp' = Just ltid
ticketRelevance _ _ _ = Nothing
askRelevantFollowers = do
hashLTID <- getEncodeKeyHashid
return $
\ shr rp wi -> followers hashLTID <$> ticketRelevance shr rp wi
where
followers hashLTID ltid =
LocalPersonCollectionRepoProposalFollowers
shrRecip rpRecip (hashLTID ltid)
insertAccept luAdd obiidAccept bnid (ltid, ticketAuthor) = do
encodeRouteLocal <- getEncodeRouteLocal
encodeRouteHome <- getEncodeRouteHome
followers <- askFollowers
hLocal <- asksSite siteInstanceHost
obikhidAccept <- encodeKeyHashid obiidAccept
ltkhid <- encodeKeyHashid ltid
bnkhid <- encodeKeyHashid bnid
ra <- getJust $ remoteAuthorId author
let ObjURI hAuthor luAuthor = remoteAuthorURI author
audAuthor =
AudRemote hAuthor [luAuthor] (maybeToList $ remoteActorFollowers ra)
audTicketContext =
AudLocal
[]
[ LocalPersonCollectionRepoTeam shrRecip rpRecip
, LocalPersonCollectionRepoFollowers shrRecip rpRecip
]
audTicketFollowers = AudLocal [] [followers ltid]
audTicketAuthor =
case ticketAuthor of
Left shr -> AudLocal [LocalActorSharer shr] []
Right (i, ro) ->
AudRemote (instanceHost i) [remoteObjectIdent ro] []
(recipientSet, remoteActors, fwdHosts, audLocal, audRemote) =
collectAudience
[ audAuthor
, audTicketAuthor
, audTicketFollowers
, audTicketContext
]
recips = map encodeRouteHome audLocal ++ audRemote
doc = Doc hLocal Activity
{ activityId =
Just $ encodeRouteLocal $
RepoOutboxItemR shrRecip rpRecip obikhidAccept
, activityActor = encodeRouteLocal $ RepoR shrRecip rpRecip
, activityCapability = Nothing
, activitySummary = Nothing
, activityAudience = Audience recips [] [] [] [] []
, activitySpecific = AcceptActivity Accept
{ acceptObject = ObjURI hAuthor luAdd
, acceptResult =
Just $ encodeRouteLocal $ RepoProposalBundleR shrRecip rpRecip ltkhid bnkhid
}
}
update obiidAccept [OutboxItemActivity =. persistJSONObjectFromDoc doc]
return (doc, recipientSet, remoteActors, fwdHosts)
where
askFollowers = do
hashLTID <- getEncodeKeyHashid
return $
\ ltid ->
LocalPersonCollectionRepoProposalFollowers
shrRecip rpRecip (hashLTID ltid)
-}
loomApplyF
:: UTCTime
-> KeyHashid Loom
-> RemoteAuthor
-> ActivityBody
-> Maybe (RecipientRoutes, ByteString)
-> LocalURI
-> AP.Apply URIMode
-> ExceptT Text Handler (Text, Maybe (ExceptT Text Worker Text))
loomApplyF now recipLoomHash author body mfwd luApply apply = (,Nothing) <$> do
-- Check input
recipLoomID <- decodeKeyHashid404 recipLoomHash
(repoID, maybeBranch, clothID, bundleID) <- do
maybeLocalTarget <- checkApplyLocalLoom apply
(repoID, maybeBranch, loomID, clothID, bundleID) <-
fromMaybeE
maybeLocalTarget
"Bundle doesn't belong to a local loom, in particular not to \
\me, so I won't apply it. Was I supposed to receive it?"
unless (loomID == recipLoomID) $
throwE
"Bundle belongs to some other local loom, so I won't apply \
\it. Was I supposed to receive it?"
return (repoID, maybeBranch, clothID, bundleID)
-- Verify the capability URI is one of:
-- * Outbox item URI of a local actor, i.e. a local activity
-- * A remote URI
uCap <- do
let muCap = activityCapability $ actbActivity body
fromMaybeE muCap "Asking to apply patch but no capability provided"
capID <- nameExceptT "Apply capability" $ parseActivityURI uCap
maybeNewApply <- runDBExcept $ do
-- Find recipient loom in DB, returning 404 if doesn't exist because
-- we're in the loom's inbox post handler
recipLoom <- lift $ get404 recipLoomID
let recipLoomActorID = loomActor recipLoom
recipLoomActor <- lift $ getJust recipLoomActorID
-- Has the loom already received this activity to its inbox? If yes, we
-- won't process it again
alreadyInInbox <- lift $ do
let hOffer = objUriAuthority $ remoteAuthorURI author
activityAlreadyInInbox hOffer luApply (actorInbox recipLoomActor)
-- Find the repo and the bundle in our DB, and verify that the loom is
-- willing to accept the request from sender to apply this specific
-- bundle to this repo/branch
if alreadyInInbox
then pure Nothing
else Just <$> do
(_, ticketID, diffs) <-
checkApplyDB
(Right $ remoteAuthorId author) capID
(repoID, maybeBranch) (recipLoomID, clothID, bundleID)
return (Entity recipLoomActorID recipLoomActor, ticketID, diffs)
case maybeNewApply of
Nothing ->
return "I already have this activity in my inbox, doing nothing"
Just (Entity recipLoomActorID recipLoomActor, ticketID, diffs) -> do
-- Apply patches
applyPatches repoID maybeBranch diffs
maybeHttp <- runDBExcept $ do
-- Insert the Apply to loom's inbox
mractid <- lift $ insertToInbox now author body (actorInbox recipLoomActor) luApply False
for mractid $ \ applyID -> do
-- Forward the Apply activity to relevant local stages, and
-- schedule delivery for unavailable remote members of them
maybeHttpFwdApply <- lift $ for mfwd $ \ (localRecips, sig) -> do
clothHash <- encodeKeyHashid clothID
let sieve =
makeRecipientSet
[]
[ LocalStageLoomFollowers recipLoomHash
, LocalStageClothFollowers recipLoomHash clothHash
]
forwardActivityDB
(actbBL body) localRecips sig recipLoomActorID
(LocalActorLoom recipLoomHash) sieve applyID
-- Mark ticket in DB as resolved by the Apply
acceptID <-
lift $ insertEmptyOutboxItem (actorOutbox recipLoomActor) now
lift $ insertResolve ticketID applyID acceptID
-- Prepare an Accept activity and insert to loom's outbox
(actionAccept, localRecipsAccept, remoteRecipsAccept, fwdHostsAccept) <-
lift $ prepareAccept clothID
_luAccept <- lift $ updateOutboxItem (LocalActorLoom recipLoomID) acceptID actionAccept
-- Deliver the Accept to local recipients, and schedule delivery
-- for unavailable remote recipients
deliverHttpAccept <-
deliverActivityDB
(LocalActorLoom recipLoomHash) recipLoomActorID
localRecipsAccept remoteRecipsAccept fwdHostsAccept
acceptID actionAccept
-- Return instructions for HTTP inbox-forwarding of the Apply
-- activity, and for HTTP delivery of the Accept activity to
-- remote recipients
return (maybeHttpFwdApply, deliverHttpAccept)
-- Launch asynchronous HTTP forwarding of the Apply activity and HTTP
-- delivery of the Accept activity
case maybeHttp of
Nothing ->
return
"When I started serving this activity, I didn't have it in my inbox, \
\but now suddenly it seems I already do, so ignoring"
Just (maybeHttpFwdApply, deliverHttpAccept) -> do
forkWorker "loomApplyF Accept HTTP delivery" deliverHttpAccept
case maybeHttpFwdApply of
Nothing -> return "Applied the patch(es), no inbox-forwarding to do"
Just forwardHttpApply -> do
forkWorker "loomApplyF inbox-forwarding" forwardHttpApply
return "Applied the patch(es) and ran inbox-forwarding of the Apply"
where
insertResolve ticketID applyID acceptID = do
trid <- insert TicketResolve
{ ticketResolveTicket = ticketID
, ticketResolveAccept = acceptID
}
insert_ TicketResolveRemote
{ ticketResolveRemoteTicket = trid
, ticketResolveRemoteActivity = applyID
, ticketResolveRemoteActor = remoteAuthorId author
}
prepareAccept clothID = do
encodeRouteHome <- getEncodeRouteHome
clothHash <- encodeKeyHashid clothID
ra <- getJust $ remoteAuthorId author
let ObjURI hAuthor luAuthor = remoteAuthorURI author
audSender =
AudRemote hAuthor
[luAuthor]
(maybeToList $ remoteActorFollowers ra)
audTracker =
AudLocal
[]
[ LocalStageLoomFollowers recipLoomHash
, LocalStageClothFollowers recipLoomHash clothHash
]
(recipientSet, remoteActors, fwdHosts, audLocal, audRemote) =
collectAudience [audSender, audTracker]
recips = map encodeRouteHome audLocal ++ audRemote
action = AP.Action
{ AP.actionCapability = Nothing
, AP.actionSummary = Nothing
, AP.actionAudience = AP.Audience recips [] [] [] [] []
, AP.actionFulfills = []
, AP.actionSpecific = AP.AcceptActivity AP.Accept
{ AP.acceptObject = ObjURI hAuthor luApply
, AP.acceptResult = Nothing
}
}
return (action, recipientSet, remoteActors, fwdHosts)
personOfferDepF
:: UTCTime
-> KeyHashid Person
-> RemoteAuthor
-> ActivityBody
-> Maybe (RecipientRoutes, ByteString)
-> LocalURI
-> AP.TicketDependency URIMode
-> FedURI
-> ExceptT Text Handler (Text, Maybe (ExceptT Text Worker Text))
personOfferDepF now recipHash author body mfwd luOffer dep uTarget = do
error "sharerOfferDepF temporarily disabled"
{-
(parent, child) <- checkDepAndTarget dep uTarget
personRecip <- lift $ runDB $ do
sid <- getKeyBy404 $ UniqueSharer shrRecip
getValBy404 $ UniquePersonIdent sid
return $ (,) "Ran initial checks, doing the rest asynchronously" $ Just $ do
manager <- asksSite appHttpManager
relevantParent <-
for (ticketRelevance shrRecip parent) $ \ (talid, patch) -> do
(parentLtid, parentCtx) <-
getSharerWorkItemDetail shrRecip talid patch
childDetail <- getWorkItemDetail "Child" child
return (talid, patch, parentLtid, parentCtx, childDetail)
mhttp <- runSiteDBExcept $ do
mractid <- lift $ insertToInbox' now author body (personInbox personRecip) luOffer True
for mractid $ \ (ractid, ibiid) -> do
insertDepOffer ibiid parent child
mremotesHttpFwd <- lift $ for mfwd $ \ (localRecips, sig) -> do
relevantFollowers <- askRelevantFollowers
let sieve =
makeRecipientSet [] $ catMaybes
[ relevantFollowers shrRecip parent
, relevantFollowers shrRecip child
]
remoteRecips <-
insertRemoteActivityToLocalInboxes
False ractid $
localRecipSieve'
sieve False False localRecips
(sig,) <$> deliverRemoteDB_S (actbBL body) ractid (personIdent personRecip) sig remoteRecips
mremotesHttpAccept <- lift $ for relevantParent $ \ ticketData@(_, _, parentLtid, _, childDetail) -> do
obiidAccept <- insertEmptyOutboxItem (personOutbox personRecip) now
tdid <- insertDep now author ractid parentLtid (widIdent childDetail) obiidAccept
(docAccept, localRecipsAccept, remoteRecipsAccept, fwdHostsAccept) <-
insertAccept luOffer obiidAccept tdid ticketData
knownRemoteRecipsAccept <-
deliverLocal'
False
(LocalActorSharer shrRecip)
(personInbox personRecip)
obiidAccept
localRecipsAccept
(obiidAccept,docAccept,fwdHostsAccept,) <$> deliverRemoteDB fwdHostsAccept obiidAccept remoteRecipsAccept knownRemoteRecipsAccept
return (mremotesHttpFwd, mremotesHttpAccept)
case mhttp of
Nothing -> return "I already have this activity in my inbox, doing nothing"
Just (mremotesHttpFwd, mremotesHttpAccept) -> do
for_ mremotesHttpFwd $ \ (sig, remotes) ->
forkWorker "sharerOfferDepF inbox-forwarding" $
deliverRemoteHTTP_S now shrRecip (actbBL body) sig remotes
for_ mremotesHttpAccept $ \ (obiid, doc, fwdHosts, remotes) ->
forkWorker "sharerOfferDepF Accept HTTP delivery" $
deliverRemoteHttp' fwdHosts obiid doc remotes
return $
case (mremotesHttpAccept, mremotesHttpFwd) of
(Nothing, Nothing) -> "Parent not mine, just stored in inbox and no inbox-forwarding to do"
(Nothing, Just _) -> "Parent not mine, just stored in inbox and ran inbox-forwarding"
(Just _, Nothing) -> "Accepted new ticket dep, no inbox-forwarding to do"
(Just _, Just _) -> "Accepted new ticket dep and ran inbox-forwarding of the Offer"
where
ticketRelevance shr (Left (WorkItemSharerTicket shr' talid patch))
| shr == shr' = Just (talid, patch)
ticketRelevance _ _ = Nothing
insertDepOffer _ (Left _) _ = return ()
insertDepOffer ibiidOffer (Right _) child =
for_ (ticketRelevance shrRecip child) $ \ (talid, patch) -> do
ltid <-
if patch
then do
(_, Entity ltid _, _, _, _, _) <- do
mticket <- lift $ getSharerProposal shrRecip talid
fromMaybeE mticket $ "Child" <> ": No such sharer-patch"
return ltid
else do
(_, Entity ltid _, _, _, _) <- do
mticket <- lift $ getSharerTicket shrRecip talid
fromMaybeE mticket $ "Child" <> ": No such sharer-ticket"
return ltid
lift $ insert_ TicketDependencyOffer
{ ticketDependencyOfferOffer = ibiidOffer
, ticketDependencyOfferChild = ltid
}
askRelevantFollowers = do
hashTALID <- getEncodeKeyHashid
return $ \ shr wi -> followers hashTALID <$> ticketRelevance shr wi
where
followers hashTALID (talid, patch) =
let coll =
if patch
then LocalPersonCollectionSharerProposalFollowers
else LocalPersonCollectionSharerTicketFollowers
in coll shrRecip (hashTALID talid)
insertAccept luOffer obiidAccept tdid (talid, patch, _, parentCtx, WorkItemDetail childId childCtx childAuthor) = do
encodeRouteLocal <- getEncodeRouteLocal
encodeRouteHome <- getEncodeRouteHome
followers <- askFollowers
workItemFollowers <- askWorkItemFollowers
hLocal <- asksSite siteInstanceHost
obikhidAccept <- encodeKeyHashid obiidAccept
tdkhid <- encodeKeyHashid tdid
ra <- getJust $ remoteAuthorId author
let ObjURI hAuthor luAuthor = remoteAuthorURI author
audAuthor =
AudRemote hAuthor [luAuthor] (maybeToList $ remoteActorFollowers ra)
audParentContext = contextAudience parentCtx
audChildContext = contextAudience childCtx
audParent = AudLocal [LocalActorSharer shrRecip] [followers talid patch]
audChildAuthor =
case childAuthor of
Left shr -> AudLocal [LocalActorSharer shr] []
Right (ObjURI h lu) -> AudRemote h [lu] []
audChildFollowers =
case childId of
Left (wi, _ltid) -> AudLocal [] [workItemFollowers wi]
Right (ObjURI h _, luFollowers) -> AudRemote h [] [luFollowers]
(recipientSet, remoteActors, fwdHosts, audLocal, audRemote) =
collectAudience $
audAuthor :
audParent :
audChildAuthor :
audChildFollowers :
audParentContext ++ audChildContext
recips = map encodeRouteHome audLocal ++ audRemote
doc = Doc hLocal Activity
{ activityId =
Just $ encodeRouteLocal $
SharerOutboxItemR shrRecip obikhidAccept
, activityActor = encodeRouteLocal $ SharerR shrRecip
, activityCapability = Nothing
, activitySummary = Nothing
, activityAudience = Audience recips [] [] [] [] []
, activitySpecific = AcceptActivity Accept
{ acceptObject = ObjURI hAuthor luOffer
, acceptResult =
Just $ encodeRouteLocal $ TicketDepR tdkhid
}
}
update obiidAccept [OutboxItemActivity =. persistJSONObjectFromDoc doc]
return (doc, recipientSet, remoteActors, fwdHosts)
where
askFollowers = do
hashTALID <- getEncodeKeyHashid
return $ \ talid patch ->
let coll =
if patch
then LocalPersonCollectionSharerProposalFollowers
else LocalPersonCollectionSharerTicketFollowers
in coll shrRecip (hashTALID talid)
-}
mkuri (i, ro) = ObjURI (instanceHost i) (remoteObjectIdent ro)
{-
insertDep
:: MonadIO m
=> UTCTime
-> RemoteAuthor
-> RemoteActivityId
-> LocalTicketId
-> Either (WorkItem, LocalTicketId) (FedURI, LocalURI)
-> OutboxItemId
-> ReaderT SqlBackend m LocalTicketDependencyId
insertDep now author ractidOffer ltidParent child obiidAccept = do
tdid <- insert LocalTicketDependency
{ localTicketDependencyParent = ltidParent
, localTicketDependencyCreated = now
, localTicketDependencyAccept = obiidAccept
}
case child of
Left (_wi, ltid) -> insert_ TicketDependencyChildLocal
{ ticketDependencyChildLocalDep = tdid
, ticketDependencyChildLocalChild = ltid
}
Right (ObjURI h lu, _luFollowers) -> do
iid <- either entityKey id <$> insertBy' (Instance h)
roid <- either entityKey id <$> insertBy' (RemoteObject iid lu)
insert_ TicketDependencyChildRemote
{ ticketDependencyChildRemoteDep = tdid
, ticketDependencyChildRemoteChild = roid
}
insert_ TicketDependencyAuthorRemote
{ ticketDependencyAuthorRemoteDep = tdid
, ticketDependencyAuthorRemoteAuthor = remoteAuthorId author
, ticketDependencyAuthorRemoteOpen = ractidOffer
}
return tdid
-}
deckOfferDepF
:: UTCTime
-> KeyHashid Deck
-> RemoteAuthor
-> ActivityBody
-> Maybe (RecipientRoutes, ByteString)
-> LocalURI
-> AP.TicketDependency URIMode
-> FedURI
-> ExceptT Text Handler (Text, Maybe (ExceptT Text Worker Text))
deckOfferDepF now recipHash author body mfwd luOffer dep uTarget = do
error "projectOfferDepF temporarily disabled"
{-
(parent, child) <- checkDepAndTarget dep uTarget
(Entity jidRecip projectRecip, actorRecip) <- lift $ runDB $ do
sid <- getKeyBy404 $ UniqueSharer shrRecip
ej@(Entity _ j) <- getBy404 $ UniqueProject prjRecip sid
(ej,) <$> getJust (projectActor j)
return $ (,) "Ran initial checks, doing the rest asynchronously" $ Just $ do
relevantParent <-
for (ticketRelevance shrRecip prjRecip parent) $ \ parentLtid -> do
parentAuthor <- runSiteDBExcept $ do
(_, _, _, _, _, _, author, _) <- do
mticket <- lift $ getProjectTicket shrRecip prjRecip parentLtid
fromMaybeE mticket $ "Parent" <> ": No such project-ticket"
lift $ getWorkItemAuthorDetail author
childDetail <- getWorkItemDetail "Child" child
return (parentLtid, parentAuthor, childDetail)
mhttp <- runSiteDBExcept $ do
mractid <- lift $ insertToInbox' now author body (actorInbox actorRecip) luOffer False
for mractid $ \ (ractid, ibiid) -> do
insertDepOffer ibiid parent child
mremotesHttpFwd <- lift $ for mfwd $ \ (localRecips, sig) -> do
relevantFollowers <- askRelevantFollowers
let rf = relevantFollowers shrRecip prjRecip
sieve =
makeRecipientSet [] $ catMaybes
[ rf parent
, rf child
]
remoteRecips <-
insertRemoteActivityToLocalInboxes
False ractid $
localRecipSieve'
sieve False False localRecips
(sig,) <$> deliverRemoteDB_J (actbBL body) ractid jidRecip sig remoteRecips
mremotesHttpAccept <- lift $ for relevantParent $ \ (parentLtid, parentAuthor, childDetail) -> do
obiidAccept <- insertEmptyOutboxItem (actorOutbox actorRecip) now
tdid <- insertDep now author ractid parentLtid (widIdent childDetail) obiidAccept
(docAccept, localRecipsAccept, remoteRecipsAccept, fwdHostsAccept) <-
insertAccept luOffer obiidAccept tdid parentLtid parentAuthor childDetail
knownRemoteRecipsAccept <-
deliverLocal'
False
(LocalActorProject shrRecip prjRecip)
(actorInbox actorRecip)
obiidAccept
localRecipsAccept
(obiidAccept,docAccept,fwdHostsAccept,) <$> deliverRemoteDB fwdHostsAccept obiidAccept remoteRecipsAccept knownRemoteRecipsAccept
return (mremotesHttpFwd, mremotesHttpAccept)
case mhttp of
Nothing -> return "I already have this activity in my inbox, doing nothing"
Just (mremotesHttpFwd, mremotesHttpAccept) -> do
for_ mremotesHttpFwd $ \ (sig, remotes) ->
forkWorker "projectOfferDepF inbox-forwarding" $
deliverRemoteHTTP_J now shrRecip prjRecip (actbBL body) sig remotes
for_ mremotesHttpAccept $ \ (obiid, doc, fwdHosts, remotes) ->
forkWorker "projectOfferDepF Accept HTTP delivery" $
deliverRemoteHttp' fwdHosts obiid doc remotes
return $
case (mremotesHttpAccept, mremotesHttpFwd) of
(Nothing, Nothing) -> "Parent not mine, just stored in inbox and no inbox-forwarding to do"
(Nothing, Just _) -> "Parent not mine, just stored in inbox and ran inbox-forwarding"
(Just _, Nothing) -> "Accepted new ticket dep, no inbox-forwarding to do"
(Just _, Just _) -> "Accepted new ticket dep and ran inbox-forwarding of the Offer"
where
ticketRelevance shr prj (Left (WorkItemProjectTicket shr' prj' ltid))
| shr == shr' && prj == prj' = Just ltid
ticketRelevance _ _ _ = Nothing
insertDepOffer _ (Left _) _ = return ()
insertDepOffer ibiidOffer (Right _) child =
for_ (ticketRelevance shrRecip prjRecip child) $ \ ltid -> do
_ <- do
mticket <- lift $ getProjectTicket shrRecip prjRecip ltid
fromMaybeE mticket $ "Child" <> ": No such project-ticket"
lift $ insert_ TicketDependencyOffer
{ ticketDependencyOfferOffer = ibiidOffer
, ticketDependencyOfferChild = ltid
}
askRelevantFollowers = do
hashLTID <- getEncodeKeyHashid
return $
\ shr prj wi -> followers hashLTID <$> ticketRelevance shr prj wi
where
followers hashLTID ltid =
LocalPersonCollectionProjectTicketFollowers
shrRecip prjRecip (hashLTID ltid)
insertAccept luOffer obiidAccept tdid ltid parentAuthor (WorkItemDetail childId childCtx childAuthor) = do
encodeRouteLocal <- getEncodeRouteLocal
encodeRouteHome <- getEncodeRouteHome
followers <- askFollowers
workItemFollowers <- askWorkItemFollowers
hLocal <- asksSite siteInstanceHost
obikhidAccept <- encodeKeyHashid obiidAccept
tdkhid <- encodeKeyHashid tdid
ra <- getJust $ remoteAuthorId author
let ObjURI hAuthor luAuthor = remoteAuthorURI author
audAuthor =
AudRemote hAuthor [luAuthor] (maybeToList $ remoteActorFollowers ra)
audParentContext =
AudLocal
[]
[ LocalPersonCollectionProjectTeam shrRecip prjRecip
, LocalPersonCollectionProjectFollowers shrRecip prjRecip
]
audChildContext = contextAudience childCtx
audParentFollowers = AudLocal [] [followers ltid]
audParentAuthor =
case parentAuthor of
Left shr -> AudLocal [LocalActorSharer shr] []
Right (i, ro) ->
AudRemote (instanceHost i) [remoteObjectIdent ro] []
audChildAuthor =
case childAuthor of
Left shr -> AudLocal [LocalActorSharer shr] []
Right (ObjURI h lu) -> AudRemote h [lu] []
audChildFollowers =
case childId of
Left (wi, _ltid) -> AudLocal [] [workItemFollowers wi]
Right (ObjURI h _, luFollowers) -> AudRemote h [] [luFollowers]
(recipientSet, remoteActors, fwdHosts, audLocal, audRemote) =
collectAudience $
audAuthor :
audParentAuthor : audParentFollowers :
audChildAuthor : audChildFollowers :
audParentContext : audChildContext
recips = map encodeRouteHome audLocal ++ audRemote
doc = Doc hLocal Activity
{ activityId =
Just $ encodeRouteLocal $
ProjectOutboxItemR shrRecip prjRecip obikhidAccept
, activityActor = encodeRouteLocal $ ProjectR shrRecip prjRecip
, activityCapability = Nothing
, activitySummary = Nothing
, activityAudience = Audience recips [] [] [] [] []
, activitySpecific = AcceptActivity Accept
{ acceptObject = ObjURI hAuthor luOffer
, acceptResult =
Just $ encodeRouteLocal $ TicketDepR tdkhid
}
}
update obiidAccept [OutboxItemActivity =. persistJSONObjectFromDoc doc]
return (doc, recipientSet, remoteActors, fwdHosts)
where
askFollowers = do
hashLTID <- getEncodeKeyHashid
return $
\ ltid ->
LocalPersonCollectionProjectTicketFollowers
shrRecip prjRecip (hashLTID ltid)
-}
repoOfferDepF
:: UTCTime
-> KeyHashid Repo
-> RemoteAuthor
-> ActivityBody
-> Maybe (RecipientRoutes, ByteString)
-> LocalURI
-> AP.TicketDependency URIMode
-> FedURI
-> ExceptT Text Handler (Text, Maybe (ExceptT Text Worker Text))
repoOfferDepF now recipHash author body mfwd luOffer dep uTarget = do
error "repoOfferDepF temporarily disabled"
{-
(parent, child) <- checkDepAndTarget dep uTarget
Entity ridRecip repoRecip <- lift $ runDB $ do
sid <- getKeyBy404 $ UniqueSharer shrRecip
getBy404 $ UniqueRepo rpRecip sid
return $ (,) "Ran initial checks, doing the rest asynchronously" $ Just $ do
relevantParent <-
for (ticketRelevance shrRecip rpRecip parent) $ \ parentLtid -> do
parentAuthor <- runSiteDBExcept $ do
(_, _, _, _, _, _, author, _, _) <- do
mticket <- lift $ getRepoProposal shrRecip rpRecip parentLtid
fromMaybeE mticket $ "Parent" <> ": No such repo-patch"
lift $ getWorkItemAuthorDetail author
childDetail <- getWorkItemDetail "Child" child
return (parentLtid, parentAuthor, childDetail)
mhttp <- runSiteDBExcept $ do
mractid <- lift $ insertToInbox' now author body (repoInbox repoRecip) luOffer False
for mractid $ \ (ractid, ibiid) -> do
insertDepOffer ibiid parent child
mremotesHttpFwd <- lift $ for mfwd $ \ (localRecips, sig) -> do
relevantFollowers <- askRelevantFollowers
let rf = relevantFollowers shrRecip rpRecip
sieve =
makeRecipientSet [] $ catMaybes
[ rf parent
, rf child
]
remoteRecips <-
insertRemoteActivityToLocalInboxes
False ractid $
localRecipSieve'
sieve False False localRecips
(sig,) <$> deliverRemoteDB_R (actbBL body) ractid ridRecip sig remoteRecips
mremotesHttpAccept <- lift $ for relevantParent $ \ (parentLtid, parentAuthor, childDetail) -> do
obiidAccept <- insertEmptyOutboxItem (repoOutbox repoRecip) now
tdid <- insertDep now author ractid parentLtid (widIdent childDetail) obiidAccept
(docAccept, localRecipsAccept, remoteRecipsAccept, fwdHostsAccept) <-
insertAccept luOffer obiidAccept tdid parentLtid parentAuthor childDetail
knownRemoteRecipsAccept <-
deliverLocal'
False
(LocalActorRepo shrRecip rpRecip)
(repoInbox repoRecip)
obiidAccept
localRecipsAccept
(obiidAccept,docAccept,fwdHostsAccept,) <$> deliverRemoteDB fwdHostsAccept obiidAccept remoteRecipsAccept knownRemoteRecipsAccept
return (mremotesHttpFwd, mremotesHttpAccept)
case mhttp of
Nothing -> return "I already have this activity in my inbox, doing nothing"
Just (mremotesHttpFwd, mremotesHttpAccept) -> do
for_ mremotesHttpFwd $ \ (sig, remotes) ->
forkWorker "repoOfferDepF inbox-forwarding" $
deliverRemoteHTTP_R now shrRecip rpRecip (actbBL body) sig remotes
for_ mremotesHttpAccept $ \ (obiid, doc, fwdHosts, remotes) ->
forkWorker "repoOfferDepF Accept HTTP delivery" $
deliverRemoteHttp' fwdHosts obiid doc remotes
return $
case (mremotesHttpAccept, mremotesHttpFwd) of
(Nothing, Nothing) -> "Parent not mine, just stored in inbox and no inbox-forwarding to do"
(Nothing, Just _) -> "Parent not mine, just stored in inbox and ran inbox-forwarding"
(Just _, Nothing) -> "Accepted new ticket dep, no inbox-forwarding to do"
(Just _, Just _) -> "Accepted new ticket dep and ran inbox-forwarding of the Offer"
where
ticketRelevance shr rp (Left (WorkItemRepoProposal shr' rp' ltid))
| shr == shr' && rp == rp' = Just ltid
ticketRelevance _ _ _ = Nothing
insertDepOffer _ (Left _) _ = return ()
insertDepOffer ibiidOffer (Right _) child =
for_ (ticketRelevance shrRecip rpRecip child) $ \ ltid -> do
_ <- do
mticket <- lift $ getRepoProposal shrRecip rpRecip ltid
fromMaybeE mticket $ "Child" <> ": No such repo-patch"
lift $ insert_ TicketDependencyOffer
{ ticketDependencyOfferOffer = ibiidOffer
, ticketDependencyOfferChild = ltid
}
askRelevantFollowers = do
hashLTID <- getEncodeKeyHashid
return $
\ shr rp wi -> followers hashLTID <$> ticketRelevance shr rp wi
where
followers hashLTID ltid =
LocalPersonCollectionRepoProposalFollowers
shrRecip rpRecip (hashLTID ltid)
insertAccept luOffer obiidAccept tdid ltid parentAuthor (WorkItemDetail childId childCtx childAuthor) = do
encodeRouteLocal <- getEncodeRouteLocal
encodeRouteHome <- getEncodeRouteHome
followers <- askFollowers
workItemFollowers <- askWorkItemFollowers
hLocal <- asksSite siteInstanceHost
obikhidAccept <- encodeKeyHashid obiidAccept
tdkhid <- encodeKeyHashid tdid
ra <- getJust $ remoteAuthorId author
let ObjURI hAuthor luAuthor = remoteAuthorURI author
audAuthor =
AudRemote hAuthor [luAuthor] (maybeToList $ remoteActorFollowers ra)
audParentContext =
AudLocal
[]
[ LocalPersonCollectionRepoTeam shrRecip rpRecip
, LocalPersonCollectionRepoFollowers shrRecip rpRecip
]
audChildContext = contextAudience childCtx
audParentFollowers = AudLocal [] [followers ltid]
audParentAuthor =
case parentAuthor of
Left shr -> AudLocal [LocalActorSharer shr] []
Right (i, ro) ->
AudRemote (instanceHost i) [remoteObjectIdent ro] []
audChildAuthor =
case childAuthor of
Left shr -> AudLocal [LocalActorSharer shr] []
Right (ObjURI h lu) -> AudRemote h [lu] []
audChildFollowers =
case childId of
Left (wi, _ltid) -> AudLocal [] [workItemFollowers wi]
Right (ObjURI h _, luFollowers) -> AudRemote h [] [luFollowers]
(recipientSet, remoteActors, fwdHosts, audLocal, audRemote) =
collectAudience $
audAuthor :
audParentAuthor : audParentFollowers :
audChildAuthor : audChildFollowers :
audParentContext : audChildContext
recips = map encodeRouteHome audLocal ++ audRemote
doc = Doc hLocal Activity
{ activityId =
Just $ encodeRouteLocal $
RepoOutboxItemR shrRecip rpRecip obikhidAccept
, activityActor = encodeRouteLocal $ RepoR shrRecip rpRecip
, activityCapability = Nothing
, activitySummary = Nothing
, activityAudience = Audience recips [] [] [] [] []
, activitySpecific = AcceptActivity Accept
{ acceptObject = ObjURI hAuthor luOffer
, acceptResult =
Just $ encodeRouteLocal $ TicketDepR tdkhid
}
}
update obiidAccept [OutboxItemActivity =. persistJSONObjectFromDoc doc]
return (doc, recipientSet, remoteActors, fwdHosts)
where
askFollowers = do
hashLTID <- getEncodeKeyHashid
return $
\ ltid ->
LocalPersonCollectionRepoProposalFollowers
shrRecip rpRecip (hashLTID ltid)
-}
{-
verifyWorkItemExists (WorkItemSharerTicket shr talid False) = do
mticket <- lift $ getSharerTicket shr talid
verifyNothingE mticket $ "Object" <> ": No such sharer-ticket"
verifyWorkItemExists (WorkItemSharerTicket shr talid True) = do
mticket <- lift $ getSharerProposal shr talid
verifyNothingE mticket $ "Object" <> ": No such sharer-patch"
verifyWorkItemExists (WorkItemProjectTicket shr prj ltid) = do
mticket <- lift $ getProjectTicket shr prj ltid
verifyNothingE mticket $ "Object" <> ": No such project-ticket"
verifyWorkItemExists (WorkItemRepoProposal shr rp ltid) = do
mticket <- lift $ getRepoProposal shr rp ltid
verifyNothingE mticket $ "Object" <> ": No such repo-patch"
insertResolve author ltid ractid obiidAccept = do
mtrid <- insertUnique TicketResolve
{ ticketResolveTicket = ltid
, ticketResolveAccept = obiidAccept
}
for mtrid $ \ trid ->
insertUnique TicketResolveRemote
{ ticketResolveRemoteTicket = trid
, ticketResolveRemoteActivity = ractid
, ticketResolveRemoteActor = remoteAuthorId author
}
-}
trackerResolveF
:: ( PersistRecordBackend r SqlBackend, ToBackendKey SqlBackend r
, ToBackendKey SqlBackend wi
)
=> (Route App -> Maybe (KeyHashid wi))
-> (r -> ActorId)
-> ( Key r
-> Key wi
-> ExceptT Text AppDB
( TicketId
, Maybe
( Entity TicketResolve
, Either
(Entity TicketResolveLocal)
(Entity TicketResolveRemote)
)
)
)
-> (Key r -> GrantResourceBy Key)
-> (KeyHashid r -> LocalStageBy KeyHashid)
-> (KeyHashid r -> KeyHashid wi -> LocalStageBy KeyHashid)
-> (forall f. f r -> LocalActorBy f)
-> UTCTime
-> KeyHashid r
-> RemoteAuthor
-> ActivityBody
-> Maybe (RecipientRoutes, ByteString)
-> LocalURI
-> AP.Resolve URIMode
-> ExceptT Text Handler (Text, Maybe (ExceptT Text Worker Text))
trackerResolveF maybeWorkItem grabActor getWorkItem makeResource trackerFollowers itemFollowers makeLocalActor now recipHash author body mfwd luResolve (AP.Resolve uObject) = (,Nothing) <$> do
-- Check input
recipID <- decodeKeyHashid404 recipHash
wiID <- nameExceptT "Resolve object" $ do
route <- do
routeOrRemote <- parseFedURIOld uObject
case routeOrRemote of
Left route -> pure route
Right _ -> throwE "Remote, so definitely not mine"
case maybeWorkItem route of
Nothing -> throwE "Local route but not a work item of mine"
Just wiHash ->
decodeKeyHashidE wiHash "Invalid work item keyhashid"
-- Verify the capability URI is one of:
-- * Outbox item URI of a local actor, i.e. a local activity
-- * A remote URI
capID <- do
uCap <- do
fromMaybeE
(activityCapability $ actbActivity body)
"Asking to resolve ticket but no capability provided"
nameExceptT "Resolve capability" $ parseActivityURI uCap
maybeHttp <- runDBExcept $ do
-- Find recipient tracker in DB, returning 404 if doesn't exist because
-- we're in the tracker's inbox post handler
recip <- lift $ get404 recipID
let recipActorID = grabActor recip
recipActor <- lift $ getJust recipActorID
-- Insert the Resolve to tracker's inbox
mractid <- lift $ insertToInbox now author body (actorInbox recipActor) luResolve False
for mractid $ \ resolveID -> do
-- Find ticket in DB, verify it's not resolved
ticketID <- do
(ticketID, maybeResolve) <- getWorkItem recipID wiID
unless (isNothing maybeResolve) $
throwE "Work item is already resolved"
return ticketID
-- Verify the sender is authorized by the tracker to resolve a ticket
capability <-
case capID of
Left (capActor, _, capItem) -> return (capActor, capItem)
Right _ -> throwE "Capability is a remote URI, i.e. not authored by the local tracker"
verifyCapability
capability
(Right $ remoteAuthorId author)
(makeResource recipID)
-- Forward the Resolve activity to relevant local stages, and
-- schedule delivery for unavailable remote members of them
maybeHttpFwdResolve <- lift $ for mfwd $ \ (localRecips, sig) -> do
wiHash <- encodeKeyHashid wiID
let sieve =
makeRecipientSet
[]
[ trackerFollowers recipHash
, itemFollowers recipHash wiHash
]
forwardActivityDB
(actbBL body) localRecips sig recipActorID
(makeLocalActor recipHash) sieve resolveID
-- Mark ticket in DB as resolved by the Resolve
acceptID <-
lift $ insertEmptyOutboxItem (actorOutbox recipActor) now
lift $ insertResolve ticketID resolveID acceptID
-- Prepare an Accept activity and insert to tracker's outbox
(actionAccept, localRecipsAccept, remoteRecipsAccept, fwdHostsAccept) <-
lift $ prepareAccept wiID
_luAccept <- lift $ updateOutboxItem (makeLocalActor recipID) acceptID actionAccept
-- Deliver the Accept to local recipients, and schedule delivery
-- for unavailable remote recipients
deliverHttpAccept <-
deliverActivityDB
(makeLocalActor recipHash) recipActorID
localRecipsAccept remoteRecipsAccept fwdHostsAccept
acceptID actionAccept
-- Return instructions for HTTP inbox-forwarding of the Resolve
-- activity, and for HTTP delivery of the Accept activity to
-- remote recipients
return (maybeHttpFwdResolve, deliverHttpAccept)
-- Launch asynchronous HTTP forwarding of the Resolve activity and HTTP
-- delivery of the Accept activity
case maybeHttp of
Nothing ->
return "I already have this activity in my inbox, doing nothing"
Just (maybeHttpFwdResolve, deliverHttpAccept) -> do
for_ maybeHttpFwdResolve $
forkWorker "trackerResolveF inbox-forwarding"
forkWorker "trackerResolveF Accept HTTP delivery" deliverHttpAccept
return $
case maybeHttpFwdResolve of
Nothing -> "Resolved ticket, no inbox-forwarding to do"
Just _ ->
"Resolved ticket and ran inbox-forwarding of the Resolve"
where
insertResolve ticketID resolveID acceptID = do
trid <- insert TicketResolve
{ ticketResolveTicket = ticketID
, ticketResolveAccept = acceptID
}
insert_ TicketResolveRemote
{ ticketResolveRemoteTicket = trid
, ticketResolveRemoteActivity = resolveID
, ticketResolveRemoteActor = remoteAuthorId author
}
prepareAccept wiID = do
encodeRouteHome <- getEncodeRouteHome
wiHash <- encodeKeyHashid wiID
ra <- getJust $ remoteAuthorId author
let ObjURI hAuthor luAuthor = remoteAuthorURI author
audSender =
AudRemote hAuthor
[luAuthor]
(maybeToList $ remoteActorFollowers ra)
audTracker =
AudLocal
[]
[ trackerFollowers recipHash
, itemFollowers recipHash wiHash
]
(recipientSet, remoteActors, fwdHosts, audLocal, audRemote) =
collectAudience [audSender, audTracker]
recips = map encodeRouteHome audLocal ++ audRemote
action = AP.Action
{ AP.actionCapability = Nothing
, AP.actionSummary = Nothing
, AP.actionAudience = AP.Audience recips [] [] [] [] []
, AP.actionFulfills = []
, AP.actionSpecific = AP.AcceptActivity AP.Accept
{ AP.acceptObject = ObjURI hAuthor luResolve
, AP.acceptResult = Nothing
}
}
return (action, recipientSet, remoteActors, fwdHosts)
deckResolveF
:: UTCTime
-> KeyHashid Deck
-> RemoteAuthor
-> ActivityBody
-> Maybe (RecipientRoutes, ByteString)
-> LocalURI
-> AP.Resolve URIMode
-> ExceptT Text Handler (Text, Maybe (ExceptT Text Worker Text))
deckResolveF now deckHash =
trackerResolveF
(\case
TicketR trackerHash taskHash | trackerHash == deckHash ->
Just taskHash
_ -> Nothing
)
deckActor
(\ deckID taskID -> do
maybeTicket <- lift $ getTicket deckID taskID
(_deck, _task, Entity ticketID _, _author, maybeResolve) <-
fromMaybeE maybeTicket "I don't have such a ticket in DB"
return (ticketID, maybeResolve)
)
GrantResourceDeck
LocalStageDeckFollowers
LocalStageTicketFollowers
LocalActorDeck
now
deckHash
loomResolveF
:: UTCTime
-> KeyHashid Loom
-> RemoteAuthor
-> ActivityBody
-> Maybe (RecipientRoutes, ByteString)
-> LocalURI
-> AP.Resolve URIMode
-> ExceptT Text Handler (Text, Maybe (ExceptT Text Worker Text))
loomResolveF now loomHash =
trackerResolveF
(\case
ClothR trackerHash clothHash | trackerHash == loomHash ->
Just clothHash
_ -> Nothing
)
loomActor
(\ loomID clothID -> do
maybeCloth <- lift $ getCloth loomID clothID
(_loom, _cloth, Entity ticketID _, _author, maybeResolve, _merge) <-
fromMaybeE maybeCloth "I don't have such a MR in DB"
return (ticketID, maybeResolve)
)
GrantResourceLoom
LocalStageLoomFollowers
LocalStageClothFollowers
LocalActorLoom
now
loomHash
|