Skip to content

Commit

Permalink
feat(dataview): support datetime types (#516)
Browse files Browse the repository at this point in the history
  • Loading branch information
DDEle authored Feb 14, 2022
1 parent 2169cdf commit dfb5801
Show file tree
Hide file tree
Showing 9 changed files with 99 additions and 20 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@ tsconfig.temp.json
/shiki
/test
/utsuho
/.yalc

todo.md
yarn.lock
yalc.lock
node_modules/
npm-debug.log
yarn-debug.log
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
"shiki",
"test",
"community/*",
".yalc/*",
".yalc/@*/*",
"packages/*",
"plugins/a11y/*",
"plugins/adapter/*",
Expand Down
7 changes: 3 additions & 4 deletions plugins/frontend/console/client/client.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { ClientConfig, Console } from '@koishijs/plugin-console'
import { Dict } from '@koishijs/utils'
import { useLocalStorage } from '@vueuse/core'
import { deserialize, serialize } from 'bson'
import { App, defineComponent, h, markRaw, reactive, ref, Ref, resolveComponent, watch } from 'vue'
import { createRouter, createWebHistory, RouteRecordNormalized, START_LOCATION } from 'vue-router'
import { Computed, Disposable, Extension, PageExtension, PageOptions, Store, ViewOptions } from '~/client'
Expand All @@ -20,7 +19,7 @@ const responseHooks: Record<string, [Function, Function]> = {}

export function send(type: string, ...args: any[]) {
const id = Math.random().toString(36).slice(2, 9)
socket.value.send(serialize({ id, type, args }))
socket.value.send(JSON.stringify({ id, type, args }))
return new Promise((resolve, reject) => {
responseHooks[id] = [resolve, reject]
setTimeout(() => {
Expand Down Expand Up @@ -60,8 +59,8 @@ receive('response', ({ id, value, error }) => {
export async function connect(endpoint: string) {
socket.value = new WebSocket(endpoint)

socket.value.onmessage = async (ev) => {
const data = deserialize(await ev.data.arrayBuffer())
socket.value.onmessage = (ev) => {
const data = JSON.parse(ev.data)
console.debug('%c', 'color:purple', data.type, data.body)
if (data.type in listeners) {
listeners[data.type](data.body)
Expand Down
1 change: 0 additions & 1 deletion plugins/frontend/console/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@
"vue-router": "^4.0.12"
},
"dependencies": {
"bson": "^4.6.1",
"open": "^8.4.0",
"uuid": "^8.3.2",
"ws": "^8.4.2"
Expand Down
10 changes: 5 additions & 5 deletions plugins/frontend/console/src/ws.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { deserialize, serialize } from 'bson'
import { App, Awaitable, coerce, Context, Dict, Logger, WebSocketLayer } from 'koishi'
import { v4 } from 'uuid'
import WebSocket from 'ws'
Expand All @@ -21,7 +20,7 @@ export class SocketHandle {
}

send(payload: any) {
this.socket.send(serialize(payload))
this.socket.send(JSON.stringify(payload))
}
}

Expand Down Expand Up @@ -53,7 +52,7 @@ class WsService extends DataService {

broadcast(type: string, body: any) {
if (!this?.layer.clients.size) return
const data = serialize({ type, body })
const data = JSON.stringify({ type, body })
this.layer.clients.forEach((socket) => socket.send(data))
}

Expand All @@ -74,12 +73,12 @@ class WsService extends DataService {
Promise.resolve(this.ctx[name]?.['get']?.()).then((value) => {
if (!value) return
const key = name.slice(8)
socket.send(serialize({ type: 'data', body: { key, value } }))
socket.send(JSON.stringify({ type: 'data', body: { key, value } }))
})
}

socket.on('message', async (data) => {
const { type, args, id } = deserialize(Array.isArray(data) ? Buffer.concat(data) : data)
const { type, args, id } = JSON.parse(data.toString())
const listener = this.listeners[type]
if (!listener) {
logger.info('unknown message:', type, ...args)
Expand All @@ -94,6 +93,7 @@ class WsService extends DataService {
const value = await listener.callback.call(handle, ...args)
return handle.send({ type: 'response', body: { id, value } })
} catch (e) {
logger.debug(e)
const error = coerce(e)
return handle.send({ type: 'response', body: { id, error } })
}
Expand Down
12 changes: 6 additions & 6 deletions plugins/frontend/dataview/client/components/data-table.vue
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,9 @@
import { Dict } from 'koishi'
import { computed, ComputedRef, nextTick, reactive, ref, watch, watchEffect } from 'vue'
import { send, store } from '~/client'
import { store } from '~/client'
import { message } from '~/components'
import { formatSize, handleError, timeStr } from '../utils'
import { formatSize, handleError, sendQuery, timeStr } from '../utils'
export interface TableStatus {
loading: boolean
Expand Down Expand Up @@ -182,7 +182,7 @@ async function updateData() {
sort: querySort,
}
// await new Promise((res) => setInterval(() => res(0), 1000))
tableData.value = await send('database/get', props.name as never, {}, modifier)
tableData.value = await sendQuery('get', props.name as never, {}, modifier)
await nextTick()
state.loading = false
}
Expand Down Expand Up @@ -384,7 +384,7 @@ async function onSubmitChanges() {
}
console.log('Update row: ', data)
// await new Promise(res => setInterval(() => res(1), 1000))
await send('database/set', props.name as never, row, data)
await sendQuery('set', props.name as never, row, data)
for (const field in validChanges.value[idx])
submitted.push({ idx, field })
Expand All @@ -409,7 +409,7 @@ async function onSubmitChanges() {
async function onDeleteRow({ row, $index }) {
state.loading = true
try {
await send('database/remove', props.name as never, row)
await sendQuery('remove', props.name as never, row)
await updateData()
message.success(`成功删除数据`)
} catch (e) {
Expand All @@ -429,7 +429,7 @@ async function onInsertRow() {
return o
}, {})
console.log('Create row: ', row)
await send('database/create', props.name as never, row)
await sendQuery('create', props.name as never, row)
await updateData()
message.success(`成功添加数据`)
for (const field in state.newRow)
Expand Down
40 changes: 40 additions & 0 deletions plugins/frontend/dataview/client/utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,45 @@
import { Query } from 'koishi'
import { send } from '~/client'
import { message } from '~/components'

export function serialize(obj: unknown): string {
if (obj instanceof Date) return `"d${obj.toJSON()}"`
return JSON.stringify(obj, (_, value) => {
if (typeof value === 'string') return 's' + value
if (typeof value === 'object') {
if (value instanceof Date) return 'd' + new Date(value).toJSON()
if (value === null) return null
const o = Array.isArray(value) ? [] : {}
for (const k in value) {
if (value[k] instanceof Date) {
o[k] = new Date(value[k])
// Remove toJson so that it won't be converted to string in recursive calls
o[k].toJSON = undefined
} else {
o[k] = value[k]
}
}
return o
}
return value
})
}

export function deserialize(str: string): unknown {
if (str === undefined) return undefined
return JSON.parse(str, (_, v) =>
typeof v === 'string'
? v[0] === 's'
? v.slice(1)
: new Date(v.slice(1))
: v,
)
}

export async function sendQuery<K extends keyof Query.Methods>(name: K, ...args: Parameters<Query.Methods[K]>): Promise<ReturnType<Query.Methods[K]>> {
return deserialize(await send(`database/${name}`, ...args.map(serialize) as any))
}

export function formatSize(size: number) {
const units = ['B', 'KB', 'MB', 'GB']
for (const idx in units) {
Expand Down
12 changes: 8 additions & 4 deletions plugins/frontend/dataview/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { Context, Dict, Model, Query, Schema } from 'koishi'
import { DataService } from '@koishijs/plugin-console'
import { resolve } from 'path'
import { deserialize, serialize } from './utils'

export type DbEvents = {
[M in keyof Query.Methods as `database/${M}`]: Query.Methods[M]
[M in keyof Query.Methods as `database/${M}`]: (...args: string[]) => Promise<string>
}

declare module '@koishijs/plugin-console' {
Expand All @@ -29,9 +30,9 @@ class DatabaseProvider extends DataService<DatabaseInfo> {

addListener<K extends keyof Query.Methods>(name: K, refresh = false) {
this.ctx.console.addListener(`database/${name}`, async (...args) => {
const result = await (this.ctx.database[name] as any)(...args)
const result = await (this.ctx.database[name] as any)(...args.map(deserialize))
if (refresh) this.refresh()
return result
return result === undefined ? result : serialize(result)
}, { authority: 4 })
}

Expand Down Expand Up @@ -59,10 +60,12 @@ class DatabaseProvider extends DataService<DatabaseInfo> {
async getInfo() {
const stats = await this.ctx.database.stats()
const result = { tables: {}, ...stats } as DatabaseInfo
const tableStats = result.tables
result.tables = {}
for (const name in this.ctx.model.config) {
result.tables[name] = {
...this.ctx.model.config[name],
...result.tables[name],
...tableStats[name],
}
}
return result
Expand All @@ -81,3 +84,4 @@ namespace DatabaseProvider {
}

export default DatabaseProvider
export * from './utils'
33 changes: 33 additions & 0 deletions plugins/frontend/dataview/src/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
export function serialize(obj: unknown): string {
if (obj instanceof Date) return `"d${obj.toJSON()}"`
return JSON.stringify(obj, (_, value) => {
if (typeof value === 'string') return 's' + value
if (typeof value === 'object') {
if (value instanceof Date) return 'd' + new Date(value).toJSON()
if (value === null) return null
const o = Array.isArray(value) ? [] : {}
for (const k in value) {
if (value[k] instanceof Date) {
o[k] = new Date(value[k])
// Remove toJson so that it won't be converted to string in recursive calls
o[k].toJSON = undefined
} else {
o[k] = value[k]
}
}
return o
}
return value
})
}

export function deserialize(str: string): unknown {
if (str === undefined) return undefined
return JSON.parse(str, (_, v) =>
typeof v === 'string'
? v[0] === 's'
? v.slice(1)
: new Date(v.slice(1))
: v,
)
}

0 comments on commit dfb5801

Please sign in to comment.