feat: entity move
All checks were successful
Build Test / test (pull_request) Successful in 27s
JS Unit Tests / test (pull_request) Successful in 28s
PHP Unit Tests / test (pull_request) Successful in 50s

Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
This commit is contained in:
2026-03-28 09:32:04 -04:00
parent 7322bb16da
commit 1c918ca55c
6 changed files with 216 additions and 6 deletions

View File

@@ -18,6 +18,8 @@ import type {
EntityDeleteResponse,
EntityDeltaRequest,
EntityDeltaResponse,
EntityMoveRequest,
EntityMoveResponse,
EntityTransmitRequest,
EntityTransmitResponse,
EntityInterface,
@@ -149,6 +151,17 @@ export const entityService = {
return await transceivePost<EntityDeltaRequest, EntityDeltaResponse>('entity.delta', request);
},
/**
* Move entities to a target collection
*
* @param request - move request parameters
*
* @returns Promise with move results keyed by source entity identifier
*/
async move(request: EntityMoveRequest): Promise<EntityMoveResponse> {
return await transceivePost<EntityMoveRequest, EntityMoveResponse>('entity.move', request);
},
/**
* Send an entity
*

View File

@@ -6,8 +6,20 @@ import { ref, computed, readonly } from 'vue'
import { defineStore } from 'pinia'
import { entityService } from '../services'
import { EntityObject } from '../models'
import type { EntityTransmitRequest, EntityTransmitResponse, EntityStreamRequest } from '../types/entity'
import type { SourceSelector, ListFilter, ListSort, ListRange } from '../types/common'
import type {
EntityMoveResponse,
EntityStreamRequest,
EntityTransmitRequest,
EntityTransmitResponse,
} from '../types/entity'
import type {
CollectionIdentifier,
EntityIdentifier,
ListFilter,
ListRange,
ListSort,
SourceSelector,
} from '../types/common'
export const useEntitiesStore = defineStore('mailEntitiesStore', () => {
// State
@@ -88,6 +100,24 @@ export const useEntitiesStore = defineStore('mailEntitiesStore', () => {
return `${provider}:${service}:${collection}:${identifier}`
}
/**
* Parse a full entity identifier into its components.
*/
function parseEntityIdentifier(identifier: EntityIdentifier): {
provider: string
service: string
collection: string
identifier: string
} {
const [provider, service, collection, entity] = identifier.split(':', 4)
return {
provider,
service,
collection,
identifier: entity,
}
}
// Actions
/**
@@ -315,6 +345,56 @@ export const useEntitiesStore = defineStore('mailEntitiesStore', () => {
}
}
/**
* Move entities to another collection.
*
* Updates local store keys for successfully moved entities when they are
* already present in cache.
*
* @param target - target collection identifier
* @param sources - source entity identifiers
*
* @returns Promise with move results keyed by source identifier
*/
async function move(target: CollectionIdentifier, sources: EntityIdentifier[]): Promise<EntityMoveResponse> {
transceiving.value = true
try {
const response = await entityService.move({ target, sources })
Object.entries(response).forEach(([sourceIdentifier, result]) => {
if (!result.success) {
return
}
const cachedEntity = _entities.value[sourceIdentifier]
if (!cachedEntity) {
return
}
const destination = parseEntityIdentifier(result.identifier)
const movedEntity = cachedEntity.clone().fromJson({
...cachedEntity.toJson(),
provider: destination.provider,
service: destination.service,
collection: destination.collection,
identifier: destination.identifier,
})
delete _entities.value[sourceIdentifier]
_entities.value[result.identifier] = movedEntity
})
console.debug('[Mail Manager][Store] - Successfully moved', Object.keys(response).length, 'entities')
return response
} catch (error: any) {
console.error('[Mail Manager][Store] - Failed to move entities:', error)
throw error
} finally {
transceiving.value = false
}
}
/**
* Send/transmit an entity
*
@@ -390,6 +470,7 @@ export const useEntitiesStore = defineStore('mailEntitiesStore', () => {
update,
delete: remove,
delta,
move,
transmit,
stream,
}

View File

@@ -113,6 +113,10 @@ export type CollectionSelector = {
export type EntitySelector = (string | number)[];
export type ProviderIdentifier = `${string}`;
export type ServiceIdentifier = `${string}:${string}`;
export type CollectionIdentifier = `${string}:${string}:${string}`;
export type EntityIdentifier = `${string}:${string}:${string}:${string}`;
/**
* Filter comparison for list operations

View File

@@ -2,10 +2,12 @@
* Entity type definitions
*/
import type {
CollectionIdentifier,
EntityIdentifier,
SourceSelector,
ListFilter,
ListRange,
ListSort,
SourceSelector,
} from './common';
import type { MessageInterface } from './message';
@@ -133,6 +135,28 @@ export interface EntityDeltaResponse {
};
}
/**
* Entity move
*/
export interface EntityMoveRequest {
target: CollectionIdentifier;
sources: EntityIdentifier[];
}
export interface EntityMoveResultSuccess {
success: boolean;
identifier: EntityIdentifier;
}
export interface EntityMoveResultFailure {
success: boolean;
error: string;
}
export interface EntityMoveResponse {
[sourceIdentifier: EntityIdentifier]: EntityMoveResultSuccess | EntityMoveResultFailure;
}
/**
* Entity transmit
*/