mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-25 03:44:29 +03:00
convert feature controller to service
closes #6170 - add gh-feature-flag component to create a checkbox (reduce duplicate code)
This commit is contained in:
parent
7a36fd597f
commit
1e3adef145
48
ghost/admin/app/components/gh-feature-flag.js
Normal file
48
ghost/admin/app/components/gh-feature-flag.js
Normal 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;
|
@ -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) {
|
||||
|
85
ghost/admin/app/services/feature.js
Normal file
85
ghost/admin/app/services/feature.js
Normal 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);
|
||||
});
|
||||
}
|
||||
});
|
3
ghost/admin/app/templates/components/gh-feature-flag.hbs
Normal file
3
ghost/admin/app/templates/components/gh-feature-flag.hbs
Normal file
@ -0,0 +1,3 @@
|
||||
{{input id=for name=name type="checkbox" checked=value}}
|
||||
<span class="input-toggle-component"></span>
|
||||
<p>{{{yield}}}</p>
|
@ -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>
|
||||
|
100
ghost/admin/tests/integration/components/gh-feature-flag-test.js
Normal file
100
ghost/admin/tests/integration/components/gh-feature-flag-test.js
Normal 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;
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
133
ghost/admin/tests/integration/services/feature-test.js
Normal file
133
ghost/admin/tests/integration/services/feature-test.js
Normal 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();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
Loading…
Reference in New Issue
Block a user