Refactored url utility to generate multiple API version URLs (#9897)

refs #9866

- added api version config to overrides, which makes it possible to have a centralized api versioning configuration
- the next PR will use this config in the web folder
- make api url generation in url service flexible and dynamic
- remove hardcoded API_PATH
- updated all places which used `urlFor('api'..)` -> we now ask for explicit api version
This commit is contained in:
Rishabh Garg 2018-09-25 01:19:21 +05:30 committed by Katharina Irrgang
parent 39485d17c0
commit 9624c88f3e
6 changed files with 68 additions and 9 deletions

View File

@ -124,7 +124,7 @@ cacheInvalidationHeader = (req, result) => {
* @return {String} Resolves to header string
*/
locationHeader = (req, result) => {
const apiRoot = urlService.utils.urlFor('api');
const apiRoot = urlService.utils.urlFor('api', {version: 'stable'});
let location,
newObject,
statusQuery;

View File

@ -64,5 +64,18 @@
},
"maintenance": {
"enabled": false
},
"api": {
"versions": {
"active": {
"admin": "v2/admin",
"content": "v2/content"
},
"stable": {
"admin": "v0.1",
"content": "v0.1"
},
"deprecated": {}
}
}
}

View File

@ -44,7 +44,7 @@ function initialiseServices() {
scheduling.init({
schedulerUrl: config.get('scheduling').schedulerUrl,
active: config.get('scheduling').active,
apiUrl: urlService.utils.urlFor('api', true),
apiUrl: urlService.utils.urlFor('api', {version: 'stable'}, true),
internalPath: config.get('paths').internalSchedulingPath,
contentPath: config.getContentPath('scheduling')
})

View File

@ -6,10 +6,21 @@ const moment = require('moment-timezone'),
cheerio = require('cheerio'),
config = require('../../config'),
settingsCache = require('../settings/cache'),
// @TODO: unify this with the path in server/app.js
API_PATH = '/ghost/api/v0.1/',
BASE_API_PATH = '/ghost/api/',
STATIC_IMAGE_URL_PREFIX = 'content/images';
/**
* Returns API path combining base path and path for specific version asked or stable by default
* @param {string} version version for which to get the path(stable, actice, deprecated: content, admin), defaults to stable:content
* @return {string} API Path for version
*/
function getApiPath(version = 'stable', admin = false) {
const apiVersions = config.get('api:versions');
let versionType = apiVersions[version] || apiVersions.stable;
let versionPath = admin ? versionType.admin : versionType.content;
return `${BASE_API_PATH}${versionPath}/`;
}
/**
* Returns the base URL of the blog as set in the config.
*
@ -238,7 +249,6 @@ function urlFor(context, data, absolute) {
// this will become really big
knownPaths = {
home: '/',
api: API_PATH,
sitemap_xsl: '/sitemap.xsl'
};
@ -303,7 +313,7 @@ function urlFor(context, data, absolute) {
}
} else if (context === 'api') {
urlPath = getAdminUrl() || getBlogUrl();
let apiPath = getApiPath('stable');
// CASE: with or without protocol? If your blog url (or admin url) is configured to http, it's still possible that e.g. nginx allows both https+http.
// So it depends how you serve your blog. The main focus here is to avoid cors problems.
// @TODO: rename cors
@ -313,10 +323,14 @@ function urlFor(context, data, absolute) {
}
}
if (data && data.version) {
apiPath = getApiPath(data.version, data.admin);
}
if (absolute) {
urlPath = urlPath.replace(/\/$/, '') + API_PATH;
urlPath = urlPath.replace(/\/$/, '') + apiPath;
} else {
urlPath = API_PATH;
urlPath = apiPath;
}
} else if (_.isString(context) && _.indexOf(_.keys(knownPaths), context) !== -1) {
// trying to create a url for a named path

View File

@ -26,7 +26,7 @@ function servePublicFile(file, type, maxAge) {
if (type === 'text/xsl' || type === 'text/plain' || type === 'application/javascript') {
buf = buf.toString().replace(blogRegex, urlService.utils.urlFor('home', true).replace(/\/$/, ''));
buf = buf.toString().replace(apiRegex, urlService.utils.urlFor('api', {cors: true}, true));
buf = buf.toString().replace(apiRegex, urlService.utils.urlFor('api', {cors: true, version: 'stable'}, true));
}
content = {
headers: {

View File

@ -522,6 +522,38 @@ describe('Url', function () {
urlService.utils.urlFor('api', {cors: true}, true).should.eql('https://my-ghost-blog.com/ghost/api/v0.1/');
});
it('api: with stable version, blog url is https: should return stable content api path', function () {
configUtils.set({
url: 'https://my-ghost-blog.com'
});
urlService.utils.urlFor('api', {cors: true, version: "stable"}, true).should.eql('https://my-ghost-blog.com/ghost/api/v0.1/');
});
it('api: with stable version and admin true, blog url is https: should return stable admin api path', function () {
configUtils.set({
url: 'https://my-ghost-blog.com'
});
urlService.utils.urlFor('api', {cors: true, version: "stable", admin: true}, true).should.eql('https://my-ghost-blog.com/ghost/api/v0.1/');
});
it('api: with active version, blog url is https: should return active content api path', function () {
configUtils.set({
url: 'https://my-ghost-blog.com'
});
urlService.utils.urlFor('api', {cors: true, version: "active"}, true).should.eql('https://my-ghost-blog.com/ghost/api/v2/content/');
});
it('api: with active version and admin true, blog url is https: should return active admin api path', function () {
configUtils.set({
url: 'https://my-ghost-blog.com'
});
urlService.utils.urlFor('api', {cors: true, version: "active", admin: true}, true).should.eql('https://my-ghost-blog.com/ghost/api/v2/admin/');
});
});
describe('replacePermalink', function () {