refactor: front end
Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
This commit is contained in:
@@ -0,0 +1,212 @@
|
||||
/**
|
||||
* Collections Store
|
||||
*/
|
||||
|
||||
import { ref, computed, readonly } from 'vue'
|
||||
import { defineStore } from 'pinia'
|
||||
import { collectionService } from '../services/collectionService'
|
||||
import type {
|
||||
SourceSelector,
|
||||
ListFilter,
|
||||
ListSort,
|
||||
CollectionMutableProperties,
|
||||
CollectionDeleteResponse,
|
||||
} from '../types'
|
||||
import { CollectionObject } from '../models/collection'
|
||||
|
||||
export const useCollectionsStore = defineStore('documentsCollectionsStore', () => {
|
||||
// State
|
||||
const _collections = ref<Record<string, CollectionObject>>({})
|
||||
const transceiving = ref(false)
|
||||
|
||||
// Getters
|
||||
const count = computed(() => Object.keys(_collections.value).length)
|
||||
const has = computed(() => count.value > 0)
|
||||
const collections = computed(() => Object.values(_collections.value))
|
||||
|
||||
const collectionsByService = computed(() => {
|
||||
const groups: Record<string, CollectionObject[]> = {}
|
||||
|
||||
Object.values(_collections.value).forEach((collection) => {
|
||||
const serviceKey = `${collection.provider}:${collection.service}`
|
||||
const serviceCollections = (groups[serviceKey] ??= [])
|
||||
serviceCollections.push(collection)
|
||||
})
|
||||
|
||||
return groups
|
||||
})
|
||||
|
||||
function identifierKey(provider: string, service: string | number | null, identifier: string | number | null): string {
|
||||
return `${provider}:${service ?? ''}:${identifier ?? ''}`
|
||||
}
|
||||
|
||||
function collection(provider: string, service: string | number, identifier: string | number, retrieve: boolean = false): CollectionObject | null {
|
||||
const key = identifierKey(provider, service, identifier)
|
||||
if (retrieve === true && !_collections.value[key]) {
|
||||
console.debug(`[Documents Manager][Store] - Force fetching collection "${key}"`)
|
||||
fetch(provider, service, identifier)
|
||||
}
|
||||
|
||||
return _collections.value[key] || null
|
||||
}
|
||||
|
||||
function collectionsForService(provider: string, service: string | number): CollectionObject[] {
|
||||
const serviceKeyPrefix = `${provider}:${service}:`
|
||||
return Object.entries(_collections.value)
|
||||
.filter(([key]) => key.startsWith(serviceKeyPrefix))
|
||||
.map(([_, collectionObj]) => collectionObj)
|
||||
}
|
||||
|
||||
function clearService(provider: string, service: string | number): void {
|
||||
const serviceKeyPrefix = `${provider}:${service}:`
|
||||
Object.keys(_collections.value)
|
||||
.filter((key) => key.startsWith(serviceKeyPrefix))
|
||||
.forEach((key) => {
|
||||
delete _collections.value[key]
|
||||
})
|
||||
}
|
||||
|
||||
function clearAll(): void {
|
||||
_collections.value = {}
|
||||
}
|
||||
|
||||
// Actions
|
||||
async function list(sources?: SourceSelector, filter?: ListFilter, sort?: ListSort): Promise<Record<string, CollectionObject>> {
|
||||
transceiving.value = true
|
||||
try {
|
||||
const response = await collectionService.list({ sources, filter, sort })
|
||||
|
||||
const hydrated: Record<string, CollectionObject> = {}
|
||||
Object.entries(response).forEach(([providerId, providerServices]) => {
|
||||
Object.entries(providerServices).forEach(([serviceId, serviceCollections]) => {
|
||||
Object.entries(serviceCollections).forEach(([collectionId, collectionObj]) => {
|
||||
const key = identifierKey(providerId, serviceId, collectionId)
|
||||
hydrated[key] = collectionObj
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
_collections.value = { ..._collections.value, ...hydrated }
|
||||
|
||||
console.debug('[Documents Manager][Store] - Successfully retrieved', Object.keys(hydrated).length, 'collections')
|
||||
return hydrated
|
||||
} catch (error: any) {
|
||||
console.error('[Documents Manager][Store] - Failed to retrieve collections:', error)
|
||||
throw error
|
||||
} finally {
|
||||
transceiving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function fetch(provider: string, service: string | number, identifier: string | number): Promise<CollectionObject> {
|
||||
transceiving.value = true
|
||||
try {
|
||||
const response = await collectionService.fetch({ provider, service, collection: identifier })
|
||||
const key = identifierKey(response.provider, response.service, response.identifier)
|
||||
_collections.value[key] = response
|
||||
|
||||
console.debug('[Documents Manager][Store] - Successfully fetched collection:', key)
|
||||
return response
|
||||
} catch (error: any) {
|
||||
console.error('[Documents Manager][Store] - Failed to fetch collection:', error)
|
||||
throw error
|
||||
} finally {
|
||||
transceiving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function extant(sources: SourceSelector) {
|
||||
transceiving.value = true
|
||||
try {
|
||||
const response = await collectionService.extant({ sources })
|
||||
console.debug('[Documents Manager][Store] - Successfully checked collection availability')
|
||||
return response
|
||||
} catch (error: any) {
|
||||
console.error('[Documents Manager][Store] - Failed to check collection availability:', error)
|
||||
throw error
|
||||
} finally {
|
||||
transceiving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function create(
|
||||
provider: string,
|
||||
service: string | number,
|
||||
collection: string | number | null,
|
||||
properties: CollectionMutableProperties,
|
||||
): Promise<CollectionObject> {
|
||||
transceiving.value = true
|
||||
try {
|
||||
const response = await collectionService.create({ provider, service, collection, properties })
|
||||
const key = identifierKey(response.provider, response.service, response.identifier)
|
||||
_collections.value[key] = response
|
||||
|
||||
console.debug('[Documents Manager][Store] - Successfully created collection:', key)
|
||||
return response
|
||||
} catch (error: any) {
|
||||
console.error('[Documents Manager][Store] - Failed to create collection:', error)
|
||||
throw error
|
||||
} finally {
|
||||
transceiving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function update(
|
||||
provider: string,
|
||||
service: string | number,
|
||||
identifier: string | number,
|
||||
properties: CollectionMutableProperties,
|
||||
): Promise<CollectionObject> {
|
||||
transceiving.value = true
|
||||
try {
|
||||
const response = await collectionService.update({ provider, service, identifier, properties })
|
||||
const key = identifierKey(response.provider, response.service, response.identifier)
|
||||
_collections.value[key] = response
|
||||
|
||||
console.debug('[Documents Manager][Store] - Successfully updated collection:', key)
|
||||
return response
|
||||
} catch (error: any) {
|
||||
console.error('[Documents Manager][Store] - Failed to update collection:', error)
|
||||
throw error
|
||||
} finally {
|
||||
transceiving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function remove(provider: string, service: string | number, identifier: string | number): Promise<CollectionDeleteResponse> {
|
||||
transceiving.value = true
|
||||
try {
|
||||
const response = await collectionService.delete({ provider, service, identifier })
|
||||
if (response.success) {
|
||||
const key = identifierKey(provider, service, identifier)
|
||||
delete _collections.value[key]
|
||||
}
|
||||
|
||||
console.debug('[Documents Manager][Store] - Successfully deleted collection:', `${provider}:${service}:${identifier}`)
|
||||
return response
|
||||
} catch (error: any) {
|
||||
console.error('[Documents Manager][Store] - Failed to delete collection:', error)
|
||||
throw error
|
||||
} finally {
|
||||
transceiving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
transceiving: readonly(transceiving),
|
||||
count,
|
||||
has,
|
||||
collections,
|
||||
collectionsByService,
|
||||
collection,
|
||||
collectionsForService,
|
||||
clearService,
|
||||
clearAll,
|
||||
list,
|
||||
fetch,
|
||||
extant,
|
||||
create,
|
||||
update,
|
||||
delete: remove,
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,334 @@
|
||||
/**
|
||||
* Entities Store
|
||||
*/
|
||||
|
||||
import { ref, computed, readonly } from 'vue'
|
||||
import { defineStore } from 'pinia'
|
||||
import { entityService } from '../services/entityService'
|
||||
import { EntityObject } from '../models'
|
||||
import type {
|
||||
SourceSelector,
|
||||
ListFilter,
|
||||
ListSort,
|
||||
ListRange,
|
||||
DocumentInterface,
|
||||
EntityDeleteResponse,
|
||||
EntityDeltaResponse,
|
||||
} from '../types'
|
||||
|
||||
export const useEntitiesStore = defineStore('documentsEntitiesStore', () => {
|
||||
// State
|
||||
const _entities = ref<Record<string, EntityObject>>({})
|
||||
const transceiving = ref(false)
|
||||
|
||||
// Getters
|
||||
const count = computed(() => Object.keys(_entities.value).length)
|
||||
const has = computed(() => count.value > 0)
|
||||
const entities = computed(() => Object.values(_entities.value))
|
||||
|
||||
const entitiesByService = computed(() => {
|
||||
const groups: Record<string, EntityObject[]> = {}
|
||||
|
||||
Object.values(_entities.value).forEach((entity) => {
|
||||
const serviceKey = `${entity.provider}:${entity.service}`
|
||||
const serviceEntities = (groups[serviceKey] ??= [])
|
||||
serviceEntities.push(entity)
|
||||
})
|
||||
|
||||
return groups
|
||||
})
|
||||
|
||||
function identifierKey(
|
||||
provider: string,
|
||||
service: string | number,
|
||||
collection: string | number,
|
||||
identifier: string | number,
|
||||
): string {
|
||||
return `${provider}:${service}:${collection}:${identifier}`
|
||||
}
|
||||
|
||||
function entity(
|
||||
provider: string,
|
||||
service: string | number,
|
||||
collection: string | number,
|
||||
identifier: string | number,
|
||||
retrieve: boolean = false,
|
||||
): EntityObject | null {
|
||||
const key = identifierKey(provider, service, collection, identifier)
|
||||
if (retrieve === true && !_entities.value[key]) {
|
||||
console.debug(`[Documents Manager][Store] - Force fetching entity "${key}"`)
|
||||
fetch(provider, service, collection, [identifier])
|
||||
}
|
||||
|
||||
return _entities.value[key] || null
|
||||
}
|
||||
|
||||
function entitiesForService(provider: string, service: string | number): EntityObject[] {
|
||||
const serviceKeyPrefix = `${provider}:${service}:`
|
||||
return Object.entries(_entities.value)
|
||||
.filter(([key]) => key.startsWith(serviceKeyPrefix))
|
||||
.map(([_, entityObj]) => entityObj)
|
||||
}
|
||||
|
||||
function entitiesForCollection(provider: string, service: string | number, collection: string | number): EntityObject[] {
|
||||
const collectionKeyPrefix = `${provider}:${service}:${collection}:`
|
||||
return Object.entries(_entities.value)
|
||||
.filter(([key]) => key.startsWith(collectionKeyPrefix))
|
||||
.map(([_, entityObj]) => entityObj)
|
||||
}
|
||||
|
||||
function clearService(provider: string, service: string | number): void {
|
||||
const serviceKeyPrefix = `${provider}:${service}:`
|
||||
Object.keys(_entities.value)
|
||||
.filter((key) => key.startsWith(serviceKeyPrefix))
|
||||
.forEach((key) => {
|
||||
delete _entities.value[key]
|
||||
})
|
||||
}
|
||||
|
||||
function clearCollection(provider: string, service: string | number, collection: string | number): void {
|
||||
const collectionKeyPrefix = `${provider}:${service}:${collection}:`
|
||||
Object.keys(_entities.value)
|
||||
.filter((key) => key.startsWith(collectionKeyPrefix))
|
||||
.forEach((key) => {
|
||||
delete _entities.value[key]
|
||||
})
|
||||
}
|
||||
|
||||
function clearAll(): void {
|
||||
_entities.value = {}
|
||||
}
|
||||
|
||||
// Actions
|
||||
async function list(sources?: SourceSelector, filter?: ListFilter, sort?: ListSort, range?: ListRange): Promise<Record<string, EntityObject>> {
|
||||
transceiving.value = true
|
||||
try {
|
||||
const response = await entityService.list({ sources, filter, sort, range })
|
||||
|
||||
const hydrated: Record<string, EntityObject> = {}
|
||||
Object.entries(response).forEach(([providerId, providerServices]) => {
|
||||
Object.entries(providerServices).forEach(([serviceId, serviceCollections]) => {
|
||||
Object.entries(serviceCollections).forEach(([collectionId, collectionEntities]) => {
|
||||
Object.entries(collectionEntities).forEach(([entityId, entityObj]) => {
|
||||
const key = identifierKey(providerId, serviceId, collectionId, entityId)
|
||||
hydrated[key] = entityObj
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
_entities.value = { ..._entities.value, ...hydrated }
|
||||
|
||||
console.debug('[Documents Manager][Store] - Successfully retrieved', Object.keys(hydrated).length, 'entities')
|
||||
return hydrated
|
||||
} catch (error: any) {
|
||||
console.error('[Documents Manager][Store] - Failed to retrieve entities:', error)
|
||||
throw error
|
||||
} finally {
|
||||
transceiving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function fetch(
|
||||
provider: string,
|
||||
service: string | number,
|
||||
collection: string | number,
|
||||
identifiers: (string | number)[],
|
||||
): Promise<Record<string, EntityObject>> {
|
||||
transceiving.value = true
|
||||
try {
|
||||
const response = await entityService.fetch({ provider, service, collection, identifiers })
|
||||
|
||||
const hydrated: Record<string, EntityObject> = {}
|
||||
Object.entries(response).forEach(([identifier, entityObj]) => {
|
||||
const key = identifierKey(provider, service, collection, identifier)
|
||||
hydrated[key] = entityObj
|
||||
_entities.value[key] = entityObj
|
||||
})
|
||||
|
||||
console.debug('[Documents Manager][Store] - Successfully fetched', Object.keys(hydrated).length, 'entities')
|
||||
return hydrated
|
||||
} catch (error: any) {
|
||||
console.error('[Documents Manager][Store] - Failed to fetch entities:', error)
|
||||
throw error
|
||||
} finally {
|
||||
transceiving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function extant(sources: SourceSelector) {
|
||||
transceiving.value = true
|
||||
try {
|
||||
const response = await entityService.extant({ sources })
|
||||
console.debug('[Documents Manager][Store] - Successfully checked entity availability')
|
||||
return response
|
||||
} catch (error: any) {
|
||||
console.error('[Documents Manager][Store] - Failed to check entity availability:', error)
|
||||
throw error
|
||||
} finally {
|
||||
transceiving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function create(
|
||||
provider: string,
|
||||
service: string | number,
|
||||
collection: string | number,
|
||||
properties: DocumentInterface,
|
||||
options?: Record<string, unknown>,
|
||||
): Promise<EntityObject> {
|
||||
transceiving.value = true
|
||||
try {
|
||||
const response = await entityService.create({ provider, service, collection, properties, options })
|
||||
const key = identifierKey(response.provider, response.service, response.collection, response.identifier)
|
||||
_entities.value[key] = response
|
||||
|
||||
console.debug('[Documents Manager][Store] - Successfully created entity:', key)
|
||||
return response
|
||||
} catch (error: any) {
|
||||
console.error('[Documents Manager][Store] - Failed to create entity:', error)
|
||||
throw error
|
||||
} finally {
|
||||
transceiving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function update(
|
||||
provider: string,
|
||||
service: string | number,
|
||||
collection: string | number,
|
||||
identifier: string | number,
|
||||
properties: DocumentInterface,
|
||||
): Promise<EntityObject> {
|
||||
transceiving.value = true
|
||||
try {
|
||||
const response = await entityService.update({ provider, service, collection, identifier, properties })
|
||||
const key = identifierKey(response.provider, response.service, response.collection, response.identifier)
|
||||
_entities.value[key] = response
|
||||
|
||||
console.debug('[Documents Manager][Store] - Successfully updated entity:', key)
|
||||
return response
|
||||
} catch (error: any) {
|
||||
console.error('[Documents Manager][Store] - Failed to update entity:', error)
|
||||
throw error
|
||||
} finally {
|
||||
transceiving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function remove(
|
||||
provider: string,
|
||||
service: string | number,
|
||||
collection: string | number,
|
||||
identifier: string | number,
|
||||
): Promise<EntityDeleteResponse> {
|
||||
transceiving.value = true
|
||||
try {
|
||||
const response = await entityService.delete({ provider, service, collection, identifier })
|
||||
if (response.success) {
|
||||
const key = identifierKey(provider, service, collection, identifier)
|
||||
delete _entities.value[key]
|
||||
}
|
||||
|
||||
console.debug('[Documents Manager][Store] - Successfully deleted entity:', `${provider}:${service}:${collection}:${identifier}`)
|
||||
return response
|
||||
} catch (error: any) {
|
||||
console.error('[Documents Manager][Store] - Failed to delete entity:', error)
|
||||
throw error
|
||||
} finally {
|
||||
transceiving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function delta(sources: SourceSelector): Promise<EntityDeltaResponse> {
|
||||
transceiving.value = true
|
||||
try {
|
||||
const response = await entityService.delta({ sources })
|
||||
|
||||
Object.entries(response).forEach(([provider, providerData]) => {
|
||||
if (providerData === false) return
|
||||
|
||||
Object.entries(providerData).forEach(([service, serviceData]) => {
|
||||
if (serviceData === false) return
|
||||
|
||||
Object.entries(serviceData).forEach(([collection, collectionData]) => {
|
||||
if (collectionData === false) return
|
||||
|
||||
collectionData.deletions.forEach((identifier) => {
|
||||
const key = identifierKey(provider, service, collection, identifier)
|
||||
delete _entities.value[key]
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
console.debug('[Documents Manager][Store] - Successfully processed entity delta')
|
||||
return response
|
||||
} catch (error: any) {
|
||||
console.error('[Documents Manager][Store] - Failed to process entity delta:', error)
|
||||
throw error
|
||||
} finally {
|
||||
transceiving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function read(
|
||||
provider: string,
|
||||
service: string | number,
|
||||
collection: string | number,
|
||||
identifier: string | number,
|
||||
): Promise<string | null> {
|
||||
transceiving.value = true
|
||||
try {
|
||||
const response = await entityService.read({ provider, service, collection, identifier })
|
||||
return response.content
|
||||
} catch (error: any) {
|
||||
console.error('[Documents Manager][Store] - Failed to read entity:', error)
|
||||
throw error
|
||||
} finally {
|
||||
transceiving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function write(
|
||||
provider: string,
|
||||
service: string | number,
|
||||
collection: string | number,
|
||||
identifier: string | number,
|
||||
content: string,
|
||||
): Promise<number> {
|
||||
transceiving.value = true
|
||||
try {
|
||||
const response = await entityService.write({ provider, service, collection, identifier, content, encoding: 'base64' })
|
||||
return response.bytesWritten
|
||||
} catch (error: any) {
|
||||
console.error('[Documents Manager][Store] - Failed to write entity:', error)
|
||||
throw error
|
||||
} finally {
|
||||
transceiving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
transceiving: readonly(transceiving),
|
||||
count,
|
||||
has,
|
||||
entities,
|
||||
entitiesByService,
|
||||
entity,
|
||||
entitiesForService,
|
||||
entitiesForCollection,
|
||||
clearService,
|
||||
clearCollection,
|
||||
clearAll,
|
||||
list,
|
||||
fetch,
|
||||
extant,
|
||||
create,
|
||||
update,
|
||||
delete: remove,
|
||||
delta,
|
||||
read,
|
||||
write,
|
||||
}
|
||||
})
|
||||
+3
-1
@@ -1,3 +1,5 @@
|
||||
export { useProvidersStore } from './providersStore';
|
||||
export { useServicesStore } from './servicesStore';
|
||||
export { useNodesStore, ROOT_ID } from './nodesStore';
|
||||
export { useCollectionsStore } from './collectionsStore';
|
||||
export { useEntitiesStore } from './entitiesStore';
|
||||
export { useNodesStore, ROOT_ID } from './nodesStore.ts';
|
||||
+228
-574
@@ -1,646 +1,322 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref, computed } from 'vue'
|
||||
import type { Ref, ComputedRef } from 'vue'
|
||||
import type { FileNode, FileCollection, FileEntity } from '../types/node'
|
||||
import type { FilterCondition, SortCondition, RangeCondition } from '../types/common'
|
||||
import { isFileCollection } from '../types/node'
|
||||
import { collectionService } from '../services/collectionService'
|
||||
import { entityService } from '../services/entityService'
|
||||
import { nodeService } from '../services/nodeService'
|
||||
import { FileCollectionObject } from '../models/collection'
|
||||
import { FileEntityObject } from '../models/entity'
|
||||
/**
|
||||
* Nodes Store (thin wrapper over collections/entities stores)
|
||||
*/
|
||||
|
||||
import { computed, ref, readonly } from 'vue'
|
||||
import { defineStore } from 'pinia'
|
||||
import type {
|
||||
SourceSelector,
|
||||
ListFilter,
|
||||
ListSort,
|
||||
ListRange,
|
||||
CollectionMutableProperties,
|
||||
DocumentInterface,
|
||||
} from '../types'
|
||||
import { CollectionObject, EntityObject } from '../models'
|
||||
import { useCollectionsStore } from './collectionsStore'
|
||||
import { useEntitiesStore } from './entitiesStore'
|
||||
|
||||
// Root collection constant
|
||||
export const ROOT_ID = '00000000-0000-0000-0000-000000000000'
|
||||
|
||||
// Store structure: provider -> service -> nodeId -> node (either collection or entity object)
|
||||
type NodeRecord = FileCollectionObject | FileEntityObject
|
||||
type ServiceNodeStore = Record<string, NodeRecord>
|
||||
type ProviderNodeStore = Record<string, ServiceNodeStore>
|
||||
type NodeStore = Record<string, ProviderNodeStore>
|
||||
type NodeRecord = CollectionObject | EntityObject
|
||||
|
||||
export const useNodesStore = defineStore('fileNodes', () => {
|
||||
const nodes: Ref<NodeStore> = ref({})
|
||||
const syncTokens: Ref<Record<string, Record<string, string>>> = ref({}) // provider -> service -> token
|
||||
const loading = ref(false)
|
||||
const error: Ref<string | null> = ref(null)
|
||||
export const useNodesStore = defineStore('documentsNodesStore', () => {
|
||||
const collectionsStore = useCollectionsStore()
|
||||
const entitiesStore = useEntitiesStore()
|
||||
|
||||
// Computed: flat list of all nodes
|
||||
const nodeList: ComputedRef<NodeRecord[]> = computed(() => {
|
||||
const result: NodeRecord[] = []
|
||||
Object.values(nodes.value).forEach(providerNodes => {
|
||||
Object.values(providerNodes).forEach(serviceNodes => {
|
||||
result.push(...Object.values(serviceNodes))
|
||||
const error = ref<string | null>(null)
|
||||
|
||||
const transceiving = computed(() => collectionsStore.transceiving || entitiesStore.transceiving)
|
||||
|
||||
const nodeList = computed<NodeRecord[]>(() => {
|
||||
return [...collectionsStore.collections, ...entitiesStore.entities]
|
||||
})
|
||||
|
||||
const collectionList = computed<CollectionObject[]>(() => collectionsStore.collections)
|
||||
const entityList = computed<EntityObject[]>(() => entitiesStore.entities)
|
||||
|
||||
function toId(value: string | number | null | undefined): string | null {
|
||||
if (value === null || value === undefined || value === '') {
|
||||
return null
|
||||
}
|
||||
|
||||
return String(value)
|
||||
}
|
||||
|
||||
function getServiceNodes(providerId: string, serviceId: string | number): NodeRecord[] {
|
||||
return [
|
||||
...collectionsStore.collectionsForService(providerId, serviceId),
|
||||
...entitiesStore.entitiesForService(providerId, serviceId),
|
||||
]
|
||||
}
|
||||
|
||||
function getNode(providerId: string, serviceId: string | number, nodeId: string | number): NodeRecord | undefined {
|
||||
const targetId = String(nodeId)
|
||||
return getServiceNodes(providerId, serviceId)
|
||||
.find((node) => String(node.identifier) === targetId)
|
||||
}
|
||||
|
||||
function isRoot(nodeId: string | number | null | undefined): boolean {
|
||||
const normalized = toId(nodeId)
|
||||
return normalized === null || normalized === ROOT_ID
|
||||
}
|
||||
|
||||
function getChildren(providerId: string, serviceId: string | number, parentId: string | number | null): NodeRecord[] {
|
||||
const serviceNodes = getServiceNodes(providerId, serviceId)
|
||||
|
||||
if (isRoot(parentId)) {
|
||||
return serviceNodes.filter((node) => {
|
||||
const parent = toId(node.collection)
|
||||
return parent === null || parent === ROOT_ID
|
||||
})
|
||||
})
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
// Computed: all collections (folders)
|
||||
const collectionList: ComputedRef<FileCollectionObject[]> = computed(() => {
|
||||
return nodeList.value.filter(
|
||||
(node): node is FileCollectionObject => node['@type'] === 'files.collection'
|
||||
)
|
||||
})
|
||||
|
||||
// Computed: all entities (files)
|
||||
const entityList: ComputedRef<FileEntityObject[]> = computed(() => {
|
||||
return nodeList.value.filter(
|
||||
(node): node is FileEntityObject => node['@type'] === 'files.entity'
|
||||
)
|
||||
})
|
||||
|
||||
// Get a specific node
|
||||
const getNode = (
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
nodeId: string
|
||||
): NodeRecord | undefined => {
|
||||
return nodes.value[providerId]?.[serviceId]?.[nodeId]
|
||||
const targetParent = String(parentId)
|
||||
return serviceNodes.filter((node) => toId(node.collection) === targetParent)
|
||||
}
|
||||
|
||||
// Get all nodes for a service
|
||||
const getServiceNodes = (
|
||||
providerId: string,
|
||||
serviceId: string
|
||||
): NodeRecord[] => {
|
||||
return Object.values(nodes.value[providerId]?.[serviceId] || {})
|
||||
function getChildCollections(providerId: string, serviceId: string | number, parentId: string | number | null): CollectionObject[] {
|
||||
return getChildren(providerId, serviceId, parentId)
|
||||
.filter((node): node is CollectionObject => node instanceof CollectionObject)
|
||||
}
|
||||
|
||||
// Get children of a parent node (or root nodes if parentId is null/ROOT_ID)
|
||||
const getChildren = (
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
parentId: string | null
|
||||
): NodeRecord[] => {
|
||||
const serviceNodes = nodes.value[providerId]?.[serviceId] || {}
|
||||
const targetParent = parentId === ROOT_ID ? ROOT_ID : parentId
|
||||
return Object.values(serviceNodes).filter(node => node.in === targetParent)
|
||||
function getChildEntities(providerId: string, serviceId: string | number, parentId: string | number | null): EntityObject[] {
|
||||
return getChildren(providerId, serviceId, parentId)
|
||||
.filter((node): node is EntityObject => node instanceof EntityObject)
|
||||
}
|
||||
|
||||
// Get child collections (folders)
|
||||
const getChildCollections = (
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
parentId: string | null
|
||||
): FileCollectionObject[] => {
|
||||
return getChildren(providerId, serviceId, parentId).filter(
|
||||
(node): node is FileCollectionObject => node['@type'] === 'files.collection'
|
||||
)
|
||||
}
|
||||
|
||||
// Get child entities (files)
|
||||
const getChildEntities = (
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
parentId: string | null
|
||||
): FileEntityObject[] => {
|
||||
return getChildren(providerId, serviceId, parentId).filter(
|
||||
(node): node is FileEntityObject => node['@type'] === 'files.entity'
|
||||
)
|
||||
}
|
||||
|
||||
// Get path to root (ancestors)
|
||||
const getPath = (
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
nodeId: string
|
||||
): NodeRecord[] => {
|
||||
function getPath(providerId: string, serviceId: string | number, nodeId: string | number): NodeRecord[] {
|
||||
const path: NodeRecord[] = []
|
||||
let currentNode = getNode(providerId, serviceId, nodeId)
|
||||
|
||||
while (currentNode) {
|
||||
path.unshift(currentNode)
|
||||
if (currentNode.in === null || currentNode.in === ROOT_ID || currentNode.id === ROOT_ID) {
|
||||
let current = getNode(providerId, serviceId, nodeId)
|
||||
|
||||
while (current) {
|
||||
path.unshift(current)
|
||||
const parentId = toId(current.collection)
|
||||
|
||||
if (parentId === null || parentId === ROOT_ID) {
|
||||
break
|
||||
}
|
||||
currentNode = getNode(providerId, serviceId, currentNode.in)
|
||||
|
||||
current = getNode(providerId, serviceId, parentId)
|
||||
}
|
||||
|
||||
|
||||
return path
|
||||
}
|
||||
|
||||
// Check if a node is the root
|
||||
const isRoot = (nodeId: string): boolean => {
|
||||
return nodeId === ROOT_ID
|
||||
}
|
||||
|
||||
// Helper to hydrate a node based on its type
|
||||
const hydrateNode = (data: FileNode): NodeRecord => {
|
||||
if (isFileCollection(data)) {
|
||||
return new FileCollectionObject().fromJson(data)
|
||||
} else {
|
||||
return new FileEntityObject().fromJson(data as FileEntity)
|
||||
}
|
||||
}
|
||||
|
||||
// Set all nodes for a provider/service
|
||||
const setNodes = (
|
||||
async function fetchCollections(
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
data: FileNode[]
|
||||
) => {
|
||||
if (!nodes.value[providerId]) {
|
||||
nodes.value[providerId] = {}
|
||||
}
|
||||
const hydrated: ServiceNodeStore = {}
|
||||
for (const nodeData of data) {
|
||||
hydrated[nodeData.id] = hydrateNode(nodeData)
|
||||
}
|
||||
nodes.value[providerId][serviceId] = hydrated
|
||||
}
|
||||
|
||||
// Add/update a single node
|
||||
const addNode = (
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
node: FileNode
|
||||
) => {
|
||||
if (!nodes.value[providerId]) {
|
||||
nodes.value[providerId] = {}
|
||||
}
|
||||
if (!nodes.value[providerId][serviceId]) {
|
||||
nodes.value[providerId][serviceId] = {}
|
||||
}
|
||||
nodes.value[providerId][serviceId][node.id] = hydrateNode(node)
|
||||
}
|
||||
|
||||
// Add multiple nodes (handles both array and object formats from API)
|
||||
const addNodes = (
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
data: FileNode[] | Record<string, FileNode>
|
||||
) => {
|
||||
if (!nodes.value[providerId]) {
|
||||
nodes.value[providerId] = {}
|
||||
}
|
||||
if (!nodes.value[providerId][serviceId]) {
|
||||
nodes.value[providerId][serviceId] = {}
|
||||
}
|
||||
// Handle both array and object (keyed by ID) formats
|
||||
const nodeArray = Array.isArray(data) ? data : Object.values(data)
|
||||
for (const nodeData of nodeArray) {
|
||||
nodes.value[providerId][serviceId][nodeData.id] = hydrateNode(nodeData)
|
||||
}
|
||||
}
|
||||
|
||||
// Remove a node
|
||||
const removeNode = (
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
nodeId: string
|
||||
) => {
|
||||
if (nodes.value[providerId]?.[serviceId]) {
|
||||
delete nodes.value[providerId][serviceId][nodeId]
|
||||
}
|
||||
}
|
||||
|
||||
// Remove multiple nodes
|
||||
const removeNodes = (
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
nodeIds: string[]
|
||||
) => {
|
||||
if (nodes.value[providerId]?.[serviceId]) {
|
||||
for (const id of nodeIds) {
|
||||
delete nodes.value[providerId][serviceId][id]
|
||||
serviceId: string | number,
|
||||
collectionId: string | number | null,
|
||||
filter?: ListFilter,
|
||||
sort?: ListSort,
|
||||
): Promise<CollectionObject[]> {
|
||||
error.value = null
|
||||
try {
|
||||
const sources: SourceSelector = {
|
||||
[providerId]: {
|
||||
[String(serviceId)]: collectionId === null
|
||||
? true
|
||||
: { [String(collectionId)]: true },
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clear all nodes for a service
|
||||
const clearServiceNodes = (
|
||||
providerId: string,
|
||||
serviceId: string
|
||||
) => {
|
||||
if (nodes.value[providerId]) {
|
||||
delete nodes.value[providerId][serviceId]
|
||||
}
|
||||
}
|
||||
|
||||
// Clear all nodes
|
||||
const clearNodes = () => {
|
||||
nodes.value = {}
|
||||
}
|
||||
|
||||
// Sync token management
|
||||
const getSyncToken = (
|
||||
providerId: string,
|
||||
serviceId: string
|
||||
): string | undefined => {
|
||||
return syncTokens.value[providerId]?.[serviceId]
|
||||
}
|
||||
|
||||
const setSyncToken = (
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
token: string
|
||||
) => {
|
||||
if (!syncTokens.value[providerId]) {
|
||||
syncTokens.value[providerId] = {}
|
||||
}
|
||||
syncTokens.value[providerId][serviceId] = token
|
||||
}
|
||||
|
||||
// ==================== API Actions ====================
|
||||
|
||||
// Fetch nodes (collections and entities) for a location
|
||||
const fetchNodes = async (
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
location?: string | null,
|
||||
recursive: boolean = false,
|
||||
filter?: FilterCondition[] | null,
|
||||
sort?: SortCondition[] | null,
|
||||
range?: RangeCondition | null
|
||||
): Promise<NodeRecord[]> => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
try {
|
||||
const data = await nodeService.list(
|
||||
providerId,
|
||||
serviceId,
|
||||
location,
|
||||
recursive,
|
||||
filter,
|
||||
sort,
|
||||
range
|
||||
)
|
||||
// API returns object keyed by ID, convert to array
|
||||
const nodeArray = Array.isArray(data) ? data : Object.values(data)
|
||||
addNodes(providerId, serviceId, nodeArray)
|
||||
return nodeArray.map(hydrateNode)
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : 'Failed to fetch nodes'
|
||||
throw e
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch collections (folders) for a location
|
||||
const fetchCollections = async (
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
location?: string | null,
|
||||
filter?: FilterCondition[] | null,
|
||||
sort?: SortCondition[] | null
|
||||
): Promise<FileCollectionObject[]> => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
try {
|
||||
const data = await collectionService.list(providerId, serviceId, location, filter, sort)
|
||||
// API returns object keyed by ID, convert to array
|
||||
const collectionArray = Array.isArray(data) ? data : Object.values(data)
|
||||
addNodes(providerId, serviceId, collectionArray)
|
||||
return collectionArray.map(c => new FileCollectionObject().fromJson(c))
|
||||
await collectionsStore.list(sources, filter, sort)
|
||||
return collectionsStore.collectionsForService(providerId, serviceId)
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : 'Failed to fetch collections'
|
||||
throw e
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch entities (files) for a collection
|
||||
const fetchEntities = async (
|
||||
async function fetchEntities(
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
collection: string,
|
||||
filter?: FilterCondition[] | null,
|
||||
sort?: SortCondition[] | null,
|
||||
range?: RangeCondition | null
|
||||
): Promise<FileEntityObject[]> => {
|
||||
loading.value = true
|
||||
serviceId: string | number,
|
||||
collectionId: string | number | null,
|
||||
filter?: ListFilter,
|
||||
sort?: ListSort,
|
||||
range?: ListRange,
|
||||
): Promise<EntityObject[]> {
|
||||
error.value = null
|
||||
try {
|
||||
const data = await entityService.list(providerId, serviceId, collection, filter, sort, range)
|
||||
// API returns object keyed by ID, convert to array
|
||||
const entityArray = Array.isArray(data) ? data : Object.values(data)
|
||||
addNodes(providerId, serviceId, entityArray)
|
||||
return entityArray.map(e => new FileEntityObject().fromJson(e))
|
||||
const sources: SourceSelector = {
|
||||
[providerId]: {
|
||||
[String(serviceId)]: collectionId === null
|
||||
? true
|
||||
: { [String(collectionId)]: true },
|
||||
},
|
||||
}
|
||||
|
||||
await entitiesStore.list(sources, filter, sort, range)
|
||||
return entitiesStore.entitiesForCollection(providerId, serviceId, collectionId)
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : 'Failed to fetch entities'
|
||||
throw e
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// Create a collection (folder)
|
||||
const createCollection = async (
|
||||
async function fetchNodes(
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
location: string | null,
|
||||
data: Partial<FileCollection>,
|
||||
options?: Record<string, unknown>
|
||||
): Promise<FileCollectionObject> => {
|
||||
loading.value = true
|
||||
serviceId: string | number,
|
||||
parentId: string | number | null = ROOT_ID,
|
||||
filter?: ListFilter,
|
||||
sort?: ListSort,
|
||||
range?: ListRange,
|
||||
): Promise<NodeRecord[]> {
|
||||
error.value = null
|
||||
try {
|
||||
const created = await collectionService.create(providerId, serviceId, location, data, options)
|
||||
addNode(providerId, serviceId, created)
|
||||
return new FileCollectionObject().fromJson(created)
|
||||
await Promise.all([
|
||||
fetchCollections(providerId, serviceId, parentId, filter, sort),
|
||||
fetchEntities(providerId, serviceId, parentId, filter, sort, range),
|
||||
])
|
||||
|
||||
return getChildren(providerId, serviceId, parentId)
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : 'Failed to fetch nodes'
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
async function createCollection(
|
||||
providerId: string,
|
||||
serviceId: string | number,
|
||||
parentCollectionId: string | number | null,
|
||||
properties: CollectionMutableProperties,
|
||||
): Promise<CollectionObject> {
|
||||
error.value = null
|
||||
try {
|
||||
return await collectionsStore.create(providerId, serviceId, parentCollectionId, properties)
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : 'Failed to create collection'
|
||||
throw e
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// Create an entity (file)
|
||||
const createEntity = async (
|
||||
async function updateCollection(
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
collection: string | null,
|
||||
data: Partial<FileEntity>,
|
||||
options?: Record<string, unknown>
|
||||
): Promise<FileEntityObject> => {
|
||||
loading.value = true
|
||||
serviceId: string | number,
|
||||
identifier: string | number,
|
||||
properties: CollectionMutableProperties,
|
||||
): Promise<CollectionObject> {
|
||||
error.value = null
|
||||
try {
|
||||
const created = await entityService.create(providerId, serviceId, collection, data, options)
|
||||
addNode(providerId, serviceId, created)
|
||||
return new FileEntityObject().fromJson(created)
|
||||
return await collectionsStore.update(providerId, serviceId, identifier, properties)
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : 'Failed to update collection'
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteCollection(
|
||||
providerId: string,
|
||||
serviceId: string | number,
|
||||
identifier: string | number,
|
||||
): Promise<boolean> {
|
||||
error.value = null
|
||||
try {
|
||||
const response = await collectionsStore.delete(providerId, serviceId, identifier)
|
||||
return response.success
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : 'Failed to delete collection'
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
async function createEntity(
|
||||
providerId: string,
|
||||
serviceId: string | number,
|
||||
collectionId: string | number,
|
||||
properties: DocumentInterface,
|
||||
options?: Record<string, unknown>,
|
||||
): Promise<EntityObject> {
|
||||
error.value = null
|
||||
try {
|
||||
return await entitiesStore.create(providerId, serviceId, collectionId, properties, options)
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : 'Failed to create entity'
|
||||
throw e
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// Modify a collection
|
||||
const modifyCollection = async (
|
||||
async function updateEntity(
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
identifier: string,
|
||||
data: Partial<FileCollection>
|
||||
): Promise<FileCollectionObject> => {
|
||||
loading.value = true
|
||||
serviceId: string | number,
|
||||
collectionId: string | number,
|
||||
identifier: string | number,
|
||||
properties: DocumentInterface,
|
||||
): Promise<EntityObject> {
|
||||
error.value = null
|
||||
try {
|
||||
const modified = await collectionService.modify(providerId, serviceId, identifier, data)
|
||||
addNode(providerId, serviceId, modified)
|
||||
return new FileCollectionObject().fromJson(modified)
|
||||
return await entitiesStore.update(providerId, serviceId, collectionId, identifier, properties)
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : 'Failed to modify collection'
|
||||
error.value = e instanceof Error ? e.message : 'Failed to update entity'
|
||||
throw e
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// Modify an entity
|
||||
const modifyEntity = async (
|
||||
async function deleteEntity(
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
collection: string | null,
|
||||
identifier: string,
|
||||
data: Partial<FileEntity>
|
||||
): Promise<FileEntityObject> => {
|
||||
loading.value = true
|
||||
serviceId: string | number,
|
||||
collectionId: string | number,
|
||||
identifier: string | number,
|
||||
): Promise<boolean> {
|
||||
error.value = null
|
||||
try {
|
||||
const modified = await entityService.modify(providerId, serviceId, collection, identifier, data)
|
||||
addNode(providerId, serviceId, modified)
|
||||
return new FileEntityObject().fromJson(modified)
|
||||
const response = await entitiesStore.delete(providerId, serviceId, collectionId, identifier)
|
||||
return response.success
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : 'Failed to modify entity'
|
||||
error.value = e instanceof Error ? e.message : 'Failed to delete entity'
|
||||
throw e
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// Destroy a collection
|
||||
const destroyCollection = async (
|
||||
async function readEntity(
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
identifier: string
|
||||
): Promise<boolean> => {
|
||||
loading.value = true
|
||||
serviceId: string | number,
|
||||
collectionId: string | number,
|
||||
identifier: string | number,
|
||||
): Promise<string | null> {
|
||||
error.value = null
|
||||
try {
|
||||
const success = await collectionService.destroy(providerId, serviceId, identifier)
|
||||
if (success) {
|
||||
removeNode(providerId, serviceId, identifier)
|
||||
}
|
||||
return success
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : 'Failed to destroy collection'
|
||||
throw e
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// Destroy an entity
|
||||
const destroyEntity = async (
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
collection: string | null,
|
||||
identifier: string
|
||||
): Promise<boolean> => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
try {
|
||||
const success = await entityService.destroy(providerId, serviceId, collection, identifier)
|
||||
if (success) {
|
||||
removeNode(providerId, serviceId, identifier)
|
||||
}
|
||||
return success
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : 'Failed to destroy entity'
|
||||
throw e
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// Copy a collection
|
||||
const copyCollection = async (
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
identifier: string,
|
||||
location?: string | null
|
||||
): Promise<FileCollectionObject> => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
try {
|
||||
const copied = await collectionService.copy(providerId, serviceId, identifier, location)
|
||||
addNode(providerId, serviceId, copied)
|
||||
return new FileCollectionObject().fromJson(copied)
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : 'Failed to copy collection'
|
||||
throw e
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// Copy an entity
|
||||
const copyEntity = async (
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
collection: string | null,
|
||||
identifier: string,
|
||||
destination?: string | null
|
||||
): Promise<FileEntityObject> => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
try {
|
||||
const copied = await entityService.copy(providerId, serviceId, collection, identifier, destination)
|
||||
addNode(providerId, serviceId, copied)
|
||||
return new FileEntityObject().fromJson(copied)
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : 'Failed to copy entity'
|
||||
throw e
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// Move a collection
|
||||
const moveCollection = async (
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
identifier: string,
|
||||
location?: string | null
|
||||
): Promise<FileCollectionObject> => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
try {
|
||||
const moved = await collectionService.move(providerId, serviceId, identifier, location)
|
||||
addNode(providerId, serviceId, moved)
|
||||
return new FileCollectionObject().fromJson(moved)
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : 'Failed to move collection'
|
||||
throw e
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// Move an entity
|
||||
const moveEntity = async (
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
collection: string | null,
|
||||
identifier: string,
|
||||
destination?: string | null
|
||||
): Promise<FileEntityObject> => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
try {
|
||||
const moved = await entityService.move(providerId, serviceId, collection, identifier, destination)
|
||||
addNode(providerId, serviceId, moved)
|
||||
return new FileEntityObject().fromJson(moved)
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : 'Failed to move entity'
|
||||
throw e
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// Read entity content
|
||||
const readEntity = async (
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
collection: string,
|
||||
identifier: string
|
||||
): Promise<string | null> => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
try {
|
||||
const result = await entityService.read(providerId, serviceId, collection, identifier)
|
||||
return result.content
|
||||
return await entitiesStore.read(providerId, serviceId, collectionId, identifier)
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : 'Failed to read entity'
|
||||
throw e
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// Write entity content
|
||||
const writeEntity = async (
|
||||
async function writeEntity(
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
collection: string | null,
|
||||
identifier: string,
|
||||
content: string
|
||||
): Promise<number> => {
|
||||
loading.value = true
|
||||
serviceId: string | number,
|
||||
collectionId: string | number,
|
||||
identifier: string | number,
|
||||
content: string,
|
||||
): Promise<number> {
|
||||
error.value = null
|
||||
try {
|
||||
return await entityService.write(providerId, serviceId, collection, identifier, content)
|
||||
return await entitiesStore.write(providerId, serviceId, collectionId, identifier, content)
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : 'Failed to write entity'
|
||||
throw e
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// Sync delta changes
|
||||
const syncDelta = async (
|
||||
providerId: string,
|
||||
serviceId: string,
|
||||
location: string | null,
|
||||
signature: string,
|
||||
recursive: boolean = false,
|
||||
detail: 'ids' | 'full' = 'full'
|
||||
): Promise<void> => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
try {
|
||||
const delta = await nodeService.delta(providerId, serviceId, location, signature, recursive, detail)
|
||||
|
||||
// Handle removed nodes
|
||||
if (delta.removed.length > 0) {
|
||||
removeNodes(providerId, serviceId, delta.removed)
|
||||
}
|
||||
|
||||
// Handle added/modified nodes
|
||||
if (detail === 'full') {
|
||||
const addedNodes = delta.added as FileNode[]
|
||||
const modifiedNodes = delta.modified as FileNode[]
|
||||
if (addedNodes.length > 0) {
|
||||
addNodes(providerId, serviceId, addedNodes)
|
||||
}
|
||||
if (modifiedNodes.length > 0) {
|
||||
addNodes(providerId, serviceId, modifiedNodes)
|
||||
}
|
||||
}
|
||||
|
||||
// Update sync token
|
||||
if (delta.signature) {
|
||||
setSyncToken(providerId, serviceId, delta.signature)
|
||||
}
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : 'Failed to sync delta'
|
||||
throw e
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
function clearServiceNodes(providerId: string, serviceId: string | number): void {
|
||||
collectionsStore.clearService(providerId, serviceId)
|
||||
entitiesStore.clearService(providerId, serviceId)
|
||||
}
|
||||
|
||||
function clearNodes(): void {
|
||||
collectionsStore.clearAll()
|
||||
entitiesStore.clearAll()
|
||||
}
|
||||
|
||||
return {
|
||||
// State
|
||||
nodes,
|
||||
syncTokens,
|
||||
loading,
|
||||
error,
|
||||
// Constants
|
||||
transceiving: readonly(transceiving),
|
||||
error: readonly(error),
|
||||
ROOT_ID,
|
||||
// Computed
|
||||
nodeList,
|
||||
collectionList,
|
||||
entityList,
|
||||
// Getters
|
||||
getNode,
|
||||
getServiceNodes,
|
||||
getChildren,
|
||||
@@ -648,40 +324,18 @@ export const useNodesStore = defineStore('fileNodes', () => {
|
||||
getChildEntities,
|
||||
getPath,
|
||||
isRoot,
|
||||
// Setters
|
||||
setNodes,
|
||||
addNode,
|
||||
addNodes,
|
||||
removeNode,
|
||||
removeNodes,
|
||||
clearServiceNodes,
|
||||
clearNodes,
|
||||
// Sync
|
||||
getSyncToken,
|
||||
setSyncToken,
|
||||
// API Actions - Fetch
|
||||
fetchNodes,
|
||||
fetchCollections,
|
||||
fetchEntities,
|
||||
// API Actions - Create
|
||||
createCollection,
|
||||
updateCollection,
|
||||
deleteCollection,
|
||||
createEntity,
|
||||
// API Actions - Modify
|
||||
modifyCollection,
|
||||
modifyEntity,
|
||||
// API Actions - Destroy
|
||||
destroyCollection,
|
||||
destroyEntity,
|
||||
// API Actions - Copy
|
||||
copyCollection,
|
||||
copyEntity,
|
||||
// API Actions - Move
|
||||
moveCollection,
|
||||
moveEntity,
|
||||
// API Actions - Content
|
||||
updateEntity,
|
||||
deleteEntity,
|
||||
readEntity,
|
||||
writeEntity,
|
||||
// API Actions - Sync
|
||||
syncDelta,
|
||||
clearServiceNodes,
|
||||
clearNodes,
|
||||
}
|
||||
})
|
||||
|
||||
+117
-79
@@ -1,104 +1,142 @@
|
||||
/**
|
||||
* Providers Store
|
||||
*/
|
||||
|
||||
import { ref, computed, readonly } from 'vue'
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref, computed } from 'vue'
|
||||
import type { Ref, ComputedRef } from 'vue'
|
||||
import type { ProviderInterface, ProviderRecord, ProviderCapabilitiesInterface } from '../types/provider'
|
||||
import type { SourceSelector } from '../types/common'
|
||||
import { providerService } from '../services/providerService'
|
||||
import { providerService } from '../services'
|
||||
import { ProviderObject } from '../models/provider'
|
||||
import type { SourceSelector } from '../types'
|
||||
|
||||
export const useProvidersStore = defineStore('fileProviders', () => {
|
||||
const providers: Ref<Record<string, ProviderObject>> = ref({})
|
||||
const loading = ref(false)
|
||||
const error: Ref<string | null> = ref(null)
|
||||
const initialized = ref(false)
|
||||
export const useProvidersStore = defineStore('documentsProvidersStore', () => {
|
||||
// State
|
||||
const _providers = ref<Record<string, ProviderObject>>({})
|
||||
const transceiving = ref(false)
|
||||
|
||||
const providerList: ComputedRef<ProviderObject[]> = computed(() =>
|
||||
Object.values(providers.value)
|
||||
)
|
||||
/**
|
||||
* Get count of providers in store
|
||||
*/
|
||||
const count = computed(() => Object.keys(_providers.value).length)
|
||||
|
||||
const providerIds: ComputedRef<string[]> = computed(() =>
|
||||
Object.keys(providers.value)
|
||||
)
|
||||
/**
|
||||
* Check if any providers are present in store
|
||||
*/
|
||||
const has = computed(() => count.value > 0)
|
||||
|
||||
const getProvider = (id: string): ProviderObject | undefined => {
|
||||
return providers.value[id]
|
||||
}
|
||||
/**
|
||||
* Get all providers present in store
|
||||
*/
|
||||
const providers = computed(() => Object.values(_providers.value))
|
||||
|
||||
const hasProvider = (id: string): boolean => {
|
||||
return id in providers.value
|
||||
}
|
||||
|
||||
const isCapable = (providerId: string, capability: keyof ProviderCapabilitiesInterface): boolean => {
|
||||
const provider = providers.value[providerId]
|
||||
return provider ? provider.capable(capability) : false
|
||||
}
|
||||
|
||||
const setProviders = (data: ProviderRecord) => {
|
||||
const hydrated: Record<string, ProviderObject> = {}
|
||||
for (const [id, providerData] of Object.entries(data)) {
|
||||
hydrated[id] = new ProviderObject().fromJson(providerData)
|
||||
/**
|
||||
* Get a specific provider from store, with optional retrieval
|
||||
*
|
||||
* @param identifier - Provider identifier
|
||||
* @param retrieve - Retrieve behavior: true = fetch if missing or refresh, false = cache only
|
||||
*
|
||||
* @returns Provider object or null
|
||||
*/
|
||||
function provider(identifier: string, retrieve: boolean = false): ProviderObject | null {
|
||||
if (retrieve === true && !_providers.value[identifier]) {
|
||||
console.debug(`[Documents Manager][Store] - Force fetching provider "${identifier}"`)
|
||||
fetch(identifier)
|
||||
}
|
||||
providers.value = hydrated
|
||||
initialized.value = true
|
||||
|
||||
return _providers.value[identifier] || null
|
||||
}
|
||||
|
||||
const addProvider = (id: string, provider: ProviderInterface) => {
|
||||
providers.value[id] = new ProviderObject().fromJson(provider)
|
||||
}
|
||||
// Actions
|
||||
|
||||
const removeProvider = (id: string) => {
|
||||
delete providers.value[id]
|
||||
}
|
||||
|
||||
const clearProviders = () => {
|
||||
providers.value = {}
|
||||
initialized.value = false
|
||||
}
|
||||
|
||||
// API actions
|
||||
const fetchProviders = async (sources?: SourceSelector): Promise<void> => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
/**
|
||||
* Retrieve all or specific providers, optionally filtered by source selector
|
||||
*
|
||||
* @param request - list request parameters
|
||||
*
|
||||
* @returns Promise with provider object list keyed by provider identifier
|
||||
*/
|
||||
async function list(sources?: SourceSelector): Promise<Record<string, ProviderObject>> {
|
||||
transceiving.value = true
|
||||
try {
|
||||
const data = await providerService.list(sources)
|
||||
setProviders(data)
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : 'Failed to fetch providers'
|
||||
throw e
|
||||
const providers = await providerService.list({ sources })
|
||||
|
||||
// Merge retrieved providers into state
|
||||
_providers.value = { ..._providers.value, ...providers }
|
||||
|
||||
console.debug('[Documents Manager][Store] - Successfully retrieved', Object.keys(providers).length, 'providers')
|
||||
return providers
|
||||
} catch (error: any) {
|
||||
console.error('[Documents Manager][Store] - Failed to retrieve providers:', error)
|
||||
throw error
|
||||
} finally {
|
||||
loading.value = false
|
||||
transceiving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const checkProviderExtant = async (sources: SourceSelector): Promise<Record<string, boolean>> => {
|
||||
/**
|
||||
* Retrieve a specific provider by identifier
|
||||
*
|
||||
* @param identifier - provider identifier
|
||||
*
|
||||
* @returns Promise with provider object
|
||||
*/
|
||||
async function fetch(identifier: string): Promise<ProviderObject> {
|
||||
transceiving.value = true
|
||||
try {
|
||||
return await providerService.extant(sources)
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : 'Failed to check providers'
|
||||
throw e
|
||||
const provider = await providerService.fetch({ identifier })
|
||||
|
||||
// Merge fetched provider into state
|
||||
_providers.value[provider.identifier] = provider
|
||||
|
||||
console.debug('[Documents Manager][Store] - Successfully fetched provider:', provider.identifier)
|
||||
return provider
|
||||
} catch (error: any) {
|
||||
console.error('[Documents Manager][Store] - Failed to fetch provider:', error)
|
||||
throw error
|
||||
} finally {
|
||||
transceiving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve provider availability status for a given source selector
|
||||
*
|
||||
* @param sources - source selector to check availability for
|
||||
*
|
||||
* @returns Promise with provider availability status
|
||||
*/
|
||||
async function extant(sources: SourceSelector) {
|
||||
transceiving.value = true
|
||||
try {
|
||||
const response = await providerService.extant({ sources })
|
||||
|
||||
Object.entries(response).forEach(([providerId, providerStatus]) => {
|
||||
if (providerStatus === false) {
|
||||
delete _providers.value[providerId]
|
||||
}
|
||||
})
|
||||
|
||||
console.debug('[Documents Manager][Store] - Successfully checked', sources ? Object.keys(sources).length : 0, 'providers')
|
||||
return response
|
||||
} catch (error: any) {
|
||||
console.error('[Documents Manager][Store] - Failed to check providers:', error)
|
||||
throw error
|
||||
} finally {
|
||||
transceiving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// Return public API
|
||||
return {
|
||||
// State
|
||||
transceiving: readonly(transceiving),
|
||||
// computed
|
||||
count,
|
||||
has,
|
||||
providers,
|
||||
loading,
|
||||
error,
|
||||
initialized,
|
||||
// Computed
|
||||
providerList,
|
||||
providerIds,
|
||||
// Getters
|
||||
getProvider,
|
||||
hasProvider,
|
||||
isCapable,
|
||||
// Setters
|
||||
setProviders,
|
||||
addProvider,
|
||||
removeProvider,
|
||||
clearProviders,
|
||||
// Actions
|
||||
fetchProviders,
|
||||
checkProviderExtant,
|
||||
provider,
|
||||
// functions
|
||||
list,
|
||||
fetch,
|
||||
extant,
|
||||
}
|
||||
})
|
||||
|
||||
+229
-101
@@ -1,131 +1,259 @@
|
||||
/**
|
||||
* Services Store
|
||||
*/
|
||||
|
||||
import { ref, computed, readonly } from 'vue'
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref, computed } from 'vue'
|
||||
import type { Ref, ComputedRef } from 'vue'
|
||||
import type { ServiceInterface, ServiceRecord } from '../types/service'
|
||||
import type { SourceSelector } from '../types/common'
|
||||
import { serviceService } from '../services/serviceService'
|
||||
import { serviceService } from '../services'
|
||||
import { ServiceObject } from '../models/service'
|
||||
import type {
|
||||
SourceSelector,
|
||||
ServiceInterface,
|
||||
} from '../types'
|
||||
|
||||
// Nested structure: provider -> service -> ServiceObject
|
||||
type ServiceStore = Record<string, Record<string, ServiceObject>>
|
||||
export const useServicesStore = defineStore('documentsServicesStore', () => {
|
||||
// State
|
||||
const _services = ref<Record<string, ServiceObject>>({})
|
||||
const transceiving = ref(false)
|
||||
|
||||
export const useServicesStore = defineStore('fileServices', () => {
|
||||
const services: Ref<ServiceStore> = ref({})
|
||||
const loading = ref(false)
|
||||
const error: Ref<string | null> = ref(null)
|
||||
const initialized = ref(false)
|
||||
/**
|
||||
* Get count of services in store
|
||||
*/
|
||||
const count = computed(() => Object.keys(_services.value).length)
|
||||
|
||||
const serviceList: ComputedRef<ServiceObject[]> = computed(() => {
|
||||
const result: ServiceObject[] = []
|
||||
Object.values(services.value).forEach(providerServices => {
|
||||
result.push(...Object.values(providerServices))
|
||||
/**
|
||||
* Check if any services are present in store
|
||||
*/
|
||||
const has = computed(() => count.value > 0)
|
||||
|
||||
/**
|
||||
* Get all services present in store
|
||||
*/
|
||||
const services = computed(() => Object.values(_services.value))
|
||||
|
||||
/**
|
||||
* Get all services present in store grouped by provider
|
||||
*/
|
||||
const servicesByProvider = computed(() => {
|
||||
const groups: Record<string, ServiceObject[]> = {}
|
||||
|
||||
Object.values(_services.value).forEach((service) => {
|
||||
const providerServices = (groups[service.provider] ??= [])
|
||||
providerServices.push(service)
|
||||
})
|
||||
return result
|
||||
|
||||
return groups
|
||||
})
|
||||
|
||||
const getService = (providerId: string, serviceId: string): ServiceObject | undefined => {
|
||||
return services.value[providerId]?.[serviceId]
|
||||
}
|
||||
|
||||
const hasService = (providerId: string, serviceId: string): boolean => {
|
||||
return !!services.value[providerId]?.[serviceId]
|
||||
}
|
||||
|
||||
const getProviderServices = (providerId: string): ServiceObject[] => {
|
||||
return Object.values(services.value[providerId] || {})
|
||||
}
|
||||
|
||||
const getRootId = (providerId: string, serviceId: string): string | undefined => {
|
||||
return services.value[providerId]?.[serviceId]?.rootId
|
||||
}
|
||||
|
||||
const setServices = (data: ServiceRecord) => {
|
||||
const hydrated: ServiceStore = {}
|
||||
for (const [id, serviceData] of Object.entries(data)) {
|
||||
const providerId = serviceData.provider
|
||||
if (!hydrated[providerId]) {
|
||||
hydrated[providerId] = {}
|
||||
}
|
||||
hydrated[providerId][id] = new ServiceObject().fromJson(serviceData)
|
||||
/**
|
||||
* Get a specific service from store, with optional retrieval
|
||||
*
|
||||
* @param provider - provider identifier
|
||||
* @param identifier - service identifier
|
||||
* @param retrieve - Retrieve behavior: true = fetch if missing or refresh, false = cache only
|
||||
*
|
||||
* @returns Service object or null
|
||||
*/
|
||||
function service(provider: string, identifier: string | number, retrieve: boolean = false): ServiceObject | null {
|
||||
const key = identifierKey(provider, identifier)
|
||||
if (retrieve === true && !_services.value[key]) {
|
||||
console.debug(`[Documents Manager][Store] - Force fetching service "${key}"`)
|
||||
fetch(provider, identifier)
|
||||
}
|
||||
services.value = hydrated
|
||||
initialized.value = true
|
||||
|
||||
return _services.value[key] || null
|
||||
}
|
||||
|
||||
const addService = (providerId: string, serviceId: string, service: ServiceInterface) => {
|
||||
if (!services.value[providerId]) {
|
||||
services.value[providerId] = {}
|
||||
}
|
||||
services.value[providerId][serviceId] = new ServiceObject().fromJson(service)
|
||||
/**
|
||||
* Unique key for a service
|
||||
*/
|
||||
function identifierKey(provider: string, identifier: string | number | null): string {
|
||||
return `${provider}:${identifier ?? ''}`
|
||||
}
|
||||
|
||||
const removeService = (providerId: string, serviceId: string) => {
|
||||
if (services.value[providerId]) {
|
||||
delete services.value[providerId][serviceId]
|
||||
}
|
||||
}
|
||||
|
||||
const clearServices = () => {
|
||||
services.value = {}
|
||||
initialized.value = false
|
||||
}
|
||||
|
||||
// API actions
|
||||
const fetchServices = async (sources?: SourceSelector): Promise<void> => {
|
||||
loading.value = true
|
||||
error.value = null
|
||||
// Actions
|
||||
|
||||
/**
|
||||
* Retrieve all or specific services, optionally filtered by source selector
|
||||
*
|
||||
* @param sources - optional source selector
|
||||
*
|
||||
* @returns Promise with service object list keyed by provider and service identifier
|
||||
*/
|
||||
async function list(sources?: SourceSelector): Promise<Record<string, ServiceObject>> {
|
||||
transceiving.value = true
|
||||
try {
|
||||
const data = await serviceService.list(sources)
|
||||
setServices(data)
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : 'Failed to fetch services'
|
||||
throw e
|
||||
const response = await serviceService.list({ sources })
|
||||
|
||||
// Flatten nested structure: provider-id: { service-id: object } -> "provider-id:service-id": object
|
||||
const services: Record<string, ServiceObject> = {}
|
||||
Object.entries(response).forEach(([_providerId, providerServices]) => {
|
||||
Object.entries(providerServices).forEach(([_serviceId, serviceObj]) => {
|
||||
const key = identifierKey(serviceObj.provider, serviceObj.identifier)
|
||||
services[key] = serviceObj
|
||||
})
|
||||
})
|
||||
|
||||
// Merge retrieved services into state
|
||||
_services.value = { ..._services.value, ...services }
|
||||
|
||||
console.debug('[Documents Manager][Store] - Successfully retrieved', Object.keys(services).length, 'services')
|
||||
return services
|
||||
} catch (error: any) {
|
||||
console.error('[Documents Manager][Store] - Failed to retrieve services:', error)
|
||||
throw error
|
||||
} finally {
|
||||
loading.value = false
|
||||
transceiving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const checkServiceExtant = async (sources: SourceSelector): Promise<Record<string, boolean>> => {
|
||||
|
||||
/**
|
||||
* Retrieve a specific service by provider and identifier
|
||||
*
|
||||
* @param provider - provider identifier
|
||||
* @param identifier - service identifier
|
||||
*
|
||||
* @returns Promise with service object
|
||||
*/
|
||||
async function fetch(provider: string, identifier: string | number): Promise<ServiceObject> {
|
||||
transceiving.value = true
|
||||
try {
|
||||
return await serviceService.extant(sources)
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : 'Failed to check services'
|
||||
throw e
|
||||
const service = await serviceService.fetch({ provider, identifier })
|
||||
|
||||
// Merge fetched service into state
|
||||
const key = identifierKey(service.provider, service.identifier)
|
||||
_services.value[key] = service
|
||||
|
||||
console.debug('[Documents Manager][Store] - Successfully fetched service:', key)
|
||||
return service
|
||||
} catch (error: any) {
|
||||
console.error('[Documents Manager][Store] - Failed to fetch service:', error)
|
||||
throw error
|
||||
} finally {
|
||||
transceiving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const fetchService = async (providerId: string, serviceId: string): Promise<ServiceObject> => {
|
||||
/**
|
||||
* Retrieve service availability status for a given source selector
|
||||
*
|
||||
* @param sources - source selector to check availability for
|
||||
*
|
||||
* @returns Promise with service availability status
|
||||
*/
|
||||
async function extant(sources: SourceSelector) {
|
||||
transceiving.value = true
|
||||
try {
|
||||
const data = await serviceService.fetch(providerId, serviceId)
|
||||
addService(providerId, serviceId, data)
|
||||
return services.value[providerId][serviceId]
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : 'Failed to fetch service'
|
||||
throw e
|
||||
const response = await serviceService.extant({ sources })
|
||||
|
||||
console.debug('[Documents Manager][Store] - Successfully checked', sources ? Object.keys(sources).length : 0, 'services')
|
||||
return response
|
||||
} catch (error: any) {
|
||||
console.error('[Documents Manager][Store] - Failed to check services:', error)
|
||||
throw error
|
||||
} finally {
|
||||
transceiving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new service with given provider and data
|
||||
*
|
||||
* @param provider - provider identifier for the new service
|
||||
* @param data - partial service data for creation
|
||||
*
|
||||
* @returns Promise with created service object
|
||||
*/
|
||||
async function create(provider: string, data: Partial<ServiceInterface>): Promise<ServiceObject> {
|
||||
transceiving.value = true
|
||||
try {
|
||||
const service = await serviceService.create({ provider, data })
|
||||
|
||||
// Merge created service into state
|
||||
const key = identifierKey(service.provider, service.identifier)
|
||||
_services.value[key] = service
|
||||
|
||||
console.debug('[Documents Manager][Store] - Successfully created service:', key)
|
||||
return service
|
||||
} catch (error: any) {
|
||||
console.error('[Documents Manager][Store] - Failed to create service:', error)
|
||||
throw error
|
||||
} finally {
|
||||
transceiving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an existing service with given provider, identifier, and data
|
||||
*
|
||||
* @param provider - provider identifier for the service to update
|
||||
* @param identifier - service identifier for the service to update
|
||||
* @param data - partial service data for update
|
||||
*
|
||||
* @returns Promise with updated service object
|
||||
*/
|
||||
async function update(provider: string, identifier: string | number, data: Partial<ServiceInterface>): Promise<ServiceObject> {
|
||||
transceiving.value = true
|
||||
try {
|
||||
const service = await serviceService.update({ provider, identifier, data })
|
||||
|
||||
// Merge updated service into state
|
||||
const key = identifierKey(service.provider, service.identifier)
|
||||
_services.value[key] = service
|
||||
|
||||
console.debug('[Documents Manager][Store] - Successfully updated service:', key)
|
||||
return service
|
||||
} catch (error: any) {
|
||||
console.error('[Documents Manager][Store] - Failed to update service:', error)
|
||||
throw error
|
||||
} finally {
|
||||
transceiving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a service by provider and identifier
|
||||
*
|
||||
* @param provider - provider identifier for the service to delete
|
||||
* @param identifier - service identifier for the service to delete
|
||||
*
|
||||
* @returns Promise with deletion result
|
||||
*/
|
||||
async function remove(provider: string, identifier: string | number): Promise<any> {
|
||||
transceiving.value = true
|
||||
try {
|
||||
await serviceService.delete({ provider, identifier })
|
||||
|
||||
// Remove deleted service from state
|
||||
const key = identifierKey(provider, identifier)
|
||||
delete _services.value[key]
|
||||
|
||||
console.debug('[Documents Manager][Store] - Successfully deleted service:', key)
|
||||
} catch (error: any) {
|
||||
console.error('[Documents Manager][Store] - Failed to delete service:', error)
|
||||
throw error
|
||||
} finally {
|
||||
transceiving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// Return public API
|
||||
return {
|
||||
// State
|
||||
services,
|
||||
loading,
|
||||
error,
|
||||
initialized,
|
||||
// Computed
|
||||
serviceList,
|
||||
// State (readonly)
|
||||
transceiving: readonly(transceiving),
|
||||
// Getters
|
||||
getService,
|
||||
hasService,
|
||||
getProviderServices,
|
||||
getRootId,
|
||||
// Setters
|
||||
setServices,
|
||||
addService,
|
||||
removeService,
|
||||
clearServices,
|
||||
count,
|
||||
has,
|
||||
services,
|
||||
servicesByProvider,
|
||||
|
||||
// Actions
|
||||
fetchServices,
|
||||
checkServiceExtant,
|
||||
fetchService,
|
||||
service,
|
||||
list,
|
||||
fetch,
|
||||
extant,
|
||||
create,
|
||||
update,
|
||||
delete: remove,
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user