mirror of
https://github.com/bitechdev/ResolveSpec.git
synced 2025-11-13 18:03:53 +00:00
Migration to Bitech
This commit is contained in:
parent
c8704c07dd
commit
3eb17666bf
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2025 Warky Devs Pty Ltd
|
Copyright (c) 2025
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
388
README.md
388
README.md
@ -1,9 +1,16 @@
|
|||||||
# 📜 ResolveSpec 📜
|
# 📜 ResolveSpec 📜
|
||||||
|
|
||||||
ResolveSpec is a flexible and powerful REST API specification and implementation that provides GraphQL-like capabilities while maintaining REST simplicity. It allows for dynamic data querying, relationship preloading, and complex filtering through a clean, URL-based interface.
|
ResolveSpec is a flexible and powerful REST API specification and implementation that provides GraphQL-like capabilities while maintaining REST simplicity. It offers **two complementary approaches**:
|
||||||
|
|
||||||
|
1. **ResolveSpec** - Body-based API with JSON request options
|
||||||
|
2. **RestHeadSpec** - Header-based API where query options are passed via HTTP headers
|
||||||
|
|
||||||
|
Both share the same core architecture and provide dynamic data querying, relationship preloading, and complex filtering.
|
||||||
|
|
||||||
**🆕 New in v2.0**: Database-agnostic architecture with support for GORM, Bun, and other ORMs. Router-flexible design works with Gorilla Mux, Gin, Echo, and more.
|
**🆕 New in v2.0**: Database-agnostic architecture with support for GORM, Bun, and other ORMs. Router-flexible design works with Gorilla Mux, Gin, Echo, and more.
|
||||||
|
|
||||||
|
**🆕 New in v2.1**: RestHeadSpec (HeaderSpec) - Header-based REST API with lifecycle hooks, cursor pagination, and advanced filtering.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Table of Contents
|
## Table of Contents
|
||||||
@ -11,30 +18,46 @@ ResolveSpec is a flexible and powerful REST API specification and implementation
|
|||||||
- [Features](#features)
|
- [Features](#features)
|
||||||
- [Installation](#installation)
|
- [Installation](#installation)
|
||||||
- [Quick Start](#quick-start)
|
- [Quick Start](#quick-start)
|
||||||
|
- [ResolveSpec (Body-Based API)](#resolvespec-body-based-api)
|
||||||
|
- [RestHeadSpec (Header-Based API)](#restheadspec-header-based-api)
|
||||||
- [Existing Code (Backward Compatible)](#option-1-existing-code-backward-compatible)
|
- [Existing Code (Backward Compatible)](#option-1-existing-code-backward-compatible)
|
||||||
- [New Database-Agnostic API](#option-2-new-database-agnostic-api)
|
- [New Database-Agnostic API](#option-2-new-database-agnostic-api)
|
||||||
- [Router Integration](#router-integration)
|
- [Router Integration](#router-integration)
|
||||||
- [Migration from v1.x](#migration-from-v1x)
|
- [Migration from v1.x](#migration-from-v1x)
|
||||||
- [Architecture](#architecture)
|
- [Architecture](#architecture)
|
||||||
- [API Structure](#api-structure)
|
- [API Structure](#api-structure)
|
||||||
|
- [RestHeadSpec: Header-Based API](#restheadspec-header-based-api-1)
|
||||||
|
- [Lifecycle Hooks](#lifecycle-hooks)
|
||||||
|
- [Cursor Pagination](#cursor-pagination)
|
||||||
- [Example Usage](#example-usage)
|
- [Example Usage](#example-usage)
|
||||||
- [Testing](#testing)
|
- [Testing](#testing)
|
||||||
- [What's New in v2.0](#whats-new-in-v20)
|
- [What's New in v2.0](#whats-new-in-v20)
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
|
### Core Features
|
||||||
- **Dynamic Data Querying**: Select specific columns and relationships to return
|
- **Dynamic Data Querying**: Select specific columns and relationships to return
|
||||||
- **Relationship Preloading**: Load related entities with custom column selection and filters
|
- **Relationship Preloading**: Load related entities with custom column selection and filters
|
||||||
- **Complex Filtering**: Apply multiple filters with various operators
|
- **Complex Filtering**: Apply multiple filters with various operators
|
||||||
- **Sorting**: Multi-column sort support
|
- **Sorting**: Multi-column sort support
|
||||||
- **Pagination**: Built-in limit and offset support
|
- **Pagination**: Built-in limit/offset and cursor-based pagination
|
||||||
- **Computed Columns**: Define virtual columns for complex calculations
|
- **Computed Columns**: Define virtual columns for complex calculations
|
||||||
- **Custom Operators**: Add custom SQL conditions when needed
|
- **Custom Operators**: Add custom SQL conditions when needed
|
||||||
|
|
||||||
|
### Architecture (v2.0+)
|
||||||
- **🆕 Database Agnostic**: Works with GORM, Bun, or any database layer through adapters
|
- **🆕 Database Agnostic**: Works with GORM, Bun, or any database layer through adapters
|
||||||
- **🆕 Router Flexible**: Integrates with Gorilla Mux, Gin, Echo, or custom routers
|
- **🆕 Router Flexible**: Integrates with Gorilla Mux, Gin, Echo, or custom routers
|
||||||
- **🆕 Backward Compatible**: Existing code works without changes
|
- **🆕 Backward Compatible**: Existing code works without changes
|
||||||
- **🆕 Better Testing**: Mockable interfaces for easy unit testing
|
- **🆕 Better Testing**: Mockable interfaces for easy unit testing
|
||||||
|
|
||||||
|
### RestHeadSpec (v2.1+)
|
||||||
|
- **🆕 Header-Based API**: All query options passed via HTTP headers instead of request body
|
||||||
|
- **🆕 Lifecycle Hooks**: Before/after hooks for create, read, update, and delete operations
|
||||||
|
- **🆕 Cursor Pagination**: Efficient cursor-based pagination with complex sort support
|
||||||
|
- **🆕 Multiple Response Formats**: Simple, detailed, and Syncfusion-compatible formats
|
||||||
|
- **🆕 Advanced Filtering**: Field filters, search operators, AND/OR logic, and custom SQL
|
||||||
|
- **🆕 Base64 Encoding**: Support for base64-encoded header values
|
||||||
|
|
||||||
## API Structure
|
## API Structure
|
||||||
|
|
||||||
### URL Patterns
|
### URL Patterns
|
||||||
@ -66,6 +89,216 @@ ResolveSpec is a flexible and powerful REST API specification and implementation
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## RestHeadSpec: Header-Based API
|
||||||
|
|
||||||
|
RestHeadSpec provides an alternative REST API approach where all query options are passed via HTTP headers instead of the request body. This provides cleaner separation between data and metadata.
|
||||||
|
|
||||||
|
### Quick Example
|
||||||
|
|
||||||
|
```http
|
||||||
|
GET /public/users HTTP/1.1
|
||||||
|
Host: api.example.com
|
||||||
|
X-Select-Fields: id,name,email,department_id
|
||||||
|
X-Preload: department:id,name
|
||||||
|
X-FieldFilter-Status: active
|
||||||
|
X-SearchOp-Gte-Age: 18
|
||||||
|
X-Sort: -created_at,+name
|
||||||
|
X-Limit: 50
|
||||||
|
X-DetailApi: true
|
||||||
|
```
|
||||||
|
|
||||||
|
### Setup with GORM
|
||||||
|
|
||||||
|
```go
|
||||||
|
import "github.com/bitechdev/ResolveSpec/pkg/restheadspec"
|
||||||
|
import "github.com/gorilla/mux"
|
||||||
|
|
||||||
|
// Create handler
|
||||||
|
handler := restheadspec.NewHandlerWithGORM(db)
|
||||||
|
|
||||||
|
// Register models using schema.table format
|
||||||
|
handler.Registry.RegisterModel("public.users", &User{})
|
||||||
|
handler.Registry.RegisterModel("public.posts", &Post{})
|
||||||
|
|
||||||
|
// Setup routes
|
||||||
|
router := mux.NewRouter()
|
||||||
|
restheadspec.SetupMuxRoutes(router, handler)
|
||||||
|
|
||||||
|
// Start server
|
||||||
|
http.ListenAndServe(":8080", router)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Setup with Bun ORM
|
||||||
|
|
||||||
|
```go
|
||||||
|
import "github.com/bitechdev/ResolveSpec/pkg/restheadspec"
|
||||||
|
import "github.com/uptrace/bun"
|
||||||
|
|
||||||
|
// Create handler with Bun
|
||||||
|
handler := restheadspec.NewHandlerWithBun(bunDB)
|
||||||
|
|
||||||
|
// Register models
|
||||||
|
handler.Registry.RegisterModel("public.users", &User{})
|
||||||
|
|
||||||
|
// Setup routes (same as GORM)
|
||||||
|
router := mux.NewRouter()
|
||||||
|
restheadspec.SetupMuxRoutes(router, handler)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Common Headers
|
||||||
|
|
||||||
|
| Header | Description | Example |
|
||||||
|
|--------|-------------|---------|
|
||||||
|
| `X-Select-Fields` | Columns to include | `id,name,email` |
|
||||||
|
| `X-Not-Select-Fields` | Columns to exclude | `password,internal_notes` |
|
||||||
|
| `X-FieldFilter-{col}` | Exact match filter | `X-FieldFilter-Status: active` |
|
||||||
|
| `X-SearchFilter-{col}` | Fuzzy search (ILIKE) | `X-SearchFilter-Name: john` |
|
||||||
|
| `X-SearchOp-{op}-{col}` | Filter with operator | `X-SearchOp-Gte-Age: 18` |
|
||||||
|
| `X-Preload` | Preload relations | `posts:id,title` |
|
||||||
|
| `X-Sort` | Sort columns | `-created_at,+name` |
|
||||||
|
| `X-Limit` | Limit results | `50` |
|
||||||
|
| `X-Offset` | Offset for pagination | `100` |
|
||||||
|
| `X-Clean-JSON` | Remove null/empty fields | `true` |
|
||||||
|
|
||||||
|
**Available Operators**: `eq`, `neq`, `gt`, `gte`, `lt`, `lte`, `contains`, `startswith`, `endswith`, `between`, `betweeninclusive`, `in`, `empty`, `notempty`
|
||||||
|
|
||||||
|
For complete header documentation, see [pkg/restheadspec/HEADERS.md](pkg/restheadspec/HEADERS.md).
|
||||||
|
|
||||||
|
### Lifecycle Hooks
|
||||||
|
|
||||||
|
RestHeadSpec supports lifecycle hooks for all CRUD operations:
|
||||||
|
|
||||||
|
```go
|
||||||
|
import "github.com/bitechdev/ResolveSpec/pkg/restheadspec"
|
||||||
|
|
||||||
|
// Create handler
|
||||||
|
handler := restheadspec.NewHandlerWithGORM(db)
|
||||||
|
|
||||||
|
// Register a before-read hook (e.g., for authorization)
|
||||||
|
handler.Hooks.Register(restheadspec.BeforeRead, func(ctx *restheadspec.HookContext) error {
|
||||||
|
// Check permissions
|
||||||
|
if !userHasPermission(ctx.Context, ctx.Entity) {
|
||||||
|
return fmt.Errorf("unauthorized access to %s", ctx.Entity)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modify query options
|
||||||
|
ctx.Options.Limit = ptr(100) // Enforce max limit
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
// Register an after-read hook (e.g., for data transformation)
|
||||||
|
handler.Hooks.Register(restheadspec.AfterRead, func(ctx *restheadspec.HookContext) error {
|
||||||
|
// Transform or filter results
|
||||||
|
if users, ok := ctx.Result.([]User); ok {
|
||||||
|
for i := range users {
|
||||||
|
users[i].Email = maskEmail(users[i].Email)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
// Register a before-create hook (e.g., for validation)
|
||||||
|
handler.Hooks.Register(restheadspec.BeforeCreate, func(ctx *restheadspec.HookContext) error {
|
||||||
|
// Validate data
|
||||||
|
if user, ok := ctx.Data.(*User); ok {
|
||||||
|
if user.Email == "" {
|
||||||
|
return fmt.Errorf("email is required")
|
||||||
|
}
|
||||||
|
// Add timestamps
|
||||||
|
user.CreatedAt = time.Now()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
**Available Hook Types**:
|
||||||
|
- `BeforeRead`, `AfterRead`
|
||||||
|
- `BeforeCreate`, `AfterCreate`
|
||||||
|
- `BeforeUpdate`, `AfterUpdate`
|
||||||
|
- `BeforeDelete`, `AfterDelete`
|
||||||
|
|
||||||
|
**HookContext** provides:
|
||||||
|
- `Context`: Request context
|
||||||
|
- `Handler`: Access to handler, database, and registry
|
||||||
|
- `Schema`, `Entity`, `TableName`: Request info
|
||||||
|
- `Model`: The registered model type
|
||||||
|
- `Options`: Parsed request options (filters, sorting, etc.)
|
||||||
|
- `ID`: Record ID (for single-record operations)
|
||||||
|
- `Data`: Request data (for create/update)
|
||||||
|
- `Result`: Operation result (for after hooks)
|
||||||
|
- `Writer`: Response writer (allows hooks to modify response)
|
||||||
|
|
||||||
|
### Cursor Pagination
|
||||||
|
|
||||||
|
RestHeadSpec supports efficient cursor-based pagination for large datasets:
|
||||||
|
|
||||||
|
```http
|
||||||
|
GET /public/posts HTTP/1.1
|
||||||
|
X-Sort: -created_at,+id
|
||||||
|
X-Limit: 50
|
||||||
|
X-Cursor-Forward: <cursor_token>
|
||||||
|
```
|
||||||
|
|
||||||
|
**How it works**:
|
||||||
|
1. First request returns results + cursor token in response
|
||||||
|
2. Subsequent requests use `X-Cursor-Forward` or `X-Cursor-Backward`
|
||||||
|
3. Cursor maintains consistent ordering even with data changes
|
||||||
|
4. Supports complex multi-column sorting
|
||||||
|
|
||||||
|
**Benefits over offset pagination**:
|
||||||
|
- Consistent results when data changes
|
||||||
|
- Better performance for large offsets
|
||||||
|
- Prevents "skipped" or duplicate records
|
||||||
|
- Works with complex sort expressions
|
||||||
|
|
||||||
|
**Example with hooks**:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Enable cursor pagination in a hook
|
||||||
|
handler.Hooks.Register(restheadspec.BeforeRead, func(ctx *restheadspec.HookContext) error {
|
||||||
|
// For large tables, enforce cursor pagination
|
||||||
|
if ctx.Entity == "posts" && ctx.Options.Offset != nil && *ctx.Options.Offset > 1000 {
|
||||||
|
return fmt.Errorf("use cursor pagination for large offsets")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### Response Formats
|
||||||
|
|
||||||
|
RestHeadSpec supports multiple response formats:
|
||||||
|
|
||||||
|
**1. Simple Format** (`X-SimpleApi: true`):
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{ "id": 1, "name": "John" },
|
||||||
|
{ "id": 2, "name": "Jane" }
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
**2. Detail Format** (`X-DetailApi: true`, default):
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"data": [...],
|
||||||
|
"metadata": {
|
||||||
|
"total": 100,
|
||||||
|
"filtered": 100,
|
||||||
|
"limit": 50,
|
||||||
|
"offset": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**3. Syncfusion Format** (`X-Syncfusion: true`):
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"result": [...],
|
||||||
|
"count": 100
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Example Usage
|
## Example Usage
|
||||||
|
|
||||||
### Reading Data with Related Entities
|
### Reading Data with Related Entities
|
||||||
@ -110,17 +343,73 @@ POST /core/users
|
|||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
go get github.com/Warky-Devs/ResolveSpec
|
go get github.com/bitechdev/ResolveSpec
|
||||||
```
|
```
|
||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
||||||
|
### ResolveSpec (Body-Based API)
|
||||||
|
|
||||||
|
ResolveSpec uses JSON request bodies to specify query options:
|
||||||
|
|
||||||
|
```go
|
||||||
|
import "github.com/bitechdev/ResolveSpec/pkg/resolvespec"
|
||||||
|
|
||||||
|
// Create handler
|
||||||
|
handler := resolvespec.NewAPIHandler(gormDB)
|
||||||
|
handler.RegisterModel("core", "users", &User{})
|
||||||
|
|
||||||
|
// Setup routes
|
||||||
|
router := mux.NewRouter()
|
||||||
|
resolvespec.SetupRoutes(router, handler)
|
||||||
|
|
||||||
|
// Client makes POST request with body:
|
||||||
|
// POST /core/users
|
||||||
|
// {
|
||||||
|
// "operation": "read",
|
||||||
|
// "options": {
|
||||||
|
// "columns": ["id", "name", "email"],
|
||||||
|
// "filters": [{"column": "status", "operator": "eq", "value": "active"}],
|
||||||
|
// "limit": 10
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
```
|
||||||
|
|
||||||
|
### RestHeadSpec (Header-Based API)
|
||||||
|
|
||||||
|
RestHeadSpec uses HTTP headers for query options instead of request body:
|
||||||
|
|
||||||
|
```go
|
||||||
|
import "github.com/bitechdev/ResolveSpec/pkg/restheadspec"
|
||||||
|
|
||||||
|
// Create handler with GORM
|
||||||
|
handler := restheadspec.NewHandlerWithGORM(db)
|
||||||
|
|
||||||
|
// Register models (schema.table format)
|
||||||
|
handler.Registry.RegisterModel("public.users", &User{})
|
||||||
|
handler.Registry.RegisterModel("public.posts", &Post{})
|
||||||
|
|
||||||
|
// Setup routes with Mux
|
||||||
|
muxRouter := mux.NewRouter()
|
||||||
|
restheadspec.SetupMuxRoutes(muxRouter, handler)
|
||||||
|
|
||||||
|
// Client makes GET request with headers:
|
||||||
|
// GET /public/users
|
||||||
|
// X-Select-Fields: id,name,email
|
||||||
|
// X-FieldFilter-Status: active
|
||||||
|
// X-Limit: 10
|
||||||
|
// X-Sort: -created_at
|
||||||
|
// X-Preload: posts:id,title
|
||||||
|
```
|
||||||
|
|
||||||
|
See [RestHeadSpec: Header-Based API](#restheadspec-header-based-api-1) for complete header documentation.
|
||||||
|
|
||||||
### Option 1: Existing Code (Backward Compatible)
|
### Option 1: Existing Code (Backward Compatible)
|
||||||
|
|
||||||
Your existing code continues to work without any changes:
|
Your existing code continues to work without any changes:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
import "github.com/Warky-Devs/ResolveSpec/pkg/resolvespec"
|
import "github.com/bitechdev/ResolveSpec/pkg/resolvespec"
|
||||||
|
|
||||||
// This still works exactly as before
|
// This still works exactly as before
|
||||||
handler := resolvespec.NewAPIHandler(gormDB)
|
handler := resolvespec.NewAPIHandler(gormDB)
|
||||||
@ -131,12 +420,36 @@ handler.RegisterModel("core", "users", &User{})
|
|||||||
|
|
||||||
ResolveSpec v2.0 introduces a new database and router abstraction layer while maintaining **100% backward compatibility**. Your existing code will continue to work without any changes.
|
ResolveSpec v2.0 introduces a new database and router abstraction layer while maintaining **100% backward compatibility**. Your existing code will continue to work without any changes.
|
||||||
|
|
||||||
|
### Repository Path Migration
|
||||||
|
|
||||||
|
**IMPORTANT**: The repository has moved from `github.com/Warky-Devs/ResolveSpec` to `github.com/bitechdev/ResolveSpec`.
|
||||||
|
|
||||||
|
To update your imports:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Update go.mod
|
||||||
|
go mod edit -replace github.com/Warky-Devs/ResolveSpec=github.com/bitechdev/ResolveSpec@latest
|
||||||
|
go mod tidy
|
||||||
|
|
||||||
|
# Or update imports manually in your code
|
||||||
|
# Old: import "github.com/Warky-Devs/ResolveSpec/pkg/resolvespec"
|
||||||
|
# New: import "github.com/bitechdev/ResolveSpec/pkg/resolvespec"
|
||||||
|
```
|
||||||
|
|
||||||
|
Alternatively, use find and replace in your project:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
find . -type f -name "*.go" -exec sed -i 's|github.com/Warky-Devs/ResolveSpec|github.com/bitechdev/ResolveSpec|g' {} +
|
||||||
|
go mod tidy
|
||||||
|
```
|
||||||
|
|
||||||
### Migration Timeline
|
### Migration Timeline
|
||||||
|
|
||||||
1. **Phase 1**: Continue using existing API (no changes needed)
|
1. **Phase 1**: Update repository path (see above)
|
||||||
2. **Phase 2**: Gradually adopt new constructors when convenient
|
2. **Phase 2**: Continue using existing API (no changes needed)
|
||||||
3. **Phase 3**: Switch to interface-based approach for new features
|
3. **Phase 3**: Gradually adopt new constructors when convenient
|
||||||
4. **Phase 4**: Optionally switch database backends
|
4. **Phase 4**: Switch to interface-based approach for new features
|
||||||
|
5. **Phase 5**: Optionally switch database backends or try RestHeadSpec
|
||||||
|
|
||||||
### Detailed Migration Guide
|
### Detailed Migration Guide
|
||||||
|
|
||||||
@ -144,6 +457,26 @@ For detailed migration instructions, examples, and best practices, see [MIGRATIO
|
|||||||
|
|
||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
|
### Two Complementary APIs
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────┐
|
||||||
|
│ ResolveSpec Framework │
|
||||||
|
├─────────────────────┬───────────────────────────────┤
|
||||||
|
│ ResolveSpec │ RestHeadSpec │
|
||||||
|
│ (Body-based) │ (Header-based) │
|
||||||
|
├─────────────────────┴───────────────────────────────┤
|
||||||
|
│ Common Core Components │
|
||||||
|
│ • Model Registry • Filters • Preloading │
|
||||||
|
│ • Sorting • Pagination • Type System │
|
||||||
|
└──────────────────────┬──────────────────────────────┘
|
||||||
|
↓
|
||||||
|
┌──────────────────────────────┐
|
||||||
|
│ Database Abstraction │
|
||||||
|
│ [GORM] [Bun] [Custom] │
|
||||||
|
└──────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
### Database Abstraction Layer
|
### Database Abstraction Layer
|
||||||
|
|
||||||
```
|
```
|
||||||
@ -151,6 +484,8 @@ Your Application Code
|
|||||||
↓
|
↓
|
||||||
Handler (Business Logic)
|
Handler (Business Logic)
|
||||||
↓
|
↓
|
||||||
|
[Hooks & Middleware] (RestHeadSpec only)
|
||||||
|
↓
|
||||||
Database Interface
|
Database Interface
|
||||||
↓
|
↓
|
||||||
[GormAdapter] [BunAdapter] [CustomAdapter]
|
[GormAdapter] [BunAdapter] [CustomAdapter]
|
||||||
@ -176,7 +511,7 @@ Your Application Code
|
|||||||
|
|
||||||
#### With GORM (Recommended Migration Path)
|
#### With GORM (Recommended Migration Path)
|
||||||
```go
|
```go
|
||||||
import "github.com/Warky-Devs/ResolveSpec/pkg/resolvespec"
|
import "github.com/bitechdev/ResolveSpec/pkg/resolvespec"
|
||||||
|
|
||||||
// Create database adapter
|
// Create database adapter
|
||||||
dbAdapter := resolvespec.NewGormAdapter(gormDB)
|
dbAdapter := resolvespec.NewGormAdapter(gormDB)
|
||||||
@ -192,7 +527,7 @@ handler := resolvespec.NewHandler(dbAdapter, registry)
|
|||||||
|
|
||||||
#### With Bun ORM
|
#### With Bun ORM
|
||||||
```go
|
```go
|
||||||
import "github.com/Warky-Devs/ResolveSpec/pkg/resolvespec"
|
import "github.com/bitechdev/ResolveSpec/pkg/resolvespec"
|
||||||
import "github.com/uptrace/bun"
|
import "github.com/uptrace/bun"
|
||||||
|
|
||||||
// Create Bun adapter (Bun dependency already included)
|
// Create Bun adapter (Bun dependency already included)
|
||||||
@ -415,12 +750,32 @@ func TestHandler(t *testing.T) {
|
|||||||
|
|
||||||
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
||||||
|
|
||||||
## What's New in v2.0
|
## What's New
|
||||||
|
|
||||||
### Breaking Changes
|
### v2.1 (Latest)
|
||||||
|
|
||||||
|
**RestHeadSpec - Header-Based REST API**:
|
||||||
|
- **Header-Based Querying**: All query options via HTTP headers instead of request body
|
||||||
|
- **Lifecycle Hooks**: Before/after hooks for create, read, update, delete operations
|
||||||
|
- **Cursor Pagination**: Efficient cursor-based pagination with complex sorting
|
||||||
|
- **Advanced Filtering**: Field filters, search operators, AND/OR logic
|
||||||
|
- **Multiple Response Formats**: Simple, detailed, and Syncfusion-compatible responses
|
||||||
|
- **Base64 Support**: Base64-encoded header values for complex queries
|
||||||
|
- **Type-Aware Filtering**: Automatic type detection and conversion for filters
|
||||||
|
|
||||||
|
**Core Improvements**:
|
||||||
|
- Better model registry with schema.table format support
|
||||||
|
- Enhanced validation and error handling
|
||||||
|
- Improved reflection safety
|
||||||
|
- Fixed COUNT query issues with table aliasing
|
||||||
|
- Better pointer handling throughout the codebase
|
||||||
|
|
||||||
|
### v2.0
|
||||||
|
|
||||||
|
**Breaking Changes**:
|
||||||
- **None!** Full backward compatibility maintained
|
- **None!** Full backward compatibility maintained
|
||||||
|
|
||||||
### New Features
|
**New Features**:
|
||||||
- **Database Abstraction**: Support for GORM, Bun, and custom ORMs
|
- **Database Abstraction**: Support for GORM, Bun, and custom ORMs
|
||||||
- **Router Flexibility**: Works with any HTTP router through adapters
|
- **Router Flexibility**: Works with any HTTP router through adapters
|
||||||
- **BunRouter Integration**: Built-in support for uptrace/bunrouter
|
- **BunRouter Integration**: Built-in support for uptrace/bunrouter
|
||||||
@ -428,7 +783,7 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
|
|||||||
- **Enhanced Testing**: Mockable interfaces for comprehensive testing
|
- **Enhanced Testing**: Mockable interfaces for comprehensive testing
|
||||||
- **Migration Guide**: Step-by-step migration instructions
|
- **Migration Guide**: Step-by-step migration instructions
|
||||||
|
|
||||||
### Performance Improvements
|
**Performance Improvements**:
|
||||||
- More efficient query building through interface design
|
- More efficient query building through interface design
|
||||||
- Reduced coupling between components
|
- Reduced coupling between components
|
||||||
- Better memory management with interface boundaries
|
- Better memory management with interface boundaries
|
||||||
@ -436,8 +791,9 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
|
|||||||
## Acknowledgments
|
## Acknowledgments
|
||||||
|
|
||||||
- Inspired by REST, OData, and GraphQL's flexibility
|
- Inspired by REST, OData, and GraphQL's flexibility
|
||||||
|
- **Header-based approach**: Inspired by REST best practices and clean API design
|
||||||
- **Database Support**: [GORM](https://gorm.io) and [Bun](https://bun.uptrace.dev/)
|
- **Database Support**: [GORM](https://gorm.io) and [Bun](https://bun.uptrace.dev/)
|
||||||
- **Router Support**: Gorilla Mux (built-in), Gin, Echo, and others through adapters
|
- **Router Support**: Gorilla Mux (built-in), BunRouter, Gin, Echo, and others through adapters
|
||||||
- Slogan generated using DALL-E
|
- Slogan generated using DALL-E
|
||||||
- AI used for documentation checking and correction
|
- AI used for documentation checking and correction
|
||||||
- Community feedback and contributions that made v2.0 possible
|
- Community feedback and contributions that made v2.0 and v2.1 possible
|
||||||
@ -7,11 +7,11 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Warky-Devs/ResolveSpec/pkg/logger"
|
"github.com/bitechdev/ResolveSpec/pkg/logger"
|
||||||
"github.com/Warky-Devs/ResolveSpec/pkg/modelregistry"
|
"github.com/bitechdev/ResolveSpec/pkg/modelregistry"
|
||||||
"github.com/Warky-Devs/ResolveSpec/pkg/testmodels"
|
"github.com/bitechdev/ResolveSpec/pkg/testmodels"
|
||||||
|
|
||||||
"github.com/Warky-Devs/ResolveSpec/pkg/resolvespec"
|
"github.com/bitechdev/ResolveSpec/pkg/resolvespec"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
|
|
||||||
"github.com/glebarez/sqlite"
|
"github.com/glebarez/sqlite"
|
||||||
|
|||||||
2
go.mod
2
go.mod
@ -1,4 +1,4 @@
|
|||||||
module github.com/Warky-Devs/ResolveSpec
|
module github.com/bitechdev/ResolveSpec
|
||||||
|
|
||||||
go 1.23.0
|
go 1.23.0
|
||||||
|
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/Warky-Devs/ResolveSpec/pkg/common"
|
"github.com/bitechdev/ResolveSpec/pkg/common"
|
||||||
"github.com/uptrace/bun"
|
"github.com/uptrace/bun"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/Warky-Devs/ResolveSpec/pkg/common"
|
"github.com/bitechdev/ResolveSpec/pkg/common"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -4,11 +4,12 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/Warky-Devs/ResolveSpec/pkg/common"
|
"github.com/bitechdev/ResolveSpec/pkg/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
// parseTableName splits a table name that may contain schema into separate schema and table
|
// parseTableName splits a table name that may contain schema into separate schema and table
|
||||||
// For example: "public.users" -> ("public", "users")
|
// For example: "public.users" -> ("public", "users")
|
||||||
|
//
|
||||||
// "users" -> ("", "users")
|
// "users" -> ("", "users")
|
||||||
func parseTableName(fullTableName string) (schema, table string) {
|
func parseTableName(fullTableName string) (schema, table string) {
|
||||||
if idx := strings.LastIndex(fullTableName, "."); idx != -1 {
|
if idx := strings.LastIndex(fullTableName, "."); idx != -1 {
|
||||||
|
|||||||
@ -3,7 +3,7 @@ package router
|
|||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/Warky-Devs/ResolveSpec/pkg/common"
|
"github.com/bitechdev/ResolveSpec/pkg/common"
|
||||||
"github.com/uptrace/bunrouter"
|
"github.com/uptrace/bunrouter"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -190,4 +190,3 @@ func DefaultBunRouterConfig() *BunRouterConfig {
|
|||||||
HandleOPTIONS: true,
|
HandleOPTIONS: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/Warky-Devs/ResolveSpec/pkg/common"
|
"github.com/bitechdev/ResolveSpec/pkg/common"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/Warky-Devs/ResolveSpec/pkg/logger"
|
"github.com/bitechdev/ResolveSpec/pkg/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ColumnValidator validates column names against a model's fields
|
// ColumnValidator validates column names against a model's fields
|
||||||
|
|||||||
@ -9,8 +9,8 @@ import (
|
|||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/Warky-Devs/ResolveSpec/pkg/common"
|
"github.com/bitechdev/ResolveSpec/pkg/common"
|
||||||
"github.com/Warky-Devs/ResolveSpec/pkg/logger"
|
"github.com/bitechdev/ResolveSpec/pkg/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Handler handles API requests using database and model abstractions
|
// Handler handles API requests using database and model abstractions
|
||||||
|
|||||||
@ -3,9 +3,9 @@ package resolvespec
|
|||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/Warky-Devs/ResolveSpec/pkg/common/adapters/database"
|
"github.com/bitechdev/ResolveSpec/pkg/common/adapters/database"
|
||||||
"github.com/Warky-Devs/ResolveSpec/pkg/common/adapters/router"
|
"github.com/bitechdev/ResolveSpec/pkg/common/adapters/router"
|
||||||
"github.com/Warky-Devs/ResolveSpec/pkg/modelregistry"
|
"github.com/bitechdev/ResolveSpec/pkg/modelregistry"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/uptrace/bun"
|
"github.com/uptrace/bun"
|
||||||
"github.com/uptrace/bunrouter"
|
"github.com/uptrace/bunrouter"
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/Warky-Devs/ResolveSpec/pkg/common"
|
"github.com/bitechdev/ResolveSpec/pkg/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CursorDirection defines pagination direction
|
// CursorDirection defines pagination direction
|
||||||
|
|||||||
@ -9,9 +9,9 @@ import (
|
|||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/Warky-Devs/ResolveSpec/pkg/common"
|
"github.com/bitechdev/ResolveSpec/pkg/common"
|
||||||
"github.com/Warky-Devs/ResolveSpec/pkg/common/adapters/database"
|
"github.com/bitechdev/ResolveSpec/pkg/common/adapters/database"
|
||||||
"github.com/Warky-Devs/ResolveSpec/pkg/logger"
|
"github.com/bitechdev/ResolveSpec/pkg/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Handler handles API requests using database and model abstractions
|
// Handler handles API requests using database and model abstractions
|
||||||
|
|||||||
@ -8,8 +8,8 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/Warky-Devs/ResolveSpec/pkg/common"
|
"github.com/bitechdev/ResolveSpec/pkg/common"
|
||||||
"github.com/Warky-Devs/ResolveSpec/pkg/logger"
|
"github.com/bitechdev/ResolveSpec/pkg/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ExtendedRequestOptions extends common.RequestOptions with additional features
|
// ExtendedRequestOptions extends common.RequestOptions with additional features
|
||||||
|
|||||||
@ -4,8 +4,8 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/Warky-Devs/ResolveSpec/pkg/common"
|
"github.com/bitechdev/ResolveSpec/pkg/common"
|
||||||
"github.com/Warky-Devs/ResolveSpec/pkg/logger"
|
"github.com/bitechdev/ResolveSpec/pkg/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
// HookType defines the type of hook to execute
|
// HookType defines the type of hook to execute
|
||||||
|
|||||||
@ -3,7 +3,7 @@ package restheadspec
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/Warky-Devs/ResolveSpec/pkg/logger"
|
"github.com/bitechdev/ResolveSpec/pkg/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
// This file contains example implementations showing how to use hooks
|
// This file contains example implementations showing how to use hooks
|
||||||
|
|||||||
@ -55,9 +55,9 @@ package restheadspec
|
|||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/Warky-Devs/ResolveSpec/pkg/common/adapters/database"
|
"github.com/bitechdev/ResolveSpec/pkg/common/adapters/database"
|
||||||
"github.com/Warky-Devs/ResolveSpec/pkg/common/adapters/router"
|
"github.com/bitechdev/ResolveSpec/pkg/common/adapters/router"
|
||||||
"github.com/Warky-Devs/ResolveSpec/pkg/modelregistry"
|
"github.com/bitechdev/ResolveSpec/pkg/modelregistry"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/uptrace/bun"
|
"github.com/uptrace/bun"
|
||||||
"github.com/uptrace/bunrouter"
|
"github.com/uptrace/bunrouter"
|
||||||
|
|||||||
@ -3,7 +3,7 @@ package testmodels
|
|||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Warky-Devs/ResolveSpec/pkg/modelregistry"
|
"github.com/bitechdev/ResolveSpec/pkg/modelregistry"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Department represents a company department
|
// Department represents a company department
|
||||||
|
|||||||
@ -60,12 +60,11 @@
|
|||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git+https://github.com/Warky-Devs/ResolveSpec"
|
"url": "git+https://github.com/bitechdev/ResolveSpec"
|
||||||
},
|
},
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/Warky-Devs/ResolveSpec/issues"
|
"url": "https://github.com/bitechdev/ResolveSpec/issues"
|
||||||
},
|
},
|
||||||
"homepage": "https://github.com/Warky-Devs/ResolveSpec#readme",
|
"homepage": "https://github.com/bitechdev/ResolveSpec#readme",
|
||||||
"packageManager": "pnpm@9.6.0+sha512.38dc6fba8dba35b39340b9700112c2fe1e12f10b17134715a4aa98ccf7bb035e76fd981cf0bb384dfa98f8d6af5481c2bef2f4266a24bfa20c34eb7147ce0b5e"
|
"packageManager": "pnpm@9.6.0+sha512.38dc6fba8dba35b39340b9700112c2fe1e12f10b17134715a4aa98ccf7bb035e76fd981cf0bb384dfa98f8d6af5481c2bef2f4266a24bfa20c34eb7147ce0b5e"
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -10,10 +10,10 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/Warky-Devs/ResolveSpec/pkg/logger"
|
"github.com/bitechdev/ResolveSpec/pkg/logger"
|
||||||
"github.com/Warky-Devs/ResolveSpec/pkg/modelregistry"
|
"github.com/bitechdev/ResolveSpec/pkg/modelregistry"
|
||||||
"github.com/Warky-Devs/ResolveSpec/pkg/resolvespec"
|
"github.com/bitechdev/ResolveSpec/pkg/resolvespec"
|
||||||
"github.com/Warky-Devs/ResolveSpec/pkg/testmodels"
|
"github.com/bitechdev/ResolveSpec/pkg/testmodels"
|
||||||
"github.com/glebarez/sqlite"
|
"github.com/glebarez/sqlite"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user