mirror of
https://github.com/DeBrosOfficial/network.git
synced 2025-10-06 10:19:07 +00:00
Add OpenAPI spec and SDK authoring guide with TypeScript example
- Add machine-readable OpenAPI spec for Storage, Database, PubSub - Document HTTP endpoints, auth, migrations, and SDK patterns - Provide minimal TypeScript SDK example and code - Update README and add example SDK files and configs - Bump version to 0.40.0-beta
This commit is contained in:
parent
829991da03
commit
3fe78ee62a
@ -259,6 +259,70 @@ Notes:
|
||||
- The gateway uses internal DB context for validation and execution to avoid circular auth checks.
|
||||
- Perform migrations by POSTing DDL statements to `/v1/db/transaction`.
|
||||
|
||||
OpenAPI specification: see `openapi/gateway.yaml` for a machine-readable contract covering Storage, Database, and PubSub REST endpoints.
|
||||
|
||||
---
|
||||
|
||||
## SDK Authoring Guide
|
||||
|
||||
### Base concepts
|
||||
- Auth: send `X-API-Key: <key>` or `Authorization: Bearer <key|JWT>` on every request.
|
||||
- Versioning: all endpoints live under `/v1/`.
|
||||
- Response envelopes today are pragmatic:
|
||||
- Success varies by endpoint (e.g., `{status:"ok"}` for mutations, JSON bodies for queries/lists)
|
||||
- Errors are returned as `{ "error": "message" }` with appropriate HTTP status
|
||||
|
||||
### HTTP Endpoints (recap)
|
||||
- Storage
|
||||
- PUT: `POST /v1/storage/put?key=<k>` body: raw bytes
|
||||
- GET: `GET /v1/storage/get?key=<k>` (returns bytes; some backends may base64-encode)
|
||||
- EXISTS: `GET /v1/storage/exists?key=<k>` → `{exists:boolean}`
|
||||
- LIST: `GET /v1/storage/list?prefix=<p>` → `{keys:[...]}`
|
||||
- DELETE: `POST /v1/storage/delete` body `{key}` → `{status:"ok"}`
|
||||
- Database
|
||||
- Create Table: `POST /v1/db/create-table` `{schema}` → `{status:"ok"}`
|
||||
- Drop Table: `POST /v1/db/drop-table` `{table}` → `{status:"ok"}`
|
||||
- Query: `POST /v1/db/query` `{sql, args?}` → `{columns, rows, count}`
|
||||
- Transaction: `POST /v1/db/transaction` `{statements:[...]}` → `{status:"ok"}`
|
||||
- Schema: `GET /v1/db/schema` → schema JSON
|
||||
- PubSub
|
||||
- WebSocket: `GET /v1/pubsub/ws?topic=<topic>` (binary frames; ping keepalive)
|
||||
- Publish: `POST /v1/pubsub/publish` `{topic, data_base64}` → `{status:"ok"}`
|
||||
- Topics: `GET /v1/pubsub/topics` → `{topics:[...]}` (already trimmed to namespace)
|
||||
|
||||
### Migrations
|
||||
- Add a column: `ALTER TABLE users ADD COLUMN age INTEGER`
|
||||
- Change type / add FK (recreate pattern):
|
||||
1. `CREATE TABLE users_new (... updated schema ...)`
|
||||
2. `INSERT INTO users_new (...) SELECT ... FROM users`
|
||||
3. `DROP TABLE users`
|
||||
4. `ALTER TABLE users_new RENAME TO users`
|
||||
- Wrap the above in a single `POST /v1/db/transaction` call.
|
||||
|
||||
### Suggested SDK surface
|
||||
- Database
|
||||
- `createTable(schema: string): Promise<void>`
|
||||
- `dropTable(name: string): Promise<void>`
|
||||
- `query<T>(sql: string, args?: any[]): Promise<{ rows: T[] }>`
|
||||
- `transaction(statements: string[]): Promise<void>`
|
||||
- `schema(): Promise<any>`
|
||||
- Storage
|
||||
- `put(key: string, value: Uint8Array | string): Promise<void>`
|
||||
- `get(key: string): Promise<Uint8Array>`
|
||||
- `exists(key: string): Promise<boolean>`
|
||||
- `list(prefix?: string): Promise<string[]>`
|
||||
- `delete(key: string): Promise<void>`
|
||||
- PubSub
|
||||
- `subscribe(topic: string, handler: (msg: Uint8Array) => void): Promise<() => void>`
|
||||
- `publish(topic: string, data: Uint8Array | string): Promise<void>`
|
||||
- `listTopics(): Promise<string[]>`
|
||||
|
||||
### Reliability guidelines
|
||||
- Timeouts: 10–30s defaults with per-call overrides
|
||||
- Retries: exponential backoff on 429/5xx; respect `Retry-After`
|
||||
- Idempotency: for transactions, consider an `Idempotency-Key` header client-side (gateway may ignore)
|
||||
- WebSocket: auto-reconnect with jitter; re-subscribe after reconnect
|
||||
|
||||
### Authentication Improvements
|
||||
|
||||
The gateway authentication system has been significantly enhanced with the following features:
|
||||
|
2
Makefile
2
Makefile
@ -21,7 +21,7 @@ test-e2e:
|
||||
|
||||
.PHONY: build clean test run-node run-node2 run-node3 run-example deps tidy fmt vet lint clear-ports
|
||||
|
||||
VERSION := 0.39.0-beta
|
||||
VERSION := 0.40.0-beta
|
||||
COMMIT ?= $(shell git rev-parse --short HEAD 2>/dev/null || echo unknown)
|
||||
DATE ?= $(shell date -u +%Y-%m-%dT%H:%M:%SZ)
|
||||
LDFLAGS := -X 'main.version=$(VERSION)' -X 'main.commit=$(COMMIT)' -X 'main.date=$(DATE)'
|
||||
|
69
README.md
69
README.md
@ -478,6 +478,75 @@ POST /v1/pubsub/publish # Publish message to topic
|
||||
GET /v1/pubsub/topics # List active topics
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## SDK Authoring Guide
|
||||
|
||||
### Base concepts
|
||||
- OpenAPI: a machine-readable spec is available at `openapi/gateway.yaml` for SDK code generation.
|
||||
- **Auth**: send `X-API-Key: <key>` or `Authorization: Bearer <key|JWT>` with every request.
|
||||
- **Versioning**: all endpoints are under `/v1/`.
|
||||
- **Responses**: mutations return `{status:"ok"}`; queries/lists return JSON; errors return `{ "error": "message" }` with proper HTTP status.
|
||||
|
||||
### Key HTTP endpoints for SDKs
|
||||
- **Storage**
|
||||
- PUT: `POST /v1/storage/put?key=<k>` body is raw bytes
|
||||
- GET: `GET /v1/storage/get?key=<k>` returns bytes (may be base64-encoded by some backends)
|
||||
- EXISTS: `GET /v1/storage/exists?key=<k>` → `{exists}`
|
||||
- LIST: `GET /v1/storage/list?prefix=<p>` → `{keys}`
|
||||
- DELETE: `POST /v1/storage/delete` `{key}` → `{status:"ok"}`
|
||||
- **Database**
|
||||
- Create Table: `POST /v1/db/create-table` `{schema}` → `{status:"ok"}`
|
||||
- Drop Table: `POST /v1/db/drop-table` `{table}` → `{status:"ok"}`
|
||||
- Query: `POST /v1/db/query` `{sql, args?}` → `{columns, rows, count}`
|
||||
- Transaction: `POST /v1/db/transaction` `{statements:[...]}` → `{status:"ok"}`
|
||||
- Schema: `GET /v1/db/schema` → schema JSON
|
||||
- **PubSub**
|
||||
- WS Subscribe: `GET /v1/pubsub/ws?topic=<topic>`
|
||||
- Publish: `POST /v1/pubsub/publish` `{topic, data_base64}` → `{status:"ok"}`
|
||||
- Topics: `GET /v1/pubsub/topics` → `{topics:[...]}`
|
||||
|
||||
### Migrations
|
||||
- Add column: `ALTER TABLE users ADD COLUMN age INTEGER`
|
||||
- Change type / add FK (recreate pattern): create `_new` table, copy data, drop old, rename.
|
||||
- Always send as one `POST /v1/db/transaction`.
|
||||
|
||||
### Minimal examples
|
||||
|
||||
TypeScript (Node)
|
||||
|
||||
```ts
|
||||
import { GatewayClient } from "../examples/sdk-typescript/src/client";
|
||||
|
||||
const client = new GatewayClient(process.env.GATEWAY_BASE_URL!, process.env.GATEWAY_API_KEY!);
|
||||
await client.createTable("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)");
|
||||
const res = await client.query("SELECT name FROM users WHERE id = ?", [1]);
|
||||
```
|
||||
|
||||
Python
|
||||
|
||||
```python
|
||||
import os, requests
|
||||
|
||||
BASE = os.environ['GATEWAY_BASE_URL']
|
||||
KEY = os.environ['GATEWAY_API_KEY']
|
||||
H = { 'X-API-Key': KEY, 'Content-Type': 'application/json' }
|
||||
|
||||
def query(sql, args=None):
|
||||
r = requests.post(f'{BASE}/v1/db/query', json={ 'sql': sql, 'args': args or [] }, headers=H, timeout=15)
|
||||
r.raise_for_status()
|
||||
return r.json()['rows']
|
||||
```
|
||||
|
||||
Go
|
||||
|
||||
```go
|
||||
req, _ := http.NewRequest(http.MethodPost, base+"/v1/db/create-table", bytes.NewBufferString(`{"schema":"CREATE TABLE ..."}`))
|
||||
req.Header.Set("X-API-Key", apiKey)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
```
|
||||
|
||||
### Security Features
|
||||
|
||||
- **Namespace Enforcement:** All operations are automatically prefixed with namespace for isolation
|
||||
|
23
examples/sdk-typescript/README.md
Normal file
23
examples/sdk-typescript/README.md
Normal file
@ -0,0 +1,23 @@
|
||||
# DeBros Gateway TypeScript SDK (Minimal Example)
|
||||
|
||||
Minimal, dependency-light wrapper around the HTTP Gateway.
|
||||
|
||||
Usage:
|
||||
|
||||
```bash
|
||||
npm i
|
||||
export GATEWAY_BASE_URL=http://127.0.0.1:8080
|
||||
export GATEWAY_API_KEY=your_api_key
|
||||
```
|
||||
|
||||
```ts
|
||||
import { GatewayClient } from './src/client';
|
||||
|
||||
const c = new GatewayClient(process.env.GATEWAY_BASE_URL!, process.env.GATEWAY_API_KEY!);
|
||||
await c.createTable('CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)');
|
||||
await c.transaction([
|
||||
'INSERT INTO users (id,name) VALUES (1,\'Alice\')'
|
||||
]);
|
||||
const res = await c.query('SELECT name FROM users WHERE id = ?', [1]);
|
||||
console.log(res.rows);
|
||||
```
|
17
examples/sdk-typescript/package.json
Normal file
17
examples/sdk-typescript/package.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "debros-gateway-sdk",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"scripts": {
|
||||
"build": "tsc -p tsconfig.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"isomorphic-ws": "^5.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5.5.4"
|
||||
}
|
||||
}
|
112
examples/sdk-typescript/src/client.ts
Normal file
112
examples/sdk-typescript/src/client.ts
Normal file
@ -0,0 +1,112 @@
|
||||
import WebSocket from 'isomorphic-ws';
|
||||
|
||||
export class GatewayClient {
|
||||
constructor(private baseUrl: string, private apiKey: string, private http = fetch) {}
|
||||
|
||||
private headers(json = true): Record<string, string> {
|
||||
const h: Record<string, string> = { 'X-API-Key': this.apiKey };
|
||||
if (json) h['Content-Type'] = 'application/json';
|
||||
return h;
|
||||
}
|
||||
|
||||
// Database
|
||||
async createTable(schema: string): Promise<void> {
|
||||
const r = await this.http(`${this.baseUrl}/v1/db/create-table`, {
|
||||
method: 'POST', headers: this.headers(), body: JSON.stringify({ schema })
|
||||
});
|
||||
if (!r.ok) throw new Error(`createTable failed: ${r.status}`);
|
||||
}
|
||||
|
||||
async dropTable(table: string): Promise<void> {
|
||||
const r = await this.http(`${this.baseUrl}/v1/db/drop-table`, {
|
||||
method: 'POST', headers: this.headers(), body: JSON.stringify({ table })
|
||||
});
|
||||
if (!r.ok) throw new Error(`dropTable failed: ${r.status}`);
|
||||
}
|
||||
|
||||
async query<T = any>(sql: string, args: any[] = []): Promise<{ rows: T[] }> {
|
||||
const r = await this.http(`${this.baseUrl}/v1/db/query`, {
|
||||
method: 'POST', headers: this.headers(), body: JSON.stringify({ sql, args })
|
||||
});
|
||||
if (!r.ok) throw new Error(`query failed: ${r.status}`);
|
||||
return r.json();
|
||||
}
|
||||
|
||||
async transaction(statements: string[]): Promise<void> {
|
||||
const r = await this.http(`${this.baseUrl}/v1/db/transaction`, {
|
||||
method: 'POST', headers: this.headers(), body: JSON.stringify({ statements })
|
||||
});
|
||||
if (!r.ok) throw new Error(`transaction failed: ${r.status}`);
|
||||
}
|
||||
|
||||
async schema(): Promise<any> {
|
||||
const r = await this.http(`${this.baseUrl}/v1/db/schema`, { headers: this.headers(false) });
|
||||
if (!r.ok) throw new Error(`schema failed: ${r.status}`);
|
||||
return r.json();
|
||||
}
|
||||
|
||||
// Storage
|
||||
async put(key: string, value: Uint8Array | string): Promise<void> {
|
||||
const body = typeof value === 'string' ? new TextEncoder().encode(value) : value;
|
||||
const r = await this.http(`${this.baseUrl}/v1/storage/put?key=${encodeURIComponent(key)}`, {
|
||||
method: 'POST', headers: { 'X-API-Key': this.apiKey }, body
|
||||
});
|
||||
if (!r.ok) throw new Error(`put failed: ${r.status}`);
|
||||
}
|
||||
|
||||
async get(key: string): Promise<Uint8Array> {
|
||||
const r = await this.http(`${this.baseUrl}/v1/storage/get?key=${encodeURIComponent(key)}`, {
|
||||
headers: { 'X-API-Key': this.apiKey }
|
||||
});
|
||||
if (!r.ok) throw new Error(`get failed: ${r.status}`);
|
||||
const buf = new Uint8Array(await r.arrayBuffer());
|
||||
return buf;
|
||||
}
|
||||
|
||||
async exists(key: string): Promise<boolean> {
|
||||
const r = await this.http(`${this.baseUrl}/v1/storage/exists?key=${encodeURIComponent(key)}`, {
|
||||
headers: this.headers(false)
|
||||
});
|
||||
if (!r.ok) throw new Error(`exists failed: ${r.status}`);
|
||||
const j = await r.json();
|
||||
return !!j.exists;
|
||||
}
|
||||
|
||||
async list(prefix = ""): Promise<string[]> {
|
||||
const r = await this.http(`${this.baseUrl}/v1/storage/list?prefix=${encodeURIComponent(prefix)}`, {
|
||||
headers: this.headers(false)
|
||||
});
|
||||
if (!r.ok) throw new Error(`list failed: ${r.status}`);
|
||||
const j = await r.json();
|
||||
return j.keys || [];
|
||||
}
|
||||
|
||||
async delete(key: string): Promise<void> {
|
||||
const r = await this.http(`${this.baseUrl}/v1/storage/delete`, {
|
||||
method: 'POST', headers: this.headers(), body: JSON.stringify({ key })
|
||||
});
|
||||
if (!r.ok) throw new Error(`delete failed: ${r.status}`);
|
||||
}
|
||||
|
||||
// PubSub (minimal)
|
||||
subscribe(topic: string, onMessage: (data: Uint8Array) => void): { close: () => void } {
|
||||
const url = new URL(`${this.baseUrl.replace(/^http/, 'ws')}/v1/pubsub/ws`);
|
||||
url.searchParams.set('topic', topic);
|
||||
const ws = new WebSocket(url.toString(), { headers: { 'X-API-Key': this.apiKey } } as any);
|
||||
ws.binaryType = 'arraybuffer';
|
||||
ws.onmessage = (ev: any) => {
|
||||
const data = ev.data instanceof ArrayBuffer ? new Uint8Array(ev.data) : new TextEncoder().encode(String(ev.data));
|
||||
onMessage(data);
|
||||
};
|
||||
return { close: () => ws.close() };
|
||||
}
|
||||
|
||||
async publish(topic: string, data: Uint8Array | string): Promise<void> {
|
||||
const bytes = typeof data === 'string' ? new TextEncoder().encode(data) : data;
|
||||
const b64 = Buffer.from(bytes).toString('base64');
|
||||
const r = await this.http(`${this.baseUrl}/v1/pubsub/publish`, {
|
||||
method: 'POST', headers: this.headers(), body: JSON.stringify({ topic, data_base64: b64 })
|
||||
});
|
||||
if (!r.ok) throw new Error(`publish failed: ${r.status}`);
|
||||
}
|
||||
}
|
12
examples/sdk-typescript/tsconfig.json
Normal file
12
examples/sdk-typescript/tsconfig.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"module": "ES2020",
|
||||
"declaration": true,
|
||||
"outDir": "dist",
|
||||
"rootDir": "src",
|
||||
"strict": true,
|
||||
"moduleResolution": "Node"
|
||||
},
|
||||
"include": ["src/**/*"]
|
||||
}
|
249
openapi/gateway.yaml
Normal file
249
openapi/gateway.yaml
Normal file
@ -0,0 +1,249 @@
|
||||
openapi: 3.0.3
|
||||
info:
|
||||
title: DeBros Gateway API
|
||||
version: 1.0.0
|
||||
description: REST API over the DeBros Network client for storage, database, and pubsub.
|
||||
servers:
|
||||
- url: http://localhost:8080
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
- BearerAuth: []
|
||||
components:
|
||||
securitySchemes:
|
||||
ApiKeyAuth:
|
||||
type: apiKey
|
||||
in: header
|
||||
name: X-API-Key
|
||||
BearerAuth:
|
||||
type: http
|
||||
scheme: bearer
|
||||
schemas:
|
||||
Error:
|
||||
type: object
|
||||
properties:
|
||||
error:
|
||||
type: string
|
||||
QueryRequest:
|
||||
type: object
|
||||
required: [sql]
|
||||
properties:
|
||||
sql:
|
||||
type: string
|
||||
args:
|
||||
type: array
|
||||
items: {}
|
||||
QueryResponse:
|
||||
type: object
|
||||
properties:
|
||||
columns:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
rows:
|
||||
type: array
|
||||
items:
|
||||
type: array
|
||||
items: {}
|
||||
count:
|
||||
type: integer
|
||||
format: int64
|
||||
TransactionRequest:
|
||||
type: object
|
||||
required: [statements]
|
||||
properties:
|
||||
statements:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
CreateTableRequest:
|
||||
type: object
|
||||
required: [schema]
|
||||
properties:
|
||||
schema:
|
||||
type: string
|
||||
DropTableRequest:
|
||||
type: object
|
||||
required: [table]
|
||||
properties:
|
||||
table:
|
||||
type: string
|
||||
TopicsResponse:
|
||||
type: object
|
||||
properties:
|
||||
topics:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
paths:
|
||||
/v1/health:
|
||||
get:
|
||||
summary: Gateway health
|
||||
responses:
|
||||
'200': { description: OK }
|
||||
/v1/storage/put:
|
||||
post:
|
||||
summary: Store a value by key
|
||||
parameters:
|
||||
- in: query
|
||||
name: key
|
||||
schema: { type: string }
|
||||
required: true
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/octet-stream:
|
||||
schema:
|
||||
type: string
|
||||
format: binary
|
||||
responses:
|
||||
'201': { description: Created }
|
||||
'400': { description: Bad Request, content: { application/json: { schema: { $ref: '#/components/schemas/Error' } } } }
|
||||
'401': { description: Unauthorized }
|
||||
'500': { description: Error, content: { application/json: { schema: { $ref: '#/components/schemas/Error' } } } }
|
||||
/v1/storage/get:
|
||||
get:
|
||||
summary: Get a value by key
|
||||
parameters:
|
||||
- in: query
|
||||
name: key
|
||||
schema: { type: string }
|
||||
required: true
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
content:
|
||||
application/octet-stream:
|
||||
schema:
|
||||
type: string
|
||||
format: binary
|
||||
'404': { description: Not Found, content: { application/json: { schema: { $ref: '#/components/schemas/Error' } } } }
|
||||
/v1/storage/exists:
|
||||
get:
|
||||
summary: Check key existence
|
||||
parameters:
|
||||
- in: query
|
||||
name: key
|
||||
schema: { type: string }
|
||||
required: true
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
exists:
|
||||
type: boolean
|
||||
/v1/storage/list:
|
||||
get:
|
||||
summary: List keys by prefix
|
||||
parameters:
|
||||
- in: query
|
||||
name: prefix
|
||||
schema: { type: string }
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
keys:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
/v1/storage/delete:
|
||||
post:
|
||||
summary: Delete a key
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
required: [key]
|
||||
properties:
|
||||
key: { type: string }
|
||||
responses:
|
||||
'200': { description: OK }
|
||||
/v1/db/create-table:
|
||||
post:
|
||||
summary: Create tables via SQL DDL
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema: { $ref: '#/components/schemas/CreateTableRequest' }
|
||||
responses:
|
||||
'201': { description: Created }
|
||||
'400': { description: Bad Request, content: { application/json: { schema: { $ref: '#/components/schemas/Error' } } } }
|
||||
'500': { description: Error, content: { application/json: { schema: { $ref: '#/components/schemas/Error' } } } }
|
||||
/v1/db/drop-table:
|
||||
post:
|
||||
summary: Drop a table
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema: { $ref: '#/components/schemas/DropTableRequest' }
|
||||
responses:
|
||||
'200': { description: OK }
|
||||
/v1/db/query:
|
||||
post:
|
||||
summary: Execute a single SQL query
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema: { $ref: '#/components/schemas/QueryRequest' }
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
content:
|
||||
application/json:
|
||||
schema: { $ref: '#/components/schemas/QueryResponse' }
|
||||
'400': { description: Bad Request, content: { application/json: { schema: { $ref: '#/components/schemas/Error' } } } }
|
||||
'500': { description: Error, content: { application/json: { schema: { $ref: '#/components/schemas/Error' } } } }
|
||||
/v1/db/transaction:
|
||||
post:
|
||||
summary: Execute multiple SQL statements atomically
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema: { $ref: '#/components/schemas/TransactionRequest' }
|
||||
responses:
|
||||
'200': { description: OK }
|
||||
'400': { description: Bad Request, content: { application/json: { schema: { $ref: '#/components/schemas/Error' } } } }
|
||||
'500': { description: Error, content: { application/json: { schema: { $ref: '#/components/schemas/Error' } } } }
|
||||
/v1/db/schema:
|
||||
get:
|
||||
summary: Get current database schema
|
||||
responses:
|
||||
'200': { description: OK }
|
||||
/v1/pubsub/publish:
|
||||
post:
|
||||
summary: Publish to a topic
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
required: [topic, data_base64]
|
||||
properties:
|
||||
topic: { type: string }
|
||||
data_base64: { type: string }
|
||||
responses:
|
||||
'200': { description: OK }
|
||||
/v1/pubsub/topics:
|
||||
get:
|
||||
summary: List topics in caller namespace
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
content:
|
||||
application/json:
|
||||
schema: { $ref: '#/components/schemas/TopicsResponse' }
|
Loading…
x
Reference in New Issue
Block a user