chore: REST refactor

This commit is contained in:
Ben Allfree 2023-11-14 16:57:35 -08:00
parent 881c42ddcb
commit e09e80fa4f
9 changed files with 73 additions and 37 deletions

View File

@ -100,13 +100,13 @@ export const createPocketbaseClient = (config: PocketbaseClientConfig) => {
const createInstance = mkRest<CreateInstancePayload, CreateInstanceResult>( const createInstance = mkRest<CreateInstancePayload, CreateInstanceResult>(
RestCommands.Instance, RestCommands.Instance,
RestMethods.Create, RestMethods.Post,
CreateInstancePayloadSchema, CreateInstancePayloadSchema,
) )
const updateInstance = mkRest<UpdateInstancePayload, UpdateInstanceResult>( const updateInstance = mkRest<UpdateInstancePayload, UpdateInstanceResult>(
RestCommands.Instance, RestCommands.Instance,
RestMethods.Update, RestMethods.Put,
UpdateInstancePayloadSchema, UpdateInstancePayloadSchema,
) )

View File

@ -14,7 +14,7 @@
const isChecked = target.checked const isChecked = target.checked
// Update the database with the new value // Update the database with the new value
updateInstance({ instanceId: id, fields: { maintenance: isChecked } }).then( updateInstance({ id, fields: { maintenance: isChecked } }).then(
() => 'saved', () => 'saved',
) )
} }

View File

@ -40,7 +40,7 @@
// If they select yes, then update the version in pocketbase // If they select yes, then update the version in pocketbase
if (confirmVersionChange) { if (confirmVersionChange) {
updateInstance({ updateInstance({
instanceId: id, id,
fields: { fields: {
subdomain: instanceNameValidation, subdomain: instanceNameValidation,
}, },

View File

@ -39,7 +39,7 @@
errorMessage = '' errorMessage = ''
client() client()
.updateInstance({ .updateInstance({
instanceId: id, id,
fields: { version: selectedVersion }, fields: { version: selectedVersion },
}) })
.then(() => { .then(() => {

View File

@ -44,7 +44,7 @@
// Save to the database // Save to the database
items.upsert({ name: secretKey, value: secretValue }) items.upsert({ name: secretKey, value: secretValue })
await client().updateInstance({ await client().updateInstance({
instanceId: $instance.id, id: $instance.id,
fields: { fields: {
secrets: reduce( secrets: reduce(
$items, $items,

View File

@ -2,7 +2,7 @@
/* /*
{ {
"instanceId": "kz4ngg77eaw1ho0", "id": "kz4ngg77eaw1ho0",
"fields": { "fields": {
"maintenance": true "maintenance": true
"name": '', "name": '',
@ -12,12 +12,11 @@
*/ */
routerAdd( routerAdd(
'PUT', 'PUT',
'/api/instance', '/api/instance/:id',
(c) => { (c) => {
console.log(`***TOP OF PUt`) console.log(`***TOP OF PUT`)
let data = new DynamicModel({ let data = new DynamicModel({
// describe the shape of the fields to read (used also as initial values) id: '',
instanceId: '',
fields: { fields: {
maintenance: null, maintenance: null,
name: null, name: null,
@ -27,21 +26,28 @@ routerAdd(
}) })
c.bind(data) c.bind(data)
console.log(`***After bind`)
// This is necessary for destructuring to work correctly // This is necessary for destructuring to work correctly
data = JSON.parse(JSON.stringify(data)) data = JSON.parse(JSON.stringify(data))
const id = c.pathParam('id')
const { const {
instanceId,
fields: { maintenance, name, version, secrets }, fields: { maintenance, name, version, secrets },
} = data } = data
console.log( console.log(
`***vars`, `***vars`,
JSON.stringify({ instanceId, maintenance, name, version, secrets }), JSON.stringify({
id,
maintenance,
name,
version,
secrets,
}),
) )
const record = $app.dao().findRecordById('instances', instanceId) const record = $app.dao().findRecordById('instances', id)
const authRecord = c.get('authRecord') // empty if not authenticated as regular auth record const authRecord = c.get('authRecord') // empty if not authenticated as regular auth record
console.log(`***authRecord`, JSON.stringify(authRecord)) console.log(`***authRecord`, JSON.stringify(authRecord))
@ -53,22 +59,23 @@ routerAdd(
} }
function cleanObject(obj) { function cleanObject(obj) {
return Object.entries(obj).reduce((acc, [key, value]) => { console.log(`***original`, JSON.stringify(obj))
const sanitized = Object.entries(obj).reduce((acc, [key, value]) => {
if (value !== null && value !== undefined) { if (value !== null && value !== undefined) {
acc[key] = value acc[key] = value
} }
return acc return acc
}, {}) }, {})
console.log(`***sanitized`, JSON.stringify(sanitized))
return sanitized
} }
console.log(`***original`, JSON.stringify(data))
const sanitized = cleanObject({ const sanitized = cleanObject({
subdomain: name, subdomain: name,
version, version,
maintenance, maintenance,
secrets, secrets,
}) })
console.log(`***sanitized`, JSON.stringify(sanitized))
const form = new RecordUpsertForm($app, record) const form = new RecordUpsertForm($app, record)
form.loadData(sanitized) form.loadData(sanitized)

View File

@ -1,5 +1,6 @@
import Ajv, { JSONSchemaType } from 'ajv' import Ajv, { JSONSchemaType } from 'ajv'
import type pocketbaseEs from 'pocketbase' import type pocketbaseEs from 'pocketbase'
import { ClientResponseError } from 'pocketbase'
import type { JsonObject } from 'type-fest' import type { JsonObject } from 'type-fest'
import { LoggerService } from '../Logger' import { LoggerService } from '../Logger'
import { RestCommands, RestMethods } from '../schema' import { RestCommands, RestMethods } from '../schema'
@ -28,13 +29,41 @@ export const createRestHelper = (config: RestHelperConfig) => {
if (!validator(payload)) { if (!validator(payload)) {
throw new Error(`Invalid REST payload: ${validator.errors}`) throw new Error(`Invalid REST payload: ${validator.errors}`)
} }
const _payload = { ...payload }
const res = await client.send(`/api/${cmd}`, { const url = `/api/${cmd}${
method: method, method === RestMethods.Post ? '' : '/:id'
body: payload, }`.replace(/:([a-zA-Z]+)/g, (_, key) => {
if (!(key in _payload)) {
throw new Error(`Payload must include '${key}`)
}
const value = _payload[key]!
delete _payload[key]
return encodeURIComponent(value.toString())
}) })
dbg(res)
return res const options: any = {
method: method,
}
if (method !== RestMethods.Get) {
options.body = _payload
}
dbg({ url, options })
try {
const res = await client.send(url, options)
dbg(res)
return res
} catch (e) {
if (e instanceof ClientResponseError) {
error(`REST error: ${e.originalError}`)
throw e.originalError
}
error(`REST error: ${e}`)
throw e
}
} }
} }

View File

@ -1,16 +1,15 @@
import { JSONSchemaType } from 'ajv' import { JSONSchemaType } from 'ajv'
import { InstanceId, Semver } from '../types' import { InstanceFields } from '../Instance'
import { InstanceId } from '../types'
export type UpdateInstancePayload = { export type UpdateInstancePayload = {
instanceId: InstanceId id: InstanceId
fields: { fields: Partial<
subdomain?: string Pick<
maintenance?: boolean InstanceFields,
version?: Semver 'maintenance' | 'secrets' | 'subdomain' | 'syncAdmin' | 'version'
secrets?: { >
[_: string]: string >
}
}
} }
export const SECRET_KEY_REGEX = /^[A-Z][A-Z0-9_]*$/ export const SECRET_KEY_REGEX = /^[A-Z][A-Z0-9_]*$/
@ -24,10 +23,11 @@ export const UpdateInstancePayloadSchema: JSONSchemaType<UpdateInstancePayload>
{ {
type: 'object', type: 'object',
properties: { properties: {
instanceId: { type: 'string' }, id: { type: 'string' },
fields: { fields: {
type: 'object', type: 'object',
properties: { properties: {
syncAdmin: { type: 'boolean', nullable: true },
subdomain: { type: 'string', nullable: true }, subdomain: { type: 'string', nullable: true },
maintenance: { type: 'boolean', nullable: true }, maintenance: { type: 'boolean', nullable: true },
version: { version: {
@ -47,6 +47,6 @@ export const UpdateInstancePayloadSchema: JSONSchemaType<UpdateInstancePayload>
}, },
}, },
}, },
required: ['instanceId', 'fields'], required: ['id', 'fields'],
additionalProperties: false, additionalProperties: false,
} }

View File

@ -2,8 +2,9 @@ import Ajv from 'ajv'
import { JsonObject } from 'type-fest' import { JsonObject } from 'type-fest'
export enum RestMethods { export enum RestMethods {
Create = 'POST', Get = 'GET',
Update = 'PUT', Post = 'POST',
Put = 'PUT',
} }
export enum RestCommands { export enum RestCommands {
@ -16,4 +17,3 @@ export const ajv = new Ajv()
export * from './CreateInstance' export * from './CreateInstance'
export * from './UpdateInstance' export * from './UpdateInstance'
// gen:export