feat(workbench): integrate operational workflows and map state
This commit is contained in:
@@ -5,11 +5,6 @@ import type { components } from "./generated/schema";
|
||||
|
||||
type Schemas = components["schemas"];
|
||||
export type PeripheralContext = Schemas["Context"];
|
||||
export type PeripheralTask = Schemas["Task"];
|
||||
export type PeripheralTaskPage = Schemas["TaskPage"];
|
||||
export type PeripheralCreateTask = Schemas["CreateTask"];
|
||||
export type PeripheralTransition = Schemas["Transition"];
|
||||
export type PeripheralAddFeedback = Schemas["AddFeedback"];
|
||||
export type PeripheralScenarioState = Schemas["ScenarioState"];
|
||||
export type PeripheralScenario = PeripheralScenarioState["scenario"];
|
||||
export type PeripheralPointPage = Schemas["PointPage"];
|
||||
@@ -26,7 +21,7 @@ export type PeripheralCreateTestDataRecord = Schemas["CreateTestDataRecord"];
|
||||
export type PeripheralUpdateTestDataRecord = Schemas["UpdateTestDataRecord"];
|
||||
export type PeripheralDeletedResource = Schemas["DeletedResource"];
|
||||
|
||||
type PeripheralMutationResult = PeripheralTask | PeripheralScenarioState | PeripheralWorkOrder |
|
||||
type PeripheralMutationResult = PeripheralScenarioState | PeripheralWorkOrder |
|
||||
PeripheralTestDataRecord | PeripheralDeletedResource;
|
||||
|
||||
const API_PREFIX = "/api/sample/v1";
|
||||
@@ -34,7 +29,6 @@ const API_PREFIX = "/api/sample/v1";
|
||||
export class PeripheralSampleClient {
|
||||
private readonly baseUrl = env.TJWATER_PERIPHERAL_SAMPLE_API_BASE_URL;
|
||||
private projectIdPromise: Promise<string> | null = null;
|
||||
private readonly taskProjection = new Map<string, PeripheralTask>();
|
||||
private readonly pendingCommands = new Map<string, { requestId: string; payload: string }>();
|
||||
private scenarioProjection: PeripheralScenarioState | null = null;
|
||||
|
||||
@@ -56,17 +50,6 @@ export class PeripheralSampleClient {
|
||||
return this.get<PeripheralObservations>(`/monitoring/observations?${query}`, signal);
|
||||
}
|
||||
|
||||
async listTasks(cursor?: string | null, signal?: AbortSignal) {
|
||||
const query = new URLSearchParams({ limit: "20" });
|
||||
if (cursor) query.set("cursor", cursor);
|
||||
const page = await this.get<PeripheralTaskPage>(`/tasks?${query}`, signal);
|
||||
return { ...page, items: page.items.map((task) => this.projectTask(task)) };
|
||||
}
|
||||
|
||||
getTask(taskId: string, signal?: AbortSignal) {
|
||||
return this.getTaskProjected(`/tasks/${encodeURIComponent(taskId)}`, signal);
|
||||
}
|
||||
|
||||
async getReceipt(requestId: string, signal?: AbortSignal) {
|
||||
const receipt = await this.get<Schemas["Receipt"]>(
|
||||
`/requests/${encodeURIComponent(requestId)}`, signal
|
||||
@@ -84,26 +67,6 @@ export class PeripheralSampleClient {
|
||||
}, signal);
|
||||
}
|
||||
|
||||
createTask(input: Omit<PeripheralCreateTask, "request_id">, signal?: AbortSignal) {
|
||||
return this.postRecoverable<PeripheralTask>("/tasks", {
|
||||
...input, request_id: crypto.randomUUID()
|
||||
}, signal);
|
||||
}
|
||||
|
||||
transition(taskId: string, input: Omit<PeripheralTransition, "request_id">, signal?: AbortSignal) {
|
||||
return this.postRecoverable<PeripheralTask>(
|
||||
`/tasks/${encodeURIComponent(taskId)}/transitions`,
|
||||
{ ...input, request_id: crypto.randomUUID() }, signal
|
||||
);
|
||||
}
|
||||
|
||||
addFeedback(taskId: string, input: Omit<PeripheralAddFeedback, "request_id" | "feedback_id">, signal?: AbortSignal) {
|
||||
return this.postRecoverable<PeripheralTask>(
|
||||
`/tasks/${encodeURIComponent(taskId)}/feedback`,
|
||||
{ ...input, request_id: crypto.randomUUID(), feedback_id: crypto.randomUUID() }, signal
|
||||
);
|
||||
}
|
||||
|
||||
createWorkOrder(input: Omit<PeripheralCreateWorkOrder, "request_id">, signal?: AbortSignal) {
|
||||
return this.postRecoverable<PeripheralWorkOrder>("/work-orders", {
|
||||
...input, request_id: crypto.randomUUID()
|
||||
@@ -144,10 +107,6 @@ export class PeripheralSampleClient {
|
||||
);
|
||||
}
|
||||
|
||||
private async getTaskProjected(path: string, signal?: AbortSignal) {
|
||||
return this.projectTask(await this.get<PeripheralTask>(path, signal));
|
||||
}
|
||||
|
||||
private async getScenarioProjected(path: string, signal?: AbortSignal) {
|
||||
return this.projectScenario(await this.get<PeripheralScenarioState>(path, signal));
|
||||
}
|
||||
@@ -268,8 +227,7 @@ export class PeripheralSampleClient {
|
||||
return this.projectIdPromise;
|
||||
}
|
||||
|
||||
private projectResult(result: PeripheralMutationResult) {
|
||||
if (isTask(result)) return this.projectTask(result);
|
||||
private projectResult(result: unknown): PeripheralMutationResult {
|
||||
if (isScenarioState(result)) return this.projectScenario(result);
|
||||
if (isManagedResult(result)) return result;
|
||||
throw new PeripheralSampleError("INVALID_RESPONSE", "响应缺少可识别的样板资源,提交是否生效未知", "unknown", null);
|
||||
@@ -283,21 +241,6 @@ export class PeripheralSampleClient {
|
||||
return next;
|
||||
}
|
||||
|
||||
private projectTask(next: PeripheralTask) {
|
||||
const current = this.taskProjection.get(next.task_id);
|
||||
if (current && next.revision < current.revision) return current;
|
||||
const feedback = [...new Map(next.feedback.map((item) => [item.feedback_id, item])).values()];
|
||||
const projected = { ...next, feedback };
|
||||
this.taskProjection.set(next.task_id, projected);
|
||||
return projected;
|
||||
}
|
||||
}
|
||||
|
||||
function isTask(value: unknown): value is PeripheralTask {
|
||||
return Boolean(value && typeof value === "object" &&
|
||||
typeof (value as Partial<PeripheralTask>).task_id === "string" &&
|
||||
typeof (value as Partial<PeripheralTask>).revision === "number" &&
|
||||
Array.isArray((value as Partial<PeripheralTask>).feedback));
|
||||
}
|
||||
|
||||
function isScenarioState(value: unknown): value is PeripheralScenarioState {
|
||||
@@ -367,10 +310,6 @@ function isRecognizedReadPayload(path: string, value: unknown): boolean {
|
||||
if (path.startsWith("/requests/")) {
|
||||
return typeof item.request_id === "string" && "result" in item;
|
||||
}
|
||||
if (path.startsWith("/tasks?")) {
|
||||
return isPageOf(value, isTask) && (item.next_cursor === null || typeof item.next_cursor === "string");
|
||||
}
|
||||
if (path.startsWith("/tasks/")) return isTask(value);
|
||||
if (path.startsWith("/monitoring/points")) return isPageOf(value, isPoint);
|
||||
if (path.startsWith("/monitoring/alerts")) return isPageOf(value, isAlert);
|
||||
if (path.startsWith("/monitoring/observations")) {
|
||||
|
||||
Reference in New Issue
Block a user