Compare commits

..
3 Commits
7 changed files with 259 additions and 193 deletions

No files matched your search

+10 -53
View File
@@ -1,57 +1,14 @@
version: 2.1
setup: true
orbs:
qqq-orb: kingsrook/qqq-orb@0.5.0
qqq-orb: kingsrook/qqq-orb@0.7.15
workflows:
test_only:
setup:
jobs:
- qqq-orb/mvn_test_only:
context: [ qqq-maven-registry-credentials ]
filters:
branches:
ignore: /(develop|main|release/.*|hotfix/.*|integration.*)/
tags:
only: []
publish_snapshot:
jobs:
- qqq-orb/mvn_publish:
context: [ qqq-maven-registry-credentials ]
branch_type: snapshot
filters:
branches:
only:
- develop
publish_release_candidate:
jobs:
- qqq-orb/mvn_publish:
context: [ qqq-maven-registry-credentials ]
branch_type: release_candidate
filters:
branches:
only:
- /release\/.*/
publish_release:
jobs:
- qqq-orb/mvn_publish:
context: [ qqq-maven-registry-credentials ]
branch_type: release
filters:
branches:
only: [ main ]
tags:
only: [ /v.*/ ]
publish_hotfix_release:
jobs:
- qqq-orb/mvn_publish:
context: [ qqq-maven-registry-credentials ]
branch_type: hotfix
filters:
branches:
only:
- /hotfix\/.*/
- qqq-orb/generate_pipeline:
orb_version: 0.7.15
filters:
branches:
ignore: automation/qbit-rc-pending
tags:
only: /v.*/
-31
View File
@@ -1,31 +0,0 @@
# GitHub Release Configuration
# Auto-generates release notes from PR labels
changelog:
exclude:
labels:
- ignore-for-release
- duplicate
- invalid
authors:
- dependabot
- dependabot[bot]
categories:
- title: Breaking Changes
labels:
- breaking-change
- breaking
- title: New Features
labels:
- enhancement
- feature
- title: Bug Fixes
labels:
- bug
- fix
- title: Dependencies
labels:
- dependencies
- title: Other Changes
labels:
- "*"
+23
View File
@@ -0,0 +1,23 @@
name: CodeQL
'on':
push:
branches:
- main
- develop
- release/**
- hotfix/**
pull_request: {}
schedule:
- cron: 41 16 * * 0
workflow_dispatch: {}
permissions:
contents: read
actions: read
security-events: write
packages: read
concurrency:
group: codeql-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
jobs:
analyze:
uses: QRun-IO/qqq-orb/.github/workflows/qbit-codeql.yml@c288a8864fe9054f68410e1d78f5863aea4c4d22
+74
View File
@@ -0,0 +1,74 @@
name: Release candidate
'on':
workflow_dispatch:
inputs:
dry_run:
description: Plan a cut without changing release state
type: boolean
default: false
finalize:
description: Retry finalization of an existing cut; do not cut another RC
type: boolean
default: false
recover:
description: Recover the same published RC after a deployment timeout; never
reupload
type: boolean
default: false
circle_build:
description: Original Circle publisher build number (recovery only)
type: string
default: ''
merge_sha:
description: Existing cut merge_sha (finalize retry only)
type: string
default: ''
bump_sha:
description: Existing cut bump_sha (finalize retry only)
type: string
default: ''
branch:
description: Existing cut branch (finalize retry only)
type: string
default: ''
version:
description: Existing cut version (finalize retry only)
type: string
default: ''
pull_request:
types:
- closed
branches:
- develop
repository_dispatch:
types:
- qbit-rc-published
permissions:
contents: write
actions: read
checks: read
jobs:
release-rc:
if: github.event_name == 'workflow_dispatch' || github.event_name == 'repository_dispatch'
|| (github.event_name == 'pull_request' && github.event.pull_request.merged
== true && github.event.pull_request.base.ref == 'develop' && contains(github.event.pull_request.labels.*.name,
'release:rc'))
uses: QRun-IO/qqq-orb/.github/workflows/qbit-release-rc.yml@cc30f599e63c8e41629c99d23395bac1adb8bc44
with:
dry_run: ${{ github.event_name == 'workflow_dispatch' && inputs.dry_run || false
}}
finalize: ${{ github.event_name == 'repository_dispatch' || (github.event_name
== 'workflow_dispatch' && inputs.finalize) }}
recover: ${{ github.event_name == 'workflow_dispatch' && inputs.recover || false
}}
circle_build: ${{ github.event_name == 'workflow_dispatch' && inputs.circle_build
|| '' }}
merge_sha: ${{ github.event_name == 'repository_dispatch' && github.event.client_payload.merge_sha
|| inputs.merge_sha || '' }}
bump_sha: ${{ github.event_name == 'repository_dispatch' && github.event.client_payload.bump_sha
|| inputs.bump_sha || '' }}
branch: ${{ github.event_name == 'repository_dispatch' && github.event.client_payload.branch
|| inputs.branch || '' }}
version: ${{ github.event_name == 'repository_dispatch' && github.event.client_payload.version
|| inputs.version || '' }}
secrets: inherit
+16
View File
@@ -0,0 +1,16 @@
profile: qbit
orb_version: 0.7.15
default_branch: develop
java_version: '21'
browsers: true
api_checks: false
docs_only: false
release_on_main: true
release_tag_policy: v-prefix
test_branch_policy: feature-no-hotfix
publish:
snapshot: true
release_candidate: true
release: true
hotfix: true
asciidoc: false
+134 -107
View File
@@ -1,154 +1,181 @@
# qbit-webhooks
# QBit: Webhooks
Outbound webhook notifications for QQQ applications.
[![Version](https://img.shields.io/badge/version-0.1.0-blue.svg)](https://github.com/Kingsrook/qbit-webhooks)
[![License](https://img.shields.io/badge/license-GNU%20Affero%20GPL%20v3-green.svg)](https://www.gnu.org/licenses/agpl-3.0.en.html)
[![Java](https://img.shields.io/badge/java-17+-blue.svg)](https://adoptium.net/)
**For:** QQQ developers who need to notify external systems when records change
**Status:** Stable
> **Webhook Implementation for QQQ Applications - Event-Driven Integration**
## Why This Exists
This QBit provides a comprehensive webhook system for QQQ applications, enabling real-time event notifications to external systems and partners.
Modern applications don't exist in isolation. When an order is created, you might need to notify a fulfillment system. When a user updates their profile, a CRM needs to know. Polling for changes is inefficient and adds latency.
## 🚀 What Makes QBit Webhooks Different?
This QBit sends HTTP callbacks when records are inserted, updated, or deleted. Configure endpoints, select which events to send, and the system handles delivery with retries and logging.
**QBit Webhooks gives you enterprise-grade webhook capabilities** - you define your event types, configure webhook endpoints, and QBit Webhooks handles the reliable delivery, retry logic, and comprehensive logging. No more building webhook infrastructure from scratch.
## Features
### ✨ Core Capabilities
- **Event-Driven Delivery** - Fires on insert, update, delete operations
- **Configurable Endpoints** - Register multiple webhook URLs per table
- **Payload Customization** - Include full record, changed fields only, or custom format
- **Retry Logic** - Automatic retries with exponential backoff
- **Delivery Logging** - Track success, failures, and response codes
- **Secret Signing** - HMAC signatures for payload verification
- **🔗 Webhook Management**: Complete webhook endpoint and subscription management
- **📡 Event Types**: Flexible event type registration and categorization
- **🔄 Reliable Delivery**: Built-in retry logic and delivery tracking
- **📊 Comprehensive Logging**: Full audit trail of webhook events and delivery attempts
- **🎯 Event Filtering**: Subscribe to specific event types and categories
- **📦 JSON Payloads**: Structured event content with API versioning support
## Quick Start
## 🔓 Open Source & Full Control
**QBit Webhooks is 100% open source** - you have complete ownership and control:
- **💻 Your Code**: Full access to QBit Webhooks source code
- **🗄️ Your Data**: All webhook data stays in your systems
- **🏗️ Your System**: Deploy anywhere - on-premises, cloud, or hybrid
- **🔒 No Vendor Lock-in**: No external webhook services required
- **⚡ Full Customization**: Modify and extend webhook behavior to your needs
### Overview
*Note: This is one of the original QBit implementations - so, some of the mechanics of how
it is loaded and used by an application are not exactly fully defined at the time of its
creation... Please excuse any dust or not-quite-round wheels you find here!*
This QBit provides a basic implementation of webhooks to a QQQ Application.
## 🏗️ Architecture
### Technology Stack
- **Java**: Java 17+ with UTF-8 encoding
- **QQQ Framework**: Built on QQQ backend modules
- **Database**: RDBMS support through QQQ tables
- **JSON**: Structured event payloads with API versioning
### What This QBit Contains
- **Webhook Management**: Complete webhook endpoint and subscription system
- **Event Processing**: Event type registration and categorization
- **Delivery System**: Reliable webhook delivery with retry logic
- **Audit Trail**: Comprehensive logging of all webhook activities
- **Database Schema**: Five core tables for webhook management
### What This QBit Does NOT Contain
- **HTTP Client**: Uses QQQ's built-in HTTP capabilities
- **Authentication**: Relies on QQQ's security framework
- **Scheduling**: Uses QQQ's job scheduling system
- **External Services**: No third-party webhook services
## 🚀 Getting Started
### Prerequisites
- QQQ application (v0.20+)
- Database backend configured
- **Java 17+** (required for QQQ features)
- **Maven 3.8+** (for build system)
- **QQQ Application** (this is a QBit, not a standalone application)
### Installation
Add to your `pom.xml`:
### Usage
#### Pom dependency
```xml
<dependency>
<groupId>io.qrun</groupId>
<groupId>com.kingsrook.qbits</groupId>
<artifactId>qbit-webhooks</artifactId>
<version>0.2.0</version>
<version>${TODO}</version>
</dependency>
```
### Register the QBit
#### Setup
##### Define the QBit
```java
public class AppMetaProvider extends QMetaProvider {
@Override
public void configure(QInstance qInstance) {
new WebhooksQBit().configure(qInstance);
}
}
WebhooksQBitProducer producer = new WebhooksQBitProducer()
.withQBitConfig(new WebhooksQBitConfig());
MetaDataProducerMultiOutput allQBitMetaData = producer.produce(qInstance);
// add it to your QInstance.
```
### Configure a Webhook
##### Define webhook event types
```java
new QWebhookMetaData()
.withName("orderCreatedNotification")
.withTable("order")
.withEvents(WebhookEvent.INSERT)
.withUrl("https://api.example.com/webhooks/orders")
.withSecret("your-signing-secret");
WebhooksRegistry webhooksRegistry = WebhooksRegistry.ofOrWithNew(qInstance);
webhooksRegistry.registerWebhookEventType(new WebhookEventType()
.withName(ORDER_CREATED_EVENT_TYPE)
.withLabel("Order Created")
.withCategory(WebhookEventCategory.INSERT)
.withTableName(Order.TABLE_NAME));
```
## Usage
### Provides
#### Tables
- `webhook` - a connection to a partner system, e.g., URL to post events to.
- `webhookSubscription` - which event types should be sent to a webhook.
- `webhookEvent` - table-as-queue plus log of events for sending to a subscription.
- `webhookEventContent` - storage of JSON bodies to post.
- `webhookEventSendLog` - tracking of attempted sends.
### Multiple Events
#### Classes
- `WebhooksRegistry` - where application-defined event types must be registered.
```java
new QWebhookMetaData()
.withName("customerSync")
.withTable("customer")
.withEvents(WebhookEvent.INSERT, WebhookEvent.UPDATE, WebhookEvent.DELETE)
.withUrl("https://crm.example.com/sync");
```
### Dependencies
- `qqq-backend-module-api` - as the objects posted are api-versioned
- `QQQTablesMetaDataProvider` - for event record foreign keys
### Conditional Delivery
## 📚 Documentation
```java
// Only fire when status changes to 'approved'
new QWebhookMetaData()
.withName("approvalNotification")
.withTable("order")
.withEvents(WebhookEvent.UPDATE)
.withCondition((oldRecord, newRecord) ->
"approved".equals(newRecord.getValue("status")) &&
!"approved".equals(oldRecord.getValue("status")))
.withUrl("https://notify.example.com/approved");
```
**📖 [Complete Documentation Wiki](https://github.com/Kingsrook/qqq/wiki)** - Start here for comprehensive guides
### Custom Payload
- **[🏠 Home](https://github.com/Kingsrook/qqq/wiki/Home)** - Project overview and quick start
- **[🏗️ Architecture](https://github.com/Kingsrook/qqq/wiki/High-Level-Architecture)** - System design and principles
- **[🔧 Development](https://github.com/Kingsrook/qqq/wiki/Developer-Onboarding)** - Setup and contribution guide
- **[📦 Modules](https://github.com/Kingsrook/qqq/wiki/Core-Modules)** - Available components and usage
- **[🚀 Building](https://github.com/Kingsrook/qqq/wiki/Building-Locally)** - Local development workflow
- **[🔌 QBits](https://github.com/Kingsrook/qqq/wiki/QBit-Development)** - QBit development guide
```java
new QWebhookMetaData()
.withName("slackNotification")
.withTable("alert")
.withEvents(WebhookEvent.INSERT)
.withPayloadTransformer((record) -> Map.of(
"text", "New alert: " + record.getValue("message"),
"channel", "#alerts"
))
.withUrl("https://hooks.slack.com/services/xxx");
```
## 🤝 Contributing
### Verifying Signatures
QBit Webhooks is **open source** and welcomes contributions!
Receiving systems can verify the payload using the HMAC-SHA256 signature in the `X-Webhook-Signature` header:
- **🐛 [Report Issues](https://github.com/Kingsrook/qqq/issues)** - Bug reports and feature requests
- **📝 [Contribution Guide](https://github.com/Kingsrook/qqq/wiki/Contribution-Guidelines)** - How to contribute code and documentation
- **🔍 [Code Standards](https://github.com/Kingsrook/qqq/wiki/Code-Review-Standards)** - QQQ's coding standards and review process
```java
String payload = requestBody;
String signature = request.getHeader("X-Webhook-Signature");
String expected = HmacUtils.hmacSha256Hex(secret, payload);
if (!expected.equals(signature)) {
throw new SecurityException("Invalid signature");
}
```
**First time contributing?** Start with our [Developer Onboarding Guide](https://github.com/Kingsrook/qqq/wiki/Developer-Onboarding) to get your environment set up.
## Configuration
## 🏢 About Kingsrook
The QBit creates these tables:
QBit Webhooks is built by **[Kingsrook](https://qrun.io)** - making engineers more productive through intelligent automation and developer tools.
| Table | Purpose |
|-------|---------|
| `webhook` | Webhook endpoint configurations |
| `webhook_log` | Delivery attempts and responses |
- **Website**: [https://qrun.io](https://qrun.io)
- **Contact**: [contact@kingsrook.com](mailto:contact@kingsrook.com)
- **GitHub**: [https://github.com/Kingsrook](https://github.com/Kingsrook)
### Retry Settings
## 📄 License
```java
new WebhooksQBit()
.withMaxRetries(5)
.withRetryDelayMs(1000)
.withRetryBackoffMultiplier(2.0);
```
This project is licensed under the **GNU Affero General Public License v3.0** - see the [LICENSE.txt](LICENSE.txt) file for details.
## Project Status
## 🆘 Support & Community
Stable and production-ready.
### ⚠️ Important: Use Main QQQ Repository
### Roadmap
**All support, issues, discussions, and community interactions should go through the main QQQ repository:**
- Batch delivery (group multiple events)
- Dead letter queue for failed deliveries
- Dashboard UI for webhook management
- **Main Repository**: https://github.com/Kingsrook/qqq
- **Issues**: https://github.com/Kingsrook/qqq/issues
- **Discussions**: https://github.com/Kingsrook/qqq/discussions
- **Wiki**: https://github.com/Kingsrook/qqq.wiki
## Contributing
### Why This Repository Exists
1. Fork the repository
2. Create a feature branch
3. Run tests: `mvn clean verify`
4. Submit a pull request
This repository is maintained separately from the main QQQ repository to:
- **Enable independent QBit development** and versioning
- **Allow QBit-specific CI/CD** and deployment pipelines
- **Provide clear separation** between QBit components and core framework concerns
- **Support different release cycles** for QBits vs. core framework
## License
### Getting Help
- **Documentation**: Check the [QQQ Wiki](https://github.com/Kingsrook/qqq.wiki)
- **Issues**: Report bugs and feature requests on [Main QQQ Issues](https://github.com/Kingsrook/qqq/issues)
- **Discussions**: Join community discussions on [Main QQQ Discussions](https://github.com/Kingsrook/qqq/discussions)
- **Questions**: Ask questions in the main QQQ repository
---
**Ready to integrate webhooks into your QQQ application?** [Get started with QBit Webhooks today!](https://github.com/Kingsrook/qqq/wiki/QBit-Development)
Proprietary - QRun.IO
+2 -2
View File
@@ -26,7 +26,7 @@
<parent>
<groupId>com.kingsrook</groupId>
<artifactId>qbit-build-parent</artifactId>
<version>1.5.1</version>
<version>1.1.0</version>
</parent>
<groupId>com.kingsrook.qbits</groupId>
@@ -64,7 +64,7 @@
</scm>
<properties>
<revision>0.3.0-SNAPSHOT</revision>
<revision>0.2.2</revision>
</properties>