Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add set & get deviceInfo & datatype #250

Merged
merged 6 commits into from
Sep 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
CREATE TABLE `localDeviceInfo` (
`deviceId` text NOT NULL,
`deviceInfo` text NOT NULL
);
--> statement-breakpoint
CREATE TABLE `project_backlink` (
`versionId` text PRIMARY KEY NOT NULL
);
Expand All @@ -20,3 +25,5 @@ CREATE TABLE `project` (
`defaultPresets` text,
`forks` text NOT NULL
);
--> statement-breakpoint
CREATE UNIQUE INDEX `localDeviceInfo_deviceId_unique` ON `localDeviceInfo` (`deviceId`);
33 changes: 32 additions & 1 deletion drizzle/client/meta/0000_snapshot.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,40 @@
{
"version": "5",
"dialect": "sqlite",
"id": "e047380e-7d10-442c-b5e4-0791b8970eeb",
"id": "518b4a6e-b310-4f08-a39e-5a046c1e1da9",
"prevId": "00000000-0000-0000-0000-000000000000",
"tables": {
"localDeviceInfo": {
"name": "localDeviceInfo",
"columns": {
"deviceId": {
"name": "deviceId",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"deviceInfo": {
"name": "deviceInfo",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {
"localDeviceInfo_deviceId_unique": {
"name": "localDeviceInfo_deviceId_unique",
"columns": [
"deviceId"
],
"isUnique": true
}
},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"project_backlink": {
"name": "project_backlink",
"columns": {
Expand Down
4 changes: 2 additions & 2 deletions drizzle/client/meta/_journal.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
{
"idx": 0,
"version": "5",
"when": 1693938675535,
"tag": "0000_needy_hex",
"when": 1694082554479,
"tag": "0000_charming_klaw",
"breakpoints": true
}
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,21 @@ CREATE TABLE `coreOwnership` (
`forks` text NOT NULL
);
--> statement-breakpoint
CREATE TABLE `deviceInfo_backlink` (
`versionId` text PRIMARY KEY NOT NULL
);
--> statement-breakpoint
CREATE TABLE `deviceInfo` (
`docId` text PRIMARY KEY NOT NULL,
`versionId` text NOT NULL,
`schemaName` text NOT NULL,
`createdAt` text NOT NULL,
`updatedAt` text NOT NULL,
`links` text NOT NULL,
`name` text NOT NULL,
`forks` text NOT NULL
);
--> statement-breakpoint
CREATE TABLE `field_backlink` (
`versionId` text PRIMARY KEY NOT NULL
);
Expand Down
83 changes: 82 additions & 1 deletion drizzle/project/meta/0000_snapshot.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"version": "5",
"dialect": "sqlite",
"id": "a4376411-f126-4579-adc2-da4f781d00bb",
"id": "c66bf0a9-28c1-446a-bc4b-51d4ec778947",
"prevId": "00000000-0000-0000-0000-000000000000",
"tables": {
"coreOwnership_backlink": {
Expand Down Expand Up @@ -113,6 +113,87 @@
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"deviceInfo_backlink": {
"name": "deviceInfo_backlink",
"columns": {
"versionId": {
"name": "versionId",
"type": "text",
"primaryKey": true,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"deviceInfo": {
"name": "deviceInfo",
"columns": {
"docId": {
"name": "docId",
"type": "text",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"versionId": {
"name": "versionId",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"schemaName": {
"name": "schemaName",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"createdAt": {
"name": "createdAt",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"updatedAt": {
"name": "updatedAt",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"links": {
"name": "links",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"forks": {
"name": "forks",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"field_backlink": {
"name": "field_backlink",
"columns": {
Expand Down
4 changes: 2 additions & 2 deletions drizzle/project/meta/_journal.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
{
"idx": 0,
"version": "5",
"when": 1693481966662,
"tag": "0000_volatile_jackal",
"when": 1693999502360,
"tag": "0000_large_wendell_rand",
"breakpoints": true
}
]
Expand Down
2 changes: 1 addition & 1 deletion src/datastore/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import pDefer from 'p-defer'

const NAMESPACE_SCHEMAS = /** @type {const} */ ({
data: ['observation'],
config: ['preset', 'field', 'project'],
config: ['preset', 'field', 'project', 'deviceInfo'],
auth: ['coreOwnership', 'role'],
})

Expand Down
1 change: 0 additions & 1 deletion src/datatype/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,6 @@ export class DataType {

// TS can't track the relationship between TDoc and TValue, so doc above is
// typed as MapeoDoc (without versionId) rather than as TDoc.
// @ts-expect-error
await this.#dataStore.write(doc)
return this.getByDocId(doc.docId)
}
Expand Down
38 changes: 37 additions & 1 deletion src/mapeo-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@ import { migrate } from 'drizzle-orm/better-sqlite3/migrator'
import Hypercore from 'hypercore'
import { IndexWriter } from './index-writer/index.js'
import { MapeoProject } from './mapeo-project.js'
import { projectKeysTable, projectTable } from './schema/client.js'
import {
localDeviceInfoTable,
projectKeysTable,
projectTable,
} from './schema/client.js'
import { ProjectKeys } from './generated/keys.js'
import {
deNullify,
Expand Down Expand Up @@ -40,6 +44,7 @@ export class MapeoManager {
/** @type {import('./types.js').CoreStorage} */
#coreStorage
#dbFolder
#deviceId
#rpc

/**
Expand All @@ -60,6 +65,9 @@ export class MapeoManager {

this.#rpc = new MapeoRPC()
this.#keyManager = new KeyManager(rootKey)
this.#deviceId = this.#keyManager
.getIdentityKeypair()
.publicKey.toString('hex')
this.#projectSettingsIndexWriter = new IndexWriter({
tables: [projectTable],
sqlite,
Expand Down Expand Up @@ -332,4 +340,32 @@ export class MapeoManager {

return projectPublicId
}

/**
* @template {import('type-fest').Exact<import('./schema/client.js').DeviceInfoParam, T>} T
* @param {T} deviceInfo
*/
async setDeviceInfo(deviceInfo) {
const values = { deviceId: this.#deviceId, deviceInfo }
this.#db
.insert(localDeviceInfoTable)
.values(values)
.onConflictDoUpdate({
target: localDeviceInfoTable.deviceId,
set: values,
})
.run()
}

/**
* @returns {Promise<Partial<import('./schema/client.js').DeviceInfoParam>>}
*/
async getDeviceInfo() {
const row = this.#db
.select()
.from(localDeviceInfoTable)
.where(eq(localDeviceInfoTable.deviceId, this.#deviceId))
.get()
return row ? row.deviceInfo : {}
}
}
34 changes: 30 additions & 4 deletions src/mapeo-project.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { IndexWriter } from './index-writer/index.js'
import { projectTable } from './schema/client.js'
import {
coreOwnershipTable,
deviceInfoTable,
fieldTable,
observationTable,
presetTable,
Expand Down Expand Up @@ -111,10 +112,13 @@ export class MapeoProject {
sqlite,
getWinner,
mapDoc: (doc, version) => {
if (doc.schemaName === 'coreOwnership') {
return mapAndValidateCoreOwnership(doc, version)
} else {
return doc
switch (doc.schemaName) {
case 'coreOwnership':
return mapAndValidateCoreOwnership(doc, version)
case 'deviceInfo':
return mapAndValidateDeviceInfo(doc, version)
default:
return doc
}
},
})
Expand Down Expand Up @@ -173,6 +177,11 @@ export class MapeoProject {
table: roleTable,
db,
}),
deviceInfo: new DataType({
dataStore: this.#dataStores.config,
table: deviceInfoTable,
db,
}),
}

this.#blobStore = new BlobStore({
Expand Down Expand Up @@ -390,3 +399,20 @@ function getCoreKeypairs({ projectKey, projectSecretKey, keyManager }) {

return keypairs
}

/**
* Validate that a deviceInfo record is written by the device that is it about,
* e.g. version.coreKey should equal docId
*
* @param {import('@mapeo/schema').DeviceInfo} doc
* @param {import('@mapeo/schema').VersionIdObject} version
* @returns {import('@mapeo/schema').DeviceInfo}
*/
function mapAndValidateDeviceInfo(doc, { coreKey }) {
if (doc.docId !== coreKey.toString('hex')) {
throw new Error(
'Invalid deviceInfo record, cannot write deviceInfo for another device'
)
}
return doc
}
15 changes: 15 additions & 0 deletions src/schema/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,18 @@ export const projectKeysTable = sqliteTable('projectKeys', {
)
.notNull(),
})

/**
* @typedef {Omit<import('@mapeo/schema').DeviceInfoValue, 'schemaName'>} DeviceInfoParam
*/

const deviceInfoColumn =
/** @type {ReturnType<typeof import('drizzle-orm/sqlite-core').customType<{data: DeviceInfoParam }>>} */ (
customJson
)

// This table only ever has one row in it.
export const localDeviceInfoTable = sqliteTable('localDeviceInfo', {
deviceId: text('deviceId').notNull().unique(),
deviceInfo: deviceInfoColumn('deviceInfo').notNull(),
})
5 changes: 5 additions & 0 deletions src/schema/project.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,14 @@ export const coreOwnershipTable = sqliteTable(
toColumns(schemas.coreOwnership)
)
export const roleTable = sqliteTable('role', toColumns(schemas.role))
export const deviceInfoTable = sqliteTable(
'deviceInfo',
toColumns(schemas.deviceInfo)
)

export const observationBacklinkTable = backlinkTable(observationTable)
export const presetBacklinkTable = backlinkTable(presetTable)
export const fieldBacklinkTable = backlinkTable(fieldTable)
export const coreOwnershipBacklinkTable = backlinkTable(coreOwnershipTable)
export const roleBacklinkTable = backlinkTable(roleTable)
export const deviceInfoBacklinkTable = backlinkTable(deviceInfoTable)
2 changes: 2 additions & 0 deletions src/schema/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,3 +149,5 @@ export type SchemaToDrizzleColumns<
: never
: never
} & { forks: JsonBuilder<'forks', string[], true, false> }

export type NonEmptyArray<T> = [T, ...T[]]
Loading