Begin implementation of billing event persistence.

[?]
Jan 20, 2017, 5:31 AM
73NDXDEZRMK672GHSTC3CI6YHXFZ2GGJI5IKQGHKFDZKTNSQXLLQC

Dependencies

  • [2] 5ZSKPQ3K Add created_at and auction_start timestamps to auction
  • [3] DFOBMSAO Initial work on payments API
  • [4] 3QVT6MA6 Add database support for event amend operations.
  • [5] 7KZP4RHZ Switch from Data.Time to Data.Thyme
  • [6] RPAJLHMT Change to use UUIDs instead of ints for primary keys.
  • [7] IZEVQF62 Work in progress replacing sqlite with postgres.
  • [8] GCVQD44V Create amends endpoint, switch to UUID primary keys
  • [9] 7HPY3QPF Fix linting errors. (yay hlint!)
  • [10] ZP62WC47 Begin conversion to build with stack.
  • [11] EKY7U7SK Finish conversion to stack.
  • [12] NEDDHXUK Reformat via stylish-haskell
  • [13] O5FVTOM6 Undo JSON silliness, enable a couple more routes.
  • [14] 2G3GNDDU Event logging is now functioning in postgres.
  • [15] XTBSG4C7 Adding serveJSON combinator to eliminate some boilerplate from handlers.
  • [16] KEP5WUFJ Convert project to stack-based build.
  • [17] NLZ3JXLO Fix formatting with stylish-haskell.
  • [18] LD4GLVSF More database stuff.
  • [19] M4KM76DG Merge branch 'stackify'
  • [20] QMRKFEPG Refactor QDB to use a free monad algebra instead.
  • [21] ZITLSTYX Fix problems with SQL queries & depreciation function parsing.
  • [22] RN7EI6IN Update database layer to use CreditTo
  • [23] NAS4BFL4 Trivial stylish-haskell reformat.
  • [24] 2XQD6KKK Add invitation logic and clean up DBProg error handling.
  • [25] 5OI44E4E Add authentication to auction search.
  • [26] FD7SV5I6 Fix handling of event_t columns.
  • [27] A6HKMINB Attempting to improve JSON handling.
  • [28] HALRDT2F Added initial auction create route.
  • [29] TNR3TEHK Switch to Postgres + snaplet arch compiles.
  • [30] Z7KS5XHH Very WIP. Wow.
  • [*] 64C6AWH6 Rename Ananke -> Quixotic, project reboot.
  • [*] BROSTG5K Beginning of modularization of server.

Change contents

  • replacement in lib/Aftok/Billables.hs at line 7
    [3.857][3.857:914]()
    import Control.Lens (makeLenses)
    [3.857]
    [3.914]
    import Control.Lens (makeLenses, makePrisms)
  • edit in lib/Aftok/Billables.hs at line 11
    [3.981]
    [3.981]
    import Aftok.Project (ProjectId)
    import Aftok (UserId)
    import Aftok.Types (Satoshi)
  • replacement in lib/Aftok/Billables.hs at line 16
    [3.1039][3.1039:1040]()
    [3.1039]
    [3.1040]
    makePrisms ''BillableId
  • replacement in lib/Aftok/Billables.hs at line 18
    [3.1041][3.1041:1064]()
    data BillingFrequency
    [3.1041]
    [3.1064]
    data Recurrence
  • edit in lib/Aftok/Billables.hs at line 23
    [3.1124][3.1124:1221]()
    makeLenses ''BillingFrequency
    data Recurrence
    = Recurring { _frequency :: BillingFrequency }
  • replacement in lib/Aftok/Billables.hs at line 26
    [3.1261][3.1261:1304]()
    data Billable (p :: *) (c :: *) = Billable
    [3.1261]
    [3.1304]
    recurrenceName :: Recurrence -> Text
    recurrenceName Annually = "annually"
    recurrenceName (Monthly _) = "monthly"
    recurrenceName SemiMonthly = "semimonthly"
    recurrenceName (Weekly _) = "weekly"
    recurrenceName OneTime = "onetime"
    recurrenceCount :: Recurrence -> Maybe Int
    recurrenceCount Annually = Nothing
    recurrenceCount (Monthly i) = Just i
    recurrenceCount SemiMonthly = Nothing
    recurrenceCount (Weekly i) = Just i
    recurrenceCount OneTime = Nothing
    data Billable' (p :: *) (u :: *) (c :: *) = Billable
  • edit in lib/Aftok/Billables.hs at line 42
    [3.1322]
    [3.1322]
    , _creator :: u
  • replacement in lib/Aftok/Billables.hs at line 49
    [3.1441][3.1441:1463]()
    makeLenses ''Billable
    [3.1441]
    [3.1463]
    makeLenses ''Billable'
    type Billable = Billable' ProjectId UserId Satoshi
  • replacement in lib/Aftok/Billables.hs at line 53
    [3.1464][3.1464:1492]()
    monthly :: BillingFrequency
    [3.1464]
    [3.1492]
    monthly :: Recurrence
  • replacement in lib/Aftok/Billables.hs at line 56
    [3.1513][3.1513:1543]()
    bimonthly :: BillingFrequency
    [3.1513]
    [3.1543]
    bimonthly :: Recurrence
  • replacement in lib/Aftok/Billables.hs at line 59
    [3.1566][3.1566:1596]()
    quarterly :: BillingFrequency
    [3.1566]
    [3.1596]
    quarterly :: Recurrence
  • replacement in lib/Aftok/Billables.hs at line 62
    [3.1619][3.1619:1653]()
    seminannually :: BillingFrequency
    [3.1619]
    [3.1653]
    seminannually :: Recurrence
  • replacement in lib/Aftok/Billables.hs at line 65
    [3.1680][3.1680:1709]()
    annually :: BillingFrequency
    [3.1680]
    [3.1709]
    annually :: Recurrence
  • edit in lib/Aftok/Billables.hs at line 68
    [3.1730]
    newtype SubscriptionId = SubscriptionId UUID deriving (Show, Eq)
  • replacement in lib/Aftok/Database/PostgreSQL.hs at line 17
    [4.1453][3.1772:1827]()
    import Database.PostgreSQL.Simple.FromField
    [4.1453]
    [4.1507]
    import Database.PostgreSQL.Simple.FromField
  • replacement in lib/Aftok/Database/PostgreSQL.hs at line 24
    [4.1583][4.476:535]()
    import Aftok.Auction as A
    [4.1583]
    [4.1614]
    import qualified Aftok.Auction as A
    import qualified Aftok.Billables as BI
  • replacement in lib/Aftok/Database/PostgreSQL.hs at line 28
    [4.1678][4.536:595]()
    import Aftok.Project as P
    [4.1678]
    [4.1678]
    import qualified Aftok.Project as P
    import Aftok.Time (Days(..), _Days)
  • replacement in lib/Aftok/Database/PostgreSQL.hs at line 45
    [4.1290][4.4:83]()
    pidParser :: FieldParser ProjectId
    pidParser f v = ProjectId <$> fromField f v
    [4.1290]
    [4.83]
    pidParser :: FieldParser P.ProjectId
    pidParser f v = P.ProjectId <$> fromField f v
  • edit in lib/Aftok/Database/PostgreSQL.hs at line 77
    [4.114]
    [4.114]
    recurrenceParser :: RowParser BI.Recurrence
    recurrenceParser =
    let prec :: Text -> RowParser BI.Recurrence
    prec "annually" = nullField *> pure BI.Annually
    prec "monthly" = BI.Monthly <$> field
    prec "semimonthly" = nullField *> pure BI.SemiMonthly
    prec "weekly" = BI.Weekly <$> field
    prec "onetime" = nullField *> pure BI.OneTime
    prec _ = empty
    in field >>= prec
  • replacement in lib/Aftok/Database/PostgreSQL.hs at line 91
    [4.213][4.213:243](),[4.243][3.2364:2765](),[3.2765][4.533:607](),[4.533][4.533:607](),[4.607][3.2766:3131](),[3.3131][4.863:892](),[4.863][4.863:892]()
    case tn of
    "event_t" ->
    let err = UnexpectedNull { errSQLType = B.unpack tn
    , errSQLTableOid = tableOid f
    , errSQLField = maybe "" B.unpack (name f)
    , errHaskellType = "UTCTime -> LogEvent"
    , errMessage = "columns of type event_t should not contain null values"
    }
    in maybe (conversionError err) (nameEvent . decodeUtf8) v
    _ ->
    let err = Incompatible { errSQLType = B.unpack tn
    , errSQLTableOid = tableOid f
    , errSQLField = maybe "" B.unpack (name f)
    , errHaskellType = "UTCTime -> LogEvent"
    , errMessage = "column was not of type event_t"
    }
    in conversionError err
    [4.213]
    [4.271]
    if tn /= "event_t"
    then returnError Incompatible f "column was not of type event_t"
    else maybe (returnError UnexpectedNull f "event type may not be null") (nameEvent . decodeUtf8) v
  • replacement in lib/Aftok/Database/PostgreSQL.hs at line 96
    [4.944][4.944:1043](),[4.1043][4.596:638](),[4.638][4.1086:1409](),[4.1086][4.1086:1409]()
    creditToParser f v = do
    tn <- typename f
    let parser :: Text -> Conversion (RowParser CreditTo)
    parser tname = pure $ case tname of
    "credit_to_btc_addr" -> CreditToAddress <$> (fieldWith btcAddrParser <* nullField <* nullField)
    "credit_to_user" -> CreditToUser <$> (nullField *> fieldWith uidParser <* nullField)
    "credit_to_project" -> CreditToProject <$> (nullField *> nullField *> fieldWith pidParser)
    _ -> empty
    [4.944]
    [4.1409]
    creditToParser f v =
    let parser :: Text -> RowParser CreditTo
    parser "credit_to_btc_addr" = CreditToAddress <$> (fieldWith btcAddrParser <* nullField <* nullField)
    parser "credit_to_user" = CreditToUser <$> (nullField *> fieldWith uidParser <* nullField)
    parser "credit_to_project" = CreditToProject <$> (nullField *> nullField *> fieldWith pidParser)
    parser _ = empty
    in do
    tn <- typename f
    if tn /= "credit_to_t"
    then returnError Incompatible f "column was not of type credit_to_t"
    else maybe empty (pure . parser . decodeUtf8) v
  • edit in lib/Aftok/Database/PostgreSQL.hs at line 108
    [4.1410][4.1410:1480](),[4.1480][4.639:685](),[4.685][4.1528:1679](),[4.1528][4.1528:1679](),[4.1679][4.686:687](),[4.687][4.1682:1683](),[4.1682][4.1682:1683]()
    case tn of
    "credit_to_t" -> maybe empty (parser . decodeUtf8) v
    _ -> conversionError $
    Incompatible
    (B.unpack tn)
    (tableOid f)
    (maybe "" B.unpack (name f))
    "RowParser CreditTo"
    "column was not of type event_t"
  • replacement in lib/Aftok/Database/PostgreSQL.hs at line 120
    [4.2022][4.617:652](),[4.617][4.617:652]()
    auctionParser :: RowParser Auction
    [4.2022]
    [4.2023]
    auctionParser :: RowParser A.Auction
  • replacement in lib/Aftok/Database/PostgreSQL.hs at line 122
    [4.2039][4.109:177](),[4.177][2.187:221](),[4.177][4.275:309](),[2.221][4.275:309](),[4.275][4.275:309](),[4.309][2.222:256](),[4.47][4.2144:2178](),[4.156][4.2144:2178](),[2.256][4.2144:2178](),[4.309][4.2144:2178](),[4.662][4.2144:2178](),[4.2144][4.2144:2178]()
    Auction <$> fieldWith pidParser
    <*> fieldWith uidParser
    <*> fieldWith utcParser
    <*> fieldWith btcParser
    <*> fieldWith utcParser
    <*> fieldWith utcParser
    [4.2039]
    [4.2729]
    A.Auction <$> fieldWith pidParser
    <*> fieldWith uidParser
    <*> fieldWith utcParser
    <*> fieldWith btcParser
    <*> fieldWith utcParser
    <*> fieldWith utcParser
  • replacement in lib/Aftok/Database/PostgreSQL.hs at line 129
    [4.2730][4.725:752]()
    bidParser :: RowParser Bid
    [4.2730]
    [4.2040]
    bidParser :: RowParser A.Bid
  • replacement in lib/Aftok/Database/PostgreSQL.hs at line 131
    [4.2052][4.2052:2146](),[4.2146][4.2179:2209](),[4.862][4.2179:2209]()
    Bid <$> fieldWith uidParser
    <*> fieldWith secondsParser
    <*> fieldWith btcParser
    <*> fieldWith utcParser
    [4.2052]
    [4.2828]
    A.Bid <$> fieldWith uidParser
    <*> fieldWith secondsParser
    <*> fieldWith btcParser
    <*> fieldWith utcParser
  • replacement in lib/Aftok/Database/PostgreSQL.hs at line 147
    [4.3062][4.1116:1151]()
    projectParser :: RowParser Project
    [4.3062]
    [4.2208]
    projectParser :: RowParser P.Project
  • replacement in lib/Aftok/Database/PostgreSQL.hs at line 149
    [4.2224][4.2224:2278](),[4.2275][4.1210:1244](),[4.2278][4.1210:1244](),[4.1210][4.1210:1244](),[4.1244][4.422:460]()
    Project <$> field
    <*> fieldWith utcParser
    <*> fieldWith uidParser
    <*> fieldWith fromJSONField
    [4.2224]
    [4.313]
    P.Project <$> field
    <*> fieldWith utcParser
    <*> fieldWith uidParser
    <*> fieldWith fromJSONField
  • replacement in lib/Aftok/Database/PostgreSQL.hs at line 154
    [4.314][4.2276:2317]()
    invitationParser :: RowParser Invitation
    [4.314]
    [4.2279]
    invitationParser :: RowParser P.Invitation
  • replacement in lib/Aftok/Database/PostgreSQL.hs at line 156
    [4.2298][4.2337:2530](),[4.2337][4.2337:2530]()
    Invitation <$> fieldWith pidParser
    <*> fieldWith uidParser
    <*> fieldWith emailParser
    <*> fieldWith utcParser
    <*> fmap (fmap toThyme) field
    [4.2298]
    [4.2530]
    P.Invitation <$> fieldWith pidParser
    <*> fieldWith uidParser
    <*> fieldWith emailParser
    <*> fieldWith utcParser
    <*> fmap (fmap toThyme) field
  • edit in lib/Aftok/Database/PostgreSQL.hs at line 166
    [4.1361]
    [4.293]
    billableParser :: RowParser BI.Billable
    billableParser =
    BI.Billable <$> fieldWith pidParser
    <*> fieldWith uidParser
    <*> field
    <*> field
    <*> recurrenceParser
    <*> fieldWith btcParser
    <*> (Days <$> field)
  • replacement in lib/Aftok/Database/PostgreSQL.hs at line 198
    [4.1362][4.581:608](),[4.608][4.4:75](),[4.75][4.688:732](),[4.732][4.121:182](),[4.121][4.121:182](),[4.182][4.1732:1885](),[4.1885][4.316:341](),[4.316][4.316:341](),[4.341][3.3184:3302]()
    instance DBEval QDBM where
    dbEval (CreateEvent (ProjectId pid) (UserId uid) (LogEntry c e m)) =
    case c of
    CreditToAddress addr ->
    pinsert EventId
    "INSERT INTO work_events \
    \(project_id, user_id, credit_to_type, credit_to_btc_addr, event_type, event_time, event_metadata) \
    \VALUES (?, ?, ?, ?, ?, ?, ?) \
    \RETURNING id"
    ( pid, uid, creditToName c, addr ^. _BtcAddr . to addrToBase58, eventName e, fromThyme $ e ^. eventTime, m)
    [4.1362]
    [4.425]
    storeEvent :: DBOp a -> Maybe (QDBM EventId)
    storeEvent (CreateBillable _) = error "Not implemented"
    storeEvent (CreateSubscription _ _) = error "Not implemented"
    storeEvent (CreatePaymentRequest _) = error "Not implemented"
    storeEvent (CreatePayment _) = error "Not implemented"
    storeEvent _ = Nothing
  • replacement in lib/Aftok/Database/PostgreSQL.hs at line 205
    [4.426][4.1987:2017](),[4.2017][4.455:516](),[4.455][4.455:516](),[4.516][4.2018:2173](),[4.2173][4.652:677](),[4.652][4.652:677](),[4.677][4.2174:2276]()
    CreditToProject pid' ->
    pinsert EventId
    "INSERT INTO work_events \
    \(project_id, user_id, credit_to_type, credit_to_project_id, event_type, event_time, event_metadata) \
    \VALUES (?, ?, ?, ?, ?, ?, ?) \
    \RETURNING id"
    ( pid, uid, creditToName c, pid' ^. _ProjectId, eventName e, fromThyme $ e ^. eventTime, m)
    [4.426]
    [4.762]
    updateCache :: DBOp a -> QDBM a
    updateCache (CreateEvent (P.ProjectId pid) (UserId uid) (LogEntry c e m)) =
    case c of
    CreditToAddress addr ->
    pinsert EventId
    "INSERT INTO work_events \
    \(project_id, user_id, credit_to_type, credit_to_btc_addr, event_type, event_time, event_metadata) \
    \VALUES (?, ?, ?, ?, ?, ?, ?) \
    \RETURNING id"
    ( pid, uid, creditToName c, addr ^. _BtcAddr . to addrToBase58, eventName e, fromThyme $ e ^. eventTime, m)
  • replacement in lib/Aftok/Database/PostgreSQL.hs at line 216
    [4.763][4.2277:2304](),[4.2304][4.789:850](),[4.789][4.789:850](),[4.850][4.2305:2457](),[4.2457][4.983:1008](),[4.983][4.983:1008](),[4.1008][4.2458:2557]()
    CreditToUser uid' ->
    pinsert EventId
    "INSERT INTO work_events \
    \(project_id, user_id, credit_to_type, credit_to_user_id, event_type, event_time, event_metadata) \
    \VALUES (?, ?, ?, ?, ?, ?, ?) \
    \RETURNING id"
    ( pid, uid, creditToName c, uid' ^. _UserId, eventName e, fromThyme $ e ^. eventTime, m)
    [4.763]
    [4.1807]
    CreditToProject pid' ->
    pinsert EventId
    "INSERT INTO work_events \
    \(project_id, user_id, credit_to_type, credit_to_project_id, event_type, event_time, event_metadata) \
    \VALUES (?, ?, ?, ?, ?, ?, ?) \
    \RETURNING id"
    ( pid, uid, creditToName c, pid' ^. P._ProjectId, eventName e, fromThyme $ e ^. eventTime, m)
  • replacement in lib/Aftok/Database/PostgreSQL.hs at line 224
    [4.1808][4.2552:2589](),[4.2589][4.236:277](),[4.236][4.236:277](),[4.277][4.2558:2746](),[4.2746][4.1162:1200](),[4.1162][4.1162:1200]()
    dbEval (FindEvent (EventId eid)) =
    headMay <$> pquery qdbLogEntryParser
    "SELECT project_id, user_id, \
    \credit_to_type, credit_to_btc_addr, credit_to_user_id, credit_to_project_id, \
    \event_type, event_time, event_metadata FROM work_events \
    \WHERE id = ?"
    (Only eid)
    [4.1808]
    [4.3405]
    CreditToUser uid' ->
    pinsert EventId
    "INSERT INTO work_events \
    \(project_id, user_id, credit_to_type, credit_to_user_id, event_type, event_time, event_metadata) \
    \VALUES (?, ?, ?, ?, ?, ?, ?) \
    \RETURNING id"
    ( pid, uid, creditToName c, uid' ^. _UserId, eventName e, fromThyme $ e ^. eventTime, m)
  • replacement in lib/Aftok/Database/PostgreSQL.hs at line 232
    [4.3406][4.1231:1289](),[4.1289][4.278:323](),[4.323][4.1323:1409](),[4.1323][4.1323:1409](),[4.1409][4.2590:2659](),[4.2659][4.2802:2836](),[4.1479][4.2802:2836](),[4.2836][4.324:371](),[4.371][4.1548:1684](),[4.1548][4.1548:1684](),[4.1684][4.2660:2712](),[4.2712][4.2837:2884](),[4.1737][4.2837:2884](),[4.2884][4.372:416](),[4.416][4.1815:1901](),[4.1815][4.1815:1901](),[4.1901][4.2713:2782](),[4.2782][4.2885:2919](),[4.1971][4.2885:2919](),[4.2919][4.417:432]()
    dbEval (FindEvents (ProjectId pid) (UserId uid) ival) =
    let q (Before e) = pquery logEntryParser
    "SELECT btc_addr, event_type, event_time, event_metadata FROM work_events \
    \WHERE project_id = ? AND user_id = ? AND event_time <= ?"
    (pid, uid, fromThyme e)
    q (During s e) = pquery logEntryParser
    "SELECT btc_addr, event_type, event_time, event_metadata FROM work_events \
    \WHERE project_id = ? AND user_id = ? \
    \AND event_time >= ? AND event_time <= ?"
    (pid, uid, fromThyme s, fromThyme e)
    q (After s) = pquery logEntryParser
    "SELECT btc_addr, event_type, event_time, event_metadata FROM work_events \
    \WHERE project_id = ? AND user_id = ? AND event_time >= ?"
    (pid, uid, fromThyme s)
    in q ival
    [4.3406]
    [4.1083]
    updateCache (FindEvent (EventId eid)) =
    headMay <$> pquery qdbLogEntryParser
    "SELECT project_id, user_id, \
    \credit_to_type, credit_to_btc_addr, credit_to_user_id, credit_to_project_id, \
    \event_type, event_time, event_metadata FROM work_events \
    \WHERE id = ?"
    (Only eid)
  • replacement in lib/Aftok/Database/PostgreSQL.hs at line 240
    [4.1084][4.2783:2839](),[4.2839][4.2092:2116](),[4.2092][4.2092:2116](),[4.2116][4.1091:1216](),[4.1216][4.2221:2276](),[4.2221][4.2221:2276]()
    dbEval (AmendEvent (EventId eid) (TimeChange mt t)) =
    pinsert AmendmentId
    "INSERT INTO event_time_amendments \
    \(event_id, amended_at, event_time) \
    \VALUES (?, ?, ?) RETURNING id"
    ( eid, fromThyme $ mt ^. _ModTime, fromThyme t )
    [4.1084]
    [4.1951]
    updateCache (FindEvents (P.ProjectId pid) (UserId uid) ival) =
    let q (Before e) = pquery logEntryParser
    "SELECT btc_addr, event_type, event_time, event_metadata FROM work_events \
    \WHERE project_id = ? AND user_id = ? AND event_time <= ?"
    (pid, uid, fromThyme e)
    q (During s e) = pquery logEntryParser
    "SELECT btc_addr, event_type, event_time, event_metadata FROM work_events \
    \WHERE project_id = ? AND user_id = ? \
    \AND event_time >= ? AND event_time <= ?"
    (pid, uid, fromThyme s, fromThyme e)
    q (After s) = pquery logEntryParser
    "SELECT btc_addr, event_type, event_time, event_metadata FROM work_events \
    \WHERE project_id = ? AND user_id = ? AND event_time >= ?"
    (pid, uid, fromThyme s)
    in q ival
  • replacement in lib/Aftok/Database/PostgreSQL.hs at line 256
    [4.1952][4.2747:2821](),[4.2821][4.1305:1532](),[4.1305][4.1305:1532](),[4.1532][3.3303:3401]()
    dbEval (AmendEvent (EventId eid) (CreditToChange mt c)) =
    case c of
    CreditToAddress addr ->
    pinsert AmendmentId
    "INSERT INTO event_credit_to_amendments \
    \(event_id, amended_at, credit_to_type, credit_to_btc_addr) \
    \VALUES (?, ?, ?, ?) RETURNING id"
    ( eid, fromThyme $ mt ^. _ModTime, creditToName c, addr ^. _BtcAddr . to addrToBase58 )
    [4.1952]
    [4.2903]
    updateCache (AmendEvent (EventId eid) (TimeChange mt t)) =
    pinsert AmendmentId
    "INSERT INTO event_time_amendments \
    \(event_id, amended_at, event_time) \
    \VALUES (?, ?, ?) RETURNING id"
    ( eid, fromThyme $ mt ^. _ModTime, fromThyme t )
  • replacement in lib/Aftok/Database/PostgreSQL.hs at line 263
    [4.2904][4.1618:1846](),[4.1846][4.2903:2984]()
    CreditToProject pid ->
    pinsert AmendmentId
    "INSERT INTO event_credit_to_amendments \
    \(event_id, amended_at, credit_to_type, credit_to_project_id) \
    \VALUES (?, ?, ?, ?) RETURNING id"
    ( eid, fromThyme $ mt ^. _ModTime, creditToName c, pid ^. _ProjectId )
    [4.2904]
    [4.1932]
    updateCache (AmendEvent (EventId eid) (CreditToChange mt c)) =
    case c of
    CreditToAddress addr ->
    pinsert AmendmentId
    "INSERT INTO event_credit_to_amendments \
    \(event_id, amended_at, credit_to_type, credit_to_btc_addr) \
    \VALUES (?, ?, ?, ?) RETURNING id"
    ( eid, fromThyme $ mt ^. _ModTime, creditToName c, addr ^. _BtcAddr . to addrToBase58 )
  • replacement in lib/Aftok/Database/PostgreSQL.hs at line 272
    [4.1933][4.733:759](),[4.759][4.2985:3013](),[4.1960][4.2985:3013](),[4.3013][4.1960:2128](),[4.1960][4.1960:2128](),[4.2128][4.3014:3092]()
    CreditToUser uid ->
    pinsert AmendmentId
    "INSERT INTO event_credit_to_amendments \
    \(event_id, amended_at, credit_to_type, credit_to_user_id) \
    \VALUES (?, ?, ?, ?) RETURNING id"
    ( eid, fromThyme $ mt ^. _ModTime, creditToName c, uid ^. _UserId )
    [4.1933]
    [4.2208]
    CreditToProject pid ->
    pinsert AmendmentId
    "INSERT INTO event_credit_to_amendments \
    \(event_id, amended_at, credit_to_type, credit_to_project_id) \
    \VALUES (?, ?, ?, ?) RETURNING id"
    ( eid, fromThyme $ mt ^. _ModTime, creditToName c, pid ^. P._ProjectId )
  • replacement in lib/Aftok/Database/PostgreSQL.hs at line 279
    [4.2209][4.2904:2964](),[4.2904][4.2904:2964](),[4.2964][4.2589:2613](),[4.2589][4.2589:2613](),[4.2613][4.2210:2343](),[4.2343][4.2720:2764](),[4.2720][4.2720:2764]()
    dbEval (AmendEvent (EventId eid) (MetadataChange mt v)) =
    pinsert AmendmentId
    "INSERT INTO event_metadata_amendments \
    \(event_id, amended_at, event_metadata) \
    \VALUES (?, ?, ?) RETURNING id"
    ( eid, fromThyme $ mt ^. _ModTime, v)
    [4.2209]
    [4.4597]
    CreditToUser uid ->
    pinsert AmendmentId
    "INSERT INTO event_credit_to_amendments \
    \(event_id, amended_at, credit_to_type, credit_to_user_id) \
    \VALUES (?, ?, ?, ?) RETURNING id"
    ( eid, fromThyme $ mt ^. _ModTime, creditToName c, uid ^. _UserId )
  • replacement in lib/Aftok/Database/PostgreSQL.hs at line 286
    [4.4598][4.2920:2966](),[4.2966][4.2799:2839](),[4.2799][4.2799:2839](),[4.2839][4.2965:3067](),[4.3067][4.433:450](),[4.2942][4.433:450](),[4.450][4.2966:2998](),[4.2986][4.2966:2998](),[4.2966][4.2966:2998]()
    dbEval (ReadWorkIndex (ProjectId pid)) = do
    logEntries <- pquery logEntryParser
    "SELECT btc_addr, event_type, event_time, event_metadata FROM work_events WHERE project_id = ?"
    (Only pid)
    pure $ workIndex logEntries
    [4.4598]
    [4.4861]
    updateCache (AmendEvent (EventId eid) (MetadataChange mt v)) =
    pinsert AmendmentId
    "INSERT INTO event_metadata_amendments \
    \(event_id, amended_at, event_metadata) \
    \VALUES (?, ?, ?) RETURNING id"
    ( eid, fromThyme $ mt ^. _ModTime, v)
  • replacement in lib/Aftok/Database/PostgreSQL.hs at line 293
    [4.4862][4.178:209](),[4.209][4.3035:3057](),[4.3103][4.3035:3057](),[4.3035][4.3035:3057](),[4.3057][4.310:427](),[4.427][4.210:252](),[4.252][4.453:492](),[4.453][4.453:492](),[4.492][3.3402:3441](),[3.3441][4.536:585](),[4.536][4.536:585]()
    dbEval (CreateAuction auc) =
    pinsert AuctionId
    "INSERT INTO auctions (project_id, user_id, raise_amount, end_time) \
    \VALUES (?, ?, ?, ?) RETURNING id"
    ( auc ^. (A.projectId . _ProjectId)
    , auc ^. (A.initiator . _UserId)
    , auc ^. (raiseAmount . satoshi)
    , auc ^. (auctionEnd.to fromThyme)
    )
    [4.4862]
    [4.5182]
    updateCache (ReadWorkIndex (P.ProjectId pid)) = do
    logEntries <- pquery logEntryParser
    "SELECT btc_addr, event_type, event_time, event_metadata FROM work_events WHERE project_id = ?"
    (Only pid)
    pure $ workIndex logEntries
  • replacement in lib/Aftok/Database/PostgreSQL.hs at line 299
    [4.5183][4.3104:3135](),[4.3135][4.574:611](),[4.574][4.574:611](),[4.611][2.257:372](),[2.372][4.3377:3412](),[4.3201][4.3377:3412](),[4.3377][4.3377:3412]()
    dbEval (FindAuction aucId) =
    headMay <$> pquery auctionParser
    "SELECT project_id, initiator_id, created_at, raise_amount, start_time, end_time FROM auctions WHERE id = ?"
    (Only (aucId ^. _AuctionId))
    [4.5183]
    [4.5424]
    updateCache (CreateAuction auc) =
    pinsert A.AuctionId
    "INSERT INTO auctions (project_id, user_id, raise_amount, end_time) \
    \VALUES (?, ?, ?, ?) RETURNING id"
    ( auc ^. (A.projectId . P._ProjectId)
    , auc ^. (A.initiator . _UserId)
    , auc ^. (A.raiseAmount . satoshi)
    , auc ^. (A.auctionEnd . to fromThyme)
    )
  • replacement in lib/Aftok/Database/PostgreSQL.hs at line 309
    [4.5425][4.3202:3247](),[4.3247][4.3487:3634](),[4.3487][4.3487:3634](),[4.3634][4.3248:3262](),[4.3262][4.3649:3733](),[4.3649][4.3649:3733](),[4.803][4.49:49](),[4.3733][3.3442:3479](),[4.49][4.3080:3118](),[4.297][4.3080:3118](),[4.800][4.3080:3118](),[4.803][4.3080:3118](),[3.3479][4.3080:3118](),[4.3768][4.3080:3118](),[4.3118][4.3791:3799](),[4.3791][4.3791:3799]()
    dbEval (CreateBid (AuctionId aucId) bid) =
    pinsert BidId
    "INSERT INTO bids (auction_id, bidder_id, bid_seconds, bid_amount, bid_time) \
    \VALUES (?, ?, ?, ?, ?) RETURNING id"
    ( aucId
    , bid ^. (bidUser._UserId)
    , case bid ^. bidSeconds of (Seconds i) -> i
    , bid ^. (bidAmount . satoshi)
    , bid ^. (bidTime.to fromThyme)
    )
    [4.5425]
    [4.5524]
    updateCache (FindAuction aucId) =
    headMay <$> pquery auctionParser
    "SELECT project_id, initiator_id, created_at, raise_amount, start_time, end_time FROM auctions WHERE id = ?"
    (Only (aucId ^. A._AuctionId))
  • replacement in lib/Aftok/Database/PostgreSQL.hs at line 314
    [4.5525][4.3263:3291](),[4.3291][4.3829:3939](),[4.3829][4.3829:3939](),[4.3939][4.612:647]()
    dbEval (ReadBids aucId) =
    pquery bidParser
    "SELECT user_id, bid_seconds, bid_amount, bid_time FROM bids WHERE auction_id = ?"
    (Only (aucId ^. _AuctionId))
    [4.5525]
    [4.667]
    updateCache (CreateBid (A.AuctionId aucId) bid) =
    pinsert A.BidId
    "INSERT INTO bids (auction_id, bidder_id, bid_seconds, bid_amount, bid_time) \
    \VALUES (?, ?, ?, ?, ?) RETURNING id"
    ( aucId
    , bid ^. (A.bidUser . _UserId)
    , case bid ^. A.bidSeconds of (Seconds i) -> i
    , bid ^. (A.bidAmount . satoshi)
    , bid ^. (A.bidTime . to fromThyme)
    )
  • replacement in lib/Aftok/Database/PostgreSQL.hs at line 325
    [4.668][4.3292:3322](),[4.3322][3.3480:3620](),[3.3620][4.4027:4109](),[4.4027][4.4027:4109](),[4.4109][3.3621:3717]()
    dbEval (CreateUser user') =
    let addrMay :: Maybe ByteString
    addrMay = user' ^? (userAddress . traverse . _BtcAddr . to addrToBase58)
    in pinsert UserId
    "INSERT INTO users (handle, btc_addr, email) VALUES (?, ?, ?) RETURNING id"
    ( user' ^. (username._UserName)
    , addrMay
    , user' ^. userEmail._Email
    )
    [4.668]
    [4.704]
    updateCache (ReadBids aucId) =
    pquery bidParser
    "SELECT user_id, bid_seconds, bid_amount, bid_time FROM bids WHERE auction_id = ?"
    (Only (aucId ^. A._AuctionId))
  • replacement in lib/Aftok/Database/PostgreSQL.hs at line 330
    [4.705][4.3323:3358](),[4.3358][4.684:718](),[4.684][4.684:718](),[4.718][4.4270:4350](),[4.4270][4.4270:4350]()
    dbEval (FindUser (UserId uid)) =
    headMay <$> pquery userParser
    "SELECT handle, btc_addr, email FROM users WHERE id = ?"
    (Only uid)
    [4.705]
    [4.1368]
    updateCache (CreateUser user') =
    let addrMay :: Maybe ByteString
    addrMay = user' ^? (userAddress . traverse . _BtcAddr . to addrToBase58)
    in pinsert UserId
    "INSERT INTO users (handle, btc_addr, email) VALUES (?, ?, ?) RETURNING id"
    ( user' ^. (username._UserName)
    , addrMay
    , user' ^. userEmail._Email
    )
  • replacement in lib/Aftok/Database/PostgreSQL.hs at line 340
    [4.1369][4.3359:3400](),[4.3400][4.761:798](),[4.761][4.761:798](),[4.798][4.4454:4540](),[4.4454][4.4454:4540]()
    dbEval (FindUserByName (UserName h)) =
    headMay <$> pquery qdbUserParser
    "SELECT id, handle, btc_addr, email FROM users WHERE handle = ?"
    (Only h)
    [4.1369]
    [4.3218]
    updateCache (FindUser (UserId uid)) =
    headMay <$> pquery userParser
    "SELECT handle, btc_addr, email FROM users WHERE id = ?"
    (Only uid)
  • replacement in lib/Aftok/Database/PostgreSQL.hs at line 345
    [4.3219][4.3219:3556]()
    dbEval (CreateInvitation (ProjectId pid) (UserId uid) (Email e) t) = do
    invCode <- liftIO randomInvCode
    void $ pexec
    "INSERT INTO invitations (project_id, invitor_id, invitee_email, invitation_key, invitation_time) \
    \VALUES (?, ?, ?, ?, ?)"
    (pid, uid, e, renderInvCode invCode, fromThyme t)
    pure invCode
    [4.3219]
    [4.3741]
    updateCache (FindUserByName (UserName h)) =
    headMay <$> pquery qdbUserParser
    "SELECT id, handle, btc_addr, email FROM users WHERE handle = ?"
    (Only h)
    updateCache (CreateInvitation (P.ProjectId pid) (UserId uid) (Email e) t) = do
    invCode <- liftIO P.randomInvCode
    void $ pexec
    "INSERT INTO invitations (project_id, invitor_id, invitee_email, invitation_key, invitation_time) \
    \VALUES (?, ?, ?, ?, ?)"
    (pid, uid, e, P.renderInvCode invCode, fromThyme t)
    pure invCode
    updateCache (FindInvitation ic) =
    headMay <$> pquery invitationParser
    "SELECT project_id, invitor_id, invitee_email, invitation_time, acceptance_time \
    \FROM invitations WHERE invitation_key = ?"
    (Only $ P.renderInvCode ic)
  • replacement in lib/Aftok/Database/PostgreSQL.hs at line 364
    [4.3742][4.3401:3432](),[4.3432][4.831:871](),[4.831][4.831:871](),[4.871][4.3634:3804](),[4.3634][4.3634:3804]()
    dbEval (FindInvitation ic) =
    headMay <$> pquery invitationParser
    "SELECT project_id, invitor_id, invitee_email, invitation_time, acceptance_time \
    \FROM invitations WHERE invitation_key = ?"
    (Only $ renderInvCode ic)
    [4.3742]
    [4.3835]
    updateCache (AcceptInvitation (UserId uid) ic t) = transactQDBM $ do
    void $ pexec
    "UPDATE invitations SET acceptance_time = ? WHERE invitation_key = ?"
    (fromThyme t, P.renderInvCode ic)
    void $ pexec
    "INSERT INTO project_companions (project_id, user_id, invited_by, joined_at) \
    \SELECT i.project_id, ?, i.invitor_id, ? \
    \FROM invitations i \
    \WHERE i.invitation_key = ?"
    (uid, fromThyme t, P.renderInvCode ic)
  • replacement in lib/Aftok/Database/PostgreSQL.hs at line 375
    [4.3836][4.3836:4290]()
    dbEval (AcceptInvitation (UserId uid) ic t) = transactQDBM $ do
    void $ pexec
    "UPDATE invitations SET acceptance_time = ? WHERE invitation_key = ?"
    (fromThyme t, renderInvCode ic)
    void $ pexec
    "INSERT INTO project_companions (project_id, user_id, invited_by, joined_at) \
    \SELECT i.project_id, ?, i.invitor_id, ? \
    \FROM invitations i \
    \WHERE i.invitation_key = ?"
    (uid, fromThyme t, renderInvCode ic)
    [4.3836]
    [4.4290]
    updateCache (CreateProject p) =
    pinsert P.ProjectId
    "INSERT INTO projects (project_name, inception_date, initiator_id, depreciation_fn) \
    \VALUES (?, ?, ?, ?) RETURNING id"
    (p ^. P.projectName, p ^. (P.inceptionDate . to fromThyme), p ^. (P.initiator . _UserId), toJSON $ p ^. P.depf)
  • replacement in lib/Aftok/Database/PostgreSQL.hs at line 381
    [4.4291][4.3433:3462](),[4.3462][4.4596:4751](),[4.4596][4.4596:4751](),[4.4751][4.586:696]()
    dbEval (CreateProject p) =
    pinsert ProjectId
    "INSERT INTO projects (project_name, inception_date, initiator_id, depreciation_fn) \
    \VALUES (?, ?, ?, ?) RETURNING id"
    (p ^. projectName, p ^. (inceptionDate.to fromThyme), p ^. (P.initiator . _UserId), toJSON $ p ^. depf)
    [4.4291]
    [4.946]
    updateCache (FindProject (P.ProjectId pid)) =
    headMay <$> pquery projectParser
    "SELECT project_name, inception_date, initiator_id, depreciation_fn FROM projects WHERE id = ?"
    (Only pid)
  • replacement in lib/Aftok/Database/PostgreSQL.hs at line 386
    [4.947][4.3463:3504](),[4.3504][4.914:951](),[4.914][4.914:951](),[4.951][4.4:106](),[4.4924][4.4:106](),[4.106][4.5009:5026](),[4.5009][4.5009:5026]()
    dbEval (FindProject (ProjectId pid)) =
    headMay <$> pquery projectParser
    "SELECT project_name, inception_date, initiator_id, depreciation_fn FROM projects WHERE id = ?"
    (Only pid)
    [4.947]
    [4.1013]
    updateCache (FindUserProjects (UserId uid)) =
    pquery qdbProjectParser
    "SELECT p.id, p.project_name, p.inception_date, p.initiator_id, p.depreciation_fn \
    \FROM projects p LEFT OUTER JOIN project_companions pc ON pc.project_id = p.id \
    \WHERE pc.user_id = ? \
    \OR p.initiator_id = ?"
    (uid, uid)
  • replacement in lib/Aftok/Database/PostgreSQL.hs at line 394
    [4.1014][4.3505:3548](),[4.3548][4.5099:5127](),[4.5099][4.5099:5127](),[4.5127][4.107:284](),[4.284][4.3549:3579](),[4.3579][4.315:345](),[4.315][4.315:345](),[4.345][4.5448:5465](),[4.5448][4.5448:5465]()
    dbEval (FindUserProjects (UserId uid)) =
    pquery qdbProjectParser
    "SELECT p.id, p.project_name, p.inception_date, p.initiator_id, p.depreciation_fn \
    \FROM projects p LEFT OUTER JOIN project_companions pc ON pc.project_id = p.id \
    \WHERE pc.user_id = ? \
    \OR p.initiator_id = ?"
    (uid, uid)
    [4.1014]
    [4.1042]
    updateCache (AddUserToProject pid current new) = void $
    pexec
    "INSERT INTO project_companions (project_id, user_id, invited_by) VALUES (?, ?, ?)"
    (pid ^. P._ProjectId, new ^. _UserId, current ^. _UserId)
  • replacement in lib/Aftok/Database/PostgreSQL.hs at line 399
    [4.1043][4.3580:3633](),[4.3633][4.1006:1016](),[4.1006][4.1006:1016](),[4.1016][4.5532:5684](),[4.5532][4.5532:5684]()
    dbEval (AddUserToProject pid current new) = void $
    pexec
    "INSERT INTO project_companions (project_id, user_id, invited_by) VALUES (?, ?, ?)"
    (pid ^. _ProjectId, new ^. _UserId, current ^. _UserId)
    [4.1043]
    [3.3718]
    updateCache dbop @ (CreateBillable b) = do
    eventId <- requireEventId dbop
    pinsert BI.BillableId
    "INSERT INTO billables \
    \(project_id, event_id, name, description, recurrence_type, recurrence_count, billing_amount, grace_period_days)\
    \VALUES (?, ?, ?, ?, ?, ?, ?, ?) RETURNING id"
    ( b ^. (BI.project . P._ProjectId)
    , eventId ^. _EventId
    , b ^. BI.name
    , b ^. BI.description
    , b ^. (BI.recurrence . to BI.recurrenceName)
    , b ^. (BI.recurrence . to BI.recurrenceCount)
    , b ^. (BI.amount . satoshi)
    , b ^. (BI.gracePeriod . _Days)
    )
  • replacement in lib/Aftok/Database/PostgreSQL.hs at line 415
    [3.3719][3.3719:3942]()
    dbEval (CreateBillable _) = error "Not implemented"
    dbEval (ReadBillable _) = error "Not implemented"
    dbEval (CreatePaymentRequest _ _) = error "Not implemented"
    dbEval (CreatePayment _ ) = error "Not implemented"
    [3.3719]
    [3.3942]
    updateCache (ReadBillable bid) =
    headMay <$> pquery billableParser
    "SELECT b.project_id, e.created_by, b.name, b.description, b.recurrence_type, b.recurrence_count, \
    \ b.billing_amount, b.grace_period_days \
    \FROM billables b JOIN aftok_events e ON e.id = b.event_id \
    \WHERE b.id = ?"
    (Only (bid ^. BI._BillableId))
  • edit in lib/Aftok/Database/PostgreSQL.hs at line 423
    [3.3943]
    [4.1044]
    updateCache (CreateSubscription _ _) = error "Not implemented"
    updateCache (CreatePaymentRequest _) = error "Not implemented"
    updateCache (CreatePayment _ ) = error "Not implemented"
  • replacement in lib/Aftok/Database/PostgreSQL.hs at line 427
    [4.1045][4.4399:4454]()
    dbEval (RaiseDBError err _) = QDBM . lift $ left err
    [4.1045]
    [3.3944]
    updateCache (RaiseDBError err _) = raiseError err
  • edit in lib/Aftok/Database/PostgreSQL.hs at line 429
    [3.3945]
    [3.3945]
    requireEventId :: DBOp a -> QDBM EventId
    requireEventId = maybe (raiseError EventStorageFailed) id . storeEvent
  • edit in lib/Aftok/Database/PostgreSQL.hs at line 432
    [3.3946]
    [3.3946]
    raiseError :: DBError -> QDBM a
    raiseError = QDBM . lift . left
  • edit in lib/Aftok/Database/PostgreSQL.hs at line 435
    [3.3947]
    instance DBEval QDBM where
    dbEval e = updateCache e
  • edit in lib/Aftok/Database.hs at line 19
    [4.364][3.4022:4051]()
    import Aftok.Types
  • replacement in lib/Aftok/Database.hs at line 53
    [3.4053][3.4053:4199]()
    CreateBillable :: Billable ProjectId Satoshi -> DBOp BillableId
    ReadBillable :: BillableId -> DBOp (Maybe (Billable ProjectId Satoshi))
    [3.4053]
    [3.4199]
    CreateBillable :: Billable -> DBOp BillableId
    ReadBillable :: BillableId -> DBOp (Maybe Billable)
    CreateSubscription :: UserId -> BillableId -> DBOp SubscriptionId
  • replacement in lib/Aftok/Database.hs at line 58
    [3.4200][3.4200:4373]()
    CreatePaymentRequest :: UserId -> PaymentRequest ProjectId BillableId -> DBOp PaymentRequestId
    CreatePayment :: Payment PaymentRequestId UserId -> DBOp PaymentId
    [3.4200]
    [4.7074]
    CreatePaymentRequest :: PaymentRequest SubscriptionId -> DBOp PaymentRequestId
    CreatePayment :: Payment PaymentRequestId -> DBOp PaymentId
  • edit in lib/Aftok/Database.hs at line 72
    [4.5074]
    [4.5074]
    | EventStorageFailed
  • replacement in lib/Aftok/Database.hs at line 177
    [4.834][3.4391:4546]()
    createBillable :: UserId -> Billable ProjectId Satoshi -> DBProg BillableId
    createBillable uid b = withProjectAuth (b ^. B.project) uid $ CreateBillable b
    [4.834]
    [3.4546]
    createBillable :: Billable -> DBProg BillableId
    createBillable b = withProjectAuth (b ^. B.project) (b ^. B.creator) $ CreateBillable b
  • replacement in lib/Aftok/Database.hs at line 180
    [3.4547][3.4547:4621]()
    readBillable :: BillableId -> DBProg (Maybe (Billable ProjectId Satoshi))
    [3.4547]
    [3.4621]
    readBillable :: BillableId -> DBProg (Maybe Billable)
  • replacement in lib/Aftok/Database.hs at line 188
    [3.4788][3.4788:4861]()
    readPaymentHistory :: UserId -> DBProg [Payment PaymentRequestId UserId]
    [3.4788]
    [3.4861]
    readPaymentHistory :: UserId -> DBProg [Payment PaymentRequestId]
  • replacement in lib/Aftok/Payments.hs at line 18
    [3.6086][3.6086:6159]()
    data PaymentRequest (p :: *) (b :: *) = PaymentRequest
    { _project :: p
    [3.6086]
    [3.6159]
    data PaymentRequest (s :: *) = PaymentRequest
    { _subscription :: s
  • edit in lib/Aftok/Payments.hs at line 22
    [3.6236][3.6236:6255]()
    , _billable :: b
  • replacement in lib/Aftok/Payments.hs at line 25
    [3.6288][3.6288:6315]()
    data Payment r u = Payment
    [3.6288]
    [3.6315]
    data Payment (r :: *) = Payment
  • edit in lib/Aftok/Payments.hs at line 29
    [3.6389][3.6389:6405]()
    , _payor :: u
  • edit in lib/Aftok/Time.hs at line 1
    [3.6473]
    [3.6474]
    {-# LANGUAGE TemplateHaskell #-}
  • edit in lib/Aftok/Time.hs at line 5
    [3.6530]
    [3.6530]
    import Control.Lens (makePrisms)
  • edit in lib/Aftok/Time.hs at line 8
    [3.6555]
    [3.6555]
    makePrisms ''Days
  • edit in migrations/2016-12-31_03-45-17_create-payments.txt at line 26
    [3.8208]
    [3.8208]
    created_by uuid references users(id) null,
  • edit in server/Aftok/Snaplet.hs at line 43
    [4.11872]
    [4.11872]
    handleDBError (EventStorageFailed) =
    snapError 500 "The event submitted could not be saved to the log."