mirror of
https://github.com/DeBrosOfficial/network.git
synced 2025-10-06 13:49: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.
|
- 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`.
|
- 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
|
### Authentication Improvements
|
||||||
|
|
||||||
The gateway authentication system has been significantly enhanced with the following features:
|
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
|
.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)
|
COMMIT ?= $(shell git rev-parse --short HEAD 2>/dev/null || echo unknown)
|
||||||
DATE ?= $(shell date -u +%Y-%m-%dT%H:%M:%SZ)
|
DATE ?= $(shell date -u +%Y-%m-%dT%H:%M:%SZ)
|
||||||
LDFLAGS := -X 'main.version=$(VERSION)' -X 'main.commit=$(COMMIT)' -X 'main.date=$(DATE)'
|
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
|
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
|
### Security Features
|
||||||
|
|
||||||
- **Namespace Enforcement:** All operations are automatically prefixed with namespace for isolation
|
- **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