Skip to content

Commit

Permalink
adding tooling around working with permissions
Browse files Browse the repository at this point in the history
  • Loading branch information
dmonad committed May 14, 2023
1 parent 9eaff2f commit bad2146
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 466 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ node_modules
dist
.vscode
.test_dbs
test.html
82 changes: 75 additions & 7 deletions src/db.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ import * as dbtypes from './dbtypes.js'
import * as isodb from 'isodb'
import * as utils from './utils.js'
import * as number from 'lib0/number'
import * as promise from 'lib0/promise'
import * as array from 'lib0/array'
import * as math from 'lib0/math'

/**
* @typedef {import('./ydb.js').Ydb} Ydb
Expand Down Expand Up @@ -32,12 +35,39 @@ export const def = {
}
},
clocks: {
key: isodb.Uint32Key, // is a clientid
key: dbtypes.ClocksKey,
value: dbtypes.ClientClockValue
}
}
}

/**
* # Algorithm to sync documents that a user just got permission to:
*
* The information that a document has been created (the first update) is always sent to all users.
* However, users that don't have permission won't receive the content and simply store a "no
* permission" information in the `oplog`.
*
* When we receive new permission, we store an "todo item in the `reqs`" table: "check table
* starting at clock X". The name should be unique. Two separate permission changes should update
* the same item. Simpler implementation: `reqs` table uses AutoKey or `collection/autokey` (by
* index) and we have to work through the list until completion.
*
* We iterate through the table and sync each document individually until we completed the whole
* collection table. Every time we synced a document, we update the todo item. Once completed,
* we delete the todo item in reqs.
*
* # Todo
* - ( ) implement request "sync document starting from clock X"
* - ( ) implement requests table and figure out key-system.
*/

/**
* @param {string} dbname
*/
export const createDb = dbname =>
isodb.openDB(dbname, def)

/**
* @param {Ydb} ydb
* @param {Array<{ value: dbtypes.OpValue, fkey: isodb.AutoKey }>} updates
Expand All @@ -49,12 +79,6 @@ const updateOpClocks = (ydb, updates) => updates.map(update => {
return update.value
})

/**
* @param {string} dbname
*/
export const createDb = dbname =>
isodb.openDB(dbname, def)

/**
* @param {Ydb} ydb
* @param {number} clock
Expand Down Expand Up @@ -111,3 +135,47 @@ export const getDocOps = async (ydb, collection, doc, clock) => {
})
return updateOpClocks(ydb, entries)
}

/**
* @param {Ydb} ydb
* @param {number} clientid
* @param {string?} collection
* @param {string?} doc
*/
export const getClock = async (ydb, clientid, collection, doc) =>
ydb.db.transact(async tr => {
if (ydb.clientid === clientid) {
const latestEntry = await tr.tables.oplog.getKeys({
end: number.HIGHEST_INT32, // @todo change to uint
reverse: true,
limit: 1
})
return latestEntry.length > 0 ? latestEntry[0].v : 0
}
const clocksTable = tr.tables.clocks
const queries = [
clocksTable.get(new dbtypes.ClocksKey(clientid, null, null))
]
collection && queries.push(clocksTable.get(new dbtypes.ClocksKey(clientid, collection, null)))
doc && queries.push(clocksTable.get(new dbtypes.ClocksKey(clientid, collection, doc)))
const clocks = await promise.all(queries)
return array.fold(clocks.map(c => c ? c.clock : 0), 0, math.max)
})

/**
* Confirm that a all updates of a doc/collection/* from a client have been received.
*
* @param {Ydb} ydb
* @param {number} clientid
* @param {string?} collection
* @param {string?} doc
* @param {number} newClock
*/
export const confirmClientClock = async (ydb, clientid, collection, doc, newClock) => {
ydb.db.transact(async tr => {
const currClock = await getClock(ydb, clientid, collection, doc)
if (currClock < newClock) {
tr.tables.clocks.set(new dbtypes.ClocksKey(clientid, collection, doc), newClock)
}
})
}
39 changes: 39 additions & 0 deletions src/dbtypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,45 @@ export class CollectionKey {
}
}

/**
* @implements isodb.IEncodable
*/
export class ClocksKey {
/**
* @param {number} clientid
* @param {string?} collection
* @param {string?} doc
*/
constructor (clientid, collection, doc) {
this.clientid = clientid
this.collection = collection
this.doc = doc
}

/**
* @param {encoding.Encoder} encoder
*/
encode (encoder) {
const info = (this.collection ? 1 : 0) + (this.doc ? 2 : 0)
encoding.writeUint8(encoder, info)
encoding.writeUint32(encoder, this.clientid)
this.collection && encoding.writeVarString(encoder, this.collection)
this.doc && encoding.writeVarString(encoder, this.doc)
}

/**
* @param {decoding.Decoder} decoder
* @return {isodb.IEncodable}
*/
static decode (decoder) {
const info = decoding.readUint8(decoder)
const clientid = decoding.readUint32(decoder)
const collection = (info & 1) > 0 ? decoding.readVarString(decoder) : null
const doc = (info & 2) > 0 ? decoding.readVarString(decoder) : null
return new ClocksKey(clientid, collection, doc)
}
}

/**
* @implements isodb.IEncodable
*/
Expand Down
Loading

0 comments on commit bad2146

Please sign in to comment.