mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-11-24 06:35:49 +03:00
Merge pull request #2877 from jaswilli/2866-slugs
Update slug API to work with additional types
This commit is contained in:
commit
42af74b073
@ -9,7 +9,9 @@ var canThis = require('../permissions').canThis,
|
||||
// `allowedTypes` is used to define allowed slug types and map them against its model class counterpart
|
||||
allowedTypes = {
|
||||
post: dataProvider.Post,
|
||||
tag: dataProvider.Tag
|
||||
tag: dataProvider.Tag,
|
||||
user: dataProvider.User,
|
||||
app: dataProvider.App
|
||||
};
|
||||
|
||||
/**
|
||||
@ -21,10 +23,9 @@ slugs = {
|
||||
|
||||
/**
|
||||
* ## Generate Slug
|
||||
* Create a unique slug for a given post title
|
||||
* TODO: make it generic for all objects: post, tag, user
|
||||
* Create a unique slug for the given type and its name
|
||||
*
|
||||
* @param {{type (required), title (required), context, transacting}} options
|
||||
* @param {{type (required), name (required), context, transacting}} options
|
||||
* @returns {Promise(String)} Unique string
|
||||
*/
|
||||
generate: function (options) {
|
||||
@ -35,14 +36,18 @@ slugs = {
|
||||
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) {
|
||||
return dataProvider.Base.Model.generateSlug(allowedTypes[options.type], options.name, {status: 'all'}).then(function (slug) {
|
||||
if (!slug) {
|
||||
return when.reject(new errors.InternalServerError('Could not generate slug.'));
|
||||
}
|
||||
|
||||
return { slugs: [{ slug: slug }] };
|
||||
});
|
||||
}, function () {
|
||||
}).catch(function (err) {
|
||||
if (err) {
|
||||
return when.reject(err);
|
||||
}
|
||||
|
||||
return when.reject(new errors.NoPermissionError('You do not have permission to generate a slug.'));
|
||||
});
|
||||
}
|
||||
|
@ -288,7 +288,7 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({
|
||||
var slug,
|
||||
slugTryCount = 1,
|
||||
baseName = Model.prototype.tableName.replace(/s$/, ''),
|
||||
// Look for a post with a matching slug, append an incrementing number if so
|
||||
// Look for a matching slug, append an incrementing number if so
|
||||
checkIfSlugExists;
|
||||
|
||||
checkIfSlugExists = function (slugToFind) {
|
||||
@ -339,10 +339,10 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({
|
||||
slug = slug.charAt(slug.length - 1) === '-' ? slug.substr(0, slug.length - 1) : slug;
|
||||
|
||||
// Check the filtered slug doesn't match any of the reserved keywords
|
||||
slug = /^(ghost|ghost\-admin|admin|wp\-admin|wp\-login|dashboard|logout|login|signin|signup|signout|register|archive|archives|category|categories|tag|tags|page|pages|post|posts|public|user|users|rss|feed)$/g
|
||||
slug = /^(ghost|ghost\-admin|admin|wp\-admin|wp\-login|dashboard|logout|login|signin|signup|signout|register|archive|archives|category|categories|tag|tags|page|pages|post|posts|public|user|users|rss|feed|app|apps)$/g
|
||||
.test(slug) ? slug + '-' + baseName : slug;
|
||||
|
||||
//if slug is empty after trimming use "post"
|
||||
//if slug is empty after trimming use the model name
|
||||
if (!slug) {
|
||||
slug = baseName;
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ apiRoutes = function (server) {
|
||||
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));
|
||||
server.get('/ghost/api/v0.1/slugs/:type/:name', api.http(api.slugs.generate));
|
||||
};
|
||||
|
||||
module.exports = apiRoutes;
|
||||
|
181
core/test/functional/routes/api/slugs_test.js
Normal file
181
core/test/functional/routes/api/slugs_test.js
Normal file
@ -0,0 +1,181 @@
|
||||
/*global describe, it, before, after */
|
||||
var supertest = require('supertest'),
|
||||
express = require('express'),
|
||||
should = require('should'),
|
||||
_ = require('lodash'),
|
||||
testUtils = require('../../../utils'),
|
||||
|
||||
ghost = require('../../../../../core'),
|
||||
|
||||
httpServer,
|
||||
request,
|
||||
agent;
|
||||
|
||||
|
||||
describe('Slug API', function () {
|
||||
var user = testUtils.DataGenerator.forModel.users[0],
|
||||
csrfToken = '';
|
||||
|
||||
before(function (done) {
|
||||
var app = express();
|
||||
|
||||
ghost({ app: app }).then(function (_httpServer) {
|
||||
httpServer = _httpServer;
|
||||
request = supertest.agent(app);
|
||||
|
||||
testUtils.clearData()
|
||||
.then(function () {
|
||||
return testUtils.initData();
|
||||
})
|
||||
.then(function () {
|
||||
return testUtils.insertDefaultFixtures();
|
||||
})
|
||||
.then(function () {
|
||||
request.get('/ghost/signin/')
|
||||
.expect(200)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
var pattern_meta = /<meta.*?name="csrf-param".*?content="(.*?)".*?>/i;
|
||||
pattern_meta.should.exist;
|
||||
csrfToken = res.text.match(pattern_meta)[1];
|
||||
|
||||
process.nextTick(function() {
|
||||
request.post('/ghost/signin/')
|
||||
.set('X-CSRF-Token', csrfToken)
|
||||
.send({email: user.email, password: user.password})
|
||||
.expect(200)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
|
||||
request.saveCookies(res);
|
||||
request.get('/ghost/')
|
||||
.expect(200)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
csrfToken = res.text.match(pattern_meta)[1];
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
}).catch(done);
|
||||
}).catch(function (e) {
|
||||
console.log('Ghost Error: ', e);
|
||||
console.log(e.stack);
|
||||
});
|
||||
});
|
||||
|
||||
after(function () {
|
||||
httpServer.close();
|
||||
});
|
||||
|
||||
it('should be able to get a post slug', function (done) {
|
||||
request.get(testUtils.API.getApiQuery('slugs/post/a post title/'))
|
||||
.expect(200)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
should.not.exist(res.headers['x-cache-invalidate']);
|
||||
res.should.be.json;
|
||||
var jsonResponse = res.body;
|
||||
jsonResponse.should.exist;
|
||||
jsonResponse.slugs.should.exist;
|
||||
jsonResponse.slugs.should.have.length(1);
|
||||
testUtils.API.checkResponse(jsonResponse.slugs[0], 'slug');
|
||||
jsonResponse.slugs[0].slug.should.equal('a-post-title');
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should be able to get a tag slug', function (done) {
|
||||
request.get(testUtils.API.getApiQuery('slugs/post/atag/'))
|
||||
.expect(200)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
should.not.exist(res.headers['x-cache-invalidate']);
|
||||
res.should.be.json;
|
||||
var jsonResponse = res.body;
|
||||
jsonResponse.should.exist;
|
||||
jsonResponse.slugs.should.exist;
|
||||
jsonResponse.slugs.should.have.length(1);
|
||||
testUtils.API.checkResponse(jsonResponse.slugs[0], 'slug');
|
||||
jsonResponse.slugs[0].slug.should.equal('atag');
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should be able to get a user slug', function (done) {
|
||||
request.get(testUtils.API.getApiQuery('slugs/user/user name/'))
|
||||
.expect(200)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
should.not.exist(res.headers['x-cache-invalidate']);
|
||||
res.should.be.json;
|
||||
var jsonResponse = res.body;
|
||||
jsonResponse.should.exist;
|
||||
jsonResponse.slugs.should.exist;
|
||||
jsonResponse.slugs.should.have.length(1);
|
||||
testUtils.API.checkResponse(jsonResponse.slugs[0], 'slug');
|
||||
jsonResponse.slugs[0].slug.should.equal('user-name');
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should be able to get an app slug', function (done) {
|
||||
request.get(testUtils.API.getApiQuery('slugs/app/cool app/'))
|
||||
.expect(200)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
should.not.exist(res.headers['x-cache-invalidate']);
|
||||
res.should.be.json;
|
||||
var jsonResponse = res.body;
|
||||
jsonResponse.should.exist;
|
||||
jsonResponse.slugs.should.exist;
|
||||
jsonResponse.slugs.should.have.length(1);
|
||||
testUtils.API.checkResponse(jsonResponse.slugs[0], 'slug');
|
||||
jsonResponse.slugs[0].slug.should.equal('cool-app');
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not be able to get a slug for an unknown type', function (done) {
|
||||
request.get(testUtils.API.getApiQuery('slugs/unknown/who knows/'))
|
||||
.expect(400)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
res.should.be.json;
|
||||
var jsonResponse = res.body;
|
||||
jsonResponse.should.not.exist;
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
@ -15,6 +15,8 @@ describe('Slug API', function () {
|
||||
beforeEach(function (done) {
|
||||
testUtils.initData().then(function () {
|
||||
return testUtils.insertDefaultFixtures();
|
||||
}).then(function () {
|
||||
return permissions.init();
|
||||
}).then(function () {
|
||||
done();
|
||||
}).catch(done);
|
||||
@ -27,9 +29,8 @@ describe('Slug API', function () {
|
||||
});
|
||||
|
||||
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) {
|
||||
slugAPI.generate({ context: { user: 1 }, type: 'post', name: 'A fancy Title' })
|
||||
.then(function (results) {
|
||||
should.exist(results);
|
||||
testUtils.API.checkResponse(results, 'slugs');
|
||||
results.slugs.length.should.be.above(0);
|
||||
@ -40,9 +41,8 @@ describe('Slug API', function () {
|
||||
});
|
||||
|
||||
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) {
|
||||
slugAPI.generate({ context: { user: 1 }, type: 'tag', name: 'A fancy Title' })
|
||||
.then(function (results) {
|
||||
should.exist(results);
|
||||
testUtils.API.checkResponse(results, 'slugs');
|
||||
results.slugs.length.should.be.above(0);
|
||||
@ -52,13 +52,36 @@ describe('Slug API', function () {
|
||||
}).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');
|
||||
it('can generate user slug', function (done) {
|
||||
slugAPI.generate({ context: { user: 1 }, type: 'tag', name: 'user name' })
|
||||
.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('user-name');
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('can generate app slug', function (done) {
|
||||
slugAPI.generate({ context: { user: 1 }, type: 'tag', name: 'app name' })
|
||||
.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('app-name');
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
||||
it('rejects unknown types', function (done) {
|
||||
slugAPI.generate({ context: { user: 1 }, type: 'unknown type', name: 'A fancy Title' })
|
||||
.then(function () {
|
||||
done(new Error('Generate a slug for an unknown type is not rejected.'));
|
||||
}).catch(function (error) {
|
||||
error.type.should.equal('BadRequestError');
|
||||
done();
|
||||
}).catch(done);
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user