Skip to content

Commit

Permalink
feat(toolbar, client): Add support for new shape sync API on toolbar (#…
Browse files Browse the repository at this point in the history
…1274)

- Exposes `SyncStatus` type as well as a method for retrieving all subs
and their status.
- Implements new method in the toolbar
  • Loading branch information
msfstef committed May 16, 2024
1 parent 5474f9b commit b966157
Show file tree
Hide file tree
Showing 7 changed files with 73 additions and 25 deletions.
5 changes: 5 additions & 0 deletions .changeset/proud-lemons-poke.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@electric-sql/debug-toolbar": minor
---

Add support for new shape sync API
5 changes: 5 additions & 0 deletions .changeset/tidy-pens-refuse.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"electric-sql": patch
---

Expose `SyncStatus` type and methods for introspecting shape subscription status
1 change: 1 addition & 0 deletions clients/typescript/src/client/model/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ export type { TableSchema } from './schema'
export { DbSchema, Relation } from './schema'
export { Table } from './table'
export type { HKT } from '../util/hkt'
export type { SyncStatus } from './shapes'
21 changes: 21 additions & 0 deletions clients/typescript/src/satellite/shapes/shapeManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,27 @@ export class ShapeManager {
}
}

/**
* List all subscriptions as defined along with their simple key and sync status
*/
public listAllSubscriptions(): {
key: string
shapes: Shape[]
status: SyncStatus
}[] {
const allKeys = Object.keys(this.activeSubscriptions).concat(
Object.keys(this.requestedSubscriptions)
)
return allKeys.map((key) => ({
shapes:
this.knownSubscriptions[
(this.activeSubscriptions[key] ?? this.requestedSubscriptions[key])!
]!.shapes,
status: this.status(key),
key,
}))
}

/**
* Store a request to sync a list of shapes.
*
Expand Down
3 changes: 2 additions & 1 deletion components/toolbar/src/api/interface.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { Shape } from 'electric-sql/satellite'
import { Row, Statement, ConnectivityState } from 'electric-sql/util'
import { SqlDialect } from './statements'
import { SyncStatus } from 'electric-sql/client/model'

export type UnsubscribeFunction = () => void

export type DebugShape = Shape & { id: string }
export type DebugShape = { key: string; shape: Shape; status: SyncStatus }

export interface TableColumn {
name: string
Expand Down
26 changes: 14 additions & 12 deletions components/toolbar/src/api/toolbar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@ import {
UnsubscribeFunction,
} from './interface'
import { Row, Statement, ConnectivityState } from 'electric-sql/util'
import { SyncStatus } from 'electric-sql/client/model'
import {
Registry,
GlobalRegistry,
Satellite,
Shape,
SatelliteProcess,
} from 'electric-sql/satellite'
import { SubscriptionsManager } from 'electric-sql/satellite/shapes'
import {
getDbTables,
getElectricTables,
Expand Down Expand Up @@ -66,17 +67,18 @@ export class Toolbar implements ToolbarInterface {
}

getSatelliteShapeSubscriptions(name: string): DebugShape[] {
const sat = this.getSatellite(name)
//@ts-expect-error accessing private field
const manager = sat['subscriptions'] as SubscriptionsManager
const shapes = JSON.parse(manager.serialize()) as Record<
string,
{ definition: Shape }[]
>
return Object.entries(shapes).flatMap((shapeKeyDef) =>
shapeKeyDef[1].map((x) => ({
id: shapeKeyDef[0],
...x.definition,
const sat = this.getSatellite(name) as SatelliteProcess
const manager = sat.subscriptionManager
const subscriptions = manager.listAllSubscriptions() as {
shapes: Shape[]
key: string
status: SyncStatus
}[]
return subscriptions.flatMap((subscription) =>
subscription.shapes.map((shape: Shape) => ({
key: subscription.key,
shape: shape,
status: subscription.status,
})),
)
}
Expand Down
37 changes: 25 additions & 12 deletions components/toolbar/src/tabs/ShapesTab.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { useEffect, useState } from 'react'
import { ToolbarTabsProps } from '../tabs'
import { Badge, Box, Table, Text } from '@radix-ui/themes'
import { SyncStatus } from 'electric-sql/client/model'

export default function ShapesTab({
dbName,
Expand All @@ -11,10 +12,11 @@ export default function ShapesTab({
)

useEffect(() => {
// TODO: need notifier API for shape status
// periodically refresh shape subscriptions
const interval = setInterval(
() => setShapes(api.getSatelliteShapeSubscriptions(dbName)),
1000,
500,
)
return () => clearInterval(interval)
}, [dbName, api])
Expand All @@ -33,7 +35,7 @@ export default function ShapesTab({
<Table.Header>
<Table.Row>
<Table.ColumnHeaderCell>
Shape Subscription ID
Shape Subscription Key
</Table.ColumnHeaderCell>
<Table.ColumnHeaderCell>Tablename</Table.ColumnHeaderCell>
<Table.ColumnHeaderCell>Include</Table.ColumnHeaderCell>
Expand All @@ -43,19 +45,18 @@ export default function ShapesTab({
</Table.Header>

<Table.Body>
{shapes.map(({ id, tablename, include = [], where }) => (
<Table.Row key={id}>
<Table.Cell>{id}</Table.Cell>
<Table.Cell>{tablename}</Table.Cell>
{shapes.map(({ key, shape, status }) => (
<Table.Row key={key}>
<Table.Cell>{key}</Table.Cell>
<Table.Cell>{shape.tablename}</Table.Cell>
<Table.Cell>
{include.length === 0
? 'N/A'
: include.map((v) => v.select.tablename).join(', ')}
{!shape.include || shape.include.length === 0
? ''
: shape.include.map((v) => v.select.tablename).join(', ')}
</Table.Cell>
<Table.Cell>{where ?? 'N/A'}</Table.Cell>
<Table.Cell>{shape.where ?? ''}</Table.Cell>
<Table.Cell>
{/* TODO: add shape status once available */}
<Badge color="green">Active</Badge>
<ShapeStatusBadge status={status} />
</Table.Cell>
</Table.Row>
))}
Expand All @@ -64,3 +65,15 @@ export default function ShapesTab({
</Box>
)
}

const ShapeStatusBadge = ({ status }: { status: SyncStatus }) => {
if (!status) return null
switch (status.status) {
case 'active':
return <Badge color="green">Active</Badge>
case 'establishing':
return <Badge color="orange">Establishing</Badge>
case 'cancelling':
return <Badge color="red">Cancelled</Badge>
}
}

0 comments on commit b966157

Please sign in to comment.