refactor: documents interfaces
Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
This commit is contained in:
@@ -16,6 +16,10 @@ import type {
|
||||
CollectionUpdateRequest,
|
||||
CollectionDeleteResponse,
|
||||
CollectionDeleteRequest,
|
||||
CollectionCopyRequest,
|
||||
CollectionCopyResponse,
|
||||
CollectionMoveRequest,
|
||||
CollectionMoveResponse,
|
||||
CollectionInterface,
|
||||
} from '../types/collection';
|
||||
import { useIntegrationStore } from '@KTXC/stores/integrationStore';
|
||||
@@ -141,15 +145,39 @@ export const collectionService = {
|
||||
|
||||
/**
|
||||
* Delete a collection
|
||||
*
|
||||
*
|
||||
* @param request - delete request parameters
|
||||
*
|
||||
*
|
||||
* @returns Promise with deletion result
|
||||
*/
|
||||
async delete(request: CollectionDeleteRequest): Promise<CollectionDeleteResponse> {
|
||||
return await transceivePost<CollectionDeleteRequest, CollectionDeleteResponse>('collection.delete', request);
|
||||
},
|
||||
|
||||
/**
|
||||
* Copy a collection to a new parent
|
||||
*
|
||||
* @param request - copy request parameters
|
||||
*
|
||||
* @returns Promise with copied collection object
|
||||
*/
|
||||
async copy(request: CollectionCopyRequest): Promise<CollectionObject> {
|
||||
const response = await transceivePost<CollectionCopyRequest, CollectionCopyResponse>('collection.copy', request);
|
||||
return createCollectionObject(response);
|
||||
},
|
||||
|
||||
/**
|
||||
* Move a collection to a new parent
|
||||
*
|
||||
* @param request - move request parameters
|
||||
*
|
||||
* @returns Promise with moved collection object
|
||||
*/
|
||||
async move(request: CollectionMoveRequest): Promise<CollectionObject> {
|
||||
const response = await transceivePost<CollectionMoveRequest, CollectionMoveResponse>('collection.move', request);
|
||||
return createCollectionObject(response);
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
export default collectionService;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* Entity management service
|
||||
*/
|
||||
|
||||
import { transceivePost } from './transceive';
|
||||
import { transceivePost, transceiveStream } from './transceive';
|
||||
import type {
|
||||
EntityListRequest,
|
||||
EntityListResponse,
|
||||
@@ -18,6 +18,10 @@ import type {
|
||||
EntityDeleteResponse,
|
||||
EntityDeltaRequest,
|
||||
EntityDeltaResponse,
|
||||
EntityCopyRequest,
|
||||
EntityCopyResponse,
|
||||
EntityMoveRequest,
|
||||
EntityMoveResponse,
|
||||
EntityReadRequest,
|
||||
EntityReadResponse,
|
||||
EntityWriteRequest,
|
||||
@@ -43,14 +47,14 @@ function createEntityObject(data: EntityInterface): EntityObject {
|
||||
export const entityService = {
|
||||
|
||||
/**
|
||||
* Retrieve list of entities, optionally filtered by source selector
|
||||
*
|
||||
* Retrieve list of entities, optionally filtered by source identifiers
|
||||
*
|
||||
* @param request - list request parameters
|
||||
*
|
||||
*
|
||||
* @returns Promise with entity object list grouped by provider, service, collection, and entity identifier
|
||||
*/
|
||||
async list(request: EntityListRequest = {}): Promise<Record<string, Record<string, Record<string, Record<string, EntityObject>>>>> {
|
||||
const response = await transceivePost<EntityListRequest, EntityListResponse>('entity.list', request);
|
||||
async listBulk(request: EntityListRequest = {}): Promise<Record<string, Record<string, Record<string, Record<string, EntityObject>>>>> {
|
||||
const response = await transceivePost<EntityListRequest, EntityListResponse>('entity.listBulk', request);
|
||||
|
||||
// Convert nested response to EntityObject instances
|
||||
const providerList: Record<string, Record<string, Record<string, Record<string, EntityObject>>>> = {};
|
||||
@@ -73,11 +77,25 @@ export const entityService = {
|
||||
return providerList;
|
||||
},
|
||||
|
||||
/**
|
||||
* Stream entities one by one, invoking the callback for each entity
|
||||
*
|
||||
* @param request - list request parameters
|
||||
* @param onEntity - callback invoked for each streamed entity
|
||||
*
|
||||
* @returns Promise with the total number of streamed entities
|
||||
*/
|
||||
async listStream(request: EntityListRequest, onEntity: (entity: EntityObject) => void): Promise<{ total: number }> {
|
||||
return await transceiveStream<EntityListRequest, EntityInterface>('entity.listStream', request, (entityData) => {
|
||||
onEntity(createEntityObject(entityData));
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieve a specific entity by provider and identifier
|
||||
*
|
||||
*
|
||||
* @param request - fetch request parameters
|
||||
*
|
||||
*
|
||||
* @returns Promise with entity objects keyed by identifier
|
||||
*/
|
||||
async fetch(request: EntityFetchRequest): Promise<Record<string, EntityObject>> {
|
||||
@@ -128,16 +146,38 @@ export const entityService = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Delete an entity
|
||||
*
|
||||
* Delete entities
|
||||
*
|
||||
* @param request - delete request parameters
|
||||
*
|
||||
* @returns Promise with deletion result
|
||||
*
|
||||
* @returns Promise with per-entity disposition results
|
||||
*/
|
||||
async delete(request: EntityDeleteRequest): Promise<EntityDeleteResponse> {
|
||||
return await transceivePost<EntityDeleteRequest, EntityDeleteResponse>('entity.delete', request);
|
||||
},
|
||||
|
||||
/**
|
||||
* Copy entities to another collection
|
||||
*
|
||||
* @param request - copy request parameters
|
||||
*
|
||||
* @returns Promise with per-entity disposition results
|
||||
*/
|
||||
async copy(request: EntityCopyRequest): Promise<EntityCopyResponse> {
|
||||
return await transceivePost<EntityCopyRequest, EntityCopyResponse>('entity.copy', request);
|
||||
},
|
||||
|
||||
/**
|
||||
* Move entities to another collection
|
||||
*
|
||||
* @param request - move request parameters
|
||||
*
|
||||
* @returns Promise with per-entity disposition results
|
||||
*/
|
||||
async move(request: EntityMoveRequest): Promise<EntityMoveResponse> {
|
||||
return await transceivePost<EntityMoveRequest, EntityMoveResponse>('entity.move', request);
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieve delta changes for entities
|
||||
*
|
||||
|
||||
@@ -6,4 +6,3 @@ export { providerService } from './providerService';
|
||||
export { serviceService } from './serviceService';
|
||||
export { collectionService } from './collectionService';
|
||||
export { entityService } from './entityService';
|
||||
export { nodeService } from './nodeService';
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
/**
|
||||
* Node (unified collection/entity) management service
|
||||
*/
|
||||
|
||||
import { transceivePost } from './transceive'
|
||||
import type { ListFilter, ListSort, ListRange } from '../types/common'
|
||||
import type { CollectionInterface } from '../types/collection'
|
||||
import type { EntityInterface } from '../types/entity'
|
||||
|
||||
export type NodeItem = CollectionInterface | EntityInterface
|
||||
|
||||
export interface NodeListRequest {
|
||||
provider: string
|
||||
service: string | number
|
||||
location?: string | number | null
|
||||
recursive?: boolean
|
||||
filter?: ListFilter | null
|
||||
sort?: ListSort | null
|
||||
range?: ListRange | null
|
||||
}
|
||||
|
||||
export type NodeListResponse = Record<string, NodeItem>
|
||||
|
||||
export interface NodeDeltaRequest {
|
||||
provider: string
|
||||
service: string | number
|
||||
location?: string | number | null
|
||||
signature: string
|
||||
recursive?: boolean
|
||||
detail?: 'ids' | 'full'
|
||||
}
|
||||
|
||||
export interface NodeDeltaResult {
|
||||
added: Array<string | number | NodeItem>
|
||||
modified: Array<string | number | NodeItem>
|
||||
removed: Array<string | number>
|
||||
signature: string
|
||||
}
|
||||
|
||||
export const nodeService = {
|
||||
|
||||
async list(request: NodeListRequest): Promise<NodeItem[]> {
|
||||
const response = await transceivePost<NodeListRequest, NodeListResponse>('node.list', {
|
||||
provider: request.provider,
|
||||
service: request.service,
|
||||
location: request.location ?? null,
|
||||
recursive: request.recursive ?? false,
|
||||
filter: request.filter ?? null,
|
||||
sort: request.sort ?? null,
|
||||
range: request.range ?? null,
|
||||
})
|
||||
|
||||
return Object.values(response)
|
||||
},
|
||||
|
||||
async delta(request: NodeDeltaRequest): Promise<NodeDeltaResult> {
|
||||
return await transceivePost<NodeDeltaRequest, NodeDeltaResult>('node.delta', {
|
||||
provider: request.provider,
|
||||
service: request.service,
|
||||
location: request.location ?? null,
|
||||
signature: request.signature,
|
||||
recursive: request.recursive ?? false,
|
||||
detail: request.detail ?? 'ids',
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
export default nodeService
|
||||
@@ -4,7 +4,7 @@
|
||||
*/
|
||||
|
||||
import { createFetchWrapper } from '@KTXC';
|
||||
import type { ApiRequest, ApiResponse } from '../types/common';
|
||||
import type { ApiRequest, ApiResponse, ApiStreamResponse } from '../types/common';
|
||||
|
||||
const fetchWrapper = createFetchWrapper();
|
||||
const API_URL = '/m/documents_manager/v1';
|
||||
@@ -40,11 +40,90 @@ export async function transceivePost<TRequest, TResponse>(
|
||||
};
|
||||
|
||||
const response: ApiResponse<TResponse> = await fetchWrapper.post(API_URL, request);
|
||||
|
||||
|
||||
if (response.status === 'error') {
|
||||
const errorMessage = `[${operation}] ${response.data.message}${response.data.code ? ` (code: ${response.data.code})` : ''}`;
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
|
||||
|
||||
return response.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stream an NDJSON API response, unwrapping data frames for the caller.
|
||||
*
|
||||
* @param operation - Operation name, e.g. 'entity.listStream'
|
||||
* @param data - Operation-specific request data
|
||||
* @param onData - Synchronous callback invoked for every unwrapped data payload.
|
||||
* @param options - Optional `user` override and an `onStart` hook.
|
||||
* @returns Promise resolving to the final stream total from the control/end frame
|
||||
*/
|
||||
export async function transceiveStream<TRequest, TData>(
|
||||
operation: string,
|
||||
data: TRequest,
|
||||
onData: (data: TData) => void,
|
||||
options?: { user?: string; onStart?: (expected?: number) => void }
|
||||
): Promise<{ total: number }> {
|
||||
const request: ApiRequest<TRequest> = {
|
||||
version: API_VERSION,
|
||||
transaction: generateTransactionId(),
|
||||
operation,
|
||||
data,
|
||||
user: options?.user,
|
||||
};
|
||||
|
||||
let total = 0;
|
||||
|
||||
const dispatch = (line: string): void => {
|
||||
const message = JSON.parse(line) as ApiStreamResponse<TData>;
|
||||
|
||||
if (message.type === 'control') {
|
||||
if (message.status === 'start') {
|
||||
options?.onStart?.(message.total);
|
||||
} else if (message.status === 'end') {
|
||||
total = message.total;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (message.type === 'error') {
|
||||
throw new Error(`[${operation}] ${message.message}`);
|
||||
}
|
||||
|
||||
onData(message.data);
|
||||
};
|
||||
|
||||
await fetchWrapper.post(API_URL, request, {
|
||||
headers: { 'Accept': 'application/json' },
|
||||
onStream: async (response: Response) => {
|
||||
if (!response.body) {
|
||||
throw new Error(`[${operation}] Response body is not readable`);
|
||||
}
|
||||
|
||||
const reader = response.body.getReader();
|
||||
const decoder = new TextDecoder();
|
||||
let buffer = '';
|
||||
|
||||
try {
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) break;
|
||||
|
||||
buffer += decoder.decode(value, { stream: true });
|
||||
const lines = buffer.split('\n');
|
||||
buffer = lines.pop()!;
|
||||
|
||||
for (const line of lines) {
|
||||
if (line.trim()) dispatch(line);
|
||||
}
|
||||
}
|
||||
|
||||
if (buffer.trim()) dispatch(buffer);
|
||||
} finally {
|
||||
reader.releaseLock();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
return { total };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user