Merge pull request #2814 from lennerd/issue-2601

Refactore slug API for generating tag and post slugs
This commit is contained in:
Hannah Wolfe 2014-05-27 10:21:39 +03:00
commit 97011e5eca
8 changed files with 142 additions and 35 deletions

View File

@ -13,13 +13,14 @@ var _ = require('lodash'),
tags = require('./tags'),
themes = require('./themes'),
users = require('./users'),
slugs = require('./slugs'),
http,
formatHttpErrors,
cacheInvalidationHeader,
locationHeader,
contentDispositionHeader,
init,
init;
/**
* ### Init
@ -247,5 +248,6 @@ module.exports = {
settings: settings,
tags: tags,
themes: themes,
users: users
users: users,
slugs: slugs
};

View File

@ -168,26 +168,6 @@ posts = {
}, function () {
return when.reject(new errors.NoPermissionError('You do not have permission to remove posts.'));
});
},
/**
* ## Generate Slug
* Create a unique slug for a given post title
* @param {{title (required), transacting}} options
* @returns {Promise(String)} Unique string
*/
generateSlug: function generateSlug(options) {
return canThis(options.context).slug.post().then(function () {
return dataProvider.Base.Model.generateSlug(dataProvider.Post, options.title, {status: 'all'})
.then(function (slug) {
if (slug) {
return slug;
}
return when.reject(new errors.InternalServerError('Could not generate slug'));
});
}, function () {
return when.reject(new errors.NoPermissionError('You do not have permission.'));
});
}
};

46
core/server/api/slugs.js Normal file
View File

@ -0,0 +1,46 @@
var canThis = require('../permissions').canThis,
dataProvider = require('../models'),
errors = require('../errors'),
when = require('when'),
slugs,
// `allowedTypes` is used to define allowed slug types and map them against it's model class counterpart
allowedTypes = {
post: dataProvider.Post,
tag: dataProvider.Tag
};
/**
* ## Generate Slug
* Create a unique slug for a given post title
* @param {{type (required), context}} options
* @param {{title (required), transacting}} options
* @returns {Promise(String)} Unique string
*/
slugs = {
// #### Generate slug
// **takes:** a string to generate the slug from
generate: function (options) {
options = options || {};
return canThis(options.context).generate.slug().then(function () {
if (allowedTypes[options.type] === undefined) {
return when.reject(new errors.BadRequestError('Unknown slug type \'' + options.type + '\'.'));
}
return dataProvider.Base.Model.generateSlug(allowedTypes[options.type], options.title, {status: 'all'}).then(function (slug) {
if (!slug) {
return when.reject(new errors.InternalServerError('Could not generate slug.'));
}
return { slugs: [{ slug: slug }] };
});
}, function () {
return when.reject(new errors.NoPermissionError('You do not have permission to generate a slug.'));
});
}
};
module.exports = slugs;

View File

@ -71,9 +71,14 @@ var fixtures = {
permissions003: [
{
"name": "Get slug",
"action_type": "slug",
"object_type": "post"
"name": "Generate post slug",
"action_type": "generate",
"object_type": "slug"
},
{
"name": "Generate tag slug",
"action_type": "generate",
"object_type": "slug"
},
{
"name": "Export database",
@ -185,7 +190,7 @@ populateFixtures = function () {
Role.forge({name: 'Editor'}).fetch({withRelated: ['permissions']}).then(function (role) {
Permissions.forge().fetch().then(function (perms) {
var editor_perm = _.map(perms.toJSON(), function (perm) {
if (perm.object_type === 'post' || perm.object_type === 'user') {
if (perm.object_type === 'post' || perm.object_type === 'user' || perm.object_type === 'slug') {
return perm.id;
}
if (perm.object_type === 'setting' &&
@ -198,12 +203,14 @@ populateFixtures = function () {
});
});
// author gets access to post.add, post.slug, settings.browse, settings.read, users.browse and users.read
// author gets access to post.add, slug.generate, settings.browse, settings.read, users.browse and users.read
Role.forge({name: 'Author'}).fetch({withRelated: ['permissions']}).then(function (role) {
Permissions.forge().fetch().then(function (perms) {
var author_perm = _.map(perms.toJSON(), function (perm) {
if (perm.object_type === 'post' &&
(perm.action_type === 'add' || perm.action_type === 'slug')) {
if (perm.object_type === 'post' && perm.action_type === 'add') {
return perm.id;
}
if (perm.object_type === 'slug' && perm.action_type === 'generate') {
return perm.id;
}
if (perm.object_type === 'setting' &&
@ -272,8 +279,10 @@ updateFixtures = function () {
Role.forge({name: 'Author'}).fetch({withRelated: ['permissions']}).then(function (role) {
Permissions.forge().fetch().then(function (perms) {
var author_perm = _.map(perms.toJSON(), function (perm) {
if (perm.object_type === 'post' &&
(perm.action_type === 'add' || perm.action_type === 'slug')) {
if (perm.object_type === 'post' && perm.action_type === 'add') {
return perm.id;
}
if (perm.object_type === 'slug' && perm.action_type === 'generate') {
return perm.id;
}
if (perm.object_type === 'setting' &&

View File

@ -38,6 +38,8 @@ apiRoutes = function (server) {
// ## Mail
server.post('/ghost/api/v0.1/mail', api.http(api.mail.send));
server.post('/ghost/api/v0.1/mail/test', api.http(api.mail.sendTest));
// #### Slugs
server.get('/ghost/api/v0.1/slugs/:type/:title', api.http(api.slugs.generate));
};
module.exports = apiRoutes;
module.exports = apiRoutes;

View File

@ -5,8 +5,8 @@ var testUtils = require('../../utils'),
// Stuff we are testing
permissions = require('../../../server/permissions'),
DataGenerator = require('../../utils/fixtures/data-generator'),
dbAPI = require('../../../server/api/db');
TagsAPI = require('../../../server/api/tags');
dbAPI = require('../../../server/api/db'),
TagsAPI = require('../../../server/api/tags'),
PostAPI = require('../../../server/api/posts');
describe('DB API', function () {

View File

@ -0,0 +1,66 @@
var testUtils = require('../../utils'),
should = require('should'),
permissions = require('../../../server/permissions'),
slugAPI = require('../../../server/api/slugs');
describe('Slug API', function () {
before(function (done) {
testUtils.clearData().then(function () {
done();
}).catch(done);
});
beforeEach(function (done) {
testUtils.initData().then(function () {
return testUtils.insertDefaultFixtures();
}).then(function () {
done();
}).catch(done);
});
afterEach(function (done) {
testUtils.clearData().then(function () {
done();
}).catch(done);
});
it('can generate post slug', function (done) {
permissions.init().then(function () {
return slugAPI.generate({ context: {user: 1}, type: 'post', title: 'A fancy Title'});
}).then(function (results) {
should.exist(results);
testUtils.API.checkResponse(results, 'slugs');
results.slugs.length.should.be.above(0);
testUtils.API.checkResponse(results.slugs[0], 'slug');
results.slugs[0].slug.should.equal('a-fancy-title');
done();
}).catch(done);
});
it('can generate tag slug', function (done) {
permissions.init().then(function () {
return slugAPI.generate({ context: {user: 1}, type: 'tag', title: 'A fancy Title'});
}).then(function (results) {
should.exist(results);
testUtils.API.checkResponse(results, 'slugs');
results.slugs.length.should.be.above(0);
testUtils.API.checkResponse(results.slugs[0], 'slug');
results.slugs[0].slug.should.equal('a-fancy-title');
done();
}).catch(done);
});
it('reject unknown type', function (done) {
permissions.init().then(function () {
return slugAPI.generate({ context: {user: 1}, type: 'unknown type', title: 'A fancy Title'});
}).then(function () {
done(new Error('Generate a slug for a unknown type is not rejected.'));
}, function (error) {
error.type.should.eql('BadRequestError');
done();
});
});
});

View File

@ -18,7 +18,9 @@ var url = require('url'),
user: ['id', 'uuid', 'name', 'slug', 'email', 'image', 'cover', 'bio', 'website',
'location', 'accessibility', 'status', 'language', 'meta_title', 'meta_description', 'last_login',
'created_at', 'created_by', 'updated_at', 'updated_by'],
notification: ['type', 'message', 'status', 'id', 'dismissable', 'location']
notification: ['type', 'message', 'status', 'id', 'dismissable', 'location'],
slugs: ['slugs'],
slug: ['slug']
};
function getApiQuery(route) {