My project's workaround for stevewithington/MuraFW1#53
Last active
October 10, 2019 16:58
-
-
Save jamiejackson/7f325428e9582b3fca8c5140910c5dee to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <cfscript> | |
| // Don't let saveFlashContext persist complex variables | |
| /* TODO: this is a stopgap until we have FW/1 consolidated across plugins, | |
| at which point, we'll have these in a CFC that extends FW/1 and we'll use | |
| that extended CFC in our plugins. At the moment, we'll include this as | |
| a mixin from plugins' Application.cfc | |
| */ | |
| // default the value to disallow queries | |
| param name="variables.framework.allowQueriesInFlashContext" value=false; | |
| private void function internalFrameworkTraceWrapper() | |
| hint="Some of the fw/1 versions we use don't have this function, so wrap it." | |
| { | |
| // only run the function if it exists | |
| if (isDefined("internalFrameworkTrace")) { | |
| internalFrameworkTrace( argumentCollection = arguments ); | |
| } | |
| } | |
| private boolean function isRcElementAllowedInFlashContext( | |
| required any element, | |
| required boolean allowQueriesInFlashContext | |
| ) | |
| hint="determines whether an RC element should be allowed in flash context" | |
| { | |
| if ( isSimpleValue(element) ) { | |
| return true; | |
| } else if ( // don't allow mura vars (e.g., $ or PC to get persisted) | |
| isValid('component', element) | |
| && getMetaData(element).name.listFirst(".") == 'mura' | |
| ) { | |
| return false; | |
| } else if ( isValid('query', element) && !allowQueriesInFlashContext ) { // no queries allowed | |
| return false; | |
| } else { | |
| return true; | |
| } | |
| } | |
| private void function logFW1Issue( | |
| required string message, | |
| required string action, | |
| string type = "warning" | |
| ) | |
| hint="Log instances of attempted complex variable storage." | |
| { | |
| var logMessage = "saveFlashContext:"; | |
| if (structKeyExists(variables.framework, "package")) { | |
| logMessage = "#logMessage# package=#variables.framework.package#;"; | |
| } | |
| logMessage = "#logMessage# action=#action#; #message#"; | |
| logWrapper( | |
| file="custom_fw_one", | |
| type=type, | |
| text=logMessage | |
| ); | |
| } | |
| private void function logWrapper( | |
| required string file, | |
| required string type, | |
| required string text | |
| ) | |
| hint="don't log automated tests. this creates a seam to bypass real logging." | |
| { | |
| writeLog(argumentCollection = arguments); | |
| } | |
| private string function saveFlashContext( | |
| string keys, | |
| struct requestScope = request, | |
| struct sessionScope = session | |
| ) | |
| hint="Override the stock FW/1 function to prevent saving of complex vars" | |
| { | |
| var curPreserveKey = getNextPreserveKeyAndPurgeOld(); | |
| var preserveKeySessionKey = getPreserveKeySessionKey( curPreserveKey ); | |
| try { | |
| param name="sessionScope.#preserveKeySessionKey#" default="#{ }#"; | |
| if ( keys == 'all' ) { | |
| keys = structKeyList(requestScope.context); | |
| } | |
| var key = 0; | |
| var keyNames = listToArray( keys ); | |
| var logMessage = ""; | |
| var logType = "information"; | |
| var interestingKeys = []; | |
| for ( key in keyNames ) { | |
| key = trim( key ); | |
| if ( !structKeyExists( requestScope.context, key ) ) { | |
| internalFrameworkTraceWrapper( | |
| message = 'key "#key#" does not exist in RC, | |
| cannot preserve.', | |
| traceType="WARNING" | |
| ); | |
| } else if ( | |
| !isRcElementAllowedInFlashContext( | |
| requestScope.context[key], | |
| variables.framework.allowQueriesInFlashContext | |
| ) | |
| ) { | |
| interestingKeys.append(key); | |
| logMessage = "keys skipped"; | |
| } else { | |
| sessionScope[ preserveKeySessionKey ][ key ] = requestScope.context[ key ]; | |
| } | |
| } | |
| if (interestingKeys.len() > 0) { | |
| // custom logging | |
| interestingKeys.sort('textnocase'); | |
| logFW1Issue( | |
| message="#logMessage#='#interestingKeys.toList()#'", | |
| action = requestScope.action, | |
| type=logType | |
| ); | |
| } | |
| } catch ( any ex ) { | |
| if ( ex.message contains 'session scope is not enabled' ) { | |
| // session scope not enabled, do nothing | |
| internalFrameworkTraceWrapper( message = 'sessionManagement not enabled, cannot preserve RC keys.', traceType = 'WARNING' ); | |
| } else { | |
| rethrow(); | |
| } | |
| } | |
| return curPreserveKey; | |
| } | |
| </cfscript> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // plugins/myplugin/Application.cfc | |
| ... | |
| // ignore this variable and what it controls. i just didn't want developers inadvertently | |
| // saving rc-scoped queries *on my project*, which could bloat the session | |
| variables.framework.allowQueriesInFlashContext = true; | |
| include template="/hudexchange_deployment_root/lib/org/framework/_fw1_override_mixin.cfm"; | |
| ... |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // /test/lib/org/framework/LegacyFrameworkOneFixture.cfc | |
| component | |
| // notice that we're extending fw/1 | |
| extends="lib.org.framework.one", | |
| hint="Since we're testing a mix-in, we need a component fixture for it. This one uses a legacy version of FW/1." | |
| { | |
| public void function setAllowQueriesInFlashContext(required boolean allow) | |
| hint="allows automated tests to set the value" | |
| { | |
| variables.framework.allowQueriesInFlashContext = allow; | |
| } | |
| // include the mix-in, which overrides fw/1 functions | |
| include template="/hudexchange_deployment_root/lib/org/framework/_fw1_override_mixin.cfm"; | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // /test/lib/org/framework/ModernFrameworkOneFixture.cfc | |
| component | |
| // notice that we're extending fw/1 | |
| extends="lib.org.framework.3_1_0.one", | |
| hint="Since we're testing a mix-in, we need a component fixture for it. This one uses a more modern FW/1 version." | |
| { | |
| public void function setAllowQueriesInFlashContext(required boolean allow) | |
| hint="allows automated tests to set the value" | |
| { | |
| variables.framework.allowQueriesInFlashContext = allow; | |
| } | |
| // include the mix-in, which overrides fw/1 functions | |
| include template="/hudexchange_deployment_root/lib/org/framework/_fw1_override_mixin.cfm"; | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // /test/lib/org/framework/OneTest.cfc | |
| component | |
| extends="testbox.system.BaseSpec" | |
| hint="Test FW/1 Overrides" | |
| { | |
| function run( testResults, testBox ){ | |
| feature("Framework one overrides and supporting code work properly.", function() { | |
| beforeEach(function (currentSpec){ | |
| cut = prepareMock(new ModernFrameworkOneFixture()); | |
| cut.$("logWrapper"); | |
| }); | |
| story("saveFlashContext() override function", function(spec, suite) { | |
| beforeEach(function (currentSpec){ | |
| makePublic(cut, 'saveFlashContext'); | |
| // mock scopes to help with testing | |
| mockRequestScope = { | |
| "action": "fakeFw1Action" | |
| }; | |
| mockSessionScope = {}; | |
| }); | |
| given( | |
| "I have a mura framework, a pluginConfig, a query, and simpler elements in the RC scope | |
| and queries are not allowed", | |
| function() { | |
| when("the flash context is saved with redirect(..., preserve='all')", function() { | |
| beforeEach(function (currentSpec){ | |
| allowedElems = { | |
| 'aString': "myString", | |
| 'aNumber': 1, | |
| 'aStruct': {"aKey": "aValue"}, | |
| 'anArray': [1,2,3] | |
| }; | |
| disallowedElems = { | |
| '$': createMock('mura.muraScope'), | |
| 'pluginConfig': createMock('mura.plugin.pluginConfig'), | |
| 'aQuery': queryNew('aColumn') | |
| }; | |
| expectedLogMessage = "saveFlashContext: action=fakeFw1Action; keys skipped='$,aQuery,pluginConfig'"; | |
| // set up the mock request context (combine both allowed | |
| // and disallowed RC scope items together) | |
| mockRequestScope.context = allowedElems.duplicate(); | |
| mockRequestScope.context.append(disallowedElems); | |
| cut.saveFlashContext( | |
| keys = "all", | |
| requestScope = mockRequestScope, | |
| sessionScope = mockSessionScope | |
| ); | |
| logWrapperLog = cut.$callLog().logWrapper; | |
| // the flash context goes inside a session['__fw1'] key | |
| expectedSession["__fw1"] = allowedElems.duplicate(); | |
| }); | |
| then("(only) non-mura and query elements are stripped out of the persistent scope.", function() { | |
| expect(mockSessionScope).toBe(expectedSession); | |
| }); | |
| then("logging is correct", function() { | |
| expect(logWrapperLog[1].text).toBe(expectedLogMessage); | |
| }); | |
| }); | |
| } | |
| ); | |
| given( | |
| "I have a mura framework, a pluginConfig, a query, and simpler elements in the RC scope | |
| and queries are allowed", | |
| function() { | |
| when("the flash context is saved with redirect(..., preserve='all')", function() { | |
| beforeEach(function (currentSpec){ | |
| cut.setAllowQueriesInFlashContext(true); | |
| allowedElems = { | |
| 'aString': "myString", | |
| 'aNumber': 1, | |
| 'aStruct': {"aKey": "aValue"}, | |
| 'anArray': [1,2,3], | |
| 'aQuery': queryNew('aColumn') | |
| }; | |
| disallowedElems = { | |
| '$': createMock('mura.muraScope'), | |
| 'pluginConfig': createMock('mura.plugin.pluginConfig') | |
| }; | |
| expectedLogMessage = "saveFlashContext: action=fakeFw1Action; keys skipped='$,pluginConfig'"; | |
| // set up the mock request context (combine both allowed | |
| // and disallowed RC scope items together) | |
| mockRequestScope.context = allowedElems.duplicate(); | |
| mockRequestScope.context.append(disallowedElems); | |
| cut.saveFlashContext( | |
| keys = "all", | |
| requestScope = mockRequestScope, | |
| sessionScope = mockSessionScope | |
| ); | |
| logWrapperLog = cut.$callLog().logWrapper; | |
| // the flash context goes inside a session['__fw1'] key | |
| expectedSession["__fw1"] = allowedElems.duplicate(); | |
| }); | |
| then("(only) mura elements are stripped out of the flash context.", function() { | |
| expect(mockSessionScope).toBe(expectedSession); | |
| }); | |
| then("logging is correct", function() { | |
| expect(logWrapperLog[1].text).toBe(expectedLogMessage); | |
| }); | |
| }); | |
| } | |
| ); | |
| }); | |
| story("internalFrameworkTraceWrapper function works properly", function(spec, suite) { | |
| given("a legacy version of fw/1 is used", function() { | |
| then("no exceptions are thrown", function() { | |
| cut = prepareMock(new LegacyFrameworkOneFixture()); | |
| makePublic(cut, 'internalFrameworkTraceWrapper'); | |
| expect( function(){ | |
| cut.internalFrameworkTraceWrapper( | |
| message = "foo", | |
| traceType = "WARNING" | |
| ); | |
| }).notToThrow(); | |
| }); | |
| }); | |
| given("a modern version of fw/1 is used", function() { | |
| then("no exceptions are thrown", function() { | |
| cut = prepareMock(new ModernFrameworkOneFixture()); | |
| makePublic(cut, 'internalFrameworkTraceWrapper'); | |
| expect( function(){ | |
| cut.internalFrameworkTraceWrapper( | |
| message = "foo", | |
| traceType = "WARNING" | |
| ); | |
| }).notToThrow(); | |
| }); | |
| }); | |
| }); | |
| describe("isRcElementAllowedInFlashContext()", function() { | |
| beforeEach(function (currentSpec){ | |
| makePublic(cut, 'isRcElementAllowedInFlashContext'); | |
| }); | |
| given("queries are allowed", function() { | |
| beforeEach(function (currentSpec){ | |
| allowQueriesInFlashContext = true | |
| }); | |
| then("a query is allowed", function() { | |
| expect( | |
| cut.isRcElementAllowedInFlashContext( | |
| element = queryNew('aColumn'), | |
| allowQueriesInFlashContext = allowQueriesInFlashContext | |
| ) | |
| ).toBeTrue(); | |
| }); | |
| then("a string is allowed", function() { | |
| expect( | |
| cut.isRcElementAllowedInFlashContext( | |
| element = 'myString', | |
| allowQueriesInFlashContext = allowQueriesInFlashContext | |
| ) | |
| ).toBeTrue(); | |
| }); | |
| then("a number is allowed", function() { | |
| expect( | |
| cut.isRcElementAllowedInFlashContext( | |
| element = 1e10, | |
| allowQueriesInFlashContext = allowQueriesInFlashContext | |
| ) | |
| ).toBeTrue(); | |
| }); | |
| then("a mura component is not allowed", function() { | |
| expect( | |
| cut.isRcElementAllowedInFlashContext( | |
| element = createMock('mura.plugin.pluginConfig'), | |
| allowQueriesInFlashContext = allowQueriesInFlashContext | |
| ) | |
| ).toBeFalse(); | |
| }); | |
| then("a struct is allowed", function() { | |
| expect( | |
| cut.isRcElementAllowedInFlashContext( | |
| element = {"aKey": "aValue"}, | |
| allowQueriesInFlashContext = allowQueriesInFlashContext | |
| ) | |
| ).toBeTrue(); | |
| }); | |
| then("an array is allowed", function() { | |
| expect( | |
| cut.isRcElementAllowedInFlashContext( | |
| element = [1,2,3], | |
| allowQueriesInFlashContext = allowQueriesInFlashContext | |
| ) | |
| ).toBeTrue(); | |
| }); | |
| }); | |
| given("queries are not allowed", function() { | |
| beforeEach(function (currentSpec){ | |
| allowQueriesInFlashContext = false | |
| }); | |
| then("a query is not allowed", function() { | |
| expect( | |
| cut.isRcElementAllowedInFlashContext( | |
| element = queryNew('aColumn'), | |
| allowQueriesInFlashContext = allowQueriesInFlashContext | |
| ) | |
| ).toBeFalse(); | |
| }); | |
| then("a string is allowed", function() { | |
| expect( | |
| cut.isRcElementAllowedInFlashContext( | |
| element = 'myString', | |
| allowQueriesInFlashContext = allowQueriesInFlashContext | |
| ) | |
| ).toBeTrue(); | |
| }); | |
| then("a number is allowed", function() { | |
| expect( | |
| cut.isRcElementAllowedInFlashContext( | |
| element = 1e10, | |
| allowQueriesInFlashContext = allowQueriesInFlashContext | |
| ) | |
| ).toBeTrue(); | |
| }); | |
| then("a mura component is not allowed", function() { | |
| expect( | |
| cut.isRcElementAllowedInFlashContext( | |
| element = createMock('mura.plugin.pluginConfig'), | |
| allowQueriesInFlashContext = allowQueriesInFlashContext | |
| ) | |
| ).toBeFalse(); | |
| }); | |
| then("a struct is allowed", function() { | |
| expect( | |
| cut.isRcElementAllowedInFlashContext( | |
| element = {"aKey": "aValue"}, | |
| allowQueriesInFlashContext = allowQueriesInFlashContext | |
| ) | |
| ).toBeTrue(); | |
| }); | |
| then("an array is allowed", function() { | |
| expect( | |
| cut.isRcElementAllowedInFlashContext( | |
| element = [1,2,3], | |
| allowQueriesInFlashContext = allowQueriesInFlashContext | |
| ) | |
| ).toBeTrue(); | |
| }); | |
| }); | |
| }); | |
| }); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment