Add plugin platform with modular backend and frontend registry.
Split graph/import/meta into Django apps, add API v1 with OpenAPI and pytest, and introduce plugin registry with the tags reference plugin on both FE and BE. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
import { tagRepository } from '../../plugins/tags/tagRepository.local'
|
||||
|
||||
/**
|
||||
* Persist plugin form payload after contact create/update.
|
||||
* @param {string|number} contactId
|
||||
* @param {{ tags?: string[] }} pluginPayload
|
||||
*/
|
||||
export async function saveContactPluginData(contactId, pluginPayload = {}) {
|
||||
if (!contactId || !pluginPayload) return
|
||||
if (Array.isArray(pluginPayload.tags)) {
|
||||
await tagRepository.setContactTags(contactId, pluginPayload.tags)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
import { getGraphBundle } from '../usecases/graph'
|
||||
import { getRelationTypes } from '../../infrastructure/repositories/repositoryFactory'
|
||||
import { isLocalMode } from '../../infrastructure/config/dataMode'
|
||||
import api from '../../api'
|
||||
import { applyGraphExtensions } from '../../core/pluginRegistry'
|
||||
|
||||
/**
|
||||
* Unified graph data access for GraphView and NetworkMapView.
|
||||
*/
|
||||
export async function fetchGraphBundle({ mapId = null } = {}) {
|
||||
let bundle
|
||||
if (isLocalMode()) {
|
||||
bundle = await getGraphBundle({ mapId })
|
||||
} else {
|
||||
const endpoint = mapId
|
||||
? `/network-map-graph/?map_id=${encodeURIComponent(mapId)}`
|
||||
: '/graph/'
|
||||
const [gRes, rtRes] = await Promise.all([
|
||||
api.get(endpoint),
|
||||
api.get('/relation-types/'),
|
||||
])
|
||||
bundle = {
|
||||
nodes: gRes.data.nodes || [],
|
||||
edges: gRes.data.edges || [],
|
||||
relationTypes: rtRes.data || [],
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...bundle,
|
||||
nodes: (bundle.nodes || []).map((n) => applyGraphExtensions('node', n)),
|
||||
edges: (bundle.edges || []).map((e) => applyGraphExtensions('edge', e)),
|
||||
}
|
||||
}
|
||||
|
||||
export async function fetchGraphBundleFromStore(store, { mapId = null } = {}) {
|
||||
const { buildGraphFromStore } = await import('../usecases/graph')
|
||||
if (!store.contacts.length) await store.fetchContacts()
|
||||
if (!store.relations.length) await store.fetchRelations()
|
||||
const relationTypes = store.relationTypes.length
|
||||
? store.relationTypes
|
||||
: await store.fetchRelationTypes()
|
||||
|
||||
const bundle = buildGraphFromStore(store.contacts, store.relations, relationTypes)
|
||||
if (mapId) {
|
||||
return fetchGraphBundle({ mapId })
|
||||
}
|
||||
return {
|
||||
...bundle,
|
||||
nodes: bundle.nodes.map((n) => applyGraphExtensions('node', n)),
|
||||
edges: bundle.edges.map((e) => applyGraphExtensions('edge', e)),
|
||||
}
|
||||
}
|
||||
|
||||
export async function fetchRelationTypes() {
|
||||
return getRelationTypes()
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import { describe, it, expect, vi } from 'vitest'
|
||||
|
||||
vi.mock('../../infrastructure/config/dataMode', () => ({
|
||||
isLocalMode: () => true,
|
||||
}))
|
||||
|
||||
vi.mock('../../core/pluginRegistry', () => ({
|
||||
applyGraphExtensions: (_kind, payload) => payload,
|
||||
}))
|
||||
|
||||
vi.mock('../usecases/graph', () => ({
|
||||
getGraphBundle: vi.fn(async () => ({
|
||||
nodes: [{ id: '1', label: 'A' }],
|
||||
edges: [],
|
||||
relationTypes: [],
|
||||
})),
|
||||
}))
|
||||
|
||||
describe('graphDataService', () => {
|
||||
it('fetchGraphBundle returns nodes from local use case', async () => {
|
||||
const { fetchGraphBundle } = await import('./graphDataService.js')
|
||||
const bundle = await fetchGraphBundle()
|
||||
expect(bundle.nodes).toHaveLength(1)
|
||||
expect(bundle.nodes[0].label).toBe('A')
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user