-
Notifications
You must be signed in to change notification settings - Fork 3
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
add project settings functionality to MapeoProject #187
Merged
Merged
Changes from 36 commits
Commits
Show all changes
38 commits
Select commit
Hold shift + click to select a range
7102623
wip: initial implementation
achou11 79c7feb
instantiate project datastore
achou11 cebc1ab
project info config opt
achou11 98ebec2
add project to NAMESPACE_SCHEMAS
achou11 bf0bcf3
update naming from project info to project settings"
achou11 636028c
fix batch function in config datastore
achou11 4bd5110
fix crud tests
achou11 d5f761e
implement setProjectSettings
achou11 4288d76
add basic test for project settings indexing
achou11 51bb7f3
use a project index writer for project crud test setup
achou11 22a6ba5
extract client and project setup to e2e test utils
achou11 bee4f4c
replace bind with call
achou11 df47358
add test for multiple projects
achou11 8204c21
update tests to pass versionId for setProjectSettings
achou11 fe9c376
add private field for project id
achou11 6018a46
implement getProjectSettings
achou11 bdbca4f
add settings test for create, read, update
achou11 1b953f3
remove unused import
achou11 6763de9
ignore errors raised by decode
achou11 2cc9d44
update setProjectSettings
achou11 82563b4
update return type of getProjectSettings
achou11 d5ed306
update tsconfig
achou11 6c5822a
simplify constructor for MapeoProject
achou11 1925230
Merge branch 'main' into 167/project-info-index
achou11 c6259d2
update @mapeo/schema to use decodeBlockPrefix
achou11 bd311a2
create private method for handling config entries
achou11 a6a468f
update getProjectSettings
achou11 9f80bc7
add comment about 'existing' check in setProjectSettings
achou11 fe56a1a
return empty object in getProjectSettings instead of null
achou11 26e9310
Remove test
achou11 13a8638
update @mapeo/schema to fix optional settings fields
achou11 b7dcdb4
cast return type in getProjectSettings
achou11 19057ce
improve types for set and get project settings
achou11 903183d
update tests after return type changes
achou11 21c9ee8
remove unused imports and variables in test
achou11 690afab
create helper function to extract editable settings fields
achou11 f0dcf62
rename dataStoreIndexWriter -> projectIndexWriter
gmaclennan 1213be6
fix param type for handleConfigEntries
achou11 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,17 +1,22 @@ | ||
// @ts-check | ||
import { decodeBlockPrefix } from '@mapeo/schema' | ||
import { drizzle } from 'drizzle-orm/better-sqlite3' | ||
import { migrate } from 'drizzle-orm/better-sqlite3/migrator' | ||
|
||
import { CoreManager } from './core-manager/index.js' | ||
import { DataStore } from './datastore/index.js' | ||
import { DataType } from './datatype/index.js' | ||
import { DataType, kCreateWithDocId } from './datatype/index.js' | ||
import { IndexWriter } from './index-writer/index.js' | ||
import { projectTable } from './schema/client.js' | ||
import { fieldTable, observationTable, presetTable } from './schema/project.js' | ||
import RandomAccessFile from 'random-access-file' | ||
import RAM from 'random-access-memory' | ||
import Database from 'better-sqlite3' | ||
import path from 'path' | ||
import { RandomAccessFilePool } from './core-manager/random-access-file-pool.js' | ||
import { valueOf } from './utils.js' | ||
|
||
/** @typedef {Omit<import('@mapeo/schema').ProjectValue, 'schemaName'>} EditableProjectSettings */ | ||
|
||
const PROJECT_SQLITE_FILE_NAME = 'project.db' | ||
const CORE_STORAGE_FOLDER_NAME = 'cores' | ||
|
@@ -26,6 +31,7 @@ export class MapeoProject { | |
#coreManager | ||
#dataStores | ||
#dataTypes | ||
#projectId | ||
|
||
/** | ||
* @param {Object} opts | ||
|
@@ -34,8 +40,18 @@ export class MapeoProject { | |
* @param {Buffer} opts.projectKey 32-byte public key of the project creator core | ||
* @param {Buffer} [opts.projectSecretKey] 32-byte secret key of the project creator core | ||
* @param {Partial<Record<import('./core-manager/index.js').Namespace, Buffer>>} [opts.encryptionKeys] Encryption keys for each namespace | ||
* @param {import('drizzle-orm/better-sqlite3').BetterSQLite3Database} opts.sharedDb | ||
* @param {IndexWriter} opts.sharedIndexWriter | ||
*/ | ||
constructor({ storagePath, ...coreManagerOpts }) { | ||
constructor({ | ||
storagePath, | ||
sharedDb, | ||
sharedIndexWriter, | ||
...coreManagerOpts | ||
}) { | ||
// TODO: Update to use @mapeo/crypto when ready (https://github.com/digidem/mapeo-core-next/issues/171) | ||
this.#projectId = coreManagerOpts.projectKey.toString('hex') | ||
Comment on lines
+52
to
+53
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. just highlighting this as something that will eventually be updated |
||
|
||
///////// 1. Setup database | ||
|
||
const dbPath = | ||
|
@@ -86,7 +102,11 @@ export class MapeoProject { | |
config: new DataStore({ | ||
coreManager: this.#coreManager, | ||
namespace: 'config', | ||
batch: (entries) => indexWriter.batch(entries), | ||
batch: (entries) => | ||
this.#handleConfigEntries(entries, { | ||
datastoreIndexWriter: indexWriter, | ||
gmaclennan marked this conversation as resolved.
Show resolved
Hide resolved
|
||
sharedIndexWriter, | ||
}), | ||
storage: indexerStorage, | ||
}), | ||
data: new DataStore({ | ||
|
@@ -112,9 +132,47 @@ export class MapeoProject { | |
table: fieldTable, | ||
db, | ||
}), | ||
project: new DataType({ | ||
dataStore: this.#dataStores.config, | ||
table: projectTable, | ||
db: sharedDb, | ||
}), | ||
} | ||
} | ||
|
||
/** | ||
* @param {import('multi-core-indexer').Entry[]} entries | ||
* @param {{datastoreIndexWriter: IndexWriter, sharedIndexWriter: IndexWriter}} indexWriters | ||
*/ | ||
async #handleConfigEntries( | ||
entries, | ||
{ datastoreIndexWriter, sharedIndexWriter } | ||
gmaclennan marked this conversation as resolved.
Show resolved
Hide resolved
|
||
) { | ||
/** @type {import('multi-core-indexer').Entry[]} */ | ||
const projectSettingsEntries = [] | ||
/** @type {import('multi-core-indexer').Entry[]} */ | ||
const otherEntries = [] | ||
|
||
for (const entry of entries) { | ||
try { | ||
const { schemaName } = decodeBlockPrefix(entry.block) | ||
|
||
if (schemaName === 'project') { | ||
projectSettingsEntries.push(entry) | ||
} else { | ||
otherEntries.push(entry) | ||
} | ||
} catch { | ||
// Ignore errors thrown by values that can't be decoded for now | ||
} | ||
} | ||
|
||
await Promise.all([ | ||
datastoreIndexWriter.batch(otherEntries), | ||
gmaclennan marked this conversation as resolved.
Show resolved
Hide resolved
|
||
sharedIndexWriter.batch(projectSettingsEntries), | ||
]) | ||
} | ||
|
||
get observation() { | ||
return this.#dataTypes.observation | ||
} | ||
|
@@ -124,4 +182,58 @@ export class MapeoProject { | |
get field() { | ||
return this.#dataTypes.field | ||
} | ||
|
||
/** | ||
* @param {Partial<EditableProjectSettings>} settings | ||
* @returns {Promise<EditableProjectSettings>} | ||
*/ | ||
async $setProjectSettings(settings) { | ||
const { project } = this.#dataTypes | ||
|
||
// We only want to catch the error to the getByDocId call | ||
// Using try/catch for this is a little verbose when dealing with TS types | ||
const existing = await project.getByDocId(this.#projectId).catch(() => { | ||
achou11 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// project does not exist so return null | ||
return null | ||
}) | ||
|
||
if (existing) { | ||
return extractEditableProjectSettings( | ||
await project.update([existing.versionId, ...existing.forks], { | ||
...valueOf(existing), | ||
...settings, | ||
}) | ||
) | ||
} | ||
|
||
return extractEditableProjectSettings( | ||
await project[kCreateWithDocId](this.#projectId, { | ||
...settings, | ||
schemaName: 'project', | ||
}) | ||
) | ||
} | ||
|
||
/** | ||
* @returns {Promise<EditableProjectSettings>} | ||
*/ | ||
async $getProjectSettings() { | ||
try { | ||
return extractEditableProjectSettings( | ||
await this.#dataTypes.project.getByDocId(this.#projectId) | ||
) | ||
} catch { | ||
return /** @type {EditableProjectSettings} */ ({}) | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* @param {import("@mapeo/schema").Project & { forks: string[] }} projectDoc | ||
* @returns {EditableProjectSettings} | ||
*/ | ||
function extractEditableProjectSettings(projectDoc) { | ||
// eslint-disable-next-line no-unused-vars | ||
const { schemaName, ...result } = valueOf(projectDoc) | ||
return result | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
achou11 marked this conversation as resolved.
Show resolved
Hide resolved
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import { test } from 'brittle' | ||
import { setupSharedResources, createProject } from './utils.js' | ||
|
||
test('Project settings create, read, and update operations', async (t) => { | ||
const shared = setupSharedResources() | ||
|
||
const project = createProject({ | ||
sharedDb: shared.db, | ||
sharedIndexWriter: shared.indexWriter, | ||
}) | ||
|
||
t.alike( | ||
await project.$getProjectSettings(), | ||
{}, | ||
'no settings when project initially created' | ||
) | ||
|
||
const expectedSettings = { | ||
name: 'updated', | ||
} | ||
|
||
const updatedSettings = await project.$setProjectSettings(expectedSettings) | ||
|
||
t.is(updatedSettings.name, expectedSettings.name, 'updatable fields change') | ||
|
||
const settings = await project.$getProjectSettings() | ||
|
||
t.alike( | ||
updatedSettings, | ||
settings, | ||
'retrieved settings are equivalent to most recently updated' | ||
) | ||
}) |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there should be a comment here explaining what this is used for. took me a bit to figure out I needed to update it to add the project schema