1 {-# LANGUAGE OverloadedStrings, RecordWildCards, TemplateHaskell, QuasiQuotes, DataKinds #-}
    2 module Model.Asset
    3   ( module Model.Asset.Types
    4   -- , blankAsset
    5   , assetBacked
    6   , lookupAsset
    7   , lookupOrigAsset
    8   , lookupVolumeAsset
    9   , addAsset
   10   , changeAsset
   11   , assetCreation
   12   , assetRowJSON
   13   , assetJSON
   14   -- , assetJSONRestricted
   15   ) where
   16 
   17 import Control.Arrow (first)
   18 import Data.Maybe (isNothing, isJust)
   19 import Data.Monoid ((<>))
   20 import qualified Data.Text as T
   21 import Database.PostgreSQL.Typed (pgSQL)
   22 import Database.PostgreSQL.Typed.Query
   23 import Database.PostgreSQL.Typed.Types
   24 import qualified Data.ByteString
   25 import Data.ByteString (ByteString)
   26 import qualified Data.String
   27 
   28 import Ops
   29 import Has (view, peek)
   30 import qualified JSON
   31 import Service.DB
   32 import Files
   33 import Store.Types
   34 import Store.Asset
   35 import Model.SQL
   36 import Model.Time
   37 import Model.Audit
   38 import Model.Id
   39 import Model.Identity
   40 import Model.Party
   41 import Model.Volume
   42 import Model.Format
   43 import Model.Asset.Types
   44 import Model.Asset.SQL
   45 
   46 mapQuery :: ByteString -> ([PGValue] -> a) -> PGSimpleQuery a
   47 mapQuery qry mkResult =
   48   fmap mkResult (rawPGSimpleQuery qry)
   49 
   50 assetBacked :: Asset -> Bool
   51 assetBacked = isJust . assetSHA1 . assetRow
   52 
   53 lookupAsset :: (MonadHasIdentity c m, MonadDB c m) => Id Asset -> m (Maybe Asset)
   54 lookupAsset ai = do
   55   ident <- peek
   56   dbQuery1 $(selectQuery (selectAsset 'ident) "$WHERE asset.id = ${ai}")
   57 
   58 lookupOrigAsset :: (MonadHasIdentity c m, MonadDB c m) => Id Asset -> m (Maybe Asset)
   59 lookupOrigAsset ai = do
   60   ident <- peek
   61   dbQuery1 $(selectQuery (selectAsset 'ident) "$left join transcode tc on tc.orig = asset.id WHERE asset.id = ${ai}")
   62 
   63 lookupVolumeAsset :: (MonadDB c m) => Volume -> Id Asset -> m (Maybe Asset)
   64 lookupVolumeAsset vol ai = do
   65   let _tenv_a87rh = unknownPGTypeEnv
   66   dbQuery1 $ (`Asset` vol) <$> -- .(selectQuery selectAssetRow "WHERE asset.id = ${ai} AND asset.volume = ${volumeId $ volumeRow vol}")
   67     fmap
   68       (\ (vid_a87qZ, vformat_a87r0, vrelease_a87r1, vduration_a87r2,
   69           vname_a87r3, vc_a87r4, vsize_a87r5)
   70          -> makeAssetRow
   71               vid_a87qZ
   72               vformat_a87r0
   73               vrelease_a87r1
   74               vduration_a87r2
   75               vname_a87r3
   76               vc_a87r4
   77               vsize_a87r5)
   78       (mapQuery
   79         ((\ _p_a87ri _p_a87rj ->
   80                        (Data.ByteString.concat
   81                           [Data.String.fromString
   82                              "SELECT asset.id,asset.format,asset.release,asset.duration,asset.name,asset.sha1,asset.size FROM asset WHERE asset.id = ",
   83                            Database.PostgreSQL.Typed.Types.pgEscapeParameter
   84                              _tenv_a87rh
   85                              (Database.PostgreSQL.Typed.Types.PGTypeProxy ::
   86                                 Database.PostgreSQL.Typed.Types.PGTypeName "integer")
   87                              _p_a87ri,
   88                            Data.String.fromString " AND asset.volume = ",
   89                            Database.PostgreSQL.Typed.Types.pgEscapeParameter
   90                              _tenv_a87rh
   91                              (Database.PostgreSQL.Typed.Types.PGTypeProxy ::
   92                                 Database.PostgreSQL.Typed.Types.PGTypeName "integer")
   93                              _p_a87rj]))
   94          ai (volumeId $ volumeRow vol))
   95                 (\
   96                   [_cid_a87rk,
   97                    _cformat_a87rl,
   98                    _crelease_a87rm,
   99                    _cduration_a87rn,
  100                    _cname_a87ro,
  101                    _csha1_a87rp,
  102                    _csize_a87rq]
  103                   -> (Database.PostgreSQL.Typed.Types.pgDecodeColumnNotNull
  104                         _tenv_a87rh
  105                         (Database.PostgreSQL.Typed.Types.PGTypeProxy ::
  106                            Database.PostgreSQL.Typed.Types.PGTypeName "integer")
  107                         _cid_a87rk,
  108                       Database.PostgreSQL.Typed.Types.pgDecodeColumnNotNull
  109                         _tenv_a87rh
  110                         (Database.PostgreSQL.Typed.Types.PGTypeProxy ::
  111                            Database.PostgreSQL.Typed.Types.PGTypeName "smallint")
  112                         _cformat_a87rl,
  113                       Database.PostgreSQL.Typed.Types.pgDecodeColumn
  114                         _tenv_a87rh
  115                         (Database.PostgreSQL.Typed.Types.PGTypeProxy ::
  116                            Database.PostgreSQL.Typed.Types.PGTypeName "release")
  117                         _crelease_a87rm,
  118                       Database.PostgreSQL.Typed.Types.pgDecodeColumn
  119                         _tenv_a87rh
  120                         (Database.PostgreSQL.Typed.Types.PGTypeProxy ::
  121                            Database.PostgreSQL.Typed.Types.PGTypeName "interval")
  122                         _cduration_a87rn,
  123                       Database.PostgreSQL.Typed.Types.pgDecodeColumn
  124                         _tenv_a87rh
  125                         (Database.PostgreSQL.Typed.Types.PGTypeProxy ::
  126                            Database.PostgreSQL.Typed.Types.PGTypeName "text")
  127                         _cname_a87ro,
  128                       Database.PostgreSQL.Typed.Types.pgDecodeColumn
  129                         _tenv_a87rh
  130                         (Database.PostgreSQL.Typed.Types.PGTypeProxy ::
  131                            Database.PostgreSQL.Typed.Types.PGTypeName "bytea")
  132                         _csha1_a87rp,
  133                       Database.PostgreSQL.Typed.Types.pgDecodeColumn
  134                         _tenv_a87rh
  135                         (Database.PostgreSQL.Typed.Types.PGTypeProxy ::
  136                            Database.PostgreSQL.Typed.Types.PGTypeName "bigint")
  137                         _csize_a87rq)))
  138 
  139 addAsset :: (MonadAudit c m, MonadStorage c m) => Asset -> Maybe RawFilePath -> m Asset
  140 addAsset ba fp = do
  141   ident <- getAuditIdentity
  142   ba' <- maybe (return ba) (storeAssetFile ba) fp
  143   let _tenv_a87Hi = unknownPGTypeEnv
  144   dbQuery1' -- .(insertAsset 'ident 'ba')
  145     (fmap
  146       (setAssetId ba')
  147       (mapQuery
  148         ((\ _p_a87Hj
  149           _p_a87Hk
  150           _p_a87Hr
  151           _p_a87Hv
  152           _p_a87Hw
  153           _p_a87Hx
  154           _p_a87Hy
  155           _p_a87Hz
  156           _p_a87HB ->
  157                        (Data.ByteString.concat
  158                           [Data.String.fromString
  159                              "WITH audit_row AS (INSERT INTO asset (volume,format,release,duration,name,sha1,size) VALUES (",
  160                            Database.PostgreSQL.Typed.Types.pgEscapeParameter
  161                              _tenv_a87Hi
  162                              (Database.PostgreSQL.Typed.Types.PGTypeProxy ::
  163                                 Database.PostgreSQL.Typed.Types.PGTypeName "integer")
  164                              _p_a87Hj,
  165                            Data.String.fromString ",",
  166                            Database.PostgreSQL.Typed.Types.pgEscapeParameter
  167                              _tenv_a87Hi
  168                              (Database.PostgreSQL.Typed.Types.PGTypeProxy ::
  169                                 Database.PostgreSQL.Typed.Types.PGTypeName "smallint")
  170                              _p_a87Hk,
  171                            Data.String.fromString ",",
  172                            Database.PostgreSQL.Typed.Types.pgEscapeParameter
  173                              _tenv_a87Hi
  174                              (Database.PostgreSQL.Typed.Types.PGTypeProxy ::
  175                                 Database.PostgreSQL.Typed.Types.PGTypeName "release")
  176                              _p_a87Hr,
  177                            Data.String.fromString ",",
  178                            Database.PostgreSQL.Typed.Types.pgEscapeParameter
  179                              _tenv_a87Hi
  180                              (Database.PostgreSQL.Typed.Types.PGTypeProxy ::
  181                                 Database.PostgreSQL.Typed.Types.PGTypeName "interval")
  182                              _p_a87Hv,
  183                            Data.String.fromString ",",
  184                            Database.PostgreSQL.Typed.Types.pgEscapeParameter
  185                              _tenv_a87Hi
  186                              (Database.PostgreSQL.Typed.Types.PGTypeProxy ::
  187                                 Database.PostgreSQL.Typed.Types.PGTypeName "text")
  188                              _p_a87Hw,
  189                            Data.String.fromString ",",
  190                            Database.PostgreSQL.Typed.Types.pgEscapeParameter
  191                              _tenv_a87Hi
  192                              (Database.PostgreSQL.Typed.Types.PGTypeProxy ::
  193                                 Database.PostgreSQL.Typed.Types.PGTypeName "bytea")
  194                              _p_a87Hx,
  195                            Data.String.fromString ",",
  196                            Database.PostgreSQL.Typed.Types.pgEscapeParameter
  197                              _tenv_a87Hi
  198                              (Database.PostgreSQL.Typed.Types.PGTypeProxy ::
  199                                 Database.PostgreSQL.Typed.Types.PGTypeName "bigint")
  200                              _p_a87Hy,
  201                            Data.String.fromString
  202                              ") RETURNING *) INSERT INTO audit.asset SELECT CURRENT_TIMESTAMP, ",
  203                            Database.PostgreSQL.Typed.Types.pgEscapeParameter
  204                              _tenv_a87Hi
  205                              (Database.PostgreSQL.Typed.Types.PGTypeProxy ::
  206                                 Database.PostgreSQL.Typed.Types.PGTypeName "integer")
  207                              _p_a87Hz,
  208                            Data.String.fromString ", ",
  209                            Database.PostgreSQL.Typed.Types.pgEscapeParameter
  210                              _tenv_a87Hi
  211                              (Database.PostgreSQL.Typed.Types.PGTypeProxy ::
  212                                 Database.PostgreSQL.Typed.Types.PGTypeName "inet")
  213                              _p_a87HB,
  214                            Data.String.fromString
  215                              ", 'add'::audit.action, * FROM audit_row RETURNING asset.id"]))
  216          (volumeId $ volumeRow $ assetVolume ba')
  217          (formatId $ assetFormat $ assetRow ba')
  218          (assetRelease $ assetRow ba')
  219          (assetDuration $ assetRow ba')
  220          (assetName $ assetRow ba')
  221          (assetSHA1 $ assetRow ba')
  222          (assetSize $ assetRow ba')
  223          (auditWho ident)
  224          (auditIp ident))
  225                (\ [_cid_a87HC]
  226                   -> (Database.PostgreSQL.Typed.Types.pgDecodeColumnNotNull
  227                         _tenv_a87Hi
  228                         (Database.PostgreSQL.Typed.Types.PGTypeProxy ::
  229                            Database.PostgreSQL.Typed.Types.PGTypeName "integer")
  230                         _cid_a87HC))))
  231 
  232 changeAsset :: (MonadAudit c m, MonadStorage c m) => Asset -> Maybe RawFilePath -> m Asset
  233 changeAsset a fp = do
  234   ident <- getAuditIdentity
  235   a2 <- maybe (return a) (storeAssetFile a) fp
  236   let _tenv_a87Mj = unknownPGTypeEnv
  237   dbExecute1' -- .(updateAsset 'ident 'a2)
  238     (mapQuery
  239       ((\ _p_a87Mk
  240        _p_a87Ml
  241        _p_a87Mm
  242        _p_a87Mn
  243        _p_a87Mo
  244        _p_a87Mp
  245        _p_a87Mq
  246        _p_a87Mr
  247        _p_a87Ms
  248        _p_a87Mt ->
  249                     (Data.ByteString.concat
  250                        [Data.String.fromString
  251                           "WITH audit_row AS (UPDATE asset SET volume=",
  252                         Database.PostgreSQL.Typed.Types.pgEscapeParameter
  253                           _tenv_a87Mj
  254                           (Database.PostgreSQL.Typed.Types.PGTypeProxy ::
  255                              Database.PostgreSQL.Typed.Types.PGTypeName "integer")
  256                           _p_a87Mk,
  257                         Data.String.fromString ",format=",
  258                         Database.PostgreSQL.Typed.Types.pgEscapeParameter
  259                           _tenv_a87Mj
  260                           (Database.PostgreSQL.Typed.Types.PGTypeProxy ::
  261                              Database.PostgreSQL.Typed.Types.PGTypeName "smallint")
  262                           _p_a87Ml,
  263                         Data.String.fromString ",release=",
  264                         Database.PostgreSQL.Typed.Types.pgEscapeParameter
  265                           _tenv_a87Mj
  266                           (Database.PostgreSQL.Typed.Types.PGTypeProxy ::
  267                              Database.PostgreSQL.Typed.Types.PGTypeName "release")
  268                           _p_a87Mm,
  269                         Data.String.fromString ",duration=",
  270                         Database.PostgreSQL.Typed.Types.pgEscapeParameter
  271                           _tenv_a87Mj
  272                           (Database.PostgreSQL.Typed.Types.PGTypeProxy ::
  273                              Database.PostgreSQL.Typed.Types.PGTypeName "interval")
  274                           _p_a87Mn,
  275                         Data.String.fromString ",name=",
  276                         Database.PostgreSQL.Typed.Types.pgEscapeParameter
  277                           _tenv_a87Mj
  278                           (Database.PostgreSQL.Typed.Types.PGTypeProxy ::
  279                              Database.PostgreSQL.Typed.Types.PGTypeName "text")
  280                           _p_a87Mo,
  281                         Data.String.fromString ",sha1=",
  282                         Database.PostgreSQL.Typed.Types.pgEscapeParameter
  283                           _tenv_a87Mj
  284                           (Database.PostgreSQL.Typed.Types.PGTypeProxy ::
  285                              Database.PostgreSQL.Typed.Types.PGTypeName "bytea")
  286                           _p_a87Mp,
  287                         Data.String.fromString ",size=",
  288                         Database.PostgreSQL.Typed.Types.pgEscapeParameter
  289                           _tenv_a87Mj
  290                           (Database.PostgreSQL.Typed.Types.PGTypeProxy ::
  291                              Database.PostgreSQL.Typed.Types.PGTypeName "bigint")
  292                           _p_a87Mq,
  293                         Data.String.fromString " WHERE id=",
  294                         Database.PostgreSQL.Typed.Types.pgEscapeParameter
  295                           _tenv_a87Mj
  296                           (Database.PostgreSQL.Typed.Types.PGTypeProxy ::
  297                              Database.PostgreSQL.Typed.Types.PGTypeName "integer")
  298                           _p_a87Mr,
  299                         Data.String.fromString
  300                           " RETURNING *) INSERT INTO audit.asset SELECT CURRENT_TIMESTAMP, ",
  301                         Database.PostgreSQL.Typed.Types.pgEscapeParameter
  302                           _tenv_a87Mj
  303                           (Database.PostgreSQL.Typed.Types.PGTypeProxy ::
  304                              Database.PostgreSQL.Typed.Types.PGTypeName "integer")
  305                           _p_a87Ms,
  306                         Data.String.fromString ", ",
  307                         Database.PostgreSQL.Typed.Types.pgEscapeParameter
  308                           _tenv_a87Mj
  309                           (Database.PostgreSQL.Typed.Types.PGTypeProxy ::
  310                              Database.PostgreSQL.Typed.Types.PGTypeName "inet")
  311                           _p_a87Mt,
  312                         Data.String.fromString
  313                           ", 'change'::audit.action, * FROM audit_row"]))
  314       (volumeId $ volumeRow $ assetVolume a2)
  315       (formatId $ assetFormat $ assetRow a2)
  316       (assetRelease $ assetRow a2)
  317       (assetDuration $ assetRow a2)
  318       (assetName $ assetRow a2)
  319       (assetSHA1 $ assetRow a2)
  320       (assetSize $ assetRow a2)
  321       (assetId $ assetRow a2)
  322       (auditWho ident)
  323       (auditIp ident))
  324             (\[] -> ()))
  325   return a2
  326 
  327 assetCreation :: MonadDB c m => Asset -> m (Maybe Timestamp, Maybe T.Text)
  328 assetCreation a = maybe (Nothing, Nothing) (first Just) <$>
  329   dbQuery1 [pgSQL|$SELECT audit_time, name FROM audit.asset WHERE id = ${assetId $ assetRow a} AND audit_action = 'add' ORDER BY audit_time DESC LIMIT 1|]
  330 
  331 assetRowJSON :: JSON.ToObject o => AssetRow -> JSON.Record (Id Asset) o
  332 assetRowJSON AssetRow{..} = JSON.Record assetId $
  333      "format" JSON..= formatId assetFormat
  334   <> "classification" `JSON.kvObjectOrEmpty` assetRelease
  335   <> "duration" `JSON.kvObjectOrEmpty` assetDuration
  336   <> "pending" `JSON.kvObjectOrEmpty` (isNothing assetSize `useWhen` isNothing assetSHA1)
  337 
  338 assetJSON :: JSON.ToObject o => Bool -> Asset -> JSON.Record (Id Asset) o
  339 assetJSON _ Asset{..} = assetRowJSON assetRow -- first parameter is publicRestricted
  340 
  341 -- assetJSONRestricted :: JSON.ToObject o => Asset -> JSON.Record (Id Asset) o
  342 -- assetJSONRestricted Asset{..} = assetRowJSON assetRow