From 21da7f6b50c97085d9a0093c005cdefcc0e96386 Mon Sep 17 00:00:00 2001 From: Cathy Sarisky <42299862+cathysarisky@users.noreply.github.com> Date: Fri, 25 Oct 2024 05:37:56 -0400 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Added=20`content=5Fapi=5Furl`=20hel?= =?UTF-8?q?per=20(#21331)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds a `content_api_url` helper, returning the url for Ghost's Content API. By default it will return an absolute URL but can be passed `absolute=false` if a relative URL is wanted. This works in tandem with the `content_api_key` helper to facilitate third party integrations with the Content API, for example - custom Portal or Search implementations. --- .../core/frontend/helpers/content_api_url.js | 28 +++++ .../frontend/helpers/content_api_url.test.js | 109 ++++++++++++++++++ .../theme-engine/handlebars/helpers.test.js | 2 +- 3 files changed, 138 insertions(+), 1 deletion(-) create mode 100644 ghost/core/core/frontend/helpers/content_api_url.js create mode 100644 ghost/core/test/unit/frontend/helpers/content_api_url.test.js diff --git a/ghost/core/core/frontend/helpers/content_api_url.js b/ghost/core/core/frontend/helpers/content_api_url.js new file mode 100644 index 0000000000..8cef0ab0da --- /dev/null +++ b/ghost/core/core/frontend/helpers/content_api_url.js @@ -0,0 +1,28 @@ +const {SafeString} = require('../services/handlebars'); +const logging = require('@tryghost/logging'); +const {urlUtils} = require('../services/proxy'); + +module.exports = function content_api_url(options) { // eslint-disable-line camelcase + let result; + const absoluteUrlRequested = getAbsoluteOption(options); + + try { + let path = urlUtils.urlFor('api', {type: 'content'}, absoluteUrlRequested); + result = new SafeString(path); + } catch (error) { + logging.error(error); + result = ''; + } + + return result; +}; + +function getAbsoluteOption(options) { + const absoluteOption = options && options.hash && options.hash.absolute; + if (absoluteOption === undefined || absoluteOption === 'true' || absoluteOption === true || absoluteOption === null) { + return true; + } else { + return false; + } +} + diff --git a/ghost/core/test/unit/frontend/helpers/content_api_url.test.js b/ghost/core/test/unit/frontend/helpers/content_api_url.test.js new file mode 100644 index 0000000000..62c820779b --- /dev/null +++ b/ghost/core/test/unit/frontend/helpers/content_api_url.test.js @@ -0,0 +1,109 @@ +/* eslint-disable no-regex-spaces */ +const should = require('should'); +const sinon = require('sinon'); +const configUtils = require('../../../utils/configUtils'); + +// Stuff we are testing +const content_api_url = require('../../../../core/frontend/helpers/content_api_url'); +const logging = require('@tryghost/logging'); + +describe('{{content_api_url}} helper', function () { + let logWarnStub; + + beforeEach(function () { + logWarnStub = sinon.stub(logging, 'warn'); + }); + + afterEach(function () { + sinon.restore(); + }); + + describe('without sub-directory', function () { + before(function () { + configUtils.set({url: 'http://localhost:65535/', 'admin:url': 'https://admin.tld:65535'}); + }); + + after(async function () { + await configUtils.restore(); + }); + + it('should output an absolute url', async function () { + let result = content_api_url(); + const rendered = new String(result); + should.exist(rendered); + rendered.should.equal('https://admin.tld:65535/ghost/api/content/'); + logWarnStub.called.should.be.false(); + }); + it('should output an absolute url when passed true', async function () { + let result = content_api_url(true); + const rendered = new String(result); + should.exist(rendered); + rendered.should.equal('https://admin.tld:65535/ghost/api/content/'); + logWarnStub.called.should.be.false(); + }); + it('should output a relative url when passed false', async function () { + let result = content_api_url(false); + const rendered = new String(result); + should.exist(rendered); + rendered.should.equal('/ghost/api/content/'); + logWarnStub.called.should.be.false(); + }); + }); + describe('with a sub-directory', function () { + before(function () { + configUtils.set({url: 'http://localhost:65535/blog', 'admin:url': 'https://admin.tld:65535/blog'}); + }); + + after(async function () { + await configUtils.restore(); + }); + + it('should output an absolute url', async function () { + let result = content_api_url(); + const rendered = new String(result); + should.exist(rendered); + rendered.should.equal('https://admin.tld:65535/blog/ghost/api/content/'); + logWarnStub.called.should.be.false(); + }); + it('should output an absolute url when passed true', async function () { + let result = content_api_url(true); + const rendered = new String(result); + should.exist(rendered); + rendered.should.equal('https://admin.tld:65535/blog/ghost/api/content/'); + logWarnStub.called.should.be.false(); + }); + it('should output a relative url when passed false', async function () { + let result = content_api_url(false); + const rendered = new String(result); + should.exist(rendered); + rendered.should.equal('/blog/ghost/api/content/'); + logWarnStub.called.should.be.false(); + }); + }); + describe('uses the site url if no admin:url is set', function () { + before(function () { + configUtils.set({url: 'http://localhost:65535/'}); + }); + + after(async function () { + await configUtils.restore(); + }); + + it('gives the site url without a subdirectory', async function () { + let result = content_api_url(); + const rendered = new String(result); + should.exist(rendered); + rendered.should.equal('http://localhost:65535/ghost/api/content/'); + logWarnStub.called.should.be.false(); + }); + it('gives the site url with a subdirectory', async function () { + configUtils.set({url: 'http://localhost:65535/blog', 'admin:url': undefined}); + let result = content_api_url(); + const rendered = new String(result); + should.exist(rendered); + rendered.should.equal('http://localhost:65535/blog/ghost/api/content/'); + logWarnStub.called.should.be.false(); + }); + }); +}); + diff --git a/ghost/core/test/unit/frontend/services/theme-engine/handlebars/helpers.test.js b/ghost/core/test/unit/frontend/services/theme-engine/handlebars/helpers.test.js index d0d7be1019..7593076e7c 100644 --- a/ghost/core/test/unit/frontend/services/theme-engine/handlebars/helpers.test.js +++ b/ghost/core/test/unit/frontend/services/theme-engine/handlebars/helpers.test.js @@ -8,7 +8,7 @@ const helpers = require('../../../../../../core/frontend/services/helpers'); describe('Helpers', function () { const hbsHelpers = ['each', 'if', 'unless', 'with', 'helperMissing', 'blockHelperMissing', 'log', 'lookup', 'block', 'contentFor']; const ghostHelpers = [ - 'asset', 'authors', 'body_class', 'cancel_link', 'concat', 'content', 'content_api_key', 'date', 'encode', 'excerpt', 'facebook_url', 'foreach', 'get', + 'asset', 'authors', 'body_class', 'cancel_link', 'concat', 'content', 'content_api_key', 'content_api_url', 'date', 'encode', 'excerpt', 'facebook_url', 'foreach', 'get', 'ghost_foot', 'ghost_head', 'has', 'img_url', 'is', 'link', 'link_class', 'meta_description', 'meta_title', 'navigation', 'next_post', 'page_url', 'pagination', 'plural', 'post_class', 'prev_post', 'price', 'raw', 'reading_time', 't', 'tags', 'title','total_members', 'total_paid_members', 'twitter_url', 'url', 'comment_count', 'collection', 'recommendations', 'readable_url'