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>(
RestCommands.Instance,
RestMethods.Create,
RestMethods.Post,
CreateInstancePayloadSchema,
)
const updateInstance = mkRest<UpdateInstancePayload, UpdateInstanceResult>(
RestCommands.Instance,
RestMethods.Update,
RestMethods.Put,
UpdateInstancePayloadSchema,
)

View File

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

View File

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

View File

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

View File

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

View File

@ -2,7 +2,7 @@
/*
{
"instanceId": "kz4ngg77eaw1ho0",
"id": "kz4ngg77eaw1ho0",
"fields": {
"maintenance": true
"name": '',
@ -12,12 +12,11 @@
*/
routerAdd(
'PUT',
'/api/instance',
'/api/instance/:id',
(c) => {
console.log(`***TOP OF PUt`)
console.log(`***TOP OF PUT`)
let data = new DynamicModel({
// describe the shape of the fields to read (used also as initial values)
instanceId: '',
id: '',
fields: {
maintenance: null,
name: null,
@ -27,21 +26,28 @@ routerAdd(
})
c.bind(data)
console.log(`***After bind`)
// This is necessary for destructuring to work correctly
data = JSON.parse(JSON.stringify(data))
const id = c.pathParam('id')
const {
instanceId,
fields: { maintenance, name, version, secrets },
} = data
console.log(
`***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
console.log(`***authRecord`, JSON.stringify(authRecord))
@ -53,22 +59,23 @@ routerAdd(
}
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) {
acc[key] = value
}
return acc
}, {})
console.log(`***sanitized`, JSON.stringify(sanitized))
return sanitized
}
console.log(`***original`, JSON.stringify(data))
const sanitized = cleanObject({
subdomain: name,
version,
maintenance,
secrets,
})
console.log(`***sanitized`, JSON.stringify(sanitized))
const form = new RecordUpsertForm($app, record)
form.loadData(sanitized)

View File

@ -1,5 +1,6 @@
import Ajv, { JSONSchemaType } from 'ajv'
import type pocketbaseEs from 'pocketbase'
import { ClientResponseError } from 'pocketbase'
import type { JsonObject } from 'type-fest'
import { LoggerService } from '../Logger'
import { RestCommands, RestMethods } from '../schema'
@ -28,13 +29,41 @@ export const createRestHelper = (config: RestHelperConfig) => {
if (!validator(payload)) {
throw new Error(`Invalid REST payload: ${validator.errors}`)
}
const _payload = { ...payload }
const res = await client.send(`/api/${cmd}`, {
method: method,
body: payload,
const url = `/api/${cmd}${
method === RestMethods.Post ? '' : '/:id'
}`.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 { InstanceId, Semver } from '../types'
import { InstanceFields } from '../Instance'
import { InstanceId } from '../types'
export type UpdateInstancePayload = {
instanceId: InstanceId
fields: {
subdomain?: string
maintenance?: boolean
version?: Semver
secrets?: {
[_: string]: string
}
}
id: InstanceId
fields: Partial<
Pick<
InstanceFields,
'maintenance' | 'secrets' | 'subdomain' | 'syncAdmin' | 'version'
>
>
}
export const SECRET_KEY_REGEX = /^[A-Z][A-Z0-9_]*$/
@ -24,10 +23,11 @@ export const UpdateInstancePayloadSchema: JSONSchemaType<UpdateInstancePayload>
{
type: 'object',
properties: {
instanceId: { type: 'string' },
id: { type: 'string' },
fields: {
type: 'object',
properties: {
syncAdmin: { type: 'boolean', nullable: true },
subdomain: { type: 'string', nullable: true },
maintenance: { type: 'boolean', nullable: true },
version: {
@ -47,6 +47,6 @@ export const UpdateInstancePayloadSchema: JSONSchemaType<UpdateInstancePayload>
},
},
},
required: ['instanceId', 'fields'],
required: ['id', 'fields'],
additionalProperties: false,
}

View File

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