# 2022-11-25 ssb-submissions design ## Example Usage ```javascript // ssb // artefactCrut // could come from ssb.artefact.create ssb.submission.registerHandler(isArtefact, artefactCrut) ssb.submission.create({ id, { date: '2022-04-XX' }, cb) // looks up record // finds type "artefact", sees we have a registered artefactCrut, // so knows how to validate/ execute stuff ``` Problems: 1. simulating a create/update - Some things let you do a dry run that show you what the output would be Ideas: 1. submitting a tombstone! ## API ### ssb.submission.registerHandler(crut) register a handler for a message type crut - crut instance (includes spec for record - has `type` and `isRoot` / `isUpdate`) ### ssb.submission.proposeEdit(recordId, details, { comment }, cb) - recordId - id of a record you're wanting to edit. empty if you're proposing a new record ### ssb.submission.proposeNew(type, details, { comment }, cb) - type - the type of the record being changed/created - details - Fields (that match the recordId) filled in with proposed changes ### ssb.submission.read(id) takes submission id, calls back with submission record - accepted or not, details etc ### ssb.submission.showProposalResult(id, cb) - id : submission id for when we want to see what would happen if you applied the edit. - algorithm - 1. read the current record state - 2. "predict" the record state if this submission was applied - 3. highlight diff (changing fields) - 1. The recordId is null - 2. "predict" what the new thing would look like - 3. Wouldn't highlight diff ### ssb.submission.accept(id, { comment }, cb) - id - submission id - cb - callback of signature (err, recordEditId) ### ssb.submission.reject(id, { comment }, cb) - id - submission id - cb - callback of signature (err) ### ssb.submission.tombstone ### ssb.submission.list --- Register handler pseudocode ```javascript const submissionCrut = new CRUT(ssb, submissionSpec) const handlers = [] const findCrut = (content) => { const handler = handlers.find(h => h[0]({ type })) if (handler) return handler[1] } const API = { registerHandler (crut) { const isType = content => content.type === crut.spec.type handlers.push([isType, crut]) }, proposeNew (type, details, { comment }, cb) { const crut = findCrut({ type }) if (!crut) return cb(new Error('unknown type: ' + type)) const isValid = crut.isRoot // check it's a well formed if (!isValid(details)) return cb(new Error(isValid.errorString)) const content = { id: null recordType: type, submission: details, comment } submissionCrut.create(content, cb) // publish message of type: submission/artefact }, proposeEdit (recordId, details, { comment }, cb) { if(!recordId) return cb(new Error('Invalid recordId: ' + recordId)) const targetRecord = lookUp(recordId) // is there something that can lookup a record of any type? if(!targetRecord) // not found, tombstoned, etc return cb(new Error('Invalid record: ' + recordId)) const crut = findCrut({ targetRecord.type }) // Is there a lookup that can get the crut of a record directly? if (!crut) return cb(new Error('unknown type: ' + targetRecord.type)) if (!crut.isValid(details) || !crut.isValidNextStep(details)) return cb(new Error(isValid.errorString)) const content = { id: recordId recordType: type, submission: details, comment } submissionCrut.create(content, cb) } } ``` --- ### Test cases: Simple: - [x] - ProposeNew,Edit,Tombstone - [x] - Accepting/rejecting proposal - [x] - Proposing identity new/edit - [ ] - ~~checking other proposal status: pending/invalid/obsolete/viewed...~~ - [ ] - ~~Kaitiaki creating submission~~ - [ ] - Reading submission - [ ] - Details - [ ] - type - [ ] - status - [ ] - approvedBy - [ ] - Differences (similar to details but uses simulation) RegisterHandler - [ ] - without crut, - [ ] - with incorrect crut, - [ ] - with multiple crut, - [ ] - with profile (not unique type) Expect errors: - [x] - Editing a profile that doesn't exist - [x] - Editing a profile that hasn't been recieved by kaitiaki - [ ] - Editing a profile from a different group - [x] - Editing null recordId - [x] - Invalid details - [ ] - Invalid nextState - [ ] - Proposal that doesn't resolve conflict - [ ] - complex: kaitiaki having different tips compared to submitter - [x] - Invalid type - [ ] - editing tombstoned profile - [x] - Accepting a submission that has been tombstoned - [x] - Accepting a submission that doesn't exist Complex: - [ ] - Rejecting an already accepted proposal (and vis versa) - [ ] - crut with special isValidNextStep - [ ] - Multiple kaitiaki accepting/rejecting - [ ] - Non-kaitiaki accepting/declining - [ ] - Simultaneous accept+reject, i.e. multiple tips - [ ] - then merging tips - [ ] - Simulating/reading rejected/tombstoned/invalid submissions