Skip to content

Commit

Permalink
Revise repo write validation for unknown Lexicons (bluesky-social#2748)
Browse files Browse the repository at this point in the history
* lexicon: validation status result from repo writes

* pds: return  validation status from repo writes, write results from applyWrites

* tidy
  • Loading branch information
devinivy authored Aug 30, 2024
1 parent 325859b commit befebc0
Show file tree
Hide file tree
Showing 25 changed files with 854 additions and 90 deletions.
49 changes: 47 additions & 2 deletions lexicons/com/atproto/repo/applyWrites.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@
},
"validate": {
"type": "boolean",
"default": true,
"description": "Can be set to 'false' to skip Lexicon schema validation of record data, for all operations."
"description": "Can be set to 'false' to skip Lexicon schema validation of record data across all operations, 'true' to require it, or leave unset to validate only for known Lexicons."
},
"writes": {
"type": "array",
Expand All @@ -37,6 +36,23 @@
}
}
},
"output": {
"encoding": "application/json",
"schema": {
"type": "object",
"required": [],
"properties": {
"results": {
"type": "array",
"items": {
"type": "union",
"refs": ["#createResult", "#updateResult", "#deleteResult"],
"closed": true
}
}
}
}
},
"errors": [
{
"name": "InvalidSwap",
Expand Down Expand Up @@ -72,6 +88,35 @@
"collection": { "type": "string", "format": "nsid" },
"rkey": { "type": "string" }
}
},
"createResult": {
"type": "object",
"required": ["uri", "cid"],
"properties": {
"uri": { "type": "string", "format": "at-uri" },
"cid": { "type": "string", "format": "cid" },
"validationStatus": {
"type": "string",
"knownValues": ["valid", "unknown"]
}
}
},
"updateResult": {
"type": "object",
"required": ["uri", "cid"],
"properties": {
"uri": { "type": "string", "format": "at-uri" },
"cid": { "type": "string", "format": "cid" },
"validationStatus": {
"type": "string",
"knownValues": ["valid", "unknown"]
}
}
},
"deleteResult": {
"type": "object",
"required": [],
"properties": {}
}
}
}
9 changes: 6 additions & 3 deletions lexicons/com/atproto/repo/createRecord.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,7 @@
},
"validate": {
"type": "boolean",
"default": true,
"description": "Can be set to 'false' to skip Lexicon schema validation of record data."
"description": "Can be set to 'false' to skip Lexicon schema validation of record data, 'true' to require it, or leave unset to validate only for known Lexicons."
},
"record": {
"type": "unknown",
Expand All @@ -50,7 +49,11 @@
"required": ["uri", "cid"],
"properties": {
"uri": { "type": "string", "format": "at-uri" },
"cid": { "type": "string", "format": "cid" }
"cid": { "type": "string", "format": "cid" },
"validationStatus": {
"type": "string",
"knownValues": ["valid", "unknown"]
}
}
}
},
Expand Down
9 changes: 6 additions & 3 deletions lexicons/com/atproto/repo/putRecord.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,7 @@
},
"validate": {
"type": "boolean",
"default": true,
"description": "Can be set to 'false' to skip Lexicon schema validation of record data."
"description": "Can be set to 'false' to skip Lexicon schema validation of record data, 'true' to require it, or leave unset to validate only for known Lexicons."
},
"record": {
"type": "unknown",
Expand All @@ -56,7 +55,11 @@
"required": ["uri", "cid"],
"properties": {
"uri": { "type": "string", "format": "at-uri" },
"cid": { "type": "string", "format": "cid" }
"cid": { "type": "string", "format": "cid" },
"validationStatus": {
"type": "string",
"knownValues": ["valid", "unknown"]
}
}
}
},
Expand Down
79 changes: 73 additions & 6 deletions packages/api/src/client/lexicons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1254,9 +1254,8 @@ export const schemaDict = {
},
validate: {
type: 'boolean',
default: true,
description:
"Can be set to 'false' to skip Lexicon schema validation of record data, for all operations.",
"Can be set to 'false' to skip Lexicon schema validation of record data across all operations, 'true' to require it, or leave unset to validate only for known Lexicons.",
},
writes: {
type: 'array',
Expand All @@ -1279,6 +1278,27 @@ export const schemaDict = {
},
},
},
output: {
encoding: 'application/json',
schema: {
type: 'object',
required: [],
properties: {
results: {
type: 'array',
items: {
type: 'union',
refs: [
'lex:com.atproto.repo.applyWrites#createResult',
'lex:com.atproto.repo.applyWrites#updateResult',
'lex:com.atproto.repo.applyWrites#deleteResult',
],
closed: true,
},
},
},
},
},
errors: [
{
name: 'InvalidSwap',
Expand Down Expand Up @@ -1336,6 +1356,47 @@ export const schemaDict = {
},
},
},
createResult: {
type: 'object',
required: ['uri', 'cid'],
properties: {
uri: {
type: 'string',
format: 'at-uri',
},
cid: {
type: 'string',
format: 'cid',
},
validationStatus: {
type: 'string',
knownValues: ['valid', 'unknown'],
},
},
},
updateResult: {
type: 'object',
required: ['uri', 'cid'],
properties: {
uri: {
type: 'string',
format: 'at-uri',
},
cid: {
type: 'string',
format: 'cid',
},
validationStatus: {
type: 'string',
knownValues: ['valid', 'unknown'],
},
},
},
deleteResult: {
type: 'object',
required: [],
properties: {},
},
},
},
ComAtprotoRepoCreateRecord: {
Expand Down Expand Up @@ -1370,9 +1431,8 @@ export const schemaDict = {
},
validate: {
type: 'boolean',
default: true,
description:
"Can be set to 'false' to skip Lexicon schema validation of record data.",
"Can be set to 'false' to skip Lexicon schema validation of record data, 'true' to require it, or leave unset to validate only for known Lexicons.",
},
record: {
type: 'unknown',
Expand Down Expand Up @@ -1401,6 +1461,10 @@ export const schemaDict = {
type: 'string',
format: 'cid',
},
validationStatus: {
type: 'string',
knownValues: ['valid', 'unknown'],
},
},
},
},
Expand Down Expand Up @@ -1778,9 +1842,8 @@ export const schemaDict = {
},
validate: {
type: 'boolean',
default: true,
description:
"Can be set to 'false' to skip Lexicon schema validation of record data.",
"Can be set to 'false' to skip Lexicon schema validation of record data, 'true' to require it, or leave unset to validate only for known Lexicons.",
},
record: {
type: 'unknown',
Expand Down Expand Up @@ -1815,6 +1878,10 @@ export const schemaDict = {
type: 'string',
format: 'cid',
},
validationStatus: {
type: 'string',
knownValues: ['valid', 'unknown'],
},
},
},
},
Expand Down
62 changes: 61 additions & 1 deletion packages/api/src/client/types/com/atproto/repo/applyWrites.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,19 @@ export interface QueryParams {}
export interface InputSchema {
/** The handle or DID of the repo (aka, current account). */
repo: string
/** Can be set to 'false' to skip Lexicon schema validation of record data, for all operations. */
/** Can be set to 'false' to skip Lexicon schema validation of record data across all operations, 'true' to require it, or leave unset to validate only for known Lexicons. */
validate?: boolean
writes: (Create | Update | Delete)[]
/** If provided, the entire operation will fail if the current repo commit CID does not match this value. Used to prevent conflicting repo mutations. */
swapCommit?: string
[k: string]: unknown
}

export interface OutputSchema {
results?: (CreateResult | UpdateResult | DeleteResult)[]
[k: string]: unknown
}

export interface CallOptions {
signal?: AbortSignal
headers?: HeadersMap
Expand All @@ -30,6 +35,7 @@ export interface CallOptions {
export interface Response {
success: boolean
headers: HeadersMap
data: OutputSchema
}

export class InvalidSwapError extends XRPCError {
Expand Down Expand Up @@ -104,3 +110,57 @@ export function isDelete(v: unknown): v is Delete {
export function validateDelete(v: unknown): ValidationResult {
return lexicons.validate('com.atproto.repo.applyWrites#delete', v)
}

export interface CreateResult {
uri: string
cid: string
validationStatus?: 'valid' | 'unknown' | (string & {})
[k: string]: unknown
}

export function isCreateResult(v: unknown): v is CreateResult {
return (
isObj(v) &&
hasProp(v, '$type') &&
v.$type === 'com.atproto.repo.applyWrites#createResult'
)
}

export function validateCreateResult(v: unknown): ValidationResult {
return lexicons.validate('com.atproto.repo.applyWrites#createResult', v)
}

export interface UpdateResult {
uri: string
cid: string
validationStatus?: 'valid' | 'unknown' | (string & {})
[k: string]: unknown
}

export function isUpdateResult(v: unknown): v is UpdateResult {
return (
isObj(v) &&
hasProp(v, '$type') &&
v.$type === 'com.atproto.repo.applyWrites#updateResult'
)
}

export function validateUpdateResult(v: unknown): ValidationResult {
return lexicons.validate('com.atproto.repo.applyWrites#updateResult', v)
}

export interface DeleteResult {
[k: string]: unknown
}

export function isDeleteResult(v: unknown): v is DeleteResult {
return (
isObj(v) &&
hasProp(v, '$type') &&
v.$type === 'com.atproto.repo.applyWrites#deleteResult'
)
}

export function validateDeleteResult(v: unknown): ValidationResult {
return lexicons.validate('com.atproto.repo.applyWrites#deleteResult', v)
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export interface InputSchema {
collection: string
/** The Record Key. */
rkey?: string
/** Can be set to 'false' to skip Lexicon schema validation of record data. */
/** Can be set to 'false' to skip Lexicon schema validation of record data, 'true' to require it, or leave unset to validate only for known Lexicons. */
validate?: boolean
/** The record itself. Must contain a $type field. */
record: {}
Expand All @@ -28,6 +28,7 @@ export interface InputSchema {
export interface OutputSchema {
uri: string
cid: string
validationStatus?: 'valid' | 'unknown' | (string & {})
[k: string]: unknown
}

Expand Down
3 changes: 2 additions & 1 deletion packages/api/src/client/types/com/atproto/repo/putRecord.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export interface InputSchema {
collection: string
/** The Record Key. */
rkey: string
/** Can be set to 'false' to skip Lexicon schema validation of record data. */
/** Can be set to 'false' to skip Lexicon schema validation of record data, 'true' to require it, or leave unset to validate only for known Lexicons. */
validate?: boolean
/** The record to write. */
record: {}
Expand All @@ -30,6 +30,7 @@ export interface InputSchema {
export interface OutputSchema {
uri: string
cid: string
validationStatus?: 'valid' | 'unknown' | (string & {})
[k: string]: unknown
}

Expand Down
Loading

0 comments on commit befebc0

Please sign in to comment.