Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
3135111
ignore ralph directory
atuttle Jan 31, 2026
946d5bf
Archive legacy test suite to tests.bak.lol
atuttle Jan 31, 2026
4682d43
testbox upgrade
atuttle Feb 1, 2026
b6f7bc3
remove old tests
atuttle Feb 1, 2026
a62ce45
tests rewritten
atuttle Feb 1, 2026
92b7907
github action test runner
atuttle Feb 1, 2026
f743503
also ignore local copy of cfdocs
atuttle Feb 1, 2026
4f8a53e
implement learning about makePublic
atuttle Feb 1, 2026
a16ecf0
refactor everything to modern cfml
atuttle Feb 1, 2026
09ed2b0
fix compat loading bug
atuttle Feb 1, 2026
089bcf3
dashboard redesign
atuttle Feb 1, 2026
1340e48
remove jquery
atuttle Feb 1, 2026
11f4cb4
Fix Lucee6 test failure
atuttle Feb 1, 2026
5925364
more cleanup
atuttle Feb 1, 2026
b8c18ca
upgrade test harness to run on lucee 5+6+7
atuttle Feb 1, 2026
9910ec1
get tests passing on lucee 6 and 7
atuttle Feb 1, 2026
eb4e5d4
Use relative path for baseSerializer in built-in serialziers
atuttle Feb 1, 2026
4f650e6
github action: update cfconfig
atuttle Feb 1, 2026
1626201
maybe this will fix lucee 7 tests?
atuttle Feb 1, 2026
c7009ea
lucee 7 tests pass plz
atuttle Feb 1, 2026
2a3df98
lucee plz
atuttle Feb 1, 2026
0cee3dd
2 steps fwd 1 step.bak
atuttle Feb 1, 2026
c3202a3
cleanup docs display
atuttle Feb 12, 2026
1ce2e4e
use Atkinson Hyperlegible by default, with option to opt-out
atuttle Feb 12, 2026
c72c6ee
Fix inverted cache timing calculation
atuttle Feb 12, 2026
63ce659
Fix dashboard logic duplication and inverted condition
atuttle Feb 12, 2026
38910f2
Fix queryToArray polyfill return type from struct to array
atuttle Feb 12, 2026
bfad90b
Remove dead Railo references
atuttle Feb 12, 2026
6dd4ff4
Decouple baseSerializer from application scope
atuttle Feb 12, 2026
ccd5580
Update box.json version to 4.0.0 to match setupFramework()
atuttle Feb 12, 2026
b161fd7
Replace stub ConfigurationSpec tests with real assertions
atuttle Feb 12, 2026
f048a0a
Add end-to-end HTTP tests via cfhttp
atuttle Feb 12, 2026
9f4845e
Replace evaluate() with invoke() for setter-based DI
atuttle Feb 12, 2026
d274c12
Fix docs: exceptionLogAdapter default is LogToDevNull, not LogToEmail
atuttle Feb 12, 2026
368409f
Fix e2e test API: add /resources mapping for resource discovery
atuttle Feb 12, 2026
5e8b086
Fix e2e tests for Lucee 5 compatibility
atuttle Feb 12, 2026
629e03e
Fix test failures on Lucee 5 and 7
atuttle Feb 12, 2026
bcf5852
Fix 3 bugs in script-based api.cfc breaking all e2e tests
atuttle Feb 12, 2026
5dcbf05
Fix isInstanceOf in factory.cfc failing on Lucee
atuttle Feb 12, 2026
6e1739d
Fix Lucee 7 CI: add taffy symlink when CFConfig unavailable
atuttle Feb 12, 2026
ba1a412
exception log adapter config can leak secrets
atuttle Mar 5, 2026
b0d6fc0
add ability to document input constraints
atuttle Mar 6, 2026
b22c604
sorting dash/docs in various ways
atuttle Mar 6, 2026
e30df22
fix: (regression) query params missing from dashboard requests
atuttle Mar 6, 2026
a325ca9
Ensure function calls match the function's signature.
gavinbaumanis Mar 7, 2026
18df842
fix: default returnExceptionsAsJson to false to prevent stack trace d…
atuttle Apr 10, 2026
217fba2
docs: update returnExceptionsAsJson default to false, add 3.7.1 docs
atuttle Apr 10, 2026
6916c23
work around some oddities in Lucee metadata checking
atuttle Apr 10, 2026
a1958a7
docs: update returnExceptionsAsJson default in 4.0.0 docs
atuttle Apr 10, 2026
f1ad4cd
fix: update test to expect returnExceptionsAsJson default of false
atuttle Apr 10, 2026
4de106f
there never was a 3.8
atuttle Apr 10, 2026
6ff9b8e
Add OpenAPI/Swagger JSON generation
atuttle Apr 18, 2026
ba92487
fixing failing tests
atuttle Apr 18, 2026
b5ed808
add dashboard and docs links to swagger json
atuttle Apr 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 64 additions & 11 deletions .github/workflows/pr_tests.yml
Original file line number Diff line number Diff line change
@@ -1,22 +1,75 @@
name: Run tests for all PRs
name: Run Tests

on:
push:
branches:
- feature/github-actions
- main
- develop
pull_request:
branches:
- main
- develop

jobs:
test:
runs-on: ubuntu-20.04
name: Tests on ${{ matrix.cfengine }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
cfengine:
- lucee@5
- lucee@6
- lucee@7

steps:
- name: Checkout Repository
uses: actions/checkout@v4

- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: "temurin"
java-version: "11"

- name: Setup CommandBox
uses: Ortus-Solutions/setup-commandbox@v2.0.1
with:
version: "6.2.0"

- name: Install Dependencies
run: box install

- name: Start Server
run: |
# Uninstall CFConfig for Lucee 7 (CFConfig doesn't support it yet)
if [[ "${{ matrix.cfengine }}" == "lucee@7" ]]; then
box uninstall commandbox-cfconfig --system
# CFConfig normally applies the /Taffy CFML mapping from .cfconfig.json.
# Without it, extends="taffy.core.api" can't resolve at compile time.
# A symlink lets the webroot resolve taffy.core.api naturally.
ln -s . taffy
fi
box server start cfengine=${{ matrix.cfengine }} port=8080 --debug
# Wait for server to be ready
sleep 10
# Verify server is running
curl -s http://localhost:8080/ || echo "Server may not have dashboard enabled"

- name: Checkout Code
id: git-checkout
uses: actions/checkout@v2
- name: Run Tests
run: |
box testbox run runner="http://localhost:8080/tests/runner.cfm" verbose=true

- name: Install dependencies
uses: pixl8/github-action-box-install@v2
- name: Stop Server
if: always()
run: box server stop

- name: Run tests
uses: pixl8/github-action-box-test@v3
- name: Upload Test Results
if: always()
uses: actions/upload-artifact@v4
with:
output_file: /tests/test-results.txt
name: test-results-${{ matrix.cfengine }}
path: |
tests/results/
.logs/
if-no-files-found: ignore
5 changes: 3 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -47,5 +47,6 @@ out/
# generated by JIRA plugin
atlassian-ide-plugin.xml

# generated by Crashlytics plugin (for Android Studio and Intellij)
com_crashlytics_export_strings.xml
# docs/etc that shouldn't be committed
.ralph
.ignore
28 changes: 0 additions & 28 deletions .travis.yml

This file was deleted.

40 changes: 40 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Taffy

REST API framework for CFML

## Running Tests

You can check if box ("commandbox") cli is install by running `box version`.
You can install box by running `brew install commandbox`.

Then install project dependencies: `box install`

### Start servers

box run-script start:all
box run-script start:lucee5
box run-script start:lucee6
box run-script start:lucee7

### Run tests

box run-script test:all
box run-script test:lucee5
box run-script test:lucee6
box run-script test:lucee7

### Stop servers

box run-script stop:all
box run-script stop:lucee5
box run-script stop:lucee6
box run-script stop:lucee7

## Documentation References

- Taffy documentation: `./docs/`
- CFML Syntax docs
- cfdocs has cross-platform reference data in `.ignore/cfdocs/`
- Lucee docs are in `.ignore/lucee-docs/`
- Testbox docs: `.ignore/testbox-docs/`
- Commandbox docs: `.ignore/commandbox-docs/`
11 changes: 5 additions & 6 deletions ReadMe.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

**PROBLEM:** Creating REST APIs with the native functionality in ColdFusion 10+ and Lucee is verbose, complex, and developer-hostile. Or maybe you're still on an older version of ColdFusion.

**SOLUTION:** A framework that focuses on Developer Experience and helps you "fall into a pit of success". **You've come to the right place.** Taffy is low friction, simple to get started, and compatible as far back as ColdFusion 8. CF8 was released in 2007! 😱
**SOLUTION:** A framework that focuses on Developer Experience and helps you "fall into a pit of success". **You've come to the right place.** Taffy is low friction, and simple to get started.

---

Expand All @@ -21,8 +21,8 @@

## Currently Supported Versions

- Taffy 3.6.x+
- Taffy 2.2.x
- Taffy 4.0.0+
- Taffy 3.7.x+

If you file a bug or ask for support please indicate which version of Taffy you're using. If it's an older release, we usually ask you to upgrade. Officially, we promise to support the current and previous **minor** releases and the last **minor** release of the previous **major** release. To the best of our abilities Taffy follows the versioning guidelines defined in [semver](https://semver.org/).

Expand All @@ -44,7 +44,7 @@ Features and bug fixes are coordinated via the [GitHub issues list](https://gith

## Community

The most active place where Taffy users and contributors gather is in the **#taffy** channel of the [CFML Slack][4]. It's a great place to ask for help. We also have [a mailing list][1], but it hasn't been used much since the CFML Slack came around.
The most active place where Taffy users and contributors gather is in the **#taffy** channel of the [CFML Slack][4]. It's a great place to ask for help.

## Need serious help?

Expand All @@ -62,7 +62,6 @@ If your problem is too big or too private to ask for help in a chat room, [I am
>
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

[1]: https://groups.google.com/forum/#!forum/taffy-users
[3]: https://github.com/atuttle/Taffy/wiki
[4]: https://cfml-slack.herokuapp.com
[5]: https://twitter.com/adamtuttle
[5]: https://bsky.app/profile/adamtuttle.codes
18 changes: 5 additions & 13 deletions bonus/AnythingToXmlSerializer.cfc
Original file line number Diff line number Diff line change
@@ -1,15 +1,7 @@
<cfcomponent extends="taffy.core.baseSerializer">
component extends="taffy.core.baseSerializer" {

<!--- remove because causing tests to fail
<cfset variables.anythingToXml = application.anythingToXml />
--->
public function getAsXML() output="false" taffy_mime="application/xml" taffy_default="true" {
return application.anythingToXml.toXml(variables.data);
}

<cffunction
name="getAsXML"
output="false"
taffy:mime="application/xml"
taffy:default="true">
<cfreturn application.anythingToXml.toXml(variables.data) />
</cffunction>

</cfcomponent>
}
13 changes: 4 additions & 9 deletions bonus/ILogAdapter.cfc
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
<cfinterface>
interface {

<cffunction name="init" hint="I accept a configuration structure to setup and return myself">
<cfargument name="config" />
<cfargument name="tracker" />
</cffunction>
<cffunction name="saveLog" hint="I log or otherwise notify you of an exception">
<cfargument name="exception" />
</cffunction>
public function init(config, tracker) hint="I accept a configuration structure to setup and return myself";
public function saveLog(exception) hint="I log or otherwise notify you of an exception";

</cfinterface>
}
16 changes: 6 additions & 10 deletions bonus/JsonUtilSerializer.cfc
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
<cfcomponent extends="taffy.core.baseSerializer">
component extends="taffy.core.baseSerializer" {

<cfset variables.jsonUtil = application.jsonUtil />
variables.jsonUtil = application.jsonUtil;

<cffunction
name="getAsJson"
output="false"
taffy:mime="application/json"
taffy:default="true">
<cfreturn variables.jsonUtil.serialize(variables.data) />
</cffunction>
public function getAsJson() output="false" taffy_mime="application/json" taffy_default="true" {
return variables.jsonUtil.serialize(variables.data);
}

</cfcomponent>
}
116 changes: 54 additions & 62 deletions bonus/LogToBugLogHQ.cfc
Original file line number Diff line number Diff line change
@@ -1,62 +1,54 @@
<cfcomponent implements="taffy.bonus.ILogAdapter">

<cffunction name="init">
<cfargument name="config" />
<cfargument name="tracker" />

<!--- <cfdump var="#arguments.config#" abort="true" /> --->
<cfif structKeyExists(arguments, "tracker")>
<!--- used to inject mocking object for testing --->
<cfset variables.blhq = arguments.tracker />
<cfelse>
<cfset var svc = "bugLog.client.bugLogService" />
<cfif structKeyExists( arguments.config, "service" )>
<cfset svc = arguments.config.service />
</cfif>

<cfset variables.blhq = createObject("component", svc) />
<cfset variables.blhq.init(
argumentCollection=arguments.config
) />
</cfif>

<cfparam name="arguments.config.message" default="Exception trapped in API" />
<cfset variables.message = arguments.config.message />

<cfreturn this />
</cffunction>

<cffunction name="saveLog">
<cfargument name="exception" />

<cfset var msg = '' />

<cfif structKeyExists(exception, 'rootcause') && structKeyExists(exception.rootcause, 'cause') && structKeyExists(exception.rootcause.cause, 'message')>
<cfset msg = exception.rootcause.cause.message />
<cfelseif structKeyExists(exception, 'cause') && structKeyExists(exception.cause, 'message')>
<cfset msg = exception.cause.message />
<cfelseif structKeyExists(exception, 'message')>
<cfset msg = exception.message />
<cfelse>
<cfset msg = variables.message />
</cfif>

<!--- You can use addDebugData() in resources to set this value --->
<cfif structKeyExists(request, "debugData") and not structKeyExists(exception, "extraInfo")>
<cfset exception.extraInfo = request.debugData />
</cfif>

<cfset var reqHeaders = getHTTPRequestData().headers />
<cfset var reqBody = getHTTPRequestData().content />
<!--- on input with content-type "application/json" CF seems to expose it as binary data. Here we convert it back to plain text --->
<cfif isBinary(reqBody)>
<cfset reqBody = charsetEncode(reqBody, "UTF-8") />
</cfif>
<cfif isJson(reqBody)>
<cfset reqBody = deserializeJson( reqBody ) />
</cfif>

<cfset variables.blhq.notifyService(msg, arguments.exception, { request_body: reqBody, request_headers: reqHeaders }) />
</cffunction>

</cfcomponent>
component implements="taffy.bonus.ILogAdapter" {

public function init(config, tracker="") {
if (structKeyExists(arguments, "tracker") && isObject(arguments.tracker)) {
// used to inject mocking object for testing
variables.blhq = arguments.tracker;
} else {
var svc = "bugLog.client.bugLogService";
if (structKeyExists(arguments.config, "service")) {
svc = arguments.config.service;
}

variables.blhq = createObject("component", svc);
variables.blhq.init(argumentCollection=arguments.config);
}

param name="arguments.config.message" default="Exception trapped in API";
variables.message = arguments.config.message;

return this;
}

public function saveLog(exception) {
var msg = '';

if (structKeyExists(exception, 'rootcause') && structKeyExists(exception.rootcause, 'cause') && structKeyExists(exception.rootcause.cause, 'message')) {
msg = exception.rootcause.cause.message;
} else if (structKeyExists(exception, 'cause') && structKeyExists(exception.cause, 'message')) {
msg = exception.cause.message;
} else if (structKeyExists(exception, 'message')) {
msg = exception.message;
} else {
msg = variables.message;
}

// You can use addDebugData() in resources to set this value
if (structKeyExists(request, "debugData") and !structKeyExists(exception, "extraInfo")) {
exception.extraInfo = request.debugData;
}

var reqHeaders = getHTTPRequestData().headers;
var reqBody = getHTTPRequestData().content;
// on input with content-type "application/json" CF seems to expose it as binary data. Here we convert it back to plain text
if (isBinary(reqBody)) {
reqBody = charsetEncode(reqBody, "UTF-8");
}
if (isJson(reqBody)) {
reqBody = deserializeJson(reqBody);
}

variables.blhq.notifyService(msg, arguments.exception, { request_body: reqBody, request_headers: reqHeaders });
}

}
Loading
Loading