convert feature controller to service

closes #6170
- add gh-feature-flag component to create a checkbox (reduce duplicate code)
This commit is contained in:
Austin Burdine 2016-01-19 08:25:36 -06:00
parent 7a36fd597f
commit 1e3adef145
7 changed files with 373 additions and 37 deletions

View File

@ -0,0 +1,48 @@
import Ember from 'ember';
const {
computed,
inject: {service},
Component
} = Ember;
const FeatureFlagComponent = Component.extend({
tagName: 'label',
classNames: 'checkbox',
attributeBindings: ['for'],
_flagValue: null,
feature: service(),
isVisible: computed.notEmpty('_flagValue'),
init() {
this._super(...arguments);
this.get(`feature.${this.get('flag')}`).then((flagValue) => {
this.set('_flagValue', flagValue);
});
},
value: computed('_flagValue', {
get() {
return this.get('_flagValue');
},
set(key, value) {
return this.set(`feature.${this.get('flag')}`, value);
}
}),
for: computed('flag', function () {
return `labs-${this.get('flag')}`;
}),
name: computed('flag', function () {
return `labs[${this.get('flag')}]`;
})
});
FeatureFlagComponent.reopenClass({
positionalParams: ['flag']
});
export default FeatureFlagComponent;

View File

@ -3,8 +3,7 @@ import Ember from 'ember';
const {
$,
Controller,
computed,
inject: {service, controller},
inject: {service},
isArray
} = Ember;
@ -17,36 +16,6 @@ export default Controller.extend({
ghostPaths: service(),
notifications: service(),
session: service(),
feature: controller(),
ajax: service(),
labsJSON: computed('model.labs', function () {
return JSON.parse(this.get('model.labs') || {});
}),
saveLabs(optionName, optionValue) {
let labsJSON = this.get('labsJSON');
// Set new value in the JSON object
labsJSON[optionName] = optionValue;
this.set('model.labs', JSON.stringify(labsJSON));
this.get('model').save().catch((errors) => {
this.showErrors(errors);
this.get('model').rollbackAttributes();
});
},
usePublicAPI: computed('feature.publicAPI', {
get() {
return this.get('feature.publicAPI');
},
set(key, value) {
this.saveLabs('publicAPI', value);
return value;
}
}),
actions: {
onUpload(file) {

View File

@ -0,0 +1,85 @@
import Ember from 'ember';
const {
Service,
computed,
inject: {service},
RSVP: {Promise},
set
} = Ember;
export function feature(name) {
return computed(`config.${name}`, `labs.${name}`, {
get() {
return new Promise((resolve) => {
if (this.get(`config.${name}`)) {
return resolve(this.get(`config.${name}`));
}
this.get('labs').then((labs) => {
resolve(labs[name] || false);
});
});
},
set(key, value) {
this.update(key, value).then((savedValue) => {
return savedValue;
});
}
});
}
export default Service.extend({
store: service(),
config: service(),
notifications: service(),
_settings: null,
publicAPI: feature('publicAPI'),
_parseLabs(settings) {
let labs = settings.get('labs');
try {
return JSON.parse(labs) || {};
} catch (e) {
return {};
}
},
labs: computed(function () {
return new Promise((resolve, reject) => {
if (this.get('_settings')) { // So we don't query the backend every single time
resolve(this._parseLabs(this.get('_settings')));
}
let store = this.get('store');
store.query('setting', {type: 'blog'}).then((settings) => {
let setting = settings.get('firstObject');
this.set('_settings', setting);
resolve(this._parseLabs(setting));
}).catch(reject);
});
}),
update(key, value) {
return new Promise((resolve, reject) => {
this.get('labs').then((labs) => {
let settings = this.get('_settings');
set(labs, key, value);
settings.set('labs', JSON.stringify(labs));
settings.save().then(() => {
resolve(labs[key]);
}).catch((errors) => {
this.get('notifications').showErrors(errors);
settings.rollbackAttributes();
resolve(this._parseLabs(settings)[key]);
});
}).catch(reject);
});
}
});

View File

@ -0,0 +1,3 @@
{{input id=for name=name type="checkbox" checked=value}}
<span class="input-toggle-component"></span>
<p>{{{yield}}}</p>

View File

@ -47,11 +47,9 @@
<fieldset>
<div class="form-group for-checkbox">
<h3>Enable Beta Features</h3>
<label class="checkbox" for="labs-publicAPI">
{{input id="labs-publicAPI" name="labs[publicAPI]" type="checkbox" checked=usePublicAPI}}
<span class="input-toggle-component"></span>
<p>Public API - For full instructions, read the <a href="http://support.ghost.org/public-api-beta/">developer guide</a>.</p>
</label>
{{#gh-feature-flag "publicAPI"}}
Public API - For full instructions, read the <a href="http://support.ghost.org/public-api-beta/">developer guide</a>.
{{/gh-feature-flag}}
</div>
</fieldset>
</form>

View File

@ -0,0 +1,100 @@
import { expect } from 'chai';
import {
describeComponent,
it
} from 'ember-mocha';
import hbs from 'htmlbars-inline-precompile';
import FeatureService, {feature} from 'ghost/services/feature';
import Pretender from 'pretender';
import wait from 'ember-test-helpers/wait';
function stubSettings(server, labs) {
server.get('/ghost/api/v0.1/settings/', function () {
return [200, {'Content-Type': 'application/json'}, JSON.stringify({settings: [{
id: '1',
type: 'blog',
key: 'labs',
value: JSON.stringify(labs)
}]})];
});
server.put('/ghost/api/v0.1/settings/', function (request) {
return [200, {'Content-Type': 'application/json'}, request.requestBody];
});
}
function addTestFlag() {
FeatureService.reopen({
testFlag: feature('testFlag')
});
}
describeComponent(
'gh-feature-flag',
'Integration: Component: gh-feature-flag',
{
integration: true
},
function() {
let server;
beforeEach(function () {
server = new Pretender();
});
afterEach(function () {
server.shutdown();
});
it('renders properties correctly', function () {
stubSettings(server, {testFlag: true});
addTestFlag();
this.render(hbs`{{gh-feature-flag "testFlag"}}`);
expect(this.$()).to.have.length(1);
expect(this.$('label').attr('for')).to.equal(this.$('input[type="checkbox"]').attr('id'));
});
it('renders correctly when flag is set to true', function () {
stubSettings(server, {testFlag: true});
addTestFlag();
this.render(hbs`{{gh-feature-flag "testFlag"}}`);
expect(this.$()).to.have.length(1);
return wait().then(() => {
expect(this.$('label input[type="checkbox"]').prop('checked')).to.be.true;
});
});
it('renders correctly when flag is set to false', function () {
stubSettings(server, {testFlag: false});
addTestFlag();
this.render(hbs`{{gh-feature-flag "testFlag"}}`);
expect(this.$()).to.have.length(1);
return wait().then(() => {
expect(this.$('label input[type="checkbox"]').prop('checked')).to.be.false;
});
});
it('updates to reflect changes in flag property', function () {
stubSettings(server, {testFlag: true});
addTestFlag();
this.render(hbs`{{gh-feature-flag "testFlag"}}`);
expect(this.$()).to.have.length(1);
return wait().then(() => {
expect(this.$('label input[type="checkbox"]').prop('checked')).to.be.true;
this.$('label').click();
return wait();
}).then(() => {
expect(this.$('label input[type="checkbox"]').prop('checked')).to.be.false;
});
});
}
);

View File

@ -0,0 +1,133 @@
import {
describeModule,
it
} from 'ember-mocha';
import Pretender from 'pretender';
import wait from 'ember-test-helpers/wait';
import FeatureService, {feature} from 'ghost/services/feature';
function stubSettings(server, labs) {
server.get('/ghost/api/v0.1/settings/', function () {
return [200, {'Content-Type': 'application/json'}, JSON.stringify({settings: [{
id: '1',
type: 'blog',
key: 'labs',
value: JSON.stringify(labs)
}]})];
});
server.put('/ghost/api/v0.1/settings/', function (request) {
return [200, {'Content-Type': 'application/json'}, request.requestBody];
});
}
function addTestFlag() {
FeatureService.reopen({
testFlag: feature('testFlag')
});
}
describeModule(
'service:feature',
'Integration: Service: feature',
{
integration: true
},
function () {
let server;
beforeEach(function () {
server = new Pretender();
});
afterEach(function () {
server.shutdown();
});
it('loads labs settings correctly', function (done) {
stubSettings(server, {testFlag: true});
let service = this.subject();
return wait().then(() => {
expect(service.get('labs').testFlag).to.be.true;
done();
});
});
it('returns false for set flag with config false and labs false', function (done) {
stubSettings(server, {testFlag: false});
addTestFlag();
let service = this.subject();
service.get('config').set('testFlag', false);
return wait().then(() => {
expect(service.get('labs').testFlag).to.be.false;
expect(service.get('testFlag')).to.be.false;
done();
});
});
it('returns true for set flag with config true and labs false', function (done) {
stubSettings(server, {testFlag: false});
addTestFlag();
let service = this.subject();
service.get('config').set('testFlag', true);
return wait().then(() => {
expect(service.get('labs').testFlag).to.be.false;
expect(service.get('testFlag')).to.be.true;
done();
});
});
it('returns true for set flag with config false and labs true', function (done) {
stubSettings(server, {testFlag: true});
addTestFlag();
let service = this.subject();
service.get('config').set('testFlag', false);
return wait().then(() => {
expect(service.get('labs').testFlag).to.be.true;
expect(service.get('testFlag')).to.be.true;
done();
});
});
it('returns true for set flag with config true and labs true', function (done) {
stubSettings(server, {testFlag: true});
addTestFlag();
let service = this.subject();
service.get('config').set('testFlag', true);
return wait().then(() => {
expect(service.get('labs').testFlag).to.be.true;
expect(service.get('testFlag')).to.be.true;
done();
});
});
it('saves correctly', function (done) {
stubSettings(server, {testFlag: false});
addTestFlag();
let service = this.subject();
return wait().then(() => {
expect(service.get('testFlag')).to.be.false;
service.set('testFlag', true);
return wait().then(() => {
expect(server.handlers[1].numberOfCalls).to.equal(1);
expect(service.get('testFlag')).to.be.true;
done();
});
});
});
}
);