Customization and Extension
This document provides detailed guidance on customizing and extending Qwello to meet specific organizational needs, covering custom entity types, relationship types, external system integration, API usage, and plugin development.
Custom Entity Types
Qwello's knowledge graph system supports the creation of custom entity types to represent domain-specific concepts and objects.
Entity Type Definition
Custom entity types are defined through configuration files that specify their properties and behaviors:
// custom-entity-types.json
{
"entityTypes": [
{
"id": "product",
"name": "Product",
"description": "A commercial product or offering",
"color": "#00BCD4",
"icon": "package",
"defaultAttributes": [
{
"id": "productId",
"name": "Product ID",
"type": "string",
"required": true
},
{
"id": "category",
"name": "Category",
"type": "string",
"required": false
},
{
"id": "price",
"name": "Price",
"type": "number",
"required": false
},
{
"id": "launchDate",
"name": "Launch Date",
"type": "date",
"required": false
}
],
"searchable": true,
"displayTemplate": "{{name}} ({{attributes.productId}})"
},
{
"id": "regulation",
"name": "Regulation",
"description": "A legal or regulatory requirement",
"color": "#9C27B0",
"icon": "gavel",
"defaultAttributes": [
{
"id": "regulationId",
"name": "Regulation ID",
"type": "string",
"required": true
},
{
"id": "jurisdiction",
"name": "Jurisdiction",
"type": "string",
"required": false
},
{
"id": "effectiveDate",
"name": "Effective Date",
"type": "date",
"required": false
},
{
"id": "complianceLevel",
"name": "Compliance Level",
"type": "enum",
"options": ["Mandatory", "Recommended", "Optional"],
"required": false
}
],
"searchable": true,
"displayTemplate": "{{name}} ({{attributes.jurisdiction}})"
}
]
}
Entity Type Registration
Custom entity types must be registered with the system:
// entity-type-registry.ts
import { EntityTypeRegistry } from '@qwello/core';
import customEntityTypes from './custom-entity-types.json';
export function registerCustomEntityTypes() {
const registry = EntityTypeRegistry.getInstance();
for (const entityType of customEntityTypes.entityTypes) {
registry.register(entityType);
}
console.log(`Registered ${customEntityTypes.entityTypes.length} custom entity types`);
}
Entity Type Usage
Custom entity types can be used throughout the system:
// Example of creating an entity with a custom type
const productEntity = {
id: generateId(),
type: 'product',
name: 'Qwello Enterprise Edition',
attributes: {
productId: 'QWL-ENT-2025',
category: 'Software',
price: 1999.99,
launchDate: '2025-01-15'
}
};
// Example of querying entities by custom type
const products = await knowledgeGraphService.findEntities({
type: 'product',
attributes: {
category: 'Software'
}
});
Entity Type Visualization
Custom entity types can have specific visualization settings:
// visualization-config.ts
export const entityTypeVisualization = {
product: {
shape: 'box',
color: {
background: '#00BCD4',
border: '#0097A7',
highlight: {
background: '#4DD0E1',
border: '#00BCD4'
}
},
font: {
color: '#FFFFFF'
},
icon: {
code: '\uf466', // Font Awesome package icon
color: '#FFFFFF'
}
},
regulation: {
shape: 'diamond',
color: {
background: '#9C27B0',
border: '#7B1FA2',
highlight: {
background: '#BA68C8',
border: '#9C27B0'
}
},
font: {
color: '#FFFFFF'
},
icon: {
code: '\uf52b', // Font Awesome gavel icon
color: '#FFFFFF'
}
}
};
Custom Relationship Types
Qwello allows the definition of custom relationship types to represent domain-specific connections between entities.
Relationship Type Definition
Custom relationship types are defined through configuration files:
// custom-relationship-types.json
{
"relationshipTypes": [
{
"id": "manufactures",
"name": "Manufactures",
"description": "Indicates that an organization manufactures a product",
"color": "#FF9800",
"validSourceTypes": ["organization"],
"validTargetTypes": ["product"],
"bidirectional": false,
"inverseName": "Manufactured by",
"defaultAttributes": [
{
"id": "since",
"name": "Since",
"type": "date",
"required": false
},
{
"id": "facility",
"name": "Manufacturing Facility",
"type": "string",
"required": false
}
]
},
{
"id": "regulates",
"name": "Regulates",
"description": "Indicates that a regulation applies to an entity",
"color": "#673AB7",
"validSourceTypes": ["regulation"],
"validTargetTypes": ["organization", "product", "method"],
"bidirectional": false,
"inverseName": "Regulated by",
"defaultAttributes": [
{
"id": "complianceStatus",
"name": "Compliance Status",
"type": "enum",
"options": ["Compliant", "Non-compliant", "In Progress", "Not Applicable"],
"required": false
},
{
"id": "reviewDate",
"name": "Last Review Date",
"type": "date",
"required": false
}
]
}
]
}
Relationship Type Registration
Custom relationship types must be registered with the system:
// relationship-type-registry.ts
import { RelationshipTypeRegistry } from '@qwello/core';
import customRelationshipTypes from './custom-relationship-types.json';
export function registerCustomRelationshipTypes() {
const registry = RelationshipTypeRegistry.getInstance();
for (const relType of customRelationshipTypes.relationshipTypes) {
registry.register(relType);
}
console.log(`Registered ${customRelationshipTypes.relationshipTypes.length} custom relationship types`);
}
Relationship Type Usage
Custom relationship types can be used to connect entities:
// Example of creating a relationship with a custom type
const manufacturingRelationship = {
source: 'org123', // ID of an organization entity
target: 'prod456', // ID of a product entity
type: 'manufactures',
attributes: {
since: '2020-03-15',
facility: 'Frankfurt Plant'
}
};
// Example of querying relationships by custom type
const manufacturingRelationships = await knowledgeGraphService.findRelationships({
type: 'manufactures',
attributes: {
facility: 'Frankfurt Plant'
}
});
Integration with External Systems
Qwello can be integrated with external systems to exchange data and enhance functionality.
Integration Architecture
The integration architecture supports various integration patterns:
graph TD
A[Qwello] --> B[Integration Layer]
B --> C[REST API Connectors]
B --> D[Webhook Handlers]
B --> E[Message Queue Connectors]
B --> F[File-Based Integrations]
C --> G[External REST APIs]
D --> H[Webhook Consumers]
E --> I[Message Queues]
F --> J[File Systems/Storage]
REST API Integration
Integrate with external REST APIs:
// external-api-connector.ts
import axios from 'axios';
import { ConfigService } from '@nestjs/config';
@Injectable()
export class ExternalApiConnector {
constructor(private configService: ConfigService) {}
async fetchData(endpoint: string, params: any = {}): Promise<any> {
const baseUrl = this.configService.get<string>('EXTERNAL_API_BASE_URL');
const apiKey = this.configService.get<string>('EXTERNAL_API_KEY');
try {
const response = await axios.get(`${baseUrl}${endpoint}`, {
params,
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
}
});
return response.data;
} catch (error) {
this.handleApiError(error);
}
}
async postData(endpoint: string, data: any): Promise<any> {
const baseUrl = this.configService.get<string>('EXTERNAL_API_BASE_URL');
const apiKey = this.configService.get<string>('EXTERNAL_API_KEY');
try {
const response = await axios.post(`${baseUrl}${endpoint}`, data, {
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
}
});
return response.data;
} catch (error) {
this.handleApiError(error);
}
}
private handleApiError(error: any): never {
// Error handling logic
throw new Error(`External API error: ${error.message}`);
}
}
Webhook Integration
Set up webhook handlers to receive data from external systems:
// webhook.controller.ts
import { Controller, Post, Body, Headers, UnauthorizedException } from '@nestjs/common';
import { WebhookService } from './webhook.service';
import { ConfigService } from '@nestjs/config';
import * as crypto from 'crypto';
@Controller('webhooks')
export class WebhookController {
constructor(
private webhookService: WebhookService,
private configService: ConfigService
) {}
@Post('document-update')
async handleDocumentUpdate(
@Body() payload: any,
@Headers('x-webhook-signature') signature: string
) {
// Verify webhook signature
this.verifySignature(payload, signature);
// Process the webhook payload
await this.webhookService.processDocumentUpdate(payload);
return { success: true };
}
private verifySignature(payload: any, signature: string): void {
const secret = this.configService.get<string>('WEBHOOK_SECRET');
const hmac = crypto.createHmac('sha256', secret);
const digest = hmac.update(JSON.stringify(payload)).digest('hex');
if (signature !== digest) {
throw new UnauthorizedException('Invalid webhook signature');
}
}
}
Message Queue Integration
Integrate with message queues for asynchronous communication:
// message-queue.service.ts
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import * as amqp from 'amqplib';
@Injectable()
export class MessageQueueService {
private connection: amqp.Connection;
private channel: amqp.Channel;
constructor(private configService: ConfigService) {}
async initialize() {
const url = this.configService.get<string>('RABBITMQ_URL');
this.connection = await amqp.connect(url);
this.channel = await this.connection.createChannel();
// Declare queues
await this.channel.assertQueue('document-updates', { durable: true });
await this.channel.assertQueue('knowledge-graph-updates', { durable: true });
// Set up consumers
this.channel.consume('document-updates', this.handleDocumentUpdate.bind(this), { noAck: false });
}
async publishMessage(queue: string, message: any) {
await this.channel.sendToQueue(
queue,
Buffer.from(JSON.stringify(message)),
{ persistent: true }
);
}
private async handleDocumentUpdate(msg: amqp.ConsumeMessage | null) {
if (!msg) return;
try {
const content = JSON.parse(msg.content.toString());
// Process the message
// Acknowledge the message
this.channel.ack(msg);
} catch (error) {
// Handle error
this.channel.nack(msg, false, true); // Requeue the message
}
}
}
API Usage
Qwello provides a comprehensive API for programmatic interaction with the system.
Authentication
Authenticate with the API:
// api-client.ts
import axios from 'axios';
export class QwelloApiClient {
private baseUrl: string;
private apiKey: string;
private token: string | null = null;
constructor(baseUrl: string, apiKey: string) {
this.baseUrl = baseUrl;
this.apiKey = apiKey;
}
async authenticate() {
const response = await axios.post(`${this.baseUrl}/api/v1/auth/token`, {
apiKey: this.apiKey
});
this.token = response.data.token;
return this.token;
}
async request(method: string, endpoint: string, data?: any) {
if (!this.token) {
await this.authenticate();
}
try {
const response = await axios({
method,
url: `${this.baseUrl}/api/v1/${endpoint}`,
data,
headers: {
'Authorization': `Bearer ${this.token}`,
'Content-Type': 'application/json'
}
});
return response.data;
} catch (error) {
if (error.response?.status === 401) {
// Token expired, re-authenticate and retry
await this.authenticate();
return this.request(method, endpoint, data);
}
throw error;
}
}
async get(endpoint: string) {
return this.request('GET', endpoint);
}
async post(endpoint: string, data: any) {
return this.request('POST', endpoint, data);
}
async put(endpoint: string, data: any) {
return this.request('PUT', endpoint, data);
}
async delete(endpoint: string) {
return this.request('DELETE', endpoint);
}
}
Document Management API
Manage documents through the API:
// Example of document management API usage
const apiClient = new QwelloApiClient('https://api.qwello.example.com', 'your-api-key');
// Upload a document
const uploadResponse = await apiClient.post('documents/upload', {
file: fileBuffer, // Base64 encoded file content
filename: 'strategic-plan-2025.pdf',
metadata: {
title: 'Strategic Plan 2025',
author: 'Executive Team',
department: 'Strategy',
tags: ['strategy', 'planning', '2025']
}
});
const documentId = uploadResponse.id;
// Get document status
const documentStatus = await apiClient.get(`documents/${documentId}/status`);
// Get document knowledge graph
const knowledgeGraph = await apiClient.get(`documents/${documentId}/knowledge-graph`);
Knowledge Graph API
Interact with knowledge graphs through the API:
// Example of knowledge graph API usage
const apiClient = new QwelloApiClient('https://api.qwello.example.com', 'your-api-key');
// Get all entities of a specific type
const products = await apiClient.get('knowledge-graph/entities?type=product');
// Get entity by ID
const entity = await apiClient.get(`knowledge-graph/entities/${entityId}`);
// Create a new entity
const newEntity = await apiClient.post('knowledge-graph/entities', {
type: 'product',
name: 'New Product Line',
attributes: {
productId: 'NPL-2025',
category: 'Hardware',
price: 299.99
}
});
// Create a relationship
const newRelationship = await apiClient.post('knowledge-graph/relationships', {
source: organizationId,
target: newEntity.id,
type: 'manufactures',
attributes: {
since: '2025-01-01',
facility: 'Main Plant'
}
});
Plugin Development
Qwello supports plugins to extend functionality and integrate with external systems.
Plugin Architecture
The plugin architecture follows a modular design:
graph TD
A[Qwello Core] --> B[Plugin Manager]
B --> C[Plugin Registry]
C --> D[Plugin 1]
C --> E[Plugin 2]
C --> F[Plugin 3]
D --> G[Plugin API]
E --> G
F --> G
G --> H[Qwello Services]
Plugin Structure
Plugins follow a standard structure:
my-qwello-plugin/
├── package.json
├── README.md
├── src/
│ ├── index.ts
│ ├── plugin.ts
│ ├── components/
│ │ └── CustomComponent.tsx
│ ├── services/
│ │ └── CustomService.ts
│ └── hooks/
│ └── useCustomHook.ts
└── dist/
└── (compiled plugin)
Plugin Manifest
Each plugin includes a manifest file:
// package.json
{
"name": "my-qwello-plugin",
"version": "1.0.0",
"description": "Custom plugin for Qwello",
"main": "dist/index.js",
"qwello": {
"id": "my-qwello-plugin",
"displayName": "My Custom Plugin",
"description": "Adds custom functionality to Qwello",
"version": "1.0.0",
"apiVersion": "1.0",
"entryPoint": "dist/index.js",
"permissions": [
"read:documents",
"write:knowledge-graph",
"read:knowledge-graph"
],
"hooks": [
"document:processed",
"entity:created",
"relationship:created"
],
"components": [
{
"id": "custom-entity-card",
"type": "entity-card",
"entityTypes": ["product", "regulation"]
},
{
"id": "custom-dashboard-widget",
"type": "dashboard-widget",
"name": "Custom Analytics"
}
]
},
"dependencies": {
"@qwello/plugin-sdk": "^1.0.0"
}
}
Plugin Implementation
Implement the plugin functionality:
// src/index.ts
import { QwelloPlugin, PluginContext } from '@qwello/plugin-sdk';
import { CustomService } from './services/CustomService';
import { CustomComponent } from './components/CustomComponent';
import { useCustomHook } from './hooks/useCustomHook';
class MyQwelloPlugin implements QwelloPlugin {
private context: PluginContext;
private service: CustomService;
async initialize(context: PluginContext): Promise<void> {
this.context = context;
this.service = new CustomService(context);
// Register event handlers
context.events.on('document:processed', this.handleDocumentProcessed.bind(this));
context.events.on('entity:created', this.handleEntityCreated.bind(this));
// Register components
context.registerComponent('custom-entity-card', CustomComponent);
context.registerComponent('custom-dashboard-widget', CustomDashboardWidget);
// Register hooks
context.registerHook('useCustomHook', useCustomHook);
this.context.logger.info('MyQwelloPlugin initialized successfully');
}
private async handleDocumentProcessed(event: any): Promise<void> {
const { documentId } = event;
this.context.logger.info(`Document processed: ${documentId}`);
// Custom logic for processed documents
await this.service.processDocument(documentId);
}
private async handleEntityCreated(event: any): Promise<void> {
const { entity } = event;
if (entity.type === 'product') {
// Custom logic for new product entities
await this.service.handleNewProduct(entity);
}
}
async shutdown(): Promise<void> {
// Cleanup resources
await this.service.shutdown();
this.context.logger.info('MyQwelloPlugin shutdown successfully');
}
}
// Export the plugin instance
export default new MyQwelloPlugin();
Custom Components
Create custom UI components:
// src/components/CustomComponent.tsx
import React, { useState, useEffect } from 'react';
import { usePluginContext, EntityCard, Button } from '@qwello/plugin-sdk';
export const CustomComponent: React.FC<{ entity: any }> = ({ entity }) => {
const { services } = usePluginContext();
const [additionalData, setAdditionalData] = useState<any>(null);
useEffect(() => {
const fetchData = async () => {
if (entity.type === 'product') {
const data = await services.customService.getProductDetails(entity.attributes.productId);
setAdditionalData(data);
}
};
fetchData();
}, [entity]);
const handleAction = async () => {
await services.customService.performAction(entity.id);
};
return (
<EntityCard entity={entity}>
{additionalData && (
<div className="additional-info">
<h4>Additional Information</h4>
<p>Sales Volume: {additionalData.salesVolume}</p>
<p>Customer Rating: {additionalData.rating}/5</p>
<Button onClick={handleAction}>Perform Custom Action</Button>
</div>
)}
</EntityCard>
);
};
Best Practices for Customization
When customizing and extending Qwello, follow these best practices to ensure maintainability, performance, and compatibility:
Entity Type Design
Consistent Naming: Use clear, consistent naming conventions for entity types and attributes
Attribute Planning: Define attributes carefully, considering search and filtering needs
Color Coding: Choose visually distinct colors for different entity types
Icon Selection: Select meaningful icons that represent the entity concept
Documentation: Document the purpose and usage of custom entity types
Integration Development
Error Handling: Implement robust error handling for all external integrations
Retry Logic: Add retry mechanisms for transient failures
Circuit Breaking: Implement circuit breakers to prevent cascading failures
Logging: Include comprehensive logging for troubleshooting
Security: Follow security best practices for authentication and data protection
Plugin Development
Isolation: Ensure plugins operate in isolation without interfering with each other
Performance: Optimize plugin performance to minimize impact on the core system
Versioning: Maintain compatibility with the Qwello API version
Testing: Thoroughly test plugins across different scenarios
Documentation: Provide clear documentation for plugin installation and usage
By following these guidelines and leveraging Qwello's extensibility features, organizations can customize the platform to meet their specific needs while maintaining a robust and maintainable implementation.