Skip to content

Commit

Permalink
Merge branch 'main' into media-manager-v1
Browse files Browse the repository at this point in the history
  • Loading branch information
EvanHahn committed Nov 19, 2024
2 parents 124c941 + 83c0b27 commit a4067fe
Show file tree
Hide file tree
Showing 13 changed files with 258 additions and 95 deletions.
6 changes: 2 additions & 4 deletions src/core-ownership.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import pDefer from 'p-defer'
import { NAMESPACES } from './constants.js'
import { TypedEmitter } from 'tiny-typed-emitter'
import { omit } from './lib/omit.js'
import { NotFoundError } from './errors.js'
/**
* @import {
* CoreOwnershipWithSignatures,
Expand Down Expand Up @@ -86,13 +87,10 @@ export class CoreOwnership extends TypedEmitter {
for (const namespace of NAMESPACES) {
expressions.push(eq(table[`${namespace}CoreId`], coreId))
}
// prettier-ignore
const result = (await this.#dataType[kSelect]())
.where(or.apply(null, expressions))
.get()
if (!result) {
throw new Error('NotFound')
}
if (!result) throw new NotFoundError()
return result.docId
}

Expand Down
7 changes: 4 additions & 3 deletions src/datastore/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import pDefer from 'p-defer'
import { discoveryKey } from 'hypercore-crypto'
import { NAMESPACE_SCHEMAS } from '../constants.js'
import { createMap } from '../utils.js'
import { NotFoundError } from '../errors.js'
/** @import { MapeoDoc } from '@comapeo/schema' */

/**
Expand Down Expand Up @@ -182,7 +183,7 @@ export class DataStore extends TypedEmitter {
const coreRecord = this.#coreManager.getCoreByDiscoveryKey(coreDiscoveryKey)
if (!coreRecord) throw new Error('Invalid versionId')
const block = await coreRecord.core.get(index, { wait: false })
if (!block) throw new Error('Not Found')
if (!block) throw new NotFoundError('Not Found')
return decode(block, { coreDiscoveryKey, index })
}

Expand All @@ -202,9 +203,9 @@ export class DataStore extends TypedEmitter {
async readRaw(versionId) {
const { coreDiscoveryKey, index } = parseVersionId(versionId)
const coreRecord = this.#coreManager.getCoreByDiscoveryKey(coreDiscoveryKey)
if (!coreRecord) throw new Error('core not found')
if (!coreRecord) throw new NotFoundError('core not found')
const block = await coreRecord.core.get(index, { wait: false })
if (!block) throw new Error('Not Found')
if (!block) throw new NotFoundError()
return block
}

Expand Down
6 changes: 5 additions & 1 deletion src/datatype/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,12 @@ export class DataType<

getByDocId(
docId: string,
opts?: { lang?: string }
opts?: { mustBeFound?: true; lang?: string }
): Promise<TDoc & { forks: string[] }>
getByDocId(
docId: string,
opts?: { mustBeFound?: boolean; lang?: string }
): Promise<null | (TDoc & { forks: string[] })>

getByVersionId(versionId: string, opts?: { lang?: string }): Promise<TDoc>

Expand Down
31 changes: 22 additions & 9 deletions src/datatype/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -164,16 +164,30 @@ export class DataType extends TypedEmitter {
}

/**
* @overload
* @param {string} docId
* @param {{ lang?: string }} [opts]
* @param {object} [options]
* @param {true} [options.mustBeFound]
* @param {string} [options.lang]
* @returns {Promise<TDoc & { forks: string[] }>}
*/
/**
* @param {string} docId
* @param {object} [options]
* @param {boolean} [options.mustBeFound]
* @param {string} [options.lang]
* @returns {Promise<null | (TDoc & { forks: string[] })>}
*/
async getByDocId(docId, { lang } = {}) {
async getByDocId(docId, { mustBeFound = true, lang } = {}) {
await this.#dataStore.indexer.idle()
const result = /** @type {undefined | MapeoDoc} */ (
this.#sql.getByDocId.get({ docId })
)
if (!result) throw new NotFoundError()
return this.#translate(deNullify(result), { lang })
const result = this.#sql.getByDocId.get({ docId })
if (result) {
return this.#translate(deNullify(result), { lang })
} else if (mustBeFound) {
throw new NotFoundError()
} else {
return null
}
}

/**
Expand All @@ -186,7 +200,7 @@ export class DataType extends TypedEmitter {
}

/**
* @param {MapeoDoc} doc
* @param {any} doc
* @param {{ lang?: string }} [opts]
*/
async #translate(doc, { lang } = {}) {
Expand Down Expand Up @@ -278,7 +292,6 @@ export class DataType extends TypedEmitter {
const doc = {
...existingDoc,
updatedAt: new Date().toISOString(),
// @ts-expect-error - TS just doesn't work in this class
links: [existingDoc.versionId, ...existingDoc.forks],
deleted: true,
}
Expand Down
4 changes: 2 additions & 2 deletions src/errors.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export class NotFoundError extends Error {
constructor() {
super('Not found')
constructor(message = 'Not found') {
super(message)
}
}
5 changes: 3 additions & 2 deletions src/mapeo-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ import {
kRequestFullStop,
kRescindFullStopRequest,
} from './sync/sync-api.js'
import { NotFoundError } from './errors.js'
/** @import { ProjectSettingsValue as ProjectValue } from '@comapeo/schema' */
/** @import NoiseSecretStream from '@hyperswarm/secret-stream' */
/** @import { SetNonNullable } from 'type-fest' */
Expand Down Expand Up @@ -456,7 +457,7 @@ export class MapeoManager extends TypedEmitter {
.get()

if (!projectKeysTableResult) {
throw new Error(`NotFound: project ID ${projectPublicId} not found`)
throw new NotFoundError(`Project ID ${projectPublicId} not found`)
}

const { projectId } = projectKeysTableResult
Expand Down Expand Up @@ -896,7 +897,7 @@ export class MapeoManager extends TypedEmitter {
.get()

if (!row) {
throw new Error(`NotFound: project ID ${projectPublicId} not found`)
throw new NotFoundError(`Project ID ${projectPublicId} not found`)
}

const { keysCipher, projectId, projectInfo } = row
Expand Down
32 changes: 14 additions & 18 deletions src/mapeo-project.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ import { Logger } from './logger.js'
import { IconApi } from './icon-api.js'
import { readConfig } from './config-import.js'
import TranslationApi from './translation-api.js'
import { NotFoundError } from './errors.js'
/** @import { ProjectSettingsValue } from '@comapeo/schema' */
/** @import { CoreStorage, KeyPair, Namespace, ReplicationStream } from './types.js' */

Expand Down Expand Up @@ -616,14 +617,9 @@ export class MapeoProject extends TypedEmitter {
async $setProjectSettings(settings) {
const { projectSettings } = 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 projectSettings
.getByDocId(this.#projectId)
.catch(() => {
// project does not exist so return null
return null
})
const existing = await projectSettings.getByDocId(this.#projectId, {
mustBeFound: false,
})

if (existing) {
return extractEditableProjectSettings(
Expand Down Expand Up @@ -676,7 +672,7 @@ export class MapeoProject extends TypedEmitter {
const coreId = this.#coreManager
.getCoreByDiscoveryKey(coreDiscoveryKey)
?.key.toString('hex')
if (!coreId) throw new Error('NotFound')
if (!coreId) throw new NotFoundError()
return this.#coreOwnership.getOwner(coreId)
}

Expand Down Expand Up @@ -731,14 +727,14 @@ export class MapeoProject extends TypedEmitter {
schemaName: /** @type {const} */ ('deviceInfo'),
}

let existingDoc
try {
existingDoc = await deviceInfo.getByDocId(configCoreId)
} catch (err) {
const existingDoc = await deviceInfo.getByDocId(configCoreId, {
mustBeFound: false,
})
if (existingDoc) {
return await deviceInfo.update(existingDoc.versionId, doc)
} else {
return await deviceInfo[kCreateWithDocId](configCoreId, doc)
}

return deviceInfo.update(existingDoc.versionId, doc)
}

/** @param {boolean} isArchiveDevice */
Expand Down Expand Up @@ -900,7 +896,7 @@ export class MapeoProject extends TypedEmitter {
const fieldRefs = fieldNames.map((fieldName) => {
const fieldRef = fieldNameToRef.get(fieldName)
if (!fieldRef) {
throw new Error(
throw new NotFoundError(
`field ${fieldName} not found (referenced by preset ${value.name})})`
)
}
Expand All @@ -912,7 +908,7 @@ export class MapeoProject extends TypedEmitter {
}
const iconRef = iconNameToRef.get(iconName)
if (!iconRef) {
throw new Error(
throw new NotFoundError(
`icon ${iconName} not found (referenced by preset ${value.name})`
)
}
Expand Down Expand Up @@ -959,7 +955,7 @@ export class MapeoProject extends TypedEmitter {
})
)
} else {
throw new Error(
throw new NotFoundError(
`docRef for ${value.docRefType} with name ${name} not found`
)
}
Expand Down
73 changes: 39 additions & 34 deletions src/roles.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,33 @@ export const CREATOR_ROLE = {
},
}

/**
* @type {Role<typeof BLOCKED_ROLE_ID>}
*/
const BLOCKED_ROLE = {
roleId: BLOCKED_ROLE_ID,
name: 'Blocked',
docs: mapObject(currentSchemaVersions, (key) => {
return [
key,
{
readOwn: false,
writeOwn: false,
readOthers: false,
writeOthers: false,
},
]
}),
roleAssignment: [],
sync: {
auth: 'blocked',
config: 'blocked',
data: 'blocked',
blobIndex: 'blocked',
blob: 'blocked',
},
}

/**
* This is the role assumed for a device when no role record can be found. This
* can happen when an invited device did not manage to sync with the device that
Expand Down Expand Up @@ -166,29 +193,7 @@ export const ROLES = {
blob: 'allowed',
},
},
[BLOCKED_ROLE_ID]: {
roleId: BLOCKED_ROLE_ID,
name: 'Blocked',
docs: mapObject(currentSchemaVersions, (key) => {
return [
key,
{
readOwn: false,
writeOwn: false,
readOthers: false,
writeOthers: false,
},
]
}),
roleAssignment: [],
sync: {
auth: 'blocked',
config: 'blocked',
data: 'blocked',
blobIndex: 'blocked',
blob: 'blocked',
},
},
[BLOCKED_ROLE_ID]: BLOCKED_ROLE,
[LEFT_ROLE_ID]: {
roleId: LEFT_ROLE_ID,
name: 'Left',
Expand Down Expand Up @@ -264,12 +269,10 @@ export class Roles extends TypedEmitter {
* @returns {Promise<Role>}
*/
async getRole(deviceId) {
/** @type {string} */
let roleId
try {
const roleAssignment = await this.#dataType.getByDocId(deviceId)
roleId = roleAssignment.roleId
} catch (e) {
const roleAssignment = await this.#dataType.getByDocId(deviceId, {
mustBeFound: false,
})
if (!roleAssignment) {
// The project creator will have the creator role
const authCoreId = await this.#coreOwnership.getCoreId(deviceId, 'auth')
if (authCoreId === this.#projectCreatorAuthCoreId) {
Expand All @@ -280,8 +283,10 @@ export class Roles extends TypedEmitter {
return NO_ROLE
}
}

const { roleId } = roleAssignment
if (!isRoleId(roleId)) {
return ROLES[BLOCKED_ROLE_ID]
return BLOCKED_ROLE
}
return ROLES[roleId]
}
Expand Down Expand Up @@ -381,9 +386,9 @@ export class Roles extends TypedEmitter {
}
}

const existingRoleDoc = await this.#dataType
.getByDocId(deviceId)
.catch(() => null)
const existingRoleDoc = await this.#dataType.getByDocId(deviceId, {
mustBeFound: false,
})

if (existingRoleDoc) {
await this.#dataType.update(
Expand All @@ -403,7 +408,7 @@ export class Roles extends TypedEmitter {
}
}

async #isProjectCreator() {
#isProjectCreator() {
const ownAuthCoreId = this.#coreManager
.getWriterCore('auth')
.key.toString('hex')
Expand Down
13 changes: 4 additions & 9 deletions src/translation-api.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { and, sql } from 'drizzle-orm'
import { kCreateWithDocId, kSelect } from './datatype/index.js'
import { hashObject } from './utils.js'
import { NotFoundError } from './errors.js'
import { omit } from './lib/omit.js'
/** @import { Translation, TranslationValue } from '@comapeo/schema' */
/** @import { SetOptional } from 'type-fest' */
Expand Down Expand Up @@ -50,15 +49,11 @@ export default class TranslationApi {
async put(value) {
const identifiers = omit(value, ['message'])
const docId = hashObject(identifiers)
try {
const doc = await this.#dataType.getByDocId(docId)
const doc = await this.#dataType.getByDocId(docId, { mustBeFound: false })
if (doc) {
return await this.#dataType.update(doc.versionId, value)
} catch (e) {
if (e instanceof NotFoundError) {
return await this.#dataType[kCreateWithDocId](docId, value)
} else {
throw new Error(`Error on translation ${e}`)
}
} else {
return await this.#dataType[kCreateWithDocId](docId, value)
}
}

Expand Down
Loading

0 comments on commit a4067fe

Please sign in to comment.