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