-
Notifications
You must be signed in to change notification settings - Fork 1
Public API Overview
QQQ provides a comprehensive set of public APIs for building applications and extending the framework. This document outlines the key public interfaces that users and contributors can depend on.
QQQ follows semantic versioning for API stability:
- Public APIs: Guaranteed stable within major versions
- Internal APIs: May change without notice
- Breaking Changes: Only in major version releases
- Deprecation Policy: Two-version timeline for removal
The foundation for business logic in QQQ:
// Core action interface
public abstract class AbstractQActionFunction<Input extends AbstractActionInput, Output extends AbstractActionOutput>
{
public abstract Output execute(Input input) throws QException;
public CompletableFuture<Output> executeAsync(Input input) throws QException;
}
// Input/Output base classes
public abstract class AbstractActionInput implements Serializable
{
// Common input functionality
}
public abstract class AbstractActionOutput implements Serializable
{
// Common output functionality
}Usage Example:
public class CreateUserAction extends AbstractQActionFunction<CreateUserInput, CreateUserOutput>
{
@Override
public CreateUserOutput execute(CreateUserInput input) throws QException
{
// Business logic implementation
User user = userService.createUser(input.getUserName(), input.getEmail());
return new CreateUserOutput(user.getId(), user.getStatus());
}
}Thread-local context for QQQ operations:
public class QContext
{
// Get current context
public static QContext getCurrent();
// Initialize context
public static void init(QInstance instance, QSession session);
// Clear context
public static void clear();
// Access context data
public QInstance getQInstance();
public QSession getQSession();
public QBackendTransaction getTransaction();
}Usage Example:
// Initialize context for action execution
QContext.init(instance, session);
try
{
// Execute actions within context
CreateUserOutput output = action.execute(input);
}
finally
{
// Always clean up context
QContext.clear();
}Configuration-driven application definition:
public class QInstance
{
// Add metadata objects
public void addMetaData(AbstractMetaData metadata);
public void addBackend(QBackendMetaData backend);
public void addTable(QTableMetaData table);
public void addAction(QActionMetaData action);
// Retrieve metadata
public <T extends AbstractMetaData> Map<String, T> getMetaData(Class<T> type);
public QBackendMetaData getBackend(String name);
public QTableMetaData getTable(String name);
}Usage Example:
QInstance instance = new QInstance();
// Configure backends
instance.addBackend(new RDBMSBackendMetaData()
.withName("mainDb")
.withConnectionUrl("jdbc:postgresql://localhost:5432/mydb"));
// Configure tables
instance.addTable(QTableMetaData.newForEntity(UserRecord.class)
.withName("users")
.withBackendName("mainDb"));
// Configure actions
instance.addAction(QActionMetaData.newForAction(CreateUserAction.class)
.withName("createUser")
.withTableName("users"));Extend QQQ with new storage backends:
public interface QBackendModuleInterface
{
// Module identification
String getBackendType();
// Metadata classes
Class<? extends QBackendMetaData> getBackendMetaDataClass();
Class<? extends QTableBackendDetails> getTableBackendDetailsClass();
// Action interfaces
QueryInterface getQueryInterface();
InsertInterface getInsertInterface();
UpdateInterface getUpdateInterface();
DeleteInterface getDeleteInterface();
CountInterface getCountInterface();
}Implementation Example:
public class RedisBackendModule implements QBackendModuleInterface
{
@Override
public String getBackendType()
{
return "redis";
}
@Override
public Class<? extends QBackendMetaData> getBackendMetaDataClass()
{
return RedisBackendMetaData.class;
}
// Implement required action interfaces...
}Standard interfaces for data operations:
public interface QueryInterface
{
QueryOutput execute(QueryInput input) throws QException;
}
public interface InsertInterface
{
InsertOutput execute(InsertInput input) throws QException;
}
public interface UpdateInterface
{
UpdateOutput execute(UpdateInput input) throws QException;
}
public interface DeleteInterface
{
DeleteOutput execute(DeleteInput input) throws QException;
}REST API endpoints and web interface:
public class QApplicationJavalinServer
{
// Start server
public void start(QInstance instance);
// Stop server
public void stop();
// Add custom routes
public void addRouteProvider(RouteProvider provider);
}
// Custom route provider
public interface RouteProvider
{
void registerRoutes(Javalin app, QInstance instance);
}Usage Example:
QApplicationJavalinServer server = new QApplicationJavalinServer();
// Add custom API endpoints
server.addRouteProvider(new CustomApiProvider());
// Start server with QQQ instance
server.start(qInstance);Command-line interface for QQQ applications:
public class QApplicationCli
{
// Execute CLI command
public void execute(String[] args);
// Add custom commands
public void addCommand(Command command);
}
// Custom CLI command
@Command(name = "custom", description = "Custom operation")
public class CustomCommand implements Runnable
{
@Option(names = {"-p", "--param"}, description = "Parameter")
private String parameter;
@Override
public void run()
{
// Command implementation
}
}Extend the Material-UI dashboard:
// Widget interface
interface QWidget {
id: string;
type: string;
props: Record<string, any>;
render: (context: QWidgetContext) => React.ReactElement;
}
// Widget context
interface QWidgetContext {
instance: QInstance;
session: QSession;
data: any;
}
// Custom widget
class CustomWidget implements QWidget {
id = 'custom-widget';
type = 'custom';
props = {};
render(context: QWidgetContext) {
return <div>Custom Widget Content</div>;
}
}Configure QQQ applications:
public class QConfiguration
{
// Load from environment variables
public static QConfiguration fromEnvironment();
// Load from properties file
public static QConfiguration fromProperties(Properties props);
// Load from YAML file
public static QConfiguration fromYaml(InputStream yaml);
// Access configuration values
public String getString(String key);
public Integer getInteger(String key);
public Boolean getBoolean(String key);
}Configuration Example:
# application.yml
qqq:
application:
name: "My QQQ App"
version: "1.0.0"
database:
backend: "rdbms"
connection:
url: "jdbc:postgresql://localhost:5432/mydb"
username: "${DB_USERNAME}"
password: "${DB_PASSWORD}"
server:
port: 8080
context-path: "/api"Create custom metadata:
public interface MetaDataProducerInterface
{
// Produce metadata
void produce(QInstance instance) throws QException;
// Execution order
int getSortOrder();
// Enable/disable
boolean isEnabled();
}
// Custom metadata producer
public class CustomMetaDataProducer implements MetaDataProducerInterface
{
@Override
public void produce(QInstance instance) throws QException
{
// Create and add custom metadata
CustomMetaData metadata = new CustomMetaData();
instance.addMetaData(metadata);
}
@Override
public int getSortOrder()
{
return 100;
}
}Custom validation rules:
public interface QInstanceValidatorPluginInterface<T extends AbstractMetaData>
{
// Validate metadata
void validate(T metadata) throws QException;
// Get metadata type
Class<T> getMetaDataClass();
}
// Custom validator
public class CustomValidator implements QInstanceValidatorPluginInterface<CustomMetaData>
{
@Override
public void validate(CustomMetaData metadata) throws QException
{
// Validation logic
if (metadata.getRequiredField() == null)
{
throw new QException("Required field is missing");
}
}
}QQQ-specific exceptions:
public class QException extends Exception
{
public QException(String message);
public QException(String message, Throwable cause);
public QException(String message, Object... args);
}
public class QValidationException extends QException
{
public QValidationException(String message);
public QValidationException(String message, String field);
}
public class QBackendException extends QException
{
public QBackendException(String message, String backendType);
public QBackendException(String message, String backendType, Throwable cause);
}Usage Example:
try
{
// QQQ operation
QueryOutput output = queryAction.execute(input);
}
catch (QValidationException e)
{
// Handle validation errors
LOG.error("Validation failed", "field", e.getField(), "error", e.getMessage());
}
catch (QBackendException e)
{
// Handle backend errors
LOG.error("Backend operation failed", "backend", e.getBackendType(), "error", e.getMessage());
}
catch (QException e)
{
// Handle general QQQ errors
LOG.error("QQQ operation failed", "error", e.getMessage());
}QQQ's logging system:
public class QLogger
{
// Log levels with structured data
public void debug(String message, Object... keyValuePairs);
public void info(String message, Object... keyValuePairs);
public void warn(String message, Object... keyValuePairs);
public void error(String message, Object... keyValuePairs);
// Exception logging
public void error(String message, Throwable throwable, Object... keyValuePairs);
}
// Usage example
private static final QLogger LOG = QLogger.getLogger(MyClass.class);
LOG.info("Processing order", "orderId", order.getId(), "customerId", order.getCustomerId());
LOG.error("Failed to process order", exception, "orderId", order.getId());QQQ maintains API compatibility:
- Major versions: May include breaking changes
- Minor versions: New features, backward compatible
- Patch versions: Bug fixes, backward compatible
- Deprecation warnings: Clear migration paths
- Migration guides: Step-by-step instructions
- Examples: Updated code samples
- Support: Help with migration questions
- Follow patterns: Use established QQQ patterns
- Handle errors: Always catch and handle QQQ exceptions
- Manage context: Properly initialize and clean up QContext
- Use logging: Leverage QQQ's structured logging
- Implement interfaces: Follow QQQ extension contracts
- Register components: Use QQQ's registration mechanisms
- Test thoroughly: Ensure extensions work with QQQ core
- Document changes: Update documentation for new APIs
QQQ's public APIs provide a solid foundation for building applications and extending the framework. Follow established patterns and maintain backward compatibility for the best user experience.
For architecture details, see High-Level Architecture. For versioning policy, see Semantic Versioning Policy.
QQQ is a powerful, open source, metadata-driven application framework designed specifically for engineers who want to build business applications quickly without starting from scratch.
Built by Kingsrook - making engineers more productive through intelligent automation and developer tools.
- π Wiki Home - Start here for comprehensive guides
- π GitHub Repository - Source code and issues
- π¦ Maven Central - Download artifacts
- π’ Kingsrook Website - Company information
- π§ Building Locally - Build QQQ from source
- π¨βπ» Developer Onboarding - Setup development environment
- ποΈ High-Level Architecture - Understand QQQ's design
- π¦ Core Modules - Available components and usage
- π Contribution Guidelines - How to contribute
- π Code Review Standards - Coding standards
- π§ͺ Testing Guide - Testing requirements and strategy
- π Feature Development - How to extend QQQ
- π Release Flow - Release process and workflow
- π·οΈ Semantic Versioning - Versioning policy
- π Public API Overview - API documentation
- π§ Compatibility Matrix - Version compatibility
- β FAQ - Frequently asked questions
- π¨ Common Errors - Troubleshooting guide
- π¬ GitHub Issues - Report bugs and request features
- π¬ GitHub Discussions - Ask questions and get help
- π License - GNU Affero General Public License v3.0
- π Security Policy - Report security vulnerabilities
- π Code of Conduct - Community standards
- π§ Contact - General inquiries
- π Security - Security issues
QQQ is 100% open source - you have complete ownership and control. No vendor lock-in, no SaaS subscriptions, just full control over your code, data, and system. π
This footer appears on all wiki pages. For the most up-to-date information, always check the GitHub repository.