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!: replace createdBy with originalVersionId #191

Merged
merged 5 commits into from
Aug 24, 2024
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
3 changes: 1 addition & 2 deletions proto/common/v1.proto
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ message Common_1 {
repeated VersionId_1 links = 2;
google.protobuf.Timestamp createdAt = 3 [(required) = true];
google.protobuf.Timestamp updatedAt = 4 [(required) = true];
// 32-byte hash of the discovery key of a core
bytes createdBy = 5 [(required) = true];
VersionId_1 originalVersionId = 5;
bool deleted = 6 [(required) = true];
}
/* ignored fields and differences from common.json jsonSchema
Expand Down
10 changes: 5 additions & 5 deletions schema/common/v1.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@
"description": "core discovery id (hex-encoded 32-byte buffer) and core index number, separated by '/'",
"type": "string"
},
"originalVersionId": {
"description": "Version ID of the original version of this document. For the original version, matches `versionId`.",
"type": "string"
},
"schemaName": {
"description": "Name of Mapeo data type / schema",
"type": "string"
Expand All @@ -22,10 +26,6 @@
"type": "string",
"format": "date-time"
},
"createdBy": {
"description": "discovery id (hex-encoded 32-byte buffer) of the hypercore where the first version of this document is written",
"type": "string"
},
"updatedAt": {
"description": "RFC3339-formatted datetime of when this version of the element was created",
"type": "string",
Expand All @@ -51,7 +51,7 @@
"updatedAt",
"links",
"versionId",
"createdBy",
"originalVersionId",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does get confusing, because is this required or not? It seems from digidem/comapeo-core#715 that we are not storing this on creation, so reads will not include this/

Trying to fully represent the type in JSONSchema might be difficult, it might be easier to do in the TypeScript defs in this repo, maybe something like:

type CommonNew = (Omit<Common, 'links' | 'originalVersionId'> & { links: [] }) | SetRequired<Common, 'originalVersionId'>

e.g. you originalVersionId can be missing if link is an empty array, but otherwise it is required.

"deleted"
]
}
4 changes: 2 additions & 2 deletions src/decode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
type SchemaName,
type DataTypeId,
type ValidSchemaDef,
type MapeoDocInternal,
type MapeoDocDecode,
} from './types.js'

import { Decode } from './proto/index.js'
Expand Down Expand Up @@ -47,7 +47,7 @@ for (const [schemaName, dataTypeId] of Object.entries(dataTypeIds) as Array<
export function decode(
buf: Buffer,
versionObj: VersionIdObject
): MapeoDocInternal {
): MapeoDocDecode {
const schemaDef = decodeBlockPrefix(buf)

const encodedMsg = buf.subarray(
Expand Down
8 changes: 3 additions & 5 deletions src/encode.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { SetOptional } from 'type-fest'
import {
type MapeoDocInternal,
type OmitUnion,
type MapeoDocEncode,
type SchemaName,
type ValidSchemaDef,
} from './types.js'
Expand Down Expand Up @@ -28,9 +28,7 @@ import { ExhaustivenessError } from './lib/utils.js'
* Encode a an object validated against a schema as a binary protobuf prefixed
* with the encoded data type ID and schema version, to send to an hypercore.
*/
export function encode(
mapeoDoc: OmitUnion<MapeoDocInternal, 'versionId'>
): Buffer {
export function encode(mapeoDoc: MapeoDocEncode): Buffer {
const { schemaName } = mapeoDoc
const schemaVersion = currentSchemaVersions[schemaName]
const schemaDef = { schemaName, schemaVersion }
Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@ export {

export * from './schema/index.js'
export * from './schemas.js'
export { type MapeoDocInternal } from './types.js'
export { type MapeoDocDecode, type MapeoDocEncode } from './types.js'
19 changes: 15 additions & 4 deletions src/lib/decode-conversions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
type MapeoCommon,
type TagValuePrimitive,
type JsonTagValue,
type MapeoDocInternal,
type MapeoDocDecode,
} from '../types.js'
import { ExhaustivenessError, VersionIdObject, getVersionId } from './utils.js'
import type { Observation, Track } from '../index.js'
Expand All @@ -32,7 +32,7 @@ import { ProjectSettings } from '../schema/projectSettings.js'
type ConvertFunction<TSchemaName extends SchemaName> = (
message: Extract<ProtoTypesWithSchemaInfo, { schemaName: TSchemaName }>,
versionObj: VersionIdObject
) => FilterBySchemaName<MapeoDocInternal, TSchemaName>
) => FilterBySchemaName<MapeoDocDecode, TSchemaName>

export const convertProjectSettings: ConvertFunction<'projectSettings'> = (
message,
Expand Down Expand Up @@ -402,13 +402,24 @@ function convertCommon(
throw new Error('Missing required common properties')
}

const versionId = getVersionId(versionObj)

/** @type {string} */ let originalVersionId
if (common.originalVersionId) {
originalVersionId = getVersionId(common.originalVersionId)
} else if (common.links.length === 0) {
originalVersionId = versionId
} else {
throw new Error('Cannot determine original version ID; data is malformed')
}

return {
docId: common.docId.toString('hex'),
versionId: getVersionId(versionObj),
versionId,
originalVersionId,
links: common.links.map((link) => getVersionId(link)),
createdAt: common.createdAt,
updatedAt: common.updatedAt,
createdBy: common.createdBy.toString('hex'),
deleted: common.deleted,
}
}
Expand Down
18 changes: 9 additions & 9 deletions src/lib/encode-conversions.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import type { SetOptional } from 'type-fest'
import { CurrentProtoTypes } from '../proto/types.js'
import {
ProtoTypesWithSchemaInfo,
SchemaName,
MapeoCommon,
TagValuePrimitive,
JsonTagValue,
OmitUnion,
CoreOwnershipSignatures,
MapeoDocInternal,
MapeoDocEncode,
} from '../types.js'
import { TagValue_1, type TagValue_1_PrimitiveValue } from '../proto/tags/v1.js'
import { Icon } from '../schema/icon.js'
Expand All @@ -27,10 +27,7 @@ import {
/** Function type for converting a protobuf type of any version for a particular
* schema name, and returning the most recent JSONSchema type */
type ConvertFunction<TSchemaName extends SchemaName> = (
mapeoDoc: Extract<
OmitUnion<MapeoDocInternal, 'versionId'>,
{ schemaName: TSchemaName }
>
mapeoDoc: Extract<MapeoDocEncode, { schemaName: TSchemaName }>
) => CurrentProtoTypes[TSchemaName]

export const convertProjectSettings: ConvertFunction<'projectSettings'> = (
Expand Down Expand Up @@ -143,7 +140,8 @@ export const convertDeviceInfo: ConvertFunction<'deviceInfo'> = (mapeoDoc) => {
}

export const convertCoreOwnership = (
mapeoDoc: Omit<CoreOwnership, 'versionId'> & CoreOwnershipSignatures
mapeoDoc: Omit<CoreOwnership, 'versionId' | 'originalVersionId'> &
CoreOwnershipSignatures
): CurrentProtoTypes['coreOwnership'] => {
return {
common: convertCommon(mapeoDoc),
Expand Down Expand Up @@ -242,13 +240,15 @@ export const convertTrack: ConvertFunction<'track'> = (mapeoDoc) => {
}

function convertCommon(
common: Omit<MapeoCommon, 'versionId'>
common: SetOptional<MapeoCommon, 'versionId' | 'originalVersionId'>
): ProtoTypesWithSchemaInfo['common'] {
return {
docId: Buffer.from(common.docId, 'hex'),
originalVersionId: common.originalVersionId
? parseVersionId(common.originalVersionId)
: undefined,
createdAt: common.createdAt,
updatedAt: common.updatedAt,
createdBy: Buffer.from(common.createdBy, 'hex'),
links: common.links.map((link) => parseVersionId(link)),
deleted: common.deleted,
}
Expand Down
4 changes: 2 additions & 2 deletions src/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export function parseVersionId(versionId: string): VersionIdObject {
/**
* @template {import('@mapeo/schema').MapeoDoc & { forks?: string[] }} T
* @param {T} doc
* @returns {Omit<T, 'docId' | 'versionId' | 'links' | 'forks' | 'createdAt' | 'updatedAt' | 'createdBy' | 'deleted'>}
* @returns {Omit<T, 'docId' | 'versionId' | 'originalVersionId' | 'links' | 'forks' | 'createdAt' | 'updatedAt' | 'deleted'>}
*/
export function valueOf<TDoc extends MapeoDoc>(
doc: TDoc & { forks?: string[] }
Expand All @@ -71,11 +71,11 @@ export function valueOf<TDoc extends MapeoDoc>(
const {
docId,
versionId,
originalVersionId,
links,
forks,
createdAt,
updatedAt,
createdBy,
deleted,
...rest
} = doc
Expand Down
12 changes: 11 additions & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,20 @@ export type MapeoValue = FilterBySchemaName<
/** The decode and encode functions expect core ownership signatures as buffers,
* but these are not included in the JSON schema definitions because they are
* stripped before they are indexed */
export type MapeoDocInternal =
export type MapeoDocDecode =
| Exclude<MapeoDoc, CoreOwnership>
| (CoreOwnership & CoreOwnershipSignatures)

/**
* MapeoDoc for encoding does not need a versionId, and if links is an empty
* array, it does not need a originalVersionId either.
*/
export type MapeoDocEncode =
| (OmitUnion<MapeoDocDecode, 'versionId' | 'originalVersionId' | 'links'> & {
links: []
})
| OmitUnion<MapeoDocDecode, 'versionId'>

/** Union of all valid data type ids */
export type DataTypeId = Values<typeof dataTypeIds>

Expand Down
30 changes: 26 additions & 4 deletions test/fixtures/bad-docs.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,27 +13,49 @@ export const badDocs = [
doc: {
docId: cachedValues.docId,
versionId: cachedValues.versionId,
originalVersionId: cachedValues.versionId,
schemaName: 'observOtion',
createdAt: cachedValues.createdAt,
updatedAt: cachedValues.updatedAt,
createdBy: cachedValues.createdBy,
links: [],
attachments: [],
tags: {},
metadata: {},
deleted: false,
},
},
{
text: 'missing expected originalVersionId',
/** @type Omit<import('../../dist/index.js').Observation, 'originalVersionId'> */
doc: {
docId: cachedValues.docId,
versionId: cachedValues.versionId,
schemaName: 'observation',
createdAt: cachedValues.createdAt,
updatedAt: cachedValues.updatedAt,
links: [
JSON.stringify({
coreDiscoveryKey: Buffer.from('abc123'),
index: 123,
}),
],
refs: [],
attachments: [],
tags: {},
metadata: {},
deleted: false,
},
},
{
text: 'role doc with empty roleId',
/** @type {import('../../dist/index.js').Role} */
doc: {
docId: cachedValues.docId,
versionId: cachedValues.versionId,
originalVersionId: cachedValues.versionId,
schemaName: 'role',
createdAt: cachedValues.createdAt,
updatedAt: cachedValues.updatedAt,
createdBy: cachedValues.createdBy,
links: [],
roleId: '',
fromIndex: 4,
Expand All @@ -45,10 +67,10 @@ export const badDocs = [
doc: {
docId: cachedValues.docId,
versionId: cachedValues.versionId,
originalVersionId: cachedValues.versionId,
schemaName: 'icon',
createdAt: cachedValues.createdAt,
updatedAt: cachedValues.updatedAt,
createdBy: cachedValues.createdBy,
links: [],
deleted: false,
variants: [
Expand All @@ -66,10 +88,10 @@ export const badDocs = [
doc: {
docId: cachedValues.docId,
versionId: cachedValues.versionId,
originalVersionId: cachedValues.versionId,
schemaName: 'preset',
createdAt: cachedValues.createdAt,
updatedAt: cachedValues.updatedAt,
createdBy: cachedValues.createdBy,
links: [],
name: 'myPreset',
geometry: ['point'],
Expand Down
2 changes: 1 addition & 1 deletion test/fixtures/cached.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ const date = new Date().toJSON()
export const cachedValues = {
docId: randomBytes(32).toString('hex'),
versionId: `${randomBytes(32).toString('hex')}/0`,
originalVersionId: `${randomBytes(32).toString('hex')}/0`,
projectId: randomBytes(32).toString('hex'),
authorId: randomBytes(32).toString('hex'),
coreId: randomBytes(32).toString('hex'),
createdBy: randomBytes(32).toString('hex'),
createdAt: date,
updatedAt: date,
attachments: {
Expand Down
Loading
Loading