Skip to content

Commit

Permalink
feat: Inject Pouch engine and local storage
Browse files Browse the repository at this point in the history
  • Loading branch information
Ldoppea committed May 23, 2024
1 parent 308052c commit 6fe85a7
Show file tree
Hide file tree
Showing 6 changed files with 165 additions and 88 deletions.
32 changes: 15 additions & 17 deletions packages/cozy-pouch-link/src/CozyPouchLink.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,8 @@ import * as jsonapi from './jsonapi'
import PouchManager from './PouchManager'
import logger from './logger'
import { migratePouch } from './migrations/adapter'
import { platformWeb } from './platformWeb'
import { getDatabaseName, getPrefix } from './utils'
import {
getPersistedSyncedDoctypes,
persistAdapterName,
getAdapterName,
destroyWarmedUpQueries
} from './localStorage'

PouchDB.plugin(PouchDBFind)

Expand Down Expand Up @@ -95,6 +90,7 @@ class PouchLink extends CozyLink {
this.doctypes = doctypes
this.doctypesReplicationOptions = doctypesReplicationOptions
this.indexes = {}
this.storage = options.platform?.storage || platformWeb.storage

/** @type {Record<string, SyncStatus>} - Stores replication states per doctype */
this.replicationStatus = this.replicationStatus || {}
Expand Down Expand Up @@ -149,15 +145,15 @@ class PouchLink extends CozyLink {
for (const plugin of plugins) {
PouchDB.plugin(plugin)
}
const doctypes = getPersistedSyncedDoctypes()
const doctypes = await this.storage.getPersistedSyncedDoctypes()
for (const doctype of Object.keys(doctypes)) {
const prefix = getPrefix(url)
const dbName = getDatabaseName(prefix, doctype)

await migratePouch({ dbName, fromAdapter, toAdapter })
destroyWarmedUpQueries() // force recomputing indexes
await this.storage.destroyWarmedUpQueries() // force recomputing indexes
}
persistAdapterName('indexeddb')
await this.storage.persistAdapterName('indexeddb')
} catch (err) {
console.error('PouchLink: PouchDB migration failed. ', err)
}
Expand Down Expand Up @@ -195,9 +191,9 @@ class PouchLink extends CozyLink {
logger.log('Create pouches with ' + prefix + ' prefix')
}

if (!getAdapterName()) {
if (!(await this.storage.getAdapterName())) {
const adapter = get(this.options, 'pouch.options.adapter')
persistAdapterName(adapter)
await this.storage.persistAdapterName(adapter)
}

this.pouches = new PouchManager(this.doctypes, {
Expand All @@ -209,8 +205,10 @@ class PouchLink extends CozyLink {
onDoctypeSyncStart: this.handleDoctypeSyncStart.bind(this),
onDoctypeSyncEnd: this.handleDoctypeSyncEnd.bind(this),
prefix,
executeQuery: this.executeQuery.bind(this)
executeQuery: this.executeQuery.bind(this),
platform: this.options.platform
})
await this.pouches.init()

if (this.client && this.options.initialSync) {
this.startReplication()
Expand Down Expand Up @@ -334,7 +332,7 @@ class PouchLink extends CozyLink {
return !!this.getPouch(impactedDoctype)
}

request(operation, result = null, forward = doNothing) {
async request(operation, result = null, forward = doNothing) {
const doctype = getDoctypeFromOperation(operation)

if (!this.pouches) {
Expand Down Expand Up @@ -387,18 +385,18 @@ class PouchLink extends CozyLink {
* and return if those queries are already warmed up or not
*
* @param {string} doctype - Doctype to check
* @returns {boolean} the need to wait for the warmup
* @returns {Promise<boolean>} the need to wait for the warmup
*/
needsToWaitWarmup(doctype) {
async needsToWaitWarmup(doctype) {
if (
this.doctypesReplicationOptions &&
this.doctypesReplicationOptions[doctype] &&
this.doctypesReplicationOptions[doctype].warmupQueries
) {
return !this.pouches.areQueriesWarmedUp(
return !(await this.pouches.areQueriesWarmedUp(
doctype,
this.doctypesReplicationOptions[doctype].warmupQueries
)
))
}
return false
}
Expand Down
73 changes: 43 additions & 30 deletions packages/cozy-pouch-link/src/PouchManager.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import PouchDB from 'pouchdb-browser'
import fromPairs from 'lodash/fromPairs'
import forEach from 'lodash/forEach'
import get from 'lodash/get'
Expand All @@ -10,9 +9,9 @@ import { QueryDefinition } from 'cozy-client'

import Loop from './loop'
import logger from './logger'
import { platformWeb } from './platformWeb'
import { fetchRemoteLastSequence } from './remote'
import { startReplication } from './startReplication'
import * as localStorage from './localStorage'
import { getDatabaseName } from './utils'

const DEFAULT_DELAY = 30 * 1000
Expand All @@ -35,20 +34,33 @@ const getQueryAlias = query => {
class PouchManager {
constructor(doctypes, options) {
this.options = options
const pouchPlugins = get(options, 'pouch.plugins', [])
const pouchOptions = get(options, 'pouch.options', {})
this.doctypes = doctypes

forEach(pouchPlugins, plugin => PouchDB.plugin(plugin))
/**
* @type {import("./types").PouchLocalStorage}
*/
this.storage = options.platform?.storage || platformWeb.storage
this.PouchDB = options.platform?.pouchEngine || platformWeb.pouchEngine
}

async init() {
const pouchPlugins = get(this.options, 'pouch.plugins', [])
const pouchOptions = get(this.options, 'pouch.options', {})

forEach(pouchPlugins, plugin => this.PouchDB.plugin(plugin))
this.pouches = fromPairs(
doctypes.map(doctype => [
this.doctypes.map(doctype => [
doctype,
new PouchDB(getDatabaseName(options.prefix, doctype), pouchOptions)
new this.PouchDB(
getDatabaseName(this.options.prefix, doctype),
pouchOptions
)
])
)
this.syncedDoctypes = localStorage.getPersistedSyncedDoctypes()
this.warmedUpQueries = localStorage.getPersistedWarmedUpQueries()
this.getReplicationURL = options.getReplicationURL
this.doctypesReplicationOptions = options.doctypesReplicationOptions || {}
this.syncedDoctypes = await this.storage.getPersistedSyncedDoctypes()
this.warmedUpQueries = await this.storage.getPersistedWarmedUpQueries()
this.getReplicationURL = this.options.getReplicationURL
this.doctypesReplicationOptions = this.options.doctypesReplicationOptions || {}
this.listenerLaunched = false

// We must ensure databases exist on the remote before
Expand Down Expand Up @@ -85,13 +97,13 @@ class PouchManager {
}
}

destroy() {
async destroy() {
this.stopReplicationLoop()
this.removeListeners()
this.clearSyncedDoctypes()
this.clearWarmedUpQueries()
localStorage.destroyAllDoctypeLastSequence()
localStorage.destroyAllLastReplicatedDocID()
await this.clearSyncedDoctypes()
await this.clearWarmedUpQueries()
await this.storage.destroyAllDoctypeLastSequence()
await this.storage.destroyAllLastReplicatedDocID()

return Promise.all(
Object.values(this.pouches).map(pouch => pouch.destroy())
Expand Down Expand Up @@ -182,9 +194,9 @@ class PouchManager {
// Before the first replication, get the last remote sequence,
// which will be used as a checkpoint for the next replication
const lastSeq = await fetchRemoteLastSequence(getReplicationURL())
localStorage.persistDoctypeLastSequence(doctype, lastSeq)
await this.storage.persistDoctypeLastSequence(doctype, lastSeq)
} else {
seq = localStorage.getDoctypeLastSequence(doctype)
seq = await this.storage.getDoctypeLastSequence(doctype)
}

const replicationOptions = get(
Expand All @@ -203,15 +215,16 @@ class PouchManager {
const res = await startReplication(
pouch,
replicationOptions,
getReplicationURL
getReplicationURL,
this.storage
)
if (seq) {
// We only need the sequence for the second replication, as PouchDB
// will use a local checkpoint for the next runs.
localStorage.destroyDoctypeLastSequence(doctype)
await this.storage.destroyDoctypeLastSequence(doctype)
}

this.updateSyncInfo(doctype)
await this.updateSyncInfo(doctype)
this.checkToWarmupDoctype(doctype, replicationOptions)
if (this.options.onDoctypeSyncEnd) {
this.options.onDoctypeSyncEnd(doctype)
Expand Down Expand Up @@ -273,9 +286,9 @@ class PouchManager {
return this.pouches[doctype]
}

updateSyncInfo(doctype) {
async updateSyncInfo(doctype) {
this.syncedDoctypes[doctype] = { date: new Date().toISOString() }
localStorage.persistSyncedDoctypes(this.syncedDoctypes)
await this.storage.persistSyncedDoctypes(this.syncedDoctypes)
}

getSyncInfo(doctype) {
Expand All @@ -287,9 +300,9 @@ class PouchManager {
return info ? !!info.date : false
}

clearSyncedDoctypes() {
async clearSyncedDoctypes() {
this.syncedDoctypes = {}
localStorage.destroySyncedDoctypes()
await this.storage.destroySyncedDoctypes()
}

async warmupQueries(doctype, queries) {
Expand All @@ -304,7 +317,7 @@ class PouchManager {
}
})
)
localStorage.persistWarmedUpQueries(this.warmedUpQueries)
await this.storage.persistWarmedUpQueries(this.warmedUpQueries)
logger.log('PouchManager: warmupQueries for ' + doctype + ' are done')
} catch (err) {
logger.error(
Expand All @@ -324,18 +337,18 @@ class PouchManager {
}
}

areQueriesWarmedUp(doctype, queries) {
const persistWarmedUpQueries = localStorage.getPersistedWarmedUpQueries()
async areQueriesWarmedUp(doctype, queries) {
const persistWarmedUpQueries = await this.storage.getPersistedWarmedUpQueries()
return queries.every(
query =>
persistWarmedUpQueries[doctype] &&
persistWarmedUpQueries[doctype].includes(getQueryAlias(query))
)
}

clearWarmedUpQueries() {
async clearWarmedUpQueries() {
this.warmedUpQueries = {}
localStorage.destroyWarmedUpQueries()
await this.storage.destroyWarmedUpQueries()
}
}

Expand Down
Loading

0 comments on commit 6fe85a7

Please sign in to comment.