Updated Tenor API to v2 (#2418)

refs: https://github.com/TryGhost/Ghost/issues/14980
refs: https://github.com/TryGhost/Ghost/pull/15087

- The Tenor v1 API has been decommissioned https://developers.google.com/tenor/guides/migrate-from-v1
- Updated the API to v2, but there are some differences we have to account for 
- Swapped from using the old "trending" API to the new "featured" API, which at present seem to be the same thing
- Added a new client_key, which identifies the integration using the google API key, as google API keys can be used for multiple APIs and projects
-  Fixed up the error handling to support Google's error format, and also caught and replaced the error that everyone with old keys will see to make it clearer. This includes adding an htmlError property so that we can output HTML safely in the frontend.

There is still an active TODO with the naming of the config key, but we will resolve this after merging admin into the monorepo.

Co-authored-by: Hannah Wolfe <github.erisds@gmail.com>
This commit is contained in:
arsereg 2022-08-03 09:11:55 -04:00 committed by GitHub
parent cf64dc89d0
commit cc276486f0
4 changed files with 23 additions and 11 deletions

View File

@ -6,8 +6,8 @@ import {isEmpty} from '@ember/utils';
import {task, taskGroup, timeout} from 'ember-concurrency'; import {task, taskGroup, timeout} from 'ember-concurrency';
import {tracked} from '@glimmer/tracking'; import {tracked} from '@glimmer/tracking';
const API_URL = 'https://g1.tenor.com'; const API_URL = 'https://tenor.googleapis.com';
const API_VERSION = 'v1'; const API_VERSION = 'v2';
const DEBOUNCE_MS = 600; const DEBOUNCE_MS = 600;
export default class TenorService extends Service { export default class TenorService extends Service {
@ -16,15 +16,17 @@ export default class TenorService extends Service {
@tracked columnCount = 4; @tracked columnCount = 4;
@tracked columns = null; @tracked columns = null;
@tracked error = null; @tracked error = null;
@tracked htmlError = null;
@tracked gifs = new TrackedArray([]); @tracked gifs = new TrackedArray([]);
@tracked searchTerm = ''; @tracked searchTerm = '';
@tracked loadedType = 'trending'; @tracked loadedType = '';
_columnHeights = []; _columnHeights = [];
_nextPos = null; _nextPos = null;
get apiKey() { get apiKey() {
return this.config.get('tenor.publicReadOnlyApiKey'); // @TODO confirm this!
return this.config.get('tenor.apiKey');
} }
get contentfilter() { get contentfilter() {
@ -94,9 +96,10 @@ export default class TenorService extends Service {
@task({group: 'loadingTasks'}) @task({group: 'loadingTasks'})
*loadTrendingTask() { *loadTrendingTask() {
this.loadedType = 'trending'; this.loadedType = 'featured';
yield this._makeRequest(this.loadedType, {params: { yield this._makeRequest(this.loadedType, {params: {
q: 'excited',
media_filter: 'minimal' media_filter: 'minimal'
}}); }});
} }
@ -134,6 +137,7 @@ export default class TenorService extends Service {
const params = new URLSearchParams(options.params); const params = new URLSearchParams(options.params);
params.set('key', this.apiKey); params.set('key', this.apiKey);
params.set('client_key', 'ghost-editor');
params.set('contentfilter', this.contentfilter); params.set('contentfilter', this.contentfilter);
url.search = params.toString(); url.search = params.toString();
@ -153,6 +157,12 @@ export default class TenorService extends Service {
if (!options.ignoreErrors && !this.error) { if (!options.ignoreErrors && !this.error) {
this.error = 'Uh-oh! Trouble reaching the Tenor API, please check your connection'; this.error = 'Uh-oh! Trouble reaching the Tenor API, please check your connection';
} }
if (this.error && this.error.startsWith('API key not valid')) {
// Added an html error field, so that we don't pass raw API errors from tenor to triple-braces in the frontend
this.htmlError = `This version of the Tenor API is no longer supported. Please update your API key by following our
<a href="https://ghost.org/docs/config/#tenor" target="_blank" rel="noopener noreferrer"> documentation here</a>.<br />`;
}
console.error(e); // eslint-disable-line console.error(e); // eslint-disable-line
}); });
} }
@ -165,8 +175,8 @@ export default class TenorService extends Service {
let responseText; let responseText;
if (response.headers.map['content-type'] === 'application/json') { if (response.headers.map['content-type'].startsWith('application/json')) {
responseText = await response.json().then(json => json.errors[0]); responseText = await response.json().then(json => json.error.message || json.error);
} else if (response.headers.map['content-type'] === 'text/xml') { } else if (response.headers.map['content-type'] === 'text/xml') {
responseText = await response.text(); responseText = await response.text();
} }
@ -192,7 +202,7 @@ export default class TenorService extends Service {
_addGif(gif) { _addGif(gif) {
// re-calculate ratio for later use // re-calculate ratio for later use
const [width, height] = gif.media[0].tinygif.dims; const [width, height] = gif.media_formats.tinygif.dims;
gif.ratio = height / width; gif.ratio = height / width;
// add to general gifs list // add to general gifs list

View File

@ -53,7 +53,9 @@
<section class="gh-unsplash-error h-100 flex items-center justify-center pb30"> <section class="gh-unsplash-error h-100 flex items-center justify-center pb30">
<div> <div>
<img class="gh-unsplash-error-404" src="assets/img/unsplash-404.png" alt="Network error" /> <img class="gh-unsplash-error-404" src="assets/img/unsplash-404.png" alt="Network error" />
<h4>{{this.tenor.error}} (<a href="#" {{on "click" this.tenor.retry}}>retry</a>)</h4> {{!-- template-lint-disable no-triple-curlies --}}
<h4>{{#if this.tenor.htmlError}}{{{this.tenor.htmlError}}}{{else}}{{this.tenor.error}}{{/if}} (<a href="#" {{on "click" this.tenor.retry}}>retry</a>)</h4>
{{!-- template-lint-enable no-triple-curlies --}}
</div> </div>
</section> </section>
{{/if}} {{/if}}

View File

@ -58,7 +58,7 @@ export default class KoenigCardImageTenorSelector extends Component {
event?.preventDefault(); event?.preventDefault();
event?.stopPropagation(); event?.stopPropagation();
const media = gif.media[0].gif; const media = gif.media_formats.gif;
const payload = { const payload = {
src: media.url, src: media.url,

View File

@ -3,7 +3,7 @@ import {htmlSafe} from '@ember/template';
export default class GhTenorGifComponent extends Component { export default class GhTenorGifComponent extends Component {
get media() { get media() {
return this.args.gif.media[0].tinygif; return this.args.gif.media_formats.tinygif;
} }
get imageUrl() { get imageUrl() {