diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index d29d97367..6fa61be27 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -1,4 +1,4 @@ -name: Pull Requests +name: Coldbox Pull Requests on: push: @@ -12,79 +12,9 @@ on: jobs: tests: - name: Tests - runs-on: ubuntu-20.04 - env: - DB_USER: root - DB_PASSWORD: root - strategy: - fail-fast: true - matrix: - cfengine: [ "lucee@5", "adobe@2016", "adobe@2018", "adobe@2021" ] - javaVersion: [ "openjdk8", "openjdk11" ] - fullNull: [ "true", "false" ] - steps: - - name: Checkout Repository - uses: actions/checkout@v2 - - - name: Setup Database and Fixtures - run: | - sudo systemctl start mysql.service - mysql -u${{ env.DB_USER }} -p${{ env.DB_PASSWORD }} -e 'CREATE DATABASE coolblog;' - mysql -u${{ env.DB_USER }} -p${{ env.DB_PASSWORD }} < tests/resources/coolblog.sql - - - name: Setup Java - uses: actions/setup-java@v2 - with: - distribution: "adopt" - java-version: "11" - - - name: Setup CommandBox - uses: elpete/setup-commandbox@v1.0.0 - - - name: Setup .env For Runner - run: | - touch .env - printf "DB_HOST=127.0.0.1\n" >> .env - printf "DB_DATABASE=coolblog\n" >> .env - printf "DB_DRIVER=MySQL\n" >> .env - printf "DB_USER=${{ env.DB_USER }}\n" >> .env - printf "DB_PASSWORD=${{ env.DB_PASSWORD }}\n" >> .env - printf "DB_CLASS=com.mysql.cj.jdbc.Driver\n" >> .env - printf "DB_BUNDLEVERSION=8.0.19\n" >> .env - printf "DB_BUNDLENAME=com.mysql.cj\n" >> .env - - - name: Install Dependencies - run: | - box install - - - name: Start ${{ matrix.cfengine }}/${{ matrix.javaVersion }} Server - run: | - box server start serverConfigFile="server-${{ matrix.cfengine }}.json" javaVersion=${{ matrix.javaVersion }} --noSaveSettings --debug - # Install Adobe 2021 cfpm modules - if [[ "${{ matrix.cfengine }}" == "adobe@2021" ]] ; then - box run-script install:2021 - fi - # Test the harness - curl http://127.0.0.1:8599/test-harness - - - name: Run Tests - env: - FULL_NULL: ${{ matrix.fullNull }} - run: | - ant -f build/build.xml run-tests - - - name: Publish PR Test Reports - uses: mikepenz/action-junit-report@v2 - with: - report_paths: 'tests/results/**/*.xml' - check_name: "${{ matrix.cfengine }}/${{ matrix.javaVersion }} Test Results" - summary: true - - - name: Failure debugging - if: ${{ failure() }} - run: | - box server log serverConfigFile="server-${{ matrix.cfengine }}.json" + uses: ColdBox/coldbox-platform/.github/workflows/tests.yml@development + secrets: + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} format: name: Format @@ -93,22 +23,11 @@ jobs: - name: Checkout Repository uses: actions/checkout@v2 - - name: Setup Java - uses: actions/setup-java@v2 + - uses: Ortus-Solutions/commandbox-action@v1.0.2 with: - distribution: "adopt" - java-version: "11" - - - name: Set Up CommandBox - uses: elpete/setup-commandbox@v1.0.0 - - - name: Install CFFormat - run: box install commandbox-cfformat - - - name: Run CFFormat - run: box run-script format + cmd: run-script format - name: Commit Format Changes uses: stefanzweifel/git-auto-commit-action@v4 with: - commit_message: Apply cfformat changes + commit_message: Apply cfformat changes \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e477a5fdd..05c3508c4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,16 +1,29 @@ -name: ColdBox Platform CI +# This workflow is used to release master releases and also by the snapshot workflow +# to release snapshots. +name: ColdBox Release -# Only on Development we build snapshots on: push: branches: - master + workflow_call: + secrets: + SLACK_WEBHOOK_URL: + required: true + FORGEBOX_API_TOKEN: + required: true + AWS_ACCESS_KEY: + required: true + AWS_ACCESS_SECRET: + required: true env: COLDBOX_PRERELEASE: false jobs: - + ############################################# + # Build Snapshot or Final Release + ############################################# build: name: Build & Publish Release runs-on: ubuntu-20.04 @@ -36,7 +49,7 @@ jobs: ${{ runner.OS }}-commandbox-cache-${{ hashFiles( 'box.json' ) }} - name: Setup CommandBox - uses: elpete/setup-commandbox@v1.0.0 + uses: Ortus-Solutions/setup-commandbox@main with: forgeboxAPIKey: ${{ secrets.FORGEBOX_API_TOKEN }} @@ -45,7 +58,12 @@ jobs: run: | echo "COLDBOX_VERSION=`cat box.json | jq '.version' -r`" >> $GITHUB_ENV box package set version=@build.version@+@build.number@ + # master or snapshot echo "BRANCH=master" >> $GITHUB_ENV + if [ $GITHUB_REF == 'refs/heads/development' ] + then + echo "BRANCH=development" >> $GITHUB_ENV + fi - name: Install Dependencies run: | diff --git a/.github/workflows/snapshot.yml b/.github/workflows/snapshot.yml index 42b79ddcc..04f1a3d91 100644 --- a/.github/workflows/snapshot.yml +++ b/.github/workflows/snapshot.yml @@ -1,6 +1,7 @@ -name: ColdBox Platform CI +# This workflow tests the ColdBox code using many permutations in the matrix. +# If successful, then it creates the bleeding edge artifact and publishes it to S3 and ForgeBox +name: ColdBox Snapshots -# Only on Development we build snapshots on: push: branches: @@ -14,216 +15,18 @@ jobs: # Tests First baby! We fail, no build :( ############################################# tests: - name: Tests - runs-on: ubuntu-20.04 - env: - DB_USER: root - DB_PASSWORD: root - strategy: - fail-fast: false - matrix: - cfengine: [ "lucee@5", "adobe@2016", "adobe@2018", "adobe@2021" ] - fullNull: [ "true", "false" ] - steps: - - name: Checkout Repository - uses: actions/checkout@v2 - - - name: Setup Database and Fixtures - run: | - sudo systemctl start mysql.service - mysql -u${{ env.DB_USER }} -p${{ env.DB_PASSWORD }} -e 'CREATE DATABASE coolblog;' - mysql -u${{ env.DB_USER }} -p${{ env.DB_PASSWORD }} < tests/resources/coolblog.sql - - - name: Setup Java - uses: actions/setup-java@v2 - with: - distribution: "adopt" - java-version: "11" - - - name: Setup CommandBox - uses: elpete/setup-commandbox@v1.0.0 - - - name: Setup .env For Runner - run: | - touch .env - printf "DB_HOST=127.0.0.1\n" >> .env - printf "DB_DATABASE=coolblog\n" >> .env - printf "DB_DRIVER=MySQL\n" >> .env - printf "DB_USER=${{ env.DB_USER }}\n" >> .env - printf "DB_PASSWORD=${{ env.DB_PASSWORD }}\n" >> .env - printf "DB_CLASS=com.mysql.cj.jdbc.Driver\n" >> .env - printf "DB_BUNDLEVERSION=8.0.19\n" >> .env - printf "DB_BUNDLENAME=com.mysql.cj\n" >> .env - - - name: Cache CommandBox Dependencies - uses: actions/cache@v1 - if: ${{ true }} - with: - path: ~/.CommandBox/artifacts - key: ${{ runner.OS }}-commandbox-cache-${{ hashFiles( 'box.json' ) }} - restore-keys: | - ${{ runner.OS }}-commandbox-cache-${{ hashFiles( 'box.json' ) }} - - - name: Install Dependencies - run: | - # Core dependencies - box install - # API Docs dependencies - cd apidocs && box install - - - name: Start ${{ matrix.cfengine }} Server - run: | - box server start serverConfigFile="server-${{ matrix.cfengine }}.json" --noSaveSettings --debug - # Install Adobe 2021 cfpm modules - if [[ "${{ matrix.cfengine }}" == "adobe@2021" ]] ; then - box run-script install:2021 - fi - # Test the harness - curl http://127.0.0.1:8599/test-harness - - - name: Run Tests Full Null (${{ matrix.fullNull }}) - env: - FULL_NULL: ${{ matrix.fullNull }} - run: | - ant -f build/build.xml run-tests - - - name: Publish Test Results - uses: EnricoMi/publish-unit-test-result-action@v1 - if: always() - with: - files: tests/results/**/*.xml - check_name: "${{ matrix.cfengine }} Test Results" - summary: true - - - name: Upload Test Results Artifacts - if: always() - uses: actions/upload-artifact@v2 - with: - name: coldbox-test-results-${{ matrix.cfengine }} - path: | - tests/results/**/* - - - name: Slack Notification - if: ${{ failure() }} - uses: rtCamp/action-slack-notify@v2 - env: - SLACK_CHANNEL: coding - SLACK_COLOR: ${{ job.status }} # or a specific color like 'green' or '#ff00ff' - SLACK_ICON_EMOJI: ":bell:" - SLACK_MESSAGE: 'ColdBox tests failed :cry:, check them out here: https://github.com/coldbox/coldbox-platform/actions' - SLACK_TITLE: ColdBox Tests For ${{ matrix.cfengine }} failed - SLACK_USERNAME: CI - SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }} - - - name: Failure Debugging Info - if: ${{ failure() }} - run: | - box server log serverConfigFile="server-${{ matrix.cfengine }}.json" - - - name: Upload Debugging Info To Artifacts - if: ${{ failure() }} - uses: actions/upload-artifact@v2 - with: - name: Failure Debugging Info - ${{ matrix.cfengine }} - path: | - .engine/**/logs/* - .engine/**/WEB-INF/cfusion/logs/* + uses: ./.github/workflows/tests.yml + secrets: + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} ############################################# - # Build ColdBox Now + # Build Snapshot Release ############################################# build: - name: Build & Publish needs: tests - runs-on: ubuntu-20.04 - steps: - - name: Checkout Repository - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - - name: Setup Java - uses: actions/setup-java@v2 - with: - distribution: "adopt" - java-version: "11" - - - name: Cache CommandBox Dependencies - uses: actions/cache@v1 - if: ${{ true }} - with: - path: ~/.CommandBox/artifacts - key: ${{ runner.OS }}-commandbox-cache-${{ hashFiles( 'box.json' ) }} - restore-keys: | - ${{ runner.OS }}-commandbox-cache-${{ hashFiles( 'box.json' ) }} - - - name: Setup CommandBox - uses: elpete/setup-commandbox@v1.0.0 - with: - forgeboxAPIKey: ${{ secrets.FORGEBOX_API_TOKEN }} - - - name: Setup Environment Variables For Build Process - id: current_version - run: | - echo "COLDBOX_VERSION=`cat box.json | jq '.version' -r`" >> $GITHUB_ENV - box package set version=@build.version@+@build.number@ - echo "BRANCH=development" >> $GITHUB_ENV - - - name: Install Dependencies - run: | - cd apidocs && box install - - - name: Build ColdBox Variants for ${{ env.BRANCH }} v${{ env.COLDBOX_VERSION }} - run: | - box server start serverConfigFile="server-lucee@5.json" --debug - ant -DisPreRelease=${{ env.COLDBOX_PRERELEASE }} -Dcoldbox.version=${{ env.COLDBOX_VERSION }} -Dbuild.branch=${{ env.BRANCH }} -Dbuild.number=${{ github.run_number }} -f build/build.xml - - - name: Upload Build Artifacts - if: success() - uses: actions/upload-artifact@v2 - with: - name: coldbox-variants - path: | - artifacts/**/* - - - name: Upload Binaries to S3 - uses: jakejarvis/s3-sync-action@master - with: - args: --acl public-read - env: - AWS_S3_BUCKET: "downloads.ortussolutions.com" - AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_ACCESS_SECRET }} - SOURCE_DIR: "artifacts" - DEST_DIR: "ortussolutions" - - - name: Upload API Docs to S3 - uses: jakejarvis/s3-sync-action@master - with: - args: --acl public-read - env: - AWS_S3_BUCKET: "apidocs.ortussolutions.com" - AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_ACCESS_SECRET }} - SOURCE_DIR: "build-coldbox/apidocs" - DEST_DIR: "" - - - name: Publish - run: | - ROOT_DIR=`pwd` - cd $ROOT_DIR/artifacts/coldbox/${{ env.COLDBOX_VERSION }} && box forgebox publish - cd $ROOT_DIR/artifacts/cachebox/${{ env.COLDBOX_VERSION }} && box forgebox publish - cd $ROOT_DIR/artifacts/wirebox/${{ env.COLDBOX_VERSION }} && box forgebox publish - cd $ROOT_DIR/artifacts/logbox/${{ env.COLDBOX_VERSION }} && box forgebox publish - - - name: Inform Slack - if: ${{ always() }} - uses: rtCamp/action-slack-notify@v2 - env: - SLACK_CHANNEL: coding - SLACK_COLOR: ${{ job.status }} # or a specific color like 'green' or '#ff00ff' - SLACK_ICON_EMOJI: ":bell:" - SLACK_MESSAGE: 'ColdBox ${{ env.COLDBOX_VERSION }} Built with ${{ job.status }}!' - SLACK_TITLE: "ColdBox Build" - SLACK_USERNAME: CI - SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }} + uses: ./.github/workflows/release.yml + secrets: + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} + FORGEBOX_API_TOKEN: ${{ secrets.FORGEBOX_API_TOKEN }} + AWS_ACCESS_KEY: ${{ secrets.AWS_ACCESS_KEY }} + AWS_ACCESS_SECRET: ${{ secrets.AWS_ACCESS_SECRET }} diff --git a/.github/workflows/cron.yml b/.github/workflows/tests.yml similarity index 64% rename from .github/workflows/cron.yml rename to .github/workflows/tests.yml index 05dcad1a2..d60852126 100644 --- a/.github/workflows/cron.yml +++ b/.github/workflows/tests.yml @@ -1,13 +1,16 @@ -name: Cron +# This is a reusable workflow for executing the full ColdBox Test Suites +name: ColdBox Test Suites +# We are a reusable Workflow only on: - schedule: - # Run Every Monday Midnight - - cron: 0 0 * * 1 + workflow_call: + secrets: + SLACK_WEBHOOK_URL: + required: true jobs: tests: - name: Tests + name: Test Suites runs-on: ubuntu-20.04 env: DB_USER: root @@ -15,8 +18,7 @@ jobs: strategy: fail-fast: false matrix: - cfengine: [ "lucee@5", "lucee@be", "adobe@2016", "adobe@2018", "adobe2021" ] - javaVersion: [ "openjdk8", "openjdk11" ] + cfengine: [ "lucee@5", "adobe@2016", "adobe@2018", "adobe@2021" ] fullNull: [ "true", "false" ] steps: - name: Checkout Repository @@ -34,8 +36,8 @@ jobs: distribution: "adopt" java-version: "11" - - name: Set Up CommandBox - uses: elpete/setup-commandbox@v1.0.0 + - name: Setup CommandBox CLI + uses: Ortus-Solutions/setup-commandbox@main - name: Setup .env For Runner run: | @@ -49,13 +51,25 @@ jobs: printf "DB_BUNDLEVERSION=8.0.19\n" >> .env printf "DB_BUNDLENAME=com.mysql.cj\n" >> .env + - name: Cache CommandBox Dependencies + uses: actions/cache@v1 + if: ${{ true }} + with: + path: ~/.CommandBox/artifacts + key: ${{ runner.OS }}-commandbox-cache-${{ hashFiles( 'box.json' ) }} + restore-keys: | + ${{ runner.OS }}-commandbox-cache-${{ hashFiles( 'box.json' ) }} + - name: Install Dependencies run: | + # Core dependencies box install + # API Docs dependencies + cd apidocs && box install - - name: Start ${{ matrix.cfengine }}/${{ matrix.javaVersion }} Server + - name: Start ${{ matrix.cfengine }} Server run: | - box server start serverConfigFile="server-${{ matrix.cfengine }}.json" javaVersion=${{ matrix.javaVersion }} --noSaveSettings --debug + box server start serverConfigFile="server-${{ matrix.cfengine }}.json" --noSaveSettings --debug # Install Adobe 2021 cfpm modules if [[ "${{ matrix.cfengine }}" == "adobe@2021" ]] ; then box run-script install:2021 @@ -63,7 +77,7 @@ jobs: # Test the harness curl http://127.0.0.1:8599/test-harness - - name: Run Tests + - name: Run Tests Full Null (${{ matrix.fullNull }}) env: FULL_NULL: ${{ matrix.fullNull }} run: | @@ -74,29 +88,34 @@ jobs: if: always() with: files: tests/results/**/*.xml - check_name: "${{ matrix.cfengine }}/${{ matrix.javaVersion }} Test Results" - summary: true + check_name: "${{ matrix.cfengine }} Test Results" - name: Upload Test Results Artifacts if: always() uses: actions/upload-artifact@v2 with: - name: coldbox-test-results-${{ matrix.cfengine }}-${{ matrix.javaVersion }} + name: coldbox-test-results-${{ matrix.cfengine }} path: | tests/results/**/* - name: Slack Notification - if: always() + # Only on failures and NOT in pull requests + if: ${{ failure() && !startsWith( 'pull_request', github.event_name ) }} uses: rtCamp/action-slack-notify@v2 env: SLACK_CHANNEL: coding SLACK_COLOR: ${{ job.status }} # or a specific color like 'green' or '#ff00ff' SLACK_ICON_EMOJI: ":bell:" - SLACK_MESSAGE: 'ColdBox tests ${{ job.status }} check them out here: https://github.com/coldbox/coldbox-platform/actions' - SLACK_TITLE: ColdBox Tests For ${{ matrix.cfengine }} ${{ job.status }} + SLACK_MESSAGE: 'ColdBox tests failed :cry:, check them out here: https://github.com/coldbox/coldbox-platform/actions' + SLACK_TITLE: ColdBox Tests For ${{ matrix.cfengine }} failed SLACK_USERNAME: CI SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }} + - name: Failure Debugging Info + if: ${{ failure() }} + run: | + box server log serverConfigFile="server-${{ matrix.cfengine }}.json" + - name: Upload Debugging Info To Artifacts if: ${{ failure() }} uses: actions/upload-artifact@v2 diff --git a/.gitignore b/.gitignore index 6637a31e5..f76592072 100644 --- a/.gitignore +++ b/.gitignore @@ -17,14 +17,16 @@ tests/suites/loadtests/settings.xml logs/* # Build artifacts -build-coldbox -build/build.number artifacts/* +apidocs/*-APIDocs +build/build.number +build-coldbox # Dependencies .engine/** testbox/* -apidocs/docbox/* +apidocs/testbox +apidocs/docbox ApplicationTemplates/* tests/suites/loadtests/logs test-harness/**/route-visualizer @@ -32,4 +34,4 @@ system/exceptions/node_modules/** # Custom Settings test-harness/config/runtime.properties.cfm -.env \ No newline at end of file +.env diff --git a/apidocs/Application.cfc b/apidocs/Application.cfc index fbc2e867f..4366909de 100755 --- a/apidocs/Application.cfc +++ b/apidocs/Application.cfc @@ -12,6 +12,7 @@ component{ // Core Mappings this.mappings[ "/docbox" ] = API_ROOT & "docbox"; + this.mappings[ "/testbox" ] = API_ROOT & "testbox"; // Standlone mappings this.mappings[ "/coldbox" ] = ( structKeyExists( url, "coldbox_root" ) ? url.coldbox_root : COLDBOX_ROOT ); diff --git a/apidocs/box.json b/apidocs/box.json index df6906753..1a9a62cc2 100644 --- a/apidocs/box.json +++ b/apidocs/box.json @@ -3,10 +3,13 @@ "version":"1.0.0", "slug":"module-apidocs", "dependencies":{ - "docbox":"^2.0.7" + "docbox":"^3.0.0" + }, + "devDependencies":{ + "testbox":"^4.5.0+5" }, - "devDependencies":{}, "installPaths":{ - "docbox":"docbox/" + "docbox":"docbox/", + "testbox":"testbox/" } -} \ No newline at end of file +} diff --git a/box.json b/box.json index 4c24a5b8f..748704634 100644 --- a/box.json +++ b/box.json @@ -1,6 +1,6 @@ { "name":"ColdBox Platform", - "version":"6.6.0", + "version":"6.6.1", "location":"https://downloads.ortussolutions.com/ortussolutions/coldbox/@build.version@/coldbox-@build.version@.zip", "author":"Ortus Solutions ", "slug":"coldbox", @@ -39,7 +39,7 @@ ] }, "devDependencies":{ - "testbox":"*", + "testbox":"be", "commandbox-dotenv":"*", "commandbox-cfconfig":"*", "commandbox-cfformat":"*" diff --git a/changelog.md b/changelog.md index e7c5a6b31..a24c6a5df 100644 --- a/changelog.md +++ b/changelog.md @@ -7,6 +7,34 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ---- +## [6.6.1] => 2022-FEB-17 + +### Coldbox HMVC Core + +#### Bug + +* [COLDBOX-1093](https://ortussolutions.atlassian.net/browse/COLDBOX-1093) Remove debug writedumps left over from previous testing +* [COLDBOX-1085](https://ortussolutions.atlassian.net/browse/COLDBOX-1085) Fix instance of bad route merging the routes but loosing the handler + +#### Improvement + +* [COLDBOX-1095](https://ortussolutions.atlassian.net/browse/COLDBOX-1095) Update Response Pagination Properties for Case-Sensitive Engines +* [COLDBOX-1091](https://ortussolutions.atlassian.net/browse/COLDBOX-1091) default status code to 302 in the internal relocate\(\) just like CFML does instead of 0 and eliminate source +* [COLDBOX-1089](https://ortussolutions.atlassian.net/browse/COLDBOX-1089) Update the internal cfml engine checker to have more engine based feature checkers +* [COLDBOX-1088](https://ortussolutions.atlassian.net/browse/COLDBOX-1088) Switch isInstance check on renderdata in controller to secondary of $renderdata check to optimize speed + +### CacheBox + +#### Bug + +* [CACHEBOX-80](https://ortussolutions.atlassian.net/browse/CACHEBOX-80) Bug in JDBCMetadataIndexer sortedKeys\(\) using non-existent variable `arguments.objectKey` + +#### Improvement + +* [CACHEBOX-81](https://ortussolutions.atlassian.net/browse/CACHEBOX-81) JDBCStore Dynamically generate queryExecute options \+ new config to always include DSN due to ACF issues + +---- + ## [6.6.0] => 2022-JAN-31 ### ColdBox HMVC Core diff --git a/system/async/proxies/BaseProxy.cfc b/system/async/proxies/BaseProxy.cfc index 8289b5b8c..8778f5d6e 100644 --- a/system/async/proxies/BaseProxy.cfc +++ b/system/async/proxies/BaseProxy.cfc @@ -141,10 +141,10 @@ component accessors="true" { } catch ( any e ) { err( "Error loading context #e.toString()#" ); writeDump( - var=[ createObject( "java", "coldfusion.filter.FusionContext" ).getCurrent() ], - output="console", - label="FusionContext Exception - Get Current", - top = 5 + var = [ createObject( "java", "coldfusion.filter.FusionContext" ).getCurrent() ], + output = "console", + label = "FusionContext Exception - Get Current", + top = 5 ); } } diff --git a/system/async/tasks/Future.cfc b/system/async/tasks/Future.cfc index f42ac7e30..c52052cd5 100644 --- a/system/async/tasks/Future.cfc +++ b/system/async/tasks/Future.cfc @@ -789,7 +789,6 @@ component accessors="true" { return this.thenAsync( function(){ // return back the completed array results return jFutures.map( function( jFuture ){ - // writeDump( var=arguments.jFuture.get(), output="console" ); return arguments.jFuture.get(); } ); } ); diff --git a/system/cache/store/JDBCStore.cfc b/system/cache/store/JDBCStore.cfc index f9fbe1e47..93af79f95 100644 --- a/system/cache/store/JDBCStore.cfc +++ b/system/cache/store/JDBCStore.cfc @@ -69,6 +69,14 @@ component implements="coldbox.system.cache.store.IObjectStore" accessors="true" type ="boolean" default="true"; + /** + * Whether to include the DSN in queryExecute statements. If not, it will use the default set in applciation.cfc + */ + property + name ="queryIncludeDsn" + type ="boolean" + default="true"; + /** * Constructor * @@ -100,6 +108,26 @@ component implements="coldbox.system.cache.store.IObjectStore" accessors="true" variables.dsnUsername = config.dsnUsername; variables.dsnPassword = config.dsnPassword; + // this struct will contain the dsn and credentials if passed into the config. Otherwise queryExecute will default + // to the datasource set in application.cfc + variables.queryOptions = {}; + + // if DSN username or password were passed, include them in the query options + if ( len( variables.dsnUsername ) || len( variables.dsnPassword ) ) { + variables.queryOptions[ "dsnUsername" ] = variables.dsnUsername; + variables.queryOptions[ "dsnPassword" ] = variables.dsnPassword; + } + + if ( isNull( config.queryIncludeDsn ) ) { + config.queryIncludeDsn = true; + } + + // if we should include the dsn in the query options, add it + if ( config.queryIncludeDsn ) { + variables.queryOptions[ "dsn" ] = variables.dsn; + } + + // Check autoCreate if ( isNull( config.tableAutoCreate ) ) { config.tableAutoCreate = true; @@ -146,11 +174,7 @@ component implements="coldbox.system.cache.store.IObjectStore" accessors="true" queryExecute( "TRUNCATE TABLE #variables.table#", {}, - { - datasource : variables.dsn, - username : variables.dsnUsername, - password : variables.dsnPassword - } + variables.queryOptions ); } @@ -163,11 +187,7 @@ component implements="coldbox.system.cache.store.IObjectStore" accessors="true" var qResults = queryExecute( "SELECT objectKey FROM #variables.table# ORDER BY objectKey ASC", {}, - { - datasource : variables.dsn, - username : variables.dsnUsername, - password : variables.dsnPassword - } + variables.queryOptions ); return ( @@ -205,11 +225,7 @@ component implements="coldbox.system.cache.store.IObjectStore" accessors="true" WHERE id = ? ", [ normalizedID ], - { - datasource : variables.dsn, - username : variables.dsnUsername, - password : variables.dsnPassword - } + variables.queryOptions ); // Update stats if found @@ -236,11 +252,7 @@ component implements="coldbox.system.cache.store.IObjectStore" accessors="true" id : { value : "#normalizedID#", cfsqltype : "varchar" }, created : { value : "#now()#", cfsqltype : "timestamp" } }, - { - datasource : variables.dsn, - username : variables.dsnUsername, - password : variables.dsnPassword - } + variables.queryOptions ); } } @@ -267,11 +279,7 @@ component implements="coldbox.system.cache.store.IObjectStore" accessors="true" WHERE id = ? ", [ getNormalizedID( arguments.objectKey ) ], - { - datasource : variables.dsn, - username : variables.dsnUsername, - password : variables.dsnPassword - } + variables.queryOptions ); // Just return if records found, else null @@ -295,11 +303,7 @@ component implements="coldbox.system.cache.store.IObjectStore" accessors="true" WHERE id = ? ", [ 1, getNormalizedID( arguments.objectKey ) ], - { - datasource : variables.dsn, - username : variables.dsnUsername, - password : variables.dsnPassword - } + variables.queryOptions ); } @@ -318,11 +322,7 @@ component implements="coldbox.system.cache.store.IObjectStore" accessors="true" WHERE id = ? ", [ getNormalizedID( arguments.objectKey ) ], - { - datasource : variables.dsn, - username : variables.dsnUsername, - password : variables.dsnPassword - } + variables.queryOptions ); return ( q.recordCount && q.isExpired ? true : false ); @@ -385,11 +385,7 @@ component implements="coldbox.system.cache.store.IObjectStore" accessors="true" isExpired : { value : "0", cfsqltype : "bit" }, isSimple : { value : "#isSimple#", cfsqltype : "bit" } }, - { - datasource : variables.dsn, - username : variables.dsnUsername, - password : variables.dsnPassword - } + variables.queryOptions ); return; @@ -423,11 +419,7 @@ component implements="coldbox.system.cache.store.IObjectStore" accessors="true" isExpired : { value : "0", cfsqltype : "bit" }, isSimple : { value : "#isSimple#", cfsqltype : "bit" } }, - { - datasource : variables.dsn, - username : variables.dsnUsername, - password : variables.dsnPassword - } + variables.queryOptions ); } } @@ -438,18 +430,16 @@ component implements="coldbox.system.cache.store.IObjectStore" accessors="true" * @objectKey The object key to clear */ function clear( required objectKey ){ + var localQueryOptions = duplicate( variables.queryOptions ); + localQueryOptions[ "result" ] = "local.q"; + queryExecute( "DELETE FROM #variables.table# WHERE id = ? ", [ getNormalizedID( arguments.objectKey ) ], - { - datasource : variables.dsn, - username : variables.dsnUsername, - password : variables.dsnPassword, - result : "local.q" - } + localQueryOptions ); return ( q.recordCount ? true : false ); @@ -464,11 +454,7 @@ component implements="coldbox.system.cache.store.IObjectStore" accessors="true" FROM #variables.table# ", {}, - { - datasource : variables.dsn, - username : variables.dsnUsername, - password : variables.dsnPassword - } + variables.queryOptions ); return q.totalCount; @@ -497,11 +483,7 @@ component implements="coldbox.system.cache.store.IObjectStore" accessors="true" WHERE id = ? ", [ getNormalizedID( arguments.objectKey ) ], - { - datasource : variables.dsn, - username : variables.dsnUsername, - password : variables.dsnPassword - } + variables.queryOptions ); } @@ -584,11 +566,7 @@ component implements="coldbox.system.cache.store.IObjectStore" accessors="true" ) #create.afterCreate# ", {}, - { - datasource : variables.dsn, - username : variables.dsnUsername, - password : variables.dsnPassword - } + variables.queryOptions ); } } diff --git a/system/cache/store/indexers/JDBCMetadataIndexer.cfc b/system/cache/store/indexers/JDBCMetadataIndexer.cfc index 6167651b9..7b822141c 100644 --- a/system/cache/store/indexers/JDBCMetadataIndexer.cfc +++ b/system/cache/store/indexers/JDBCMetadataIndexer.cfc @@ -203,9 +203,11 @@ component extends="coldbox.system.cache.store.indexers.MetadataIndexer" accessor /** * Get an array of sorted keys for this indexer according to parameters * - * @objectKey - * @property - * @value + * @property The property to order the keys with + * @sortType The sorting type, not used by this indexer + * @sortOrder Either `asc` or `desc` for sorting the keys + * + * @return Sorted keys */ array function getSortedKeys( required property, @@ -216,7 +218,7 @@ component extends="coldbox.system.cache.store.indexers.MetadataIndexer" accessor "SELECT id, objectKey FROM #variables.config.table# ORDER BY #arguments.property# #arguments.sortOrder#", - [ variables.store.getNormalizedID( arguments.objectKey ) ], + [], { datasource : variables.config.dsn, username : variables.config.dsnUsername, diff --git a/system/core/util/CFMLEngine.cfc b/system/core/util/CFMLEngine.cfc index 84ed998d1..2cda87e39 100644 --- a/system/core/util/CFMLEngine.cfc +++ b/system/core/util/CFMLEngine.cfc @@ -7,8 +7,8 @@ component { // setup the engine properties - this.ADOBE = "ADOBE"; - this.LUCEE = "LUCEE"; + this.ADOBE = "adobe"; + this.LUCEE = "lucee"; // JDK Version this.JDK_VERSION = createObject( "java", "java.lang.System" ).getProperty( "java.version" ); @@ -17,19 +17,23 @@ component { * Constructor */ function init(){ - // Feature map - variables.features = { adobe : {}, lucee : {} }; + // Features map by engine + variables.features = { + adobe2016 : { invokeArray : false }, + adobe2018 : { invokeArray : false }, + adobe2021 : { invokeArray : false }, + lucee : { invokeArray : true } + }; + variables.productVersion = listFirst( server.coldfusion.productversion ); return this; } - // ------------------------------------------- PUBLIC ------------------------------------------- - /** - * Returns the current running CFML major version + * Returns the current running CFML major version level */ numeric function getVersion(){ - return listFirst( server.coldfusion.productversion ); + return variables.productVersion; } /** @@ -40,25 +44,44 @@ component { } /** - * Get the current CFML Engine + * Verify if this is a lucee server */ - string function getEngine(){ - var engine = this.adobe; + boolean function isLucee(){ + return structKeyExists( server, "lucee" ); + } - if ( server.coldfusion.productname eq "Lucee" ) { - engine = this.lucee; - } + /** + * Verify if this is an adobe server + */ + boolean function isAdobe(){ + return !isLucee(); + } - return engine; + /** + * Get the current CFML Engine name + * + * @return Either 'lucee' or 'adobe' + */ + string function getEngine(){ + return ( isLucee() ? this.lucee : this.adobe ); + } + + /** + * Discover the running engine slug for feature checks + * + * @return lucee, adobe{version} + */ + string function getFeatureEngineSlug(){ + return isLucee() ? this.lucee : this.adobe & getVersion(); } /** - * Feature Active Check + * CFML Engine based features checker. Pass in the feature and engine and see if you can use it. * * @feature The feature to check - * @engine The engine we are checking + * @engine The engine we are checking or defaults to the running engine */ - boolean function featureCheck( required feature, required engine ){ + boolean function hasFeature( required feature, engine = getFeatureEngineSlug() ){ return variables.features[ arguments.engine ][ arguments.feature ]; } diff --git a/system/logging/appenders/EmailAppender.cfc b/system/logging/appenders/EmailAppender.cfc index 79f8d3af9..92ca94147 100644 --- a/system/logging/appenders/EmailAppender.cfc +++ b/system/logging/appenders/EmailAppender.cfc @@ -103,7 +103,7 @@ component accessors="true" extends="coldbox.system.logging.AbstractAppender" {

Extra Info Dump:

" ); - writeDump( loge.getExtraInfo() ); + writeDump( var = loge.getExtraInfo(), top = 10 ); } } diff --git a/system/testing/BaseTestCase.cfc b/system/testing/BaseTestCase.cfc index 122713a84..9b39d920a 100755 --- a/system/testing/BaseTestCase.cfc +++ b/system/testing/BaseTestCase.cfc @@ -438,22 +438,25 @@ component extends="testbox.system.compat.framework.TestCase" accessors="true" { } try { + // Make sure our routing service can be manipulated + prepareMock( routingService ) + .$( "getCGIElement" ) + .$args( "script_name", requestContext ) + .$results( "" ) + .$( "getCGIElement" ) + .$args( "server_name", requestContext ) + .$results( arguments.domain ); + // If the route is for the home page, use the default event in the config/ColdBox.cfc if ( arguments.route == "/" ) { // Set the default app event arguments.event = getController().getSetting( "defaultEvent" ); requestContext.setValue( requestContext.getEventName(), arguments.event ); // Prepare all mocking data for simulating routing request - prepareMock( routingService ) + routingService .$( "getCGIElement" ) .$args( "path_info", requestContext ) - .$results( arguments.route ) - .$( "getCGIElement" ) - .$args( "script_name", requestContext ) - .$results( "" ) - .$( "getCGIElement" ) - .$args( "server_name", requestContext ) - .$results( arguments.domain ); + .$results( arguments.route ); // No route, it's the route arguments.route = ""; // Capture the route request @@ -462,7 +465,7 @@ component extends="testbox.system.compat.framework.TestCase" accessors="true" { // if we were passed a route, parse it and prepare the SES interceptor for routing. else if ( arguments.route.len() ) { // enable the SES interceptor - getInstance( "router@coldbox" ).setEnabled( true ); + // getInstance( "router@coldbox" ).setEnabled( true ); // separate the route into the route and the query string var routeParts = explodeRoute( arguments.route ); // add the query string parameters from the route to the request context @@ -471,20 +474,18 @@ component extends="testbox.system.compat.framework.TestCase" accessors="true" { prepareMock( routingService ) .$( "getCGIElement" ) .$args( "path_info", requestContext ) - .$results( routeParts.route ) - .$( "getCGIElement" ) - .$args( "script_name", requestContext ) - .$results( "" ) - .$( "getCGIElement" ) - .$args( "server_name", requestContext ) - .$results( arguments.domain ); + .$results( routeParts.route ); // Capture the route request controller.getRequestService().requestCapture(); } else { // If we were passed just an event, remove routing since we don't need it - getInstance( "router@coldbox" ).setEnabled( false ); + // getInstance( "router@coldbox" ).setEnabled( false ); // Capture the request using our passed in event to execute controller.getRequestService().requestCapture( arguments.event ); + routingService + .$( "getCGIElement" ) + .$args( "path_info", requestContext ) + .$results( "" ); } // add the query string parameters from the route to the request context diff --git a/system/web/Controller.cfc b/system/web/Controller.cfc index 6d9e7e48f..14ea3e5a1 100644 --- a/system/web/Controller.cfc +++ b/system/web/Controller.cfc @@ -365,7 +365,7 @@ component serializable="false" accessors="true" { boolean postProcessExempt = false, URL, URI, - numeric statusCode = 0 + numeric statusCode = 302 ){ // Determine the type of relocation var relocationType = "EVENT"; @@ -671,12 +671,14 @@ component serializable="false" accessors="true" { !isNull( results.data ) && isObject( results.data ) ) { - // Ignore, if request context - if ( isInstanceOf( results.data, "coldbox.system.web.context.RequestContext" ) ) { - results.delete( "data" ); - } else if ( structKeyExists( results.data, "$renderdata" ) ) { + // Verify $renderdata method convention + if ( structKeyExists( results.data, "$renderdata" ) ) { results.data = results.data.$renderdata(); } + // Check if request context and ignore + else if ( isInstanceOf( results.data, "coldbox.system.web.context.RequestContext" ) ) { + results.delete( "data" ); + } } // Do we need to do action renderings? @@ -1200,22 +1202,18 @@ component serializable="false" accessors="true" { } /** - * Send a CF relocation + * Encapsulate a cf relocation tag. Encapsulated so we can mock it. */ private function sendRelocation( required URL, boolean addToken = false, - statusCode = 0 + statusCode = 302 ){ - if ( arguments.statusCode neq 0 ) { - location( - url = "#arguments.url#", - addtoken = "#addtoken#", - statuscode = "#arguments.statusCode#" - ); - } else { - location( url = "#arguments.url#", addtoken = "#addtoken#" ); - } + location( + url = "#arguments.URL#", + addtoken = "#arguments.addtoken#", + statuscode = "#arguments.statusCode#" + ); return this; } diff --git a/system/web/context/Response.cfc b/system/web/context/Response.cfc index e88e1a955..ca068d114 100644 --- a/system/web/context/Response.cfc +++ b/system/web/context/Response.cfc @@ -199,7 +199,13 @@ component accessors="true" { variables.responsetime = 0; variables.headers = []; - setPagination(); + variables.pagination = { + "offset" : 0, + "maxRows" : 0, + "page" : 1, + "totalRecords" : 0, + "totalPages" : 1 + }; return this; } @@ -260,7 +266,7 @@ component accessors="true" { numeric totalRecords = 0, numeric totalPages = 1 ){ - variables.pagination = arguments; + structAppend( variables.pagination, arguments, true ); return this; } @@ -304,9 +310,9 @@ component accessors="true" { } /** - * Sets the data and pagination from a struct with `results` and `pagination`. + * Sets the data and pagination from a struct with a `results` and `pagination` key. * - * @data The struct containing both results and pagination. + * @data The struct containing both 'results' and 'pagination' keys * @resultsKey The name of the key with the results. * @paginationKey The name of the key with the pagination. * diff --git a/system/web/routing/Router.cfc b/system/web/routing/Router.cfc index d3041306d..334737aa9 100644 --- a/system/web/routing/Router.cfc +++ b/system/web/routing/Router.cfc @@ -902,7 +902,19 @@ component } } var thisRouteActions = isStruct( thisRoute.action ) ? thisRoute.action : {}; - structAppend( actions, thisRouteActions, true ); + for ( var newActionVerb in thisRouteActions ) { + var newEvent = thisRouteActions[ newActionVerb ]; + if ( thisRoute.handler != "" ) { + newEvent = thisRoute.handler & "." & newEvent; + } + if ( actions.keyExists( newActionVerb ) && variables.log.canWarn() ) { + variables.log.warn( + "Duplicate HTTP verb found when merging routing for pattern: [#thisRoute.pattern#]. Changing #uCase( newActionVerb )# action from [#actions[ newActionVerb ]#] to [#newEvent#]", + { existingRoute : matchingRoute, newRoute : thisRoute } + ); + } + actions[ newActionVerb ] = newEvent; + } if ( thisRoute.event != "" ) { for ( var verb in thisRoute.verbs ) { structInsert( actions, verb, thisRoute.event ); diff --git a/system/web/services/RoutingService.cfc b/system/web/services/RoutingService.cfc index bab60dc25..ed5455634 100644 --- a/system/web/services/RoutingService.cfc +++ b/system/web/services/RoutingService.cfc @@ -171,8 +171,6 @@ component extends="coldbox.system.web.services.BaseService" accessors="true" { // Clean incoming paths var cleanedPaths = getCleanedPaths( rc, arguments.event ); - writeDump( var = "******> Cleaned Paths: #cleanedPaths.toString()#", output = "console" ); - // Check if disabled or in proxy mode, if it is, then exit out. if ( !variables.router.getEnabled() OR arguments.event.isProxyRequest() ) { return; @@ -207,11 +205,8 @@ component extends="coldbox.system.web.services.BaseService" accessors="true" { domain = cleanedPaths[ "domain" ] ); - writeDump( var = "******> Found route: #routeResults.toString()#", output = "console" ); - // Process the route var discoveredEvent = processRoute( routeResults, event, rc, prc ); - writeDump( var = "******> Discovered Event: #discoveredEvent ?: "N/A"#", output = "console" ); // Do we use the discovered event? if ( !isNull( local.discoveredEvent ) and discoveredEvent.len() ) { @@ -269,7 +264,6 @@ component extends="coldbox.system.web.services.BaseService" accessors="true" { routeResults.route.redirect.len() ) { // Debugging - // writeDump( var=[ cgi , prc ], output="console" ); return processRedirect( routeResults, event ); } diff --git a/tests/Application.cfc b/tests/Application.cfc index d6438d77c..fe24af204 100755 --- a/tests/Application.cfc +++ b/tests/Application.cfc @@ -44,6 +44,7 @@ component{ pagePoolClear(); } ormReload(); + onRequestEnd( arguments.targetPage ); } return true; @@ -66,4 +67,4 @@ component{ return isNull( value ) ? false : !!value; } -} \ No newline at end of file +} diff --git a/tests/specs/async/AsyncManagerSpec.cfc b/tests/specs/async/AsyncManagerSpec.cfc index 15405b30e..d9610bae3 100644 --- a/tests/specs/async/AsyncManagerSpec.cfc +++ b/tests/specs/async/AsyncManagerSpec.cfc @@ -3,17 +3,15 @@ */ component extends="BaseAsyncSpec" { - /*********************************** BDD SUITES ***********************************/ + function beforeAll(){ + asyncManager = new coldbox.system.async.AsyncManager( debug = true ); + } function run( testResults, testBox ){ variables.out = createObject( "java", "java.lang.System" ).out; // all your suites go here. describe( "ColdBox Async Programming", function(){ - beforeEach( function( currentSpec ){ - asyncManager = new coldbox.system.async.AsyncManager( debug = true ); - } ); - it( "can produce durations", function(){ expect( asyncManager @@ -51,7 +49,6 @@ component extends="BaseAsyncSpec" { expect( f.isDone() ).toBeTrue(); } ); - it( "can create a future with a custom CFML executor", function(){ var f = asyncManager.newFuture( function(){ return 2; diff --git a/tests/specs/async/BaseAsyncSpec.cfc b/tests/specs/async/BaseAsyncSpec.cfc index f85642c8f..78fafffc9 100644 --- a/tests/specs/async/BaseAsyncSpec.cfc +++ b/tests/specs/async/BaseAsyncSpec.cfc @@ -5,14 +5,6 @@ component extends="testbox.system.BaseSpec" skip="true" { /*********************************** LIFE CYCLE Methods ***********************************/ - // executes before all suites+specs in the run() method - function beforeAll(){ - } - - // executes after all suites+specs in the run() method - function afterAll(){ - } - /** * Send output to the console */ diff --git a/tests/specs/async/ExecutorServicesSpec.cfc b/tests/specs/async/ExecutorServicesSpec.cfc index 6be94b5e6..278cf16b4 100644 --- a/tests/specs/async/ExecutorServicesSpec.cfc +++ b/tests/specs/async/ExecutorServicesSpec.cfc @@ -3,8 +3,6 @@ */ component extends="BaseAsyncSpec" { - /*********************************** BDD SUITES ***********************************/ - function run( testResults, testBox ){ // all your suites go here. describe( "ColdBox Async Executor Services", function(){ diff --git a/tests/specs/async/tasks/ScheduledTaskSpec.cfc b/tests/specs/async/tasks/ScheduledTaskSpec.cfc index 17ddb151f..f1e3e26d0 100644 --- a/tests/specs/async/tasks/ScheduledTaskSpec.cfc +++ b/tests/specs/async/tasks/ScheduledTaskSpec.cfc @@ -4,7 +4,6 @@ component extends="tests.specs.async.BaseAsyncSpec" { function beforeAll(){ variables.asyncManager = new coldbox.system.async.AsyncManager(); - super.beforeAll(); } function run( testResults, testBox ){ diff --git a/tests/specs/web/controllerTest.cfc b/tests/specs/web/controllerTest.cfc index f897cacf0..eda1f7e9a 100755 --- a/tests/specs/web/controllerTest.cfc +++ b/tests/specs/web/controllerTest.cfc @@ -116,7 +116,7 @@ controller.relocate( URL = "http://www.coldbox.org", addToken = true ); assertEquals( "http://www.coldbox.org", controller.$callLog().sendRelocation[ 1 ].URL ); assertEquals( true, controller.$callLog().sendRelocation[ 1 ].addToken ); - assertEquals( 0, controller.$callLog().sendRelocation[ 1 ].statusCode ); + assertEquals( 302, controller.$callLog().sendRelocation[ 1 ].statusCode ); // Full URL with more stuff controller.relocate( diff --git a/tests/specs/web/routing/RouterTest.cfc b/tests/specs/web/routing/RouterTest.cfc index 6dd1c9d3f..4be4b4473 100644 --- a/tests/specs/web/routing/RouterTest.cfc +++ b/tests/specs/web/routing/RouterTest.cfc @@ -578,6 +578,31 @@ component extends="coldbox.system.testing.BaseModelTest" { } ); } ); } ); + + describe( "merging routes", function(){ + it( "can merge a route with event and a route with handler", function(){ + router.post( "/notifications/read", "Notifications.ReadNotifications.create" ); + router + .route( "/notifications/read" ) + .withHandler( "Notifications.ReadNotifications" ) + .toAction( { "POST" : "create", "DELETE" : "delete" } ); + + var routes = router.getRoutes(); + expect( routes ).toBeArray(); + expect( routes ).toHaveLength( 1 ); + var route = routes[ 1 ]; + expect( route ).toHaveKey( "event" ); + expect( route.event ).toBe( "" ); + expect( route ).toHaveKey( "handler" ); + expect( route.handler ).toBe( "" ); + expect( route ).toHaveKey( "action" ); + expect( route.action ).toBeStruct(); + expect( route.action ).toHaveKey( "POST" ); + expect( route.action.POST ).toBe( "Notifications.ReadNotifications.create" ); + expect( route.action ).toHaveKey( "DELETE" ); + expect( route.action.DELETE ).toBe( "Notifications.ReadNotifications.delete" ); + } ); + } ); } ); }