Skip to content

Commit

Permalink
fix: Change prop names and type names [5/6] (#77)
Browse files Browse the repository at this point in the history
* fix!: id -> docId version -> versionId

* chore!: rename type exports

* Add schemaName to MapeoCommon

* fix: Simplify exported types

This improves type hints in editors

* Avoid double Position export for observation.ts

This was caused by (probably a bug in json-schema-ts) that
caused the referenced definition to be duplicated.

* fix: Strict types for validate function

* fix: fix type error

* Release v3.0.0-next.4 (#86)

Co-authored-by: gmaclennan <actions@users.noreply.github.com>

---------

Co-authored-by: optic-release-automation[bot] <94357573+optic-release-automation[bot]@users.noreply.github.com>
Co-authored-by: gmaclennan <actions@users.noreply.github.com>
  • Loading branch information
3 people committed Aug 1, 2023
1 parent e3e2780 commit e8220f7
Show file tree
Hide file tree
Showing 14 changed files with 131 additions and 86 deletions.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@mapeo/schema",
"version": "3.0.0-next.3",
"version": "3.0.0-next.4",
"description": "JSON schema and flow types for Mapeo",
"main": "dist/index.js",
"type": "module",
Expand Down
2 changes: 1 addition & 1 deletion proto/common/v1.proto
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import "options.proto";

message Common_1 {
// 32-byte random generated number
optional bytes id = 1 [(required) = true];
optional bytes docId = 1 [(required) = true];
message Link {
bytes coreId = 1;
int32 seq = 2;
Expand Down
17 changes: 14 additions & 3 deletions schema/common/v1.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,18 @@
"description": "These properties are shared by all objects in the Mapeo database.",
"type": "object",
"properties": {
"id": {
"docId": {
"description": "Hex-encoded 32-byte buffer",
"type": "string"
},
"version": {
"versionId": {
"description": "id (hex-encoded 32-byte buffer) and core sequence number, separated by '/'",
"type": "string"
},
"schemaName": {
"description": "Name of Mapeo data type / schema",
"type": "string"
},
"createdAt": {
"description": "RFC3339-formatted datetime of when the first version of the element was created",
"type": "string",
Expand All @@ -32,5 +36,12 @@
}
}
},
"required": ["id", "createdAt", "updatedAt", "links", "version"]
"required": [
"docId",
"createdAt",
"schemaName",
"updatedAt",
"links",
"versionId"
]
}
6 changes: 2 additions & 4 deletions schema/observation/v5.json
Original file line number Diff line number Diff line change
Expand Up @@ -155,12 +155,10 @@
"default": false
},
"position": {
"$ref": "#/definitions/position",
"description": "Details of the position recorded for the observation"
"$ref": "#/definitions/position"
},
"lastSavedPosition": {
"$ref": "#/definitions/position",
"description": "Details of the last saved position when the observation was recorded - useful if position is not recorded"
"$ref": "#/definitions/position"
},
"positionProvider": {
"description": "Details of the location providers that were available on the device when the observation was recorded",
Expand Down
62 changes: 50 additions & 12 deletions scripts/lib/generate-jsonschema-ts.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ import { capitalize } from './utils.js'
/**
* Returns generated typescript definitions for JSONSchemas
*
* @param {ReturnType<import('./parse-config').parseConfig>} config
* @param {Record<string, import('json-schema').JSONSchema7>} jsonSchemas
* @param {ReturnType<import('./parse-config.js').parseConfig>} config
* @param {ReturnType<import('./read-json-schema.js').readJSONSchema>} jsonSchemas
*/
export async function generateJSONSchemaTS(config, jsonSchemas) {
/** @type {Record<string, string>} */
const typescriptDefs = {}
for (const [schemaName, jsonSchema] of Object.entries(jsonSchemas)) {
for (const [schemaName, jsonSchema] of Object.entries(jsonSchemas.values)) {
// @ts-ignore
const ts = await compile(jsonSchema, capitalize(schemaName), {
additionalProperties: false,
Expand All @@ -22,27 +22,65 @@ export async function generateJSONSchemaTS(config, jsonSchemas) {

const indexLines = []

for (const schemaName of Object.keys(jsonSchemas)) {
for (const schemaName of Object.keys(jsonSchemas.values)) {
const typeName = capitalize(schemaName)
indexLines.push(`import { type ${typeName} } from './${schemaName}.js'`)
const asName = '_' + typeName

indexLines.push(
`import { type ${typeName} as ${asName} } from './${schemaName}.js'`
)
}

indexLines.push('')
indexLines.push('export type JsonSchemaTypes = ')
indexLines.push(
'',
'export type MapeoCommon = Simplify<_Common>',
'',
'type Simplify<T> = {[KeyType in keyof T]: T[KeyType]} & {};',
''
)

for (const schemaName of Object.keys(jsonSchemas)) {
for (const [schemaName, schema] of Object.entries(jsonSchemas.values)) {
if (schemaName === 'common') continue
const typeName = capitalize(schemaName)
indexLines.push(` | ${typeName}`)
const interfaceName = '_' + typeName
const valueName = getValueName(schemaName)
if (schema.description) indexLines.push(`/** ${schema.description} */`)
indexLines.push(
`export type ${typeName} = Simplify<${interfaceName} & _Common>`
)
if (schema.description) indexLines.push(`/** ${schema.description} */`)
// Unwrap generated TS, from an interface to a type alias, for improved type
// hints and to aide assignability see
// https://github.com/sindresorhus/type-fest/blob/main/source/simplify.d.ts
indexLines.push(`export type ${valueName} = Simplify<${interfaceName}>`)
}

indexLines.push('')
indexLines.push('', 'export type MapeoDoc = ')

for (const schemaName of Object.keys(jsonSchemas)) {
for (const schemaName of Object.keys(jsonSchemas.values)) {
if (schemaName === 'common') continue
const typeName = capitalize(schemaName)
indexLines.push(`export { ${typeName} }`)
indexLines.push(` | ${typeName}`)
}

indexLines.push('', 'export type MapeoValue = ')

for (const schemaName of Object.keys(jsonSchemas.values)) {
if (schemaName === 'common') continue
const typeName = getValueName(schemaName)
indexLines.push(` | ${typeName}`)
}

indexLines.push('')

typescriptDefs.index = indexLines.join('\n') + '\n'

return typescriptDefs
}

/** @param {string} schemaName */
function getValueName(schemaName) {
return schemaName === 'common'
? 'MapeoCommon'
: capitalize(schemaName) + 'Value'
}
9 changes: 5 additions & 4 deletions scripts/lib/generate-validations.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,17 @@ import standaloneCode from 'ajv/dist/standalone/index.js'
/**
* Returns generated code for validation functions
*
* @param {ReturnType<import('./parse-config').parseConfig>} config
* @param {Record<string, import('json-schema').JSONSchema7>} jsonSchemas
* @param {ReturnType<import('./parse-config.js').parseConfig>} config
* @param {ReturnType<import('./read-json-schema.js').readJSONSchema>} jsonSchemas
*/
export function generateValidations(config, jsonSchemas) {
const schemas = Object.entries(jsonSchemas)
const schemas = Object.entries(jsonSchemas.values)

const schemaExports = schemas.reduce((acc, [schemaName, jsonSchema]) => {
if (!jsonSchema['$id']) throw new Error(`Missing $id prop on ${schemaName}`)
acc[schemaName] = jsonSchema['$id']
return acc
}, {})
}, /** @type {Record<string, string>} */ ({}))

// compile schemas
const ajv = new Ajv({
Expand Down
32 changes: 16 additions & 16 deletions scripts/lib/read-json-schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ function readJSON(relativeFilepath) {
* Returns the most recent version of JSONSchema files in `schema/**` with
* properties from schema/common/v1.json merged
*
* @param {ReturnType<import('./parse-config').parseConfig>} config
* @param {ReturnType<import('./parse-config.js').parseConfig>} config
*/
export function readJSONSchema({ currentSchemaVersions }) {
const jsonSchemaFiles = glob.sync(`schema/!(common)/*.json`, {
Expand All @@ -24,6 +24,7 @@ export function readJSONSchema({ currentSchemaVersions }) {
})

const common = readJSON('./schema/common/v1.json')
common.properties.schemaName.enum = []

const jsonSchemaDefs = jsonSchemaFiles.map((filepath) => {
/** @type {import('json-schema').JSONSchema7} */
Expand All @@ -40,34 +41,33 @@ export function readJSONSchema({ currentSchemaVersions }) {
return {
schemaName,
schemaVersion,
jsonSchema: mergeCommon(jsonSchema, common),
jsonSchema,
}
})

/** @type {Record<string, import('json-schema').JSONSchema7>} schemaName: JSONSchema */
const jsonSchemas = {}
const merged = {}
/** @type {Record<string, import('json-schema').JSONSchema7>} schemaName: JSONSchema */
const values = {
common,
}

for (const { schemaName, schemaVersion, jsonSchema } of jsonSchemaDefs) {
if (schemaVersion !== currentSchemaVersions[schemaName]) continue
jsonSchemas[schemaName] = jsonSchema
common.properties.schemaName.enum.push(schemaName)
values[schemaName] = jsonSchema
merged[schemaName] = mergeCommon(jsonSchema, common)
}

for (const schemaName of Object.keys(currentSchemaVersions)) {
if (!jsonSchemas[schemaName]) {
if (!values[schemaName]) {
throw new Error(
`Missing JSON schema def for ${schemaName} v${currentSchemaVersions[schemaName]}`
)
}
}

return jsonSchemas
}

function removeDuplicates(arr, elem) {
if (!arr.includes(elem)) {
arr.push(elem)
}
return arr
return { merged, values }
}

/**
Expand All @@ -77,13 +77,13 @@ function removeDuplicates(arr, elem) {
* @returns {import('json-schema').JSONSchema7}
*/
function mergeCommon(schema, common) {
const required = [
const required = new Set([
...(schema.required || []),
...(common.required || []),
].reduce(removeDuplicates, [])
])
return {
...schema,
required,
required: Array.from(required),
properties: {
...common.properties,
...schema.properties,
Expand Down
4 changes: 2 additions & 2 deletions src/decode.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ProtoTypes } from './proto/types.js'
import {
type JsonSchemaTypes,
type MapeoDoc,
type ProtoTypesWithSchemaInfo,
type VersionObj,
type SchemaName,
Expand Down Expand Up @@ -35,7 +35,7 @@ for (const [schemaName, dataTypeId] of Object.entries(dataTypeIds) as Array<
* @param buf Buffer to be decoded
* @param versionObj public key (coreId) of the core where this block is stored, and the index (seq) of the block in the core.
* */
export function decode(buf: Buffer, versionObj: VersionObj): JsonSchemaTypes {
export function decode(buf: Buffer, versionObj: VersionObj): MapeoDoc {
const schemaDef = decodeBlockPrefix(buf)

const encodedMsg = buf.subarray(
Expand Down
11 changes: 2 additions & 9 deletions src/encode.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
import {
JsonSchemaTypes,
OmitUnion,
SchemaName,
ValidSchemaDef,
} from './types.js'
import { MapeoDoc, OmitUnion, SchemaName, ValidSchemaDef } from './types.js'
import { currentSchemaVersions, dataTypeIds } from './config.js'
// @ts-ignore
import * as cenc from 'compact-encoding'
Expand All @@ -20,9 +15,7 @@ import {
* 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<JsonSchemaTypes, 'version'>
): Buffer {
export function encode(mapeoDoc: OmitUnion<MapeoDoc, 'version'>): Buffer {
const { schemaName } = mapeoDoc
const schemaVersion = currentSchemaVersions[schemaName]
const schemaDef = { schemaName, schemaVersion }
Expand Down
18 changes: 9 additions & 9 deletions src/lib/decode-conversions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ import {
type TagValue_1_PrimitiveValue,
} from '../proto/tags/v1.js'
import {
type JsonSchemaTypes,
type MapeoDoc,
type ProtoTypesWithSchemaInfo,
type VersionObj,
type SchemaName,
type FilterBySchemaName,
type JsonSchemaCommon,
type MapeoCommon,
type TagValuePrimitive,
type JsonTagValue,
} from '../types.js'
Expand All @@ -20,7 +20,7 @@ import {
type ConvertFunction<TSchemaName extends SchemaName> = (
message: Extract<ProtoTypesWithSchemaInfo, { schemaName: TSchemaName }>,
versionObj: VersionObj
) => FilterBySchemaName<JsonSchemaTypes, TSchemaName>
) => FilterBySchemaName<MapeoDoc, TSchemaName>

export const convertProject: ConvertFunction<'project'> = (
message,
Expand Down Expand Up @@ -52,7 +52,7 @@ export const convertObservation: ConvertFunction<'observation'> = (
}
}

type FieldOptions = FilterBySchemaName<JsonSchemaTypes, 'field'>['options']
type FieldOptions = FilterBySchemaName<MapeoDoc, 'field'>['options']

export const convertField: ConvertFunction<'field'> = (message, versionObj) => {
const { common, schemaVersion, ...rest } = message
Expand Down Expand Up @@ -87,7 +87,7 @@ export const convertField: ConvertFunction<'field'> = (message, versionObj) => {
}

type JsonSchemaPresetGeomItem = FilterBySchemaName<
JsonSchemaTypes,
MapeoDoc,
'preset'
>['geometry'][number]

Expand Down Expand Up @@ -172,14 +172,14 @@ function convertTagPrimitive({
function convertCommon(
common: ProtoTypesWithSchemaInfo['common'],
versionObj: VersionObj
): JsonSchemaCommon {
if (!common || !common.id || !common.createdAt || !common.updatedAt) {
): Omit<MapeoCommon, 'schemaName'> {
if (!common || !common.docId || !common.createdAt || !common.updatedAt) {
throw new Error('Missing required common properties')
}

return {
id: common.id.toString('hex'),
version: versionObjToString(versionObj),
docId: common.docId.toString('hex'),
versionId: versionObjToString(versionObj),
links: common.links.map(versionObjToString),
createdAt: common.createdAt,
updatedAt: common.updatedAt,
Expand Down
Loading

0 comments on commit e8220f7

Please sign in to comment.