Skip to content

Commit

Permalink
chore: make CoreOwnership core IDs required
Browse files Browse the repository at this point in the history
This change:

- Marks all `CoreOwnership` core IDs `required` in the proto
- Gives each core ID a minimum length in the schema
- Validates this length when decoding
- Adds a test for the above
  • Loading branch information
EvanHahn committed Sep 16, 2024
1 parent 149845f commit fa5ed3d
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 13 deletions.
12 changes: 6 additions & 6 deletions proto/coreOwnership/v1.proto
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ message CoreOwnership_1 {

Common_1 common = 1;

bytes authCoreId = 5;
bytes configCoreId = 6;
bytes dataCoreId = 7;
bytes blobCoreId = 8;
bytes blobIndexCoreId = 9;
CoreSignatures coreSignatures = 10;
bytes authCoreId = 5 [(required) = true];
bytes configCoreId = 6 [(required) = true];
bytes dataCoreId = 7 [(required) = true];
bytes blobCoreId = 8 [(required) = true];
bytes blobIndexCoreId = 9 [(required) = true];
CoreSignatures coreSignatures = 10 [(required) = true];
bytes identitySignature = 11;

message CoreSignatures {
Expand Down
15 changes: 10 additions & 5 deletions schema/coreOwnership/v1.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,28 @@
},
"authCoreId": {
"type": "string",
"description": "Hex-encoded key of auth store writer core"
"description": "Hex-encoded key of auth store writer core",
"minLength": 1
},
"configCoreId": {
"type": "string",
"description": "Hex-encoded key of config store writer core"
"description": "Hex-encoded key of config store writer core",
"minLength": 1
},
"dataCoreId": {
"type": "string",
"description": "Hex-encoded key of data store writer core"
"description": "Hex-encoded key of data store writer core",
"minLength": 1
},
"blobCoreId": {
"type": "string",
"description": "Hex-encoded key of blob store writer core"
"description": "Hex-encoded key of blob store writer core",
"minLength": 1
},
"blobIndexCoreId": {
"type": "string",
"description": "Hex-encoded key of blobIndex store writer core"
"description": "Hex-encoded key of blobIndex store writer core",
"minLength": 1
}
},
"required": [
Expand Down
24 changes: 22 additions & 2 deletions src/lib/decode-conversions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import {
type JsonTagValue,
type MapeoDocDecode,
} from '../types.js'
import { ExhaustivenessError, VersionIdObject, getVersionId } from './utils.js'
import type { Icon, Observation, Track } from '../index.js'
import type {
Observation_1_Attachment,
Expand All @@ -31,6 +30,12 @@ import type { Track_1_Position } from '../proto/track/v1.js'
import { ProjectSettings_1_ConfigMetadata } from '../proto/projectSettings/v1.js'
import { ProjectSettings } from '../schema/projectSettings.js'
import type { Position } from '../schema/observation.js'
import {
assert,
ExhaustivenessError,
getVersionId,
VersionIdObject,
} from './utils.js'

/** Function type for converting a protobuf type of any version for a particular
* schema name, and returning the most recent JSONSchema type */
Expand All @@ -39,6 +44,14 @@ type ConvertFunction<TSchemaName extends SchemaName> = (
versionObj: VersionIdObject
) => FilterBySchemaName<MapeoDocDecode, TSchemaName>

function ensure(
condition: unknown,
objectName: string,
propertyName: string
): asserts condition {
assert(condition, `${objectName} missing required property ${propertyName}`)
}

export const convertProjectSettings: ConvertFunction<'projectSettings'> = (
message,
versionObj
Expand Down Expand Up @@ -223,8 +236,15 @@ export const convertCoreOwnership: ConvertFunction<'coreOwnership'> = (
dataCoreId,
blobCoreId,
blobIndexCoreId,
coreSignatures,
...rest
} = message
ensure(coreSignatures, 'coreOwnership', 'coreSignatures')
ensure(authCoreId.byteLength, 'coreOwnership', 'authCoreId')
ensure(configCoreId.byteLength, 'coreOwnership', 'configCoreId')
ensure(dataCoreId.byteLength, 'coreOwnership', 'dataCoreId')
ensure(blobCoreId.byteLength, 'coreOwnership', 'blobCoreId')
ensure(blobIndexCoreId.byteLength, 'coreOwnership', 'blobIndexCoreId')
const jsonSchemaCommon = convertCommon(common, versionObj)
return {
...jsonSchemaCommon,
Expand All @@ -234,7 +254,7 @@ export const convertCoreOwnership: ConvertFunction<'coreOwnership'> = (
dataCoreId: dataCoreId.toString('hex'),
blobCoreId: blobCoreId.toString('hex'),
blobIndexCoreId: blobIndexCoreId.toString('hex'),
coreSignatures: message.coreSignatures,
coreSignatures,
}
}

Expand Down
4 changes: 4 additions & 0 deletions src/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ export function getOwn<T extends object, K extends keyof T>(
return Object.hasOwn(obj, key) ? obj[key] : undefined
}

export function assert(condition: unknown, message: string): asserts condition {
if (!condition) throw new Error(message)
}

export class ExhaustivenessError extends Error {
constructor(value: never) {
super(`Exhaustiveness check failed. ${value} should be impossible`)
Expand Down
19 changes: 19 additions & 0 deletions test/fixtures/bad-docs.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,25 @@ export const badDocs = [
deleted: false,
},
},
{
text: 'core ownership with empty core ID',
/** @type {import('../../dist/index.js').CoreOwnership} */
doc: {
docId: cachedValues.docId,
versionId: cachedValues.versionId,
originalVersionId: cachedValues.versionId,
schemaName: 'coreOwnership',
createdAt: cachedValues.createdAt,
updatedAt: cachedValues.updatedAt,
links: [],
deleted: false,
authCoreId: cachedValues.coreId,
configCoreId: '',
dataCoreId: cachedValues.coreId,
blobCoreId: cachedValues.coreId,
blobIndexCoreId: cachedValues.coreId,
},
},
{
text: 'role doc with empty roleId',
/** @type {import('../../dist/index.js').Role} */
Expand Down

0 comments on commit fa5ed3d

Please sign in to comment.