Switched from v3 to canary API

refs https://github.com/TryGhost/Team/issues/221

- we're getting ready for the 4.0 API version so we should be using canary to fully test the changes
- changed from `v3` to `canary` in `utils/ghost-paths.js`
- updated mirage and tests to use `ghostPaths` util so we only need to change the version in one place in the future
This commit is contained in:
Kevin Ansfield 2021-02-05 09:12:26 +00:00
parent 957f5703ba
commit 1d96f785c3
15 changed files with 58 additions and 46 deletions

View File

@ -18,7 +18,7 @@ export default function () {
let subdir = path.substr(0, path.search('/ghost/'));
let adminRoot = `${subdir}/ghost/`;
let assetRoot = `${subdir}/ghost/assets/`;
let apiRoot = `${subdir}/ghost/api/v3/admin`;
let apiRoot = `${subdir}/ghost/api/canary/admin`;
function assetUrl(src) {
return subdir + src;

View File

@ -1,3 +1,4 @@
import ghostPaths from 'ghost-admin/utils/ghost-paths';
import mockApiKeys from './config/api-keys';
import mockAuthentication from './config/authentication';
import mockConfig from './config/config';
@ -26,8 +27,7 @@ export default function () {
// _must_ be called before the namespace property is set
this.passthrough('/ghost/assets/**');
// this.urlPrefix = ''; // make this `http://localhost:8080`, for example, if your API is on a different server
this.namespace = '/ghost/api/v3/admin'; // make this `api`, for example, if your API is namespaced
this.namespace = ghostPaths().apiRoot;
this.timing = 1000; // delay for each request, automatically set to 0 during testing
this.logging = true;
@ -49,8 +49,7 @@ export default function () {
// Mock all endpoints here as there is no real API during testing
export function testConfig() {
// this.urlPrefix = ''; // make this `http://localhost:8080`, for example, if your API is on a different server
this.namespace = '/ghost/api/v3/admin'; // make this `api`, for example, if your API is namespaced
this.namespace = ghostPaths().apiRoot;
// this.timing = 400; // delay for each request, automatically set to 0 during testing
this.logging = false;

View File

@ -1,4 +1,5 @@
import Pretender from 'pretender';
import ghostPaths from 'ghost-admin/utils/ghost-paths';
import {describe, it} from 'mocha';
import {expect} from 'chai';
import {setupTest} from 'ember-mocha';
@ -18,7 +19,7 @@ describe('Integration: Adapter: tag', function () {
});
it('loads tags from regular endpoint when all are fetched', function (done) {
server.get('/ghost/api/v3/admin/tags/', function () {
server.get(`${ghostPaths().apiRoot}/tags/`, function () {
return [200, {'Content-Type': 'application/json'}, JSON.stringify({tags: [
{
id: 1,
@ -40,7 +41,7 @@ describe('Integration: Adapter: tag', function () {
});
it('loads tag from slug endpoint when single tag is queried and slug is passed in', function (done) {
server.get('/ghost/api/v3/admin/tags/slug/tag-1/', function () {
server.get(`${ghostPaths().apiRoot}/tags/slug/tag-1/`, function () {
return [200, {'Content-Type': 'application/json'}, JSON.stringify({tags: [
{
id: 1,

View File

@ -1,4 +1,5 @@
import Pretender from 'pretender';
import ghostPaths from 'ghost-admin/utils/ghost-paths';
import {describe, it} from 'mocha';
import {expect} from 'chai';
import {setupTest} from 'ember-mocha';
@ -18,7 +19,7 @@ describe('Integration: Adapter: user', function () {
});
it('loads users from regular endpoint when all are fetched', function (done) {
server.get('/ghost/api/v3/admin/users/', function () {
server.get(`${ghostPaths().apiRoot}/users/`, function () {
return [200, {'Content-Type': 'application/json'}, JSON.stringify({users: [
{
id: 1,
@ -40,7 +41,7 @@ describe('Integration: Adapter: user', function () {
});
it('loads user from slug endpoint when single user is queried and slug is passed in', function (done) {
server.get('/ghost/api/v3/admin/users/slug/user-1/', function () {
server.get(`${ghostPaths().apiRoot}/users/slug/user-1/`, function () {
return [200, {'Content-Type': 'application/json'}, JSON.stringify({users: [
{
id: 1,
@ -58,7 +59,7 @@ describe('Integration: Adapter: user', function () {
});
it('handles "include" parameter when querying single user via slug', function (done) {
server.get('/ghost/api/v3/admin/users/slug/user-1/', (request) => {
server.get(`${ghostPaths().apiRoot}/users/slug/user-1/`, (request) => {
let params = request.queryParams;
expect(params.include, 'include query').to.equal('roles,count.posts');

View File

@ -1,6 +1,7 @@
import $ from 'jquery';
import Pretender from 'pretender';
import Service from '@ember/service';
import ghostPaths from 'ghost-admin/utils/ghost-paths';
import hbs from 'htmlbars-inline-precompile';
import sinon from 'sinon';
import {UnsupportedMediaTypeError} from 'ghost-admin/services/ajax';
@ -18,13 +19,13 @@ const notificationsStub = Service.extend({
});
const stubSuccessfulUpload = function (server, delay = 0) {
server.post('/ghost/api/v3/admin/images/', function () {
server.post(`${ghostPaths().apiRoot}/images/`, function () {
return [200, {'Content-Type': 'application/json'}, '{"url":"/content/images/test.png"}'];
}, delay);
};
const stubFailedUpload = function (server, code, error, delay = 0) {
server.post('/ghost/api/v3/admin/images/', function () {
server.post(`${ghostPaths().apiRoot}/images/`, function () {
return [code, {'Content-Type': 'application/json'}, JSON.stringify({
errors: [{
type: error,
@ -41,7 +42,7 @@ describe('Integration: Component: gh-file-uploader', function () {
beforeEach(function () {
server = new Pretender();
this.set('uploadUrl', '/ghost/api/v3/admin/images/');
this.set('uploadUrl', `${ghostPaths().apiRoot}/images/`);
this.owner.register('service:notifications', notificationsStub);
});
@ -86,7 +87,7 @@ describe('Integration: Component: gh-file-uploader', function () {
await fileUpload('input[type="file"]', ['test'], {name: 'test.csv'});
expect(server.handledRequests.length).to.equal(1);
expect(server.handledRequests[0].url).to.equal('/ghost/api/v3/admin/images/');
expect(server.handledRequests[0].url).to.equal(`${ghostPaths().apiRoot}/images/`);
});
it('fires uploadSuccess action on successful upload', async function () {
@ -185,7 +186,7 @@ describe('Integration: Component: gh-file-uploader', function () {
});
it('handles file too large error directly from the web server', async function () {
server.post('/ghost/api/v3/admin/images/', function () {
server.post(`${ghostPaths().apiRoot}/images/`, function () {
return [413, {}, ''];
});
await render(hbs`{{gh-file-uploader url=uploadUrl}}`);
@ -205,7 +206,7 @@ describe('Integration: Component: gh-file-uploader', function () {
});
it('handles unknown failure', async function () {
server.post('/ghost/api/v3/admin/images/', function () {
server.post(`${ghostPaths().apiRoot}/images/`, function () {
return [500, {'Content-Type': 'application/json'}, ''];
});
await render(hbs`{{gh-file-uploader url=uploadUrl}}`);

View File

@ -1,6 +1,7 @@
import $ from 'jquery';
import Pretender from 'pretender';
import Service from '@ember/service';
import ghostPaths from 'ghost-admin/utils/ghost-paths';
import hbs from 'htmlbars-inline-precompile';
import sinon from 'sinon';
import {UnsupportedMediaTypeError} from 'ghost-admin/services/ajax';
@ -29,13 +30,13 @@ const sessionStub = Service.extend({
});
const stubSuccessfulUpload = function (server, delay = 0) {
server.post('/ghost/api/v3/admin/images/upload/', function () {
server.post(`${ghostPaths().apiRoot}/images/upload/`, function () {
return [200, {'Content-Type': 'application/json'}, '{"images": [{"url":"/content/images/test.png"}]}'];
}, delay);
};
const stubFailedUpload = function (server, code, error, delay = 0) {
server.post('/ghost/api/v3/admin/images/upload/', function () {
server.post(`${ghostPaths().apiRoot}/images/upload/`, function () {
return [code, {'Content-Type': 'application/json'}, JSON.stringify({
errors: [{
type: error,
@ -78,7 +79,7 @@ describe('Integration: Component: gh-image-uploader', function () {
await fileUpload('input[type="file"]', ['test'], {name: 'test.png'});
expect(server.handledRequests.length).to.equal(1);
expect(server.handledRequests[0].url).to.equal('/ghost/api/v3/admin/images/upload/');
expect(server.handledRequests[0].url).to.equal(`${ghostPaths().apiRoot}/images/upload/`);
expect(server.handledRequests[0].requestHeaders.Authorization).to.be.undefined;
});
@ -177,7 +178,7 @@ describe('Integration: Component: gh-image-uploader', function () {
});
it('handles file too large error directly from the web server', async function () {
server.post('/ghost/api/v3/admin/images/upload/', function () {
server.post(`${ghostPaths().apiRoot}/images/upload/`, function () {
return [413, {}, ''];
});
await render(hbs`{{gh-image-uploader image=image update=(action update)}}`);
@ -197,7 +198,7 @@ describe('Integration: Component: gh-image-uploader', function () {
});
it('handles unknown failure', async function () {
server.post('/ghost/api/v3/admin/images/upload/', function () {
server.post(`${ghostPaths().apiRoot}/images/upload/`, function () {
return [500, {'Content-Type': 'application/json'}, ''];
});
await render(hbs`{{gh-image-uploader image=image update=(action update)}}`);

View File

@ -1,4 +1,5 @@
import Pretender from 'pretender';
import ghostPaths from 'ghost-admin/utils/ghost-paths';
import hbs from 'htmlbars-inline-precompile';
import sinon from 'sinon';
import {click, find, findAll, render, settled, waitFor, waitUntil} from '@ember/test-helpers';
@ -8,13 +9,13 @@ import {expect} from 'chai';
import {setupRenderingTest} from 'ember-mocha';
const stubSuccessfulUpload = function (server, delay = 0) {
server.post('/ghost/api/v3/admin/images/upload/', function () {
server.post(`${ghostPaths().apiRoot}/images/upload/`, function () {
return [200, {'Content-Type': 'application/json'}, '{"images": [{"url": "/content/images/test.png"}]}'];
}, delay);
};
const stubFailedUpload = function (server, code, error, delay = 0) {
server.post('/ghost/api/v3/admin/images/upload/', function () {
server.post(`${ghostPaths().apiRoot}/images/upload/`, function () {
return [code, {'Content-Type': 'application/json'}, JSON.stringify({
errors: [{
type: error,
@ -50,7 +51,7 @@ describe('Integration: Component: gh-uploader', function () {
let [lastRequest] = server.handledRequests;
expect(server.handledRequests.length).to.equal(1);
expect(lastRequest.url).to.equal('/ghost/api/v3/admin/images/upload/');
expect(lastRequest.url).to.equal(`${ghostPaths().apiRoot}/images/upload/`);
// requestBody is a FormData object
// this will fail in anything other than Chrome and Firefox
// https://developer.mozilla.org/en-US/docs/Web/API/FormData#Browser_compatibility
@ -135,7 +136,7 @@ describe('Integration: Component: gh-uploader', function () {
it('onComplete returns results in same order as selected', async function () {
// first request has a delay to simulate larger file
server.post('/ghost/api/v3/admin/images/upload/', function () {
server.post(`${ghostPaths().apiRoot}/images/upload/`, function () {
// second request has no delay to simulate small file
stubSuccessfulUpload(server, 0);
@ -257,7 +258,7 @@ describe('Integration: Component: gh-uploader', function () {
});
it('uploads to supplied `uploadUrl`', async function () {
server.post('/ghost/api/v3/admin/images/', function () {
server.post(`${ghostPaths().apiRoot}/images/`, function () {
return [200, {'Content-Type': 'application/json'}, '{"images": [{"url": "/content/images/test.png"}]'];
});
@ -266,7 +267,7 @@ describe('Integration: Component: gh-uploader', function () {
await settled();
let [lastRequest] = server.handledRequests;
expect(lastRequest.url).to.equal('/ghost/api/v3/admin/images/');
expect(lastRequest.url).to.equal(`${ghostPaths().apiRoot}/images/`);
});
it('passes supplied paramName in request', async function () {

View File

@ -1,5 +1,6 @@
import Pretender from 'pretender';
import Service from '@ember/service';
import ghostPaths from 'ghost-admin/utils/ghost-paths';
import hbs from 'htmlbars-inline-precompile';
import sinon from 'sinon';
import {click, find, findAll, render, waitFor} from '@ember/test-helpers';
@ -15,13 +16,13 @@ const notificationsStub = Service.extend({
});
const stubSuccessfulUpload = function (server, delay = 0) {
server.post('/ghost/api/v3/admin/members/upload/', function () {
server.post(`${ghostPaths().apiRoot}/members/upload/`, function () {
return [200, {'Content-Type': 'application/json'}, '{"url":"/content/images/test.png"}'];
}, delay);
};
const stubFailedUpload = function (server, code, error, delay = 0) {
server.post('/ghost/api/v3/admin/members/upload/', function () {
server.post(`${ghostPaths().apiRoot}/members/upload/`, function () {
return [code, {'Content-Type': 'application/json'}, JSON.stringify({
errors: [{
type: error,
@ -38,7 +39,7 @@ describe('Integration: Component: modal-import-members-test', function () {
beforeEach(function () {
server = new Pretender();
this.set('uploadUrl', '/ghost/api/v3/admin/members/upload/');
this.set('uploadUrl', `${ghostPaths().apiRoot}/members/upload/`);
this.owner.register('service:notifications', notificationsStub);
});
@ -71,7 +72,7 @@ describe('Integration: Component: modal-import-members-test', function () {
await click('.gh-btn-green');
expect(server.handledRequests.length).to.equal(1);
expect(server.handledRequests[0].url).to.equal('/ghost/api/v3/admin/members/upload/');
expect(server.handledRequests[0].url).to.equal(`${ghostPaths().apiRoot}/members/upload/`);
});
it('displays server error', async function () {
@ -101,7 +102,7 @@ describe('Integration: Component: modal-import-members-test', function () {
});
it('handles file too large error directly from the web server', async function () {
server.post('/ghost/api/v3/admin/members/upload/', function () {
server.post(`${ghostPaths().apiRoot}/members/upload/`, function () {
return [413, {}, ''];
});
await render(hbs`{{modal-import-members}}`);
@ -129,7 +130,7 @@ describe('Integration: Component: modal-import-members-test', function () {
});
it('handles unknown failure', async function () {
server.post('/ghost/api/v3/admin/members/upload/', function () {
server.post(`${ghostPaths().apiRoot}/members/upload/`, function () {
return [500, {'Content-Type': 'application/json'}, ''];
});
await render(hbs`{{modal-import-members}}`);

View File

@ -1,4 +1,5 @@
import Pretender from 'pretender';
import ghostPaths from 'ghost-admin/utils/ghost-paths';
import wait from 'ember-test-helpers/wait';
import {describe, it} from 'mocha';
import {expect} from 'chai';
@ -32,7 +33,7 @@ describe('Integration: Service: config', function () {
it('normalizes blogUrl to non-trailing-slash', function (done) {
let stubBlogUrl = function stubBlogUrl(url) {
server.get('/ghost/api/v3/admin/config/', function () {
server.get(`${ghostPaths().apiRoot}/config/`, function () {
return [
200,
{'Content-Type': 'application/json'},
@ -40,7 +41,7 @@ describe('Integration: Service: config', function () {
];
});
server.get('/ghost/api/v3/admin/site/', function () {
server.get(`${ghostPaths().apiRoot}/site/`, function () {
return [
200,
{'Content-Type': 'application/json'},

View File

@ -1,6 +1,7 @@
import EmberError from '@ember/error';
import FeatureService, {feature} from 'ghost-admin/services/feature';
import Pretender from 'pretender';
import ghostPaths from 'ghost-admin/utils/ghost-paths';
import wait from 'ember-test-helpers/wait';
import {describe, it} from 'mocha';
import {expect} from 'chai';
@ -17,11 +18,11 @@ function stubSettings(server, labs, validSave = true) {
}
];
server.get('/ghost/api/v3/admin/settings/', function () {
server.get(`${ghostPaths().apiRoot}/settings/`, function () {
return [200, {'Content-Type': 'application/json'}, JSON.stringify({settings})];
});
server.put('/ghost/api/v3/admin/settings/', function (request) {
server.put(`${ghostPaths().apiRoot}/settings/`, function (request) {
let statusCode = (validSave) ? 200 : 400;
let response = (validSave) ? request.requestBody : JSON.stringify({
errors: [{
@ -47,11 +48,11 @@ function stubUser(server, accessibility, validSave = true) {
}]
}];
server.get('/ghost/api/v3/admin/users/me/', function () {
server.get(`${ghostPaths().apiRoot}/users/me/`, function () {
return [200, {'Content-Type': 'application/json'}, JSON.stringify({users})];
});
server.put('/ghost/api/v3/admin/users/1/', function (request) {
server.put(`${ghostPaths().apiRoot}/users/1/`, function (request) {
let statusCode = (validSave) ? 200 : 400;
let response = (validSave) ? request.requestBody : JSON.stringify({
errors: [{

View File

@ -1,11 +1,12 @@
import Pretender from 'pretender';
import ghostPaths from 'ghost-admin/utils/ghost-paths';
import {dasherize} from '@ember/string';
import {describe, it} from 'mocha';
import {expect} from 'chai';
import {setupTest} from 'ember-mocha';
function stubSlugEndpoint(server, type, slug) {
server.get('/ghost/api/v3/admin/slugs/:type/:slug/', function (request) {
server.get(`${ghostPaths().apiRoot}/slugs/:type/:slug/`, function (request) {
expect(request.params.type).to.equal(type);
expect(request.params.slug).to.equal(slug);

View File

@ -1,5 +1,6 @@
import Pretender from 'pretender';
import config from 'ghost-admin/config/environment';
import ghostPaths from 'ghost-admin/utils/ghost-paths';
import {describe, it} from 'mocha';
import {expect} from 'chai';
import {setupTest} from 'ember-mocha';
@ -21,7 +22,7 @@ describe('Integration: Service: store', function () {
let {version} = config.APP;
let store = this.owner.lookup('service:store');
server.get('/ghost/api/v3/admin/posts/1/', function () {
server.get(`${ghostPaths().apiRoot}/posts/1/`, function () {
return [
404,
{'Content-Type': 'application/json'},

View File

@ -1,4 +1,5 @@
import Service from '@ember/service';
import ghostPaths from 'ghost-admin/utils/ghost-paths';
import sinon from 'sinon';
import {beforeEach, describe, it} from 'mocha';
import {expect} from 'chai';
@ -42,7 +43,7 @@ const mockTour = Service.extend({
});
const mockGhostPaths = Service.extend({
apiRoot: '/ghost/api/v3/admin'
apiRoot: ghostPaths().apiRoot
});
describe('Unit: Authenticator: cookie', () => {
@ -74,7 +75,7 @@ describe('Unit: Authenticator: cookie', () => {
let tour = this.owner.lookup('service:tour');
return authenticator.authenticate('AzureDiamond', 'hunter2').then(() => {
expect(post.args[0][0]).to.equal('/ghost/api/v3/admin/session');
expect(post.args[0][0]).to.equal(`${ghostPaths().apiRoot}/session`);
expect(post.args[0][1]).to.deep.include({
data: {
username: 'AzureDiamond',
@ -103,7 +104,7 @@ describe('Unit: Authenticator: cookie', () => {
let del = authenticator.ajax.del;
return authenticator.invalidate().then(() => {
expect(del.args[0][0]).to.equal('/ghost/api/v3/admin/session');
expect(del.args[0][0]).to.equal(`${ghostPaths().apiRoot}/session`);
});
});
});

View File

@ -1,4 +1,5 @@
import Pretender from 'pretender';
import ghostPaths from 'ghost-admin/utils/ghost-paths';
import {describe, it} from 'mocha';
import {expect} from 'chai';
import {run} from '@ember/runloop';
@ -23,7 +24,7 @@ describe('Unit: Model: invite', function () {
let model = store.createRecord('invite');
let role;
server.post('/ghost/api/v3/admin/invites/', function () {
server.post(`${ghostPaths().apiRoot}/invites/`, function () {
return [200, {}, '{}'];
});

View File

@ -1,4 +1,5 @@
import Pretender from 'pretender';
import ghostPaths from 'ghost-admin/utils/ghost-paths';
import {describe, it} from 'mocha';
import {expect} from 'chai';
import {setupTest} from 'ember-mocha';
@ -17,7 +18,7 @@ describe('Unit: Serializer: notification', function () {
});
it('converts location->key when deserializing', function () {
server.get('/ghost/api/v3/admin/notifications', function () {
server.get(`${ghostPaths().apiRoot}/notifications`, function () {
let response = {
notifications: [{
id: 1,