Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
722c5942c5 | ||
|
|
b77e74b003 | ||
|
|
e659452e01 |
No files matched your search
+10
-53
@@ -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.*/
|
||||
@@ -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:
|
||||
- "*"
|
||||
@@ -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
|
||||
@@ -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
@@ -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
|
||||
@@ -1,154 +1,181 @@
|
||||
# qbit-webhooks
|
||||
# QBit: Webhooks
|
||||
|
||||
Outbound webhook notifications for QQQ applications.
|
||||
[](https://github.com/Kingsrook/qbit-webhooks)
|
||||
[](https://www.gnu.org/licenses/agpl-3.0.en.html)
|
||||
[](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
|
||||
@@ -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>
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user