Skip to content

Commit 10385cc

Browse files
committed
Merge branch 'development'
2 parents 03b72ca + ba5c248 commit 10385cc

19 files changed

+532
-125
lines changed

.github/copilot-instructions.md

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
# Copilot Instructions for CBValidation
2+
3+
## Project Overview
4+
5+
CBValidation is a server-side validation engine for ColdBox applications providing unified object, struct, and form validation. The module implements a comprehensive validator pattern with extensible constraint rules, localization support via cbi18n, and deep integration with ColdBox's WireBox dependency injection container.
6+
7+
## Architecture & Core Components
8+
9+
### Validation Engine Architecture
10+
- **ValidationManager** (`models/ValidationManager.cfc`): Central orchestrator handling constraint discovery, rule processing, and validator delegation
11+
- **Validators** (`models/validators/`): Individual constraint validators extending BaseValidator with specific validation logic
12+
- **ValidationResult** (`models/result/ValidationResult.cfc`): Immutable result container with error aggregation and i18n message formatting
13+
- **GenericObject** (`models/GenericObject.cfc`): Wrapper for validating simple structures/forms as objects
14+
15+
### Constraint System Patterns
16+
```javascript
17+
// Object-based constraints (preferred approach)
18+
component {
19+
this.constraints = {
20+
email: { required: true, type: "email" },
21+
age: { required: true, min: 18, max: 65 }
22+
};
23+
24+
// Profile-based field selection for targeted validation
25+
this.constraintProfiles = {
26+
registration: "email,password,confirmPassword",
27+
update: "email,firstName,lastName"
28+
};
29+
}
30+
31+
// Shared constraints via module settings (global reuse)
32+
moduleSettings = {
33+
cbValidation = {
34+
sharedConstraints = {
35+
userRegistration = {
36+
email: { required: true, type: "email" }
37+
}
38+
}
39+
}
40+
};
41+
```
42+
43+
### Validator Discovery & Extension
44+
- **Auto-Discovery**: ValidationManager scans `models/validators/` and builds wirebox mapping for `*Validator.cfc` files
45+
- **Validator Aliases**: `items``arrayItem`, `constraints``nestedConstraints` for shorthand usage
46+
- **Custom Validators**: Implement IValidator interface, register via `validator: "path.to.CustomValidator"` or wirebox ID
47+
48+
## Key Implementation Patterns
49+
50+
### Validation Workflow
51+
1. **Constraint Resolution**: `determineConstraintsDefinition()` - object properties → shared constraints → inline structs
52+
2. **Profile Processing**: Constraint profiles expand to `includeFields` for targeted validation scenarios
53+
3. **Rule Processing**: `processRules()` iterates constraints, delegating to specific validators
54+
4. **Result Aggregation**: ValidationResult accumulates errors with i18n message formatting
55+
56+
### Mixin Integration Pattern
57+
The module injects validation methods globally via `helpers/Mixins.cfm`:
58+
```javascript
59+
// In any ColdBox component (handlers, views, layouts, interceptors)
60+
var result = validate(target=user, constraints="userValidation");
61+
if (result.hasErrors()) {
62+
// Handle validation errors
63+
}
64+
65+
// Throws ValidationException on failure with JSON error details
66+
var validUser = validateOrFail(target=user, profiles="registration");
67+
```
68+
69+
### Advanced Constraint Features
70+
```javascript
71+
// Nested object validation with dot notation
72+
this.constraints = {
73+
"address.street": { required: true, size: "5..100" },
74+
"preferences.*": { type: "string" } // Array item validation
75+
};
76+
77+
// Conditional validation
78+
email: {
79+
requiredIf: { userType: "premium" },
80+
requiredUnless: { authProvider: "oauth" }
81+
}
82+
83+
// Custom validation methods
84+
password: {
85+
method: "validatePasswordStrength", // Call this.validatePasswordStrength()
86+
udf: variables.customValidator // Direct function reference
87+
}
88+
```
89+
90+
## Developer Workflows
91+
92+
### Testing Strategy
93+
- **Test Harness**: Full ColdBox application in `test-harness/` for integration testing
94+
- **Unit Tests**: `test-harness/tests/specs/` - focus on ValidationManager and individual validators
95+
- **Test Execution**: `box testbox run` from module root, leverages test-harness Application.cfc
96+
97+
### Build & Release Process
98+
- **Build Script**: `build/Build.cfc` handles testing, docs generation, and packaging
99+
- **Commands**: `box task run build/Build.cfc` for full build pipeline
100+
- **API Docs**: Generated for `models/` directory only (public API surface)
101+
102+
### Development Environment Setup
103+
```bash
104+
# Install dependencies and start development
105+
box install
106+
box server start [email protected]
107+
108+
# Run tests during development
109+
box testbox run
110+
111+
# Format code to standards
112+
box run-script format
113+
114+
# Build and package module
115+
box task run build/Build.cfc
116+
```
117+
118+
## Dependency Integration
119+
120+
### CBi18n Integration (Required Dependency)
121+
- **Message Localization**: ValidationResult uses ResourceService for translating error messages
122+
- **Constraint Messages**: Support `{propertyName}Message` keys for field-specific i18n overrides
123+
- **Locale Support**: Validation methods accept `locale` parameter for multilingual applications
124+
125+
### WireBox Registration Patterns
126+
```javascript
127+
// Module auto-registers core services
128+
"ValidationManager@cbvalidation" // Primary validation engine
129+
"validationManager@cbvalidation" // Alias for convenience
130+
131+
// Custom validation manager override
132+
moduleSettings = {
133+
cbValidation = {
134+
manager: "path.to.CustomValidationManager" // Replace default engine
135+
}
136+
};
137+
```
138+
139+
## Integration Examples
140+
141+
### Form Validation in Handlers
142+
```javascript
143+
function saveUser(event, rc, prc) {
144+
var result = validate(target=rc, constraints="userRegistration");
145+
if (result.hasErrors()) {
146+
prc.errors = result.getAllErrors();
147+
return event.setView("users/registration");
148+
}
149+
// Process valid data
150+
}
151+
```
152+
153+
### API Validation with Exception Handling
154+
```javascript
155+
function apiCreateUser(event, rc, prc) {
156+
try {
157+
var validData = validateOrFail(target=rc, profiles="api");
158+
var user = userService.create(validData);
159+
return event.renderData(data=user);
160+
} catch(ValidationException e) {
161+
return event.renderData(
162+
statusCode=422,
163+
data={errors: deserializeJSON(e.extendedInfo)}
164+
);
165+
}
166+
}
167+
```
168+
169+
## Common Conventions
170+
171+
### Constraint Naming
172+
- Use descriptive field names matching object properties exactly
173+
- Leverage profiles for context-specific validation (registration, update, etc.)
174+
- Group related constraints in shared constraint structures for reuse
175+
176+
### Error Handling Patterns
177+
- Prefer `validate()` for form scenarios where you display errors inline
178+
- Use `validateOrFail()` for API endpoints requiring immediate exception handling
179+
- Always check `result.hasErrors()` before proceeding with business logic
180+
181+
### Testing Validators
182+
- Extend `coldbox.system.testing.BaseTestCase` for integration tests
183+
- Mock ValidationResult for unit testing individual validators
184+
- Test constraint edge cases: null values, empty strings, boundary conditions

.github/dependabot.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
version: 2
2+
updates:
3+
# GitHub Actions - updates uses: statements in workflows
4+
- package-ecosystem: "github-actions"
5+
directory: "/" # Where your .github/workflows/ folder is
6+
schedule:
7+
interval: "monthly"

.github/workflows/cron.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
name: Daily Tests
2+
3+
on:
4+
schedule:
5+
- cron: '0 0 * * *' # Runs at 00:00 UTC every day
6+
7+
jobs:
8+
tests:
9+
uses: ./.github/workflows/tests.yml
10+
secrets: inherit

.github/workflows/pr.yml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@ on:
66
- "main"
77
- "master"
88
- "development"
9+
- "releases/v*"
910
pull_request:
1011
branches:
12+
- "releases/v*"
1113
- development
1214

1315
jobs:
@@ -18,11 +20,11 @@ jobs:
1820
# Format PR
1921
format_check:
2022
name: Checks Source Code Formatting
21-
runs-on: ubuntu-24.04
23+
runs-on: ubuntu-latest
2224
steps:
2325
- name: Checkout Repository
24-
uses: actions/checkout@v4
26+
uses: actions/checkout@v5
2527

26-
- uses: Ortus-Solutions/[email protected].2
28+
- uses: Ortus-Solutions/[email protected].3
2729
with:
2830
cmd: run-script format:check

.github/workflows/release.yml

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,26 +16,41 @@ on:
1616
default: false
1717
type: boolean
1818

19+
# Manual Trigger
20+
workflow_dispatch:
1921
env:
2022
MODULE_ID: ${{ github.event.repository.name }}
23+
JDK: 21
2124
SNAPSHOT: ${{ inputs.snapshot || false }}
25+
BUILD_ID: ${{ github.run_number }}
2226

2327
jobs:
2428
##########################################################################################
2529
# Build & Publish
2630
##########################################################################################
2731
build:
2832
name: Build & Publish
29-
runs-on: ubuntu-24.04
33+
runs-on: ubuntu-latest
34+
permissions:
35+
checks: write
36+
pull-requests: write
37+
contents: write
38+
issues: write
3039
steps:
3140
- name: Checkout Repository
32-
uses: actions/checkout@v4
41+
uses: actions/checkout@v5
3342

3443
- name: Setup CommandBox
3544
uses: Ortus-Solutions/[email protected]
3645
with:
3746
forgeboxAPIKey: ${{ secrets.FORGEBOX_TOKEN }}
3847

48+
- name: Setup Java
49+
uses: actions/setup-java@v5
50+
with:
51+
distribution: "temurin"
52+
java-version: ${{ env.JDK }}
53+
3954
- name: "Setup Environment Variables For Build Process"
4055
id: current_version
4156
run: |
@@ -50,7 +65,7 @@ jobs:
5065
fi
5166
5267
- name: Update changelog [unreleased] with latest version
53-
uses: thomaseizinger/keep-a-changelog-new-release@1.3.0
68+
uses: thomaseizinger/keep-a-changelog-new-release@3.1.0
5469
if: env.SNAPSHOT == 'false'
5570
with:
5671
changelogPath: ./changelog.md
@@ -61,9 +76,9 @@ jobs:
6176
npm install -g markdownlint-cli
6277
markdownlint changelog.md --fix
6378
box install commandbox-docbox
64-
box task run taskfile=build/Build target=run :version=${{ env.VERSION }} :projectName=${{ env.MODULE_ID }} :buildID=${{ github.run_number }} :branch=${{ env.BRANCH }}
79+
box task run taskfile=build/Build target=run :version=${{ env.VERSION }} :projectName=${{ env.MODULE_ID }} :buildID=${{ env.BUILD_ID }} :branch=${{ env.BRANCH }}
6580
66-
- name: Commit Changelog To Master
81+
- name: Commit Changelog [unreleased] with latest version
6782
uses: EndBug/[email protected]
6883
if: env.SNAPSHOT == 'false'
6984
with:
@@ -118,7 +133,7 @@ jobs:
118133
box forgebox publish --force
119134
120135
- name: Create Github Release
121-
uses: taiki-e/create-gh-release-action@v1.8.2
136+
uses: taiki-e/create-gh-release-action@v1.9.1
122137
continue-on-error: true
123138
if: env.SNAPSHOT == 'false'
124139
with:
@@ -127,18 +142,35 @@ jobs:
127142
token: ${{ secrets.GITHUB_TOKEN }}
128143
ref: refs/tags/v${{ env.VERSION }}
129144

145+
- name: Inform Slack
146+
if: ${{ always() }}
147+
uses: rtCamp/action-slack-notify@v2
148+
env:
149+
SLACK_CHANNEL: coding
150+
SLACK_COLOR: ${{ job.status }} # or a specific color like 'green' or '#ff00ff'
151+
SLACK_ICON_EMOJI: ":bell:"
152+
SLACK_MESSAGE: "Module ${{ env.MODULE_ID }} v${{ env.VERSION }} Built with ${{ job.status }}!"
153+
SLACK_TITLE: "ColdBox Module ${{ env.MODULE_ID }}"
154+
SLACK_USERNAME: CI
155+
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
156+
130157
##########################################################################################
131158
# Prep Next Release
132159
##########################################################################################
133160
prep_next_release:
134161
name: Prep Next Release
135162
if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main'
136-
runs-on: ubuntu-24.04
163+
runs-on: ubuntu-latest
137164
needs: [ build ]
165+
permissions:
166+
checks: write
167+
pull-requests: write
168+
contents: write
169+
issues: write
138170
steps:
139171
# Checkout development
140172
- name: Checkout Repository
141-
uses: actions/checkout@v4
173+
uses: actions/checkout@v5
142174
with:
143175
ref: development
144176

@@ -148,7 +180,7 @@ jobs:
148180
forgeboxAPIKey: ${{ secrets.FORGEBOX_TOKEN }}
149181

150182
- name: Download build artifacts
151-
uses: actions/download-artifact@v4
183+
uses: actions/download-artifact@v5
152184
with:
153185
name: ${{ env.MODULE_ID }}
154186
path: .tmp

.github/workflows/snapshot.yml

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ on:
99

1010
# Unique group name per workflow-branch/tag combo
1111
concurrency:
12-
group: ${{ github.workflow }}-${{ github.ref }}
12+
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
1313
cancel-in-progress: true
1414

1515
jobs:
@@ -25,17 +25,20 @@ jobs:
2525
##########################################################################################
2626
format:
2727
name: Code Auto-Formatting
28-
runs-on: ubuntu-24.04
28+
runs-on: ubuntu-latest
29+
permissions:
30+
contents: write
31+
checks: write
2932
steps:
30-
- uses: actions/checkout@v4
33+
- uses: actions/checkout@v5
3134

3235
- name: Auto-format
33-
uses: Ortus-Solutions/[email protected].2
36+
uses: Ortus-Solutions/[email protected].3
3437
with:
3538
cmd: run-script format
3639

3740
- name: Commit Format Changes
38-
uses: stefanzweifel/git-auto-commit-action@v5
41+
uses: stefanzweifel/git-auto-commit-action@v6
3942
with:
4043
commit_message: Apply cfformat changes
4144

@@ -46,5 +49,10 @@ jobs:
4649
uses: ./.github/workflows/release.yml
4750
needs: [ tests, format ]
4851
secrets: inherit
52+
permissions:
53+
checks: write
54+
pull-requests: write
55+
contents: write
56+
issues: write
4957
with:
5058
snapshot: true

0 commit comments

Comments
 (0)