Hi there, I need to load another cfconfig file (as...
# box-products
m
Hi there, I need to load another cfconfig file (aside from server and web) on server startup. I am looking at the possibility of using the
scripts:onServerStart
But doing the following to debug and see exactly when they are triggered doesnt seem to do any output:
Copy code
"scripts":{
        "preServerStart":"echo '---------preServerStart'",
        "onServerStart":"echo '---------onServerStart'",
        "onServerInstall":"echo '---------onServerInstall'",
        "onServerStop":"echo '---------onServerStop'",
        "preServerForget":"echo '---------preServerForget'",
        "postServerForget":"echo '---------postServerForget'"
    }
When I do
server start --debug
b
@Mark Drew (he/him) Did you put those in your
box.json
or your
server.json
?
A better solution seems to either • allow a comma-delimited list of files in the
server.json
• allow a file glob which could match multiple files
m
server.json
b
What version of CommandBox are you on?
m
I am just checking. 5.5.something
b
Ok, because server scripts was added in 5.5.0
m
• allow a comma-delimited list of files in the
server.json
• allow a file glob which could match multiple files
I like either or both of those options!
b
And FWIW, the existing CFConfig interceptor listens to
onServerInstall
m
OK, I think it was my local (non docker/prod/actualdev) environment
AHA, it would help being on the right version!
b
FWIW, the scripts work for me
message has been deleted
m
does server.json:cfconfig.web allow comma delimted or globbing patterns already or as feature request?
b
It would be a feature request
I probably thought about it at the time, but it just really starts to complicate the logic and it's more of an edge case
I did it for server rule files and it was pretty hairy
m
upgrading fixed it locally 🙂
b
Copy code
if( serverJSON.keyExists( 'web' ) && serverJSON.web.keyExists( 'rules' ) ) {
			if( !isArray( serverJSON.web.rules ) ) {
				throw( message="'rules' key in your server.json must be an array of strings.", type="commandException" );
			}
			serverInfo.webRules.append( serverJSON.web.rules, true);
		}
		if( serverJSON.keyExists( 'web' ) && serverJSON.web.keyExists( 'rulesFile' ) ) {
			if( isSimpleValue( serverJSON.web.rulesFile ) ) {
				serverJSON.web.rulesFile = serverJSON.web.rulesFile.listToArray();
			}
			serverInfo.webRules.append( serverJSON.web.rulesFile.reduce((predicates,fg)=>{
				fg = fileSystemUtil.resolvePath( fg, defaultServerConfigFileDirectory );
				return predicates.append( wirebox.getInstance( 'Globber' ).setPattern( fg ).matches().reduce( (predicates,file)=>{
						if( lCase( file ).endsWith( '.json' ) ) {
							return predicates.append( deserializeJSON( fileRead( file ) ), true );
						} else {
							return predicates.append( fileRead( file ).listToArray( chr(13)&chr(10) ), true );
						}
					}, [] ), true );
			}, []), true );
		}
		if( defaults.keyExists( 'web' ) && defaults.web.keyExists( 'rules' ) ) {
			serverInfo.webRules.append( defaults.web.rules, true);
		}

		if( defaults.keyExists( 'web' ) && defaults.web.keyExists( 'rulesFile' ) ) {
			var defaultsRulesFile = defaults.web.rulesFile;
			if( isSimpleValue( defaultsRulesFile ) ) {
				defaultsRulesFile = defaultsRulesFile.listToArray();
			}
			serverInfo.webRules.append( defaultsRulesFile.reduce((predicates,fg)=>{
				fg = fileSystemUtil.resolvePath( fg, defaultwebroot );
				return predicates.append( wirebox.getInstance( 'Globber' ).setPattern( fg ).matches().reduce( (predicates,file)=>{
						if( lCase( file ).endsWith( '.json' ) ) {
							return predicates.append( deserializeJSON( fileRead( file ) ), true );
						} else {
							return predicates.append( fileRead( file ).listToArray( chr(13)&chr(10) ), true );
						}
					}, [] ), true );
			}, []), true);
		}
☝️ All that just to allow for a server.json or server defaults list of file globbing patterns
🤯 1
m
Now to see how to add that file under certain conditions
b
I wasn't keen to write all that again for CFConfig files so I just kept it simple, lol
CFConfig will basically load all JSON files it can fine across all valid settings
m
I think the issue I am trying to fix (maybe context helps) is that I have a massive config file (as we do) but for one environment (and one environment only) I need to overwrite a datasource
the rest can be done with variables
${DB_USERNAME}
for example but for this environment we need a different datasource all together.
b
So, for example, you could set both
Copy code
cfconfigfile
cfconfigserver
env vars and set both
Copy code
cfconfigFile 
cfconfig.file
in the
server.json
and basically get 4 files imported right there!
m
I am using cfconfig.web if that is what you mean?
b
No, that's a different setting that goes into the web context. There aren't as many ones that go there, but you could set the
cfconfigweb
env var and use
cfconfig.web
in your
server.json
and the would both get loaded
m
I think the issue here would be othe order
So cfconfigweb loaded first and then cfconfig.web ?
b
As far as your specific use case-- it's worth noting, CFConfig will never remove a datasource. it will only add additional datasources on top of the existing ones
m
Cos that's the issue there for me 😕
b
So I'm not sure if any of these options actually work for you
m
Would it replace a datasource?
b
You could toss in a
cfconfig datasource remove
command, but the issue would be getting it to run at the right time (after the cfconfig imports, not before)
m
.cfconfig-web.json <- has all settings and datasources including "bob" .cfconfig-bob.json <- only has the bob datasource
So my thought was load .cfconfig-web.json, then overwrite it with .cfconfig-bob.json
b
So cfconfigweb loaded first and then cfconfig.web ?
Um, that's not necessarily a documented behavior, but glancing at the code, it seems we check env vars prior to JSON files.
There's a lot that goes on in the background however-- for instance, all individual settings found in env vars are combined into a single struct and then mass-imported all at once so you can spread, say, a datasource definition across 10 different env vars and it will still import correctly
Wait, hold on. Are you trying to REMOVE a datasource a put in one of a totally different name, or just overwrite a couple details of an existing datasource without changing the name
m
Makes sense. I feared that was the case.
b
because I thought you were doing the former, but your last example with bob seems the opposite
Because the latter is very easy to do
m
I have a bob datasrouce to Microsoft SQL, in localdev we want bob datasource to use MSQL
(as an example)
so I want one uber config. but in localdev I want to replace it with a mysql config , because I am cheap and dont want a MS SQL license
b
So you still want a datasource named bob, you just want it to use a different type?
m
Yarp!
b
Exactly how many bits of bob are different?
m
Ahh, you mean using env variables to replace it? I think rather a lot, but going to check now
b
well hold on, I have two specific suggestions, but I want to understand just how much is different between prod and dev first before I present them
m
DM'ing you the diff.
b
I just need to know what keys are different
not the values
m
There are a few differences, as in one we have username, password and in the other we dont
The rest could be variables (we use that a lot)
b
Ok, so here are three possible (and fairly similiar) approaches. The first is to make the actual JSON have placeholders for everything that may be different
Copy code
"password":"${DB_PASSWORD}",
"class":"${DB_CLASS}",
"dbdriver":"${DB_DBDRIVER}",
etc...
Then provide the proper env vars in each environment. The second option is to override the individual bits with env vars like so:
Copy code
cfconfig_datasources_bob_password=myPass
cfconfig_datasources_bob_class=my.class
cfconfig_datasources_bob_dbdriver=MySQL
etc...
The third option is the same as above, but do it with one big env var that simply contains JSON
Copy code
cfconfig_datasources_bob={"password":"myPass","class=":"my.class","dbdriver":"MySQL",etc..}
escaping as necessary based on how/where you set that
👍🏾 1
1
You could probably also make it work to have two JSON files, however I'd caution against that as i can't promise the JSON files will always be imported in the same order in future versions of CFConfig.
m
I think I like option 1 as that is how we do everything else.
and by using defaults, it all works nicely and the whole team knows how to do it. If I can get it to work in this case.
Thank you for being a great sounding board!
b
Yep, really the best flow will just depend on how your team is used to dealing with this stuff
m
Basically I am rationalising a bunch of configs into one
this would be the best approach as having another method is a bit of a step backwards in this case
I think it worked, I will test with the live dsn tomorrow! NOICE!
👍 1
c
@Mark Drew (he/him) sorry for jumping into the discussion late. I noticed you wanted to use MySQL locally due to cost of MS SQL. In case you weren't aware, there is a free Developer Edition of MS SQL you can run locally for dev/testing (and it has all of the functionality of MS SQL Enterprise Edition). You don't have to buy a seat to develop locally. You can also install MS SQL Express (also free) which has pretty much the same functionality as MS SQL Standard Edition, but it has resource limits (size of database, 1 CPU thread, etc.). Just thought I'd throw that out there.
2
m
@cfvonner I was actually just making up a scenario, it's more about using another database that takes too long to setup for local development. 🙂
Thank you for the info though 🙂
👍 1