Separate environment config in Ember - ember.js

I'm using the ember-cli to build my app, which gives me a nice app.js file that I can server up on a static asset server. What is the most idiomatic way to allow for a separate configuration at deployment time?
For example, I might tell the consumer of my app.js file to include an extra config.[js|json] file which will get loaded, and the values from that file would go into the ENV object... so that I can point the app at a different REST endpoint, for example (QA, Sandbox, Pre-release, etc) without re-compiling.
I figure there must be a way, I'm just not seeing it. I get that there is the config/environment.js file, but that gets compiled into the dist folder. I'm looking for something that sits next to the packaged JS. I can certainly hack something together, so I'm not looking for a hack. An ember-cli-addon, perhaps? I figure there must be an "ember way" to do this.
I'm just not finding it :)

Ok, here is what I did. Basically, I allow some settings to be overridden by the host application. I register an initializer to jam them into the configuration object, and then I use the config options like normal. It looks a little something like this:
config/environment.js
// This is just normal ENV.APP configuration stuff. Nothing odd here
module.exports = function(environment) {
var ENV = {
// snip
APP: {
API_HOST: 'http://defaultAPIHost.com',
AUTH_PROVIDER: 'http://defaultAuthProvider.com'
}
};
return ENV;
};
app/initializers/parameter-overrides.js
import config from '../config/environment';
// This is the custom stuff. If the values have been defined globally,
// override them on the config object. I suppose this can be done a
// bit more dynamically, but this explicit code is for illustrative purposes.
export function initialize() {
let apiOverride = window.MyAppEnv && window.MyAppEnv.API_HOST;
let authOverride = window.MyAppEnv && window.MyAppEnv.AUTH_PROVIDER;
config.APP.API_HOST = apiOverride || config.APP.API_HOST;
config.APP.AUTH_PROVIDER = authOverride || config.APP.AUTH_PROVIDER;
}
export default {
name: 'parameter-overrides',
initialize: initialize
};
app/adapters/application
import DS from 'ember-data';
import config from '../config/environment';
// Then consume the config properties as you normally would
export default DS.RESTAdapter.extend({
host: config.APP.API_HOST,
namespace: "api"
});
Now, the hosting application can include this in the page, and it will override the values from the config/environment.js:
<script type="text/javascript">
// Override this value in production to the proper API host and Auth host
window.MyAppEnv = {
AUTH_PROVIDER: null, //'http://oauthhost.com/OAuth2'
API_HOST: null //"http://apihost.com"
};
</script>
Is this a reasonable approach? Is there something better out there?

Related

Ember-cli-build, exclude components ember addon

I'm using a "core" ember addon in a boilerplate, with
npm link core-addon
This addon contains generic components, helpers, routes...
Is there a way to exclude some of these components in the boilerplate's ember-cli-build file?
I already tried the following in the ember-build-cli in my boilerplate project, which is probably wrong:
const EmberApp = require('ember-cli/lib/broccoli/ember-app');
const environment = EmberApp.env();
module.exports = function (defaults) {
let app = new EmberApp(defaults, {
funnel: {
enabled:true,
exclude:['core-addon/pods/components/pages/**/*']
},
});
return app.toTree();
};
Ember version: 3.5.0
Ember cli version 3.5.0 node version 8.11.3
Addons generally take the reverse approach of this: The addon manages what gets merged into the consuming app via configuration in the consuming app.
At the highest level, each addon has an entry point that is the index.js file sitting in the root directory of the addon. The addon provides certain configuration options that it reads from config/environment.js of the consuming app when installing.
I think a really good case study for you would be ember-bootstrap. Look at their configuration options and more specifically the blacklist option. They allow the consuming application to only install a subset of the bootstrap components. Furthermore, the project supports bootstrap 3 or bootstrap 4, but the consuming app isn't getting both! The work is done in index.js
Let's look just at how they blacklist (ie exclude) certain components from being added to the consuming app:
treeForApp(tree) {
tree = this.filterComponents(tree);
return this._super.treeForApp.call(this, tree);
},
filterComponents(tree) {
let whitelist = this.generateWhitelist(this.bootstrapOptions.whitelist);
let blacklist = this.bootstrapOptions.blacklist || [];
// exit early if no opts defined
if (whitelist.length === 0 && blacklist.length === 0) {
return tree;
}
return new Funnel(tree, {
exclude: [(name) => this.excludeComponent(name, whitelist, blacklist)]
});
}
where this.excludeComponent at it's core is a boolean returning filter function that returns true if the blacklist contains it in the blacklist case (there for excluding it). The treeForApp function returns the tree for all app files, ie what will be merged from the addon's app dir into the consuming app:
The consuming app's ember-cli-build would look something like this:
//your-bootstrap-app/ember-cli-build.js
module.exports = function(defaults) {
let app = new EmberApp(defaults, {
'ember-bootstrap': {
blacklist: ['bs-popover', 'bs-accordion']
}
});
return app.toTree();
};
and the result would be no bs-popover and no bs-accordion available in the consuming apps app tree. These options are obtained in the index.js file like so:
let options =Object.assign({}, defaultOptions, app.options['ember-bootstrap']);
this.bootstrapOptions = options;
Check this general guide to building addons and the more advanced api for more info.

Ember.js - get value of `--proxy` parameter passed into `ember server`

I need my Ember app to make an AJAX request to the API backend it is proxying to. I have some kludgy code that inserts one of several hard-coded values but I'd much rather the AJAX request just grabs the value passed into --proxy when I run ember server.
Does anyone know if there's a way to retrieve this value from within Ember?
It appears it is not exposed, at least not by any public API. If you don't mind a bit of duplication at the command line, this would work:
$ proxy=http://myproxy ember server --proxy http://myproxy
The env params need to come before ember server or else ember throws them away.
// app/config/environment.js
const proxy = process.env.proxy;
const ENV = {
APP: {
// Here you can pass flags/options to your application instance
// when it is created
proxy,
},
};
// app/routes/my-route.js (or wherever)
import ENV from 'myAppName/config/environment';
export default Route.extend({
model() {
return ENV.APP.proxy;
},
});
You can of course access process.env.proxy from anywhere, but this is cleaner and keeps the property where it belongs.
You can get it in the file config/environment.js.
const proxy = (process.argv.indexOf('--proxy') != -1) ? process.argv[process.argv.indexOf('--proxy') + 1] : '';

ember build is not setting the correct rootUrl

Messing around with deploying, and I'm going to need to put the app outside the root url of the server. Based on this answer all I need to do is change the environment.js file to look like this.
module.exports = function(environment) {
var ENV = {
modulePrefix: 'ember-drupal',
environment: environment,
rootURL: '/',
locationType: 'auto'
};
if (environment === 'production') {
ENV.rootUrl = '/myApp/';
ENV.locationType = 'hash';
}
return ENV;
};
So when I run
ember build --environment=production
I expect it to set the rootUrl to be /myApp/, yet when I load up localhost/myApp/ it gives me 404 saying that it's still looking for /assets/ instead of /myApp/assets.
Two interesting notes.
If I change the default rootUrl to /myApp/, it works.
The source code has a meta tag called "ember-drupal/config/environment". The content of the meta tag is json of my environment variables.
The 'ember build' command spits out this:
{
"modulePrefix":"ember-drupal",
"environment":"development",
"rootURL":"/",
"locationType":"auto",
"exportApplicationGlobal":true
}
And the 'ember build --environment=production' spits out this:
{
"modulePrefix":"ember-drupal",
"environment":"production",
"rootURL":"/",
"locationType":"hash",
"rootUrl":"/myApp/",
"exportApplicationGlobal":false
}
So it's setting the locationType correctly, but setting the rootUrl twice.
A freaking capitalization error..... rootUrl vs rootURL. It took me all the way of typing this out to find it.

Global variable in Jest Unit Test

I am new to Jest and trying to write some unit tests for my existing React App. I have a global variable window.CONFIG which stores some configurations which are used at different places in app. This variable is initialised in a script tag of landing HTML page
Now I am trying to write a test of an helper function which depends on this window.CONFIG and it is always undefined when accesses
Here is the code:
config.js
export default window.CONFIG;
app/helper.js
import config from "../config";
export default {
getCompanyURL(company) {
return config.baseUrl + "/companies/" + company.id;
},
}
_ tests _/helpers-test.js
jest.dontMock('../app/helpers.js');
var helper = require('../app/helpers.js').default;
describe('Get company URL', function() {
it('returns company url with company id appended', function() {
expect(companies.getCompanyURL({id: 1})).toBe('test_base_url/companies/1');
});
});
config for Get Company Url is always undefined. As the browser landing page is not loaded window.CONFIG is not initialised. How can I mock this config module in my unit test in Jest?
Thanks in advance!!
I'm not sure if this helps you or not but you can put global variables directly into your package.json
"jest":{
"globals": {
"config": {"foo":"bar"}
}
}
In a recent project (using create-react-app, jest, react testing library and TypeScript), I was using the variable globalThis._env_.
All my attempts to override it in the files containing the tests failed, but adding it to setupTests.ts worked in the end:
// in setuTests.ts
globalThis._env_ = {
UPLOAD_FILE_SIZE_LIMIT_MB: '5'
};
(I think setupTests.js / setupTests.ts is a react testing library thing - https://create-react-app.dev/docs/running-tests/#react-testing-library )
I solved this by creating a manual mock of config.js in __mocks__ directory as follows:
let configMock = jest.genMockFromModule('../config');
let __mockConfig = {};
__mockConfig.baseUrl = "test_base_url/";
configMock = __mockConfig;
export default configMock;

ember cli multiple index files

I have an ember-cli based app which needs to be integrated into an existing java/JSP app. For this to happen I need to generate a JSP file with js/css fingerprinted URLs which are generated by ember-cli/broccoli-asset-rev.
This is working fine for a html file and I can set it use a JSP file by changing my Brocfile.js to include:
var app = new EmberApp({
outputPaths: {
app : {
html: 'index.jsp'
}
}
});
but this prevents ember serve working as it uses the index.jsp as the html file. Is it possible to have both generated?
After trying many things I have come up with two solutions, both have drawbacks. The first is to use make a new broccoli tree and merge it with he app tree then explicity run broccoli-asset-rev on the resulting tree. The downside of this is that the mustache does not get hydrated, this is useful for outputting config. This would look something like:
//Brocfile.js
var mergeTrees = require('broccoli-merge-trees');
var funnel = require('broccoli-funnel');
var assetRev = require('broccoli-asset-rev');
var EmberApp = require('ember-cli/lib/broccoli/ember-app');
var jspTree;
var app = new EmberApp({
fingerprint: {
enabled: false
},
storeConfigInMeta: false
});
jspTree = funnel('app', {
files: ['index.jsp']
});
module.exports = assetRev(mergeTrees([appTree = app.toTree(), jspTree]), {
extensions: ['js', 'css'],
replaceExtensions: ['jsp', 'html']
});
The other solution is the override a private api method in ember-cli which builds the tree for the index. This solution does let the mustache get hydrated but relies on a private method. You can find details here and here
How about adding symbolic link?
ln -s index.jsp index.html
Depending on what build tool you're using in your project, I'd probably recommend something like the following:
Put some placeholder sections in your index.html.
Copy index.jsp to index.jsp.tmp.
Copy in code from index.jsp into your placeholder sections.
Move index.jsp.tmp back to index.jsp and clean up.
You might consider something like gulp-replace to do the work.