1 package expo.modules.updates.db.dao
2 
3 import androidx.room.*
4 import expo.modules.updates.db.entity.AssetEntity
5 import expo.modules.updates.db.entity.UpdateEntity
6 import expo.modules.updates.db.enums.UpdateStatus
7 import java.util.*
8 
9 /**
10  * Utility class for accessing and modifying data in SQLite relating to updates.
11  */
12 @Dao
13 abstract class UpdateDao {
14   /**
15    * for private use only
16    * must be marked public for Room
17    * so we use the underscore to discourage use
18    */
19 
20   // if an update has successfully launched at least once, we treat it as launchable
21   // even if it has also failed to launch at least once
22   @Query("SELECT * FROM updates WHERE scope_key = :scopeKey AND (successful_launch_count > 0 OR failed_launch_count < 1) AND status IN (:statuses);")
_loadLaunchableUpdatesForProjectWithStatusesnull23   abstract fun _loadLaunchableUpdatesForProjectWithStatuses(scopeKey: String?, statuses: List<UpdateStatus>): List<UpdateEntity>
24 
25   @Query("SELECT * FROM updates WHERE id = :id;")
26   abstract fun _loadUpdatesWithId(id: UUID): List<UpdateEntity>
27 
28   @Query("SELECT assets.* FROM assets INNER JOIN updates ON updates.launch_asset_id = assets.id WHERE updates.id = :id;")
29   abstract fun _loadLaunchAsset(id: UUID): AssetEntity
30 
31   @Query("UPDATE updates SET keep = 1 WHERE id = :id;")
32   abstract fun _keepUpdate(id: UUID)
33 
34   @Query("UPDATE updates SET status = :status WHERE id = :id;")
35   abstract fun _markUpdateWithStatus(status: UpdateStatus, id: UUID)
36 
37   @Query(
38     "UPDATE updates SET status = :status WHERE id IN (" +
39       "SELECT DISTINCT update_id FROM updates_assets WHERE asset_id IN (:missingAssetIds));"
40   )
41   abstract fun _markUpdatesWithMissingAssets(missingAssetIds: List<Long>, status: UpdateStatus)
42 
43   /**
44    * for public use
45    */
46   @Query("SELECT * FROM updates;")
47   abstract fun loadAllUpdates(): List<UpdateEntity>
48 
49   fun loadLaunchableUpdatesForScope(scopeKey: String?): List<UpdateEntity> {
50     return _loadLaunchableUpdatesForProjectWithStatuses(
51       scopeKey,
52       listOf(UpdateStatus.READY, UpdateStatus.EMBEDDED, UpdateStatus.DEVELOPMENT)
53     )
54   }
55 
56   @Query("SELECT * FROM updates WHERE status = :status;")
loadAllUpdatesWithStatusnull57   abstract fun loadAllUpdatesWithStatus(status: UpdateStatus): List<UpdateEntity>
58 
59   @Query("SELECT id FROM updates WHERE status = :status;")
60   abstract fun loadAllUpdateIdsWithStatus(status: UpdateStatus): List<UUID>
61 
62   fun loadUpdateWithId(id: UUID): UpdateEntity? {
63     val updateEntities = _loadUpdatesWithId(id)
64     return if (updateEntities.isNotEmpty()) updateEntities[0] else null
65   }
66 
loadLaunchAssetnull67   fun loadLaunchAsset(id: UUID): AssetEntity {
68     val assetEntity = _loadLaunchAsset(id)
69     assetEntity.isLaunchAsset = true
70     return assetEntity
71   }
72 
73   @Insert
insertUpdatenull74   abstract fun insertUpdate(update: UpdateEntity)
75 
76   fun setUpdateScopeKey(update: UpdateEntity, newScopeKey: String) {
77     update.scopeKey = newScopeKey
78     _setUpdateScopeKeyInternal(update.id, newScopeKey)
79   }
80 
81   @Query("UPDATE updates SET scope_key = :newScopeKey WHERE id = :id;")
_setUpdateScopeKeyInternalnull82   abstract fun _setUpdateScopeKeyInternal(id: UUID, newScopeKey: String)
83 
84   fun setUpdateCommitTime(update: UpdateEntity, commitTime: Date) {
85     update.commitTime = commitTime
86     _setUpdateCommitTime(update.id, commitTime)
87   }
88 
89   @Query("UPDATE updates SET commit_time = :commitTime WHERE id = :id;")
_setUpdateCommitTimenull90   abstract fun _setUpdateCommitTime(id: UUID, commitTime: Date)
91 
92   @Transaction
93   open fun markUpdateFinished(update: UpdateEntity, hasSkippedEmbeddedAssets: Boolean) {
94     var statusToMark = UpdateStatus.READY
95     if (update.status === UpdateStatus.DEVELOPMENT) {
96       statusToMark = UpdateStatus.DEVELOPMENT
97     } else if (hasSkippedEmbeddedAssets) {
98       statusToMark = UpdateStatus.EMBEDDED
99     }
100     _markUpdateWithStatus(statusToMark, update.id)
101     _keepUpdate(update.id)
102   }
103 
markUpdateFinishednull104   fun markUpdateFinished(update: UpdateEntity) {
105     markUpdateFinished(update, false)
106   }
107 
markUpdateAccessednull108   fun markUpdateAccessed(update: UpdateEntity) {
109     val newLastAccessed = Date()
110     update.lastAccessed = newLastAccessed
111     _markUpdateAccessed(update.id, newLastAccessed)
112   }
113 
114   @Query("UPDATE updates SET last_accessed = :lastAccessed WHERE id = :id;")
_markUpdateAccessednull115   abstract fun _markUpdateAccessed(id: UUID, lastAccessed: Date)
116 
117   fun incrementSuccessfulLaunchCount(update: UpdateEntity) {
118     update.successfulLaunchCount++
119     _incrementSuccessfulLaunchCount(update.id)
120   }
121 
122   @Query("UPDATE updates SET successful_launch_count = successful_launch_count + 1 WHERE id = :id;")
_incrementSuccessfulLaunchCountnull123   abstract fun _incrementSuccessfulLaunchCount(id: UUID)
124 
125   fun incrementFailedLaunchCount(update: UpdateEntity) {
126     update.failedLaunchCount++
127     _incrementFailedLaunchCount(update.id)
128   }
129 
130   @Query("UPDATE updates SET failed_launch_count = failed_launch_count + 1 WHERE id = :id;")
_incrementFailedLaunchCountnull131   abstract fun _incrementFailedLaunchCount(id: UUID)
132 
133   fun markUpdatesWithMissingAssets(missingAssets: List<AssetEntity>) {
134     val missingAssetIds = mutableListOf<Long>()
135     for (asset in missingAssets) {
136       missingAssetIds.add(asset.id)
137     }
138     _markUpdatesWithMissingAssets(missingAssetIds, UpdateStatus.PENDING)
139   }
140 
141   @Delete
deleteUpdatesnull142   abstract fun deleteUpdates(updates: List<UpdateEntity>)
143 }
144