fix(Ghost Node): Fix post tags and add credential tests (#3278)

* Renamed Tag IDs to Tags and changed the value to tag.name

* Updated credentials to use new system

* Nodelinter changes
This commit is contained in:
Jonathan Bennetts 2022-05-14 09:39:28 +01:00 committed by GitHub
parent 7090a79b5d
commit a14d85ea48
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 79 additions and 46 deletions

View file

@ -1,8 +1,12 @@
import {
ICredentialDataDecryptedObject,
ICredentialTestRequest,
ICredentialType,
IHttpRequestOptions,
INodeProperties,
} from 'n8n-workflow';
import jwt from 'jsonwebtoken';
export class GhostAdminApi implements ICredentialType {
name = 'ghostAdminApi';
displayName = 'Ghost Admin API';
@ -22,4 +26,27 @@ export class GhostAdminApi implements ICredentialType {
default: '',
},
];
async authenticate(credentials: ICredentialDataDecryptedObject, requestOptions: IHttpRequestOptions): Promise<IHttpRequestOptions> {
const [id, secret] = (credentials.apiKey as string).split(':');
const token = jwt.sign({}, Buffer.from(secret, 'hex'), {
keyid: id,
algorithm: 'HS256',
expiresIn: '5m',
audience: `/v2/admin/`,
});
requestOptions.headers = {
...requestOptions.headers,
Authorization: `Ghost ${token}`,
};
return requestOptions;
}
test: ICredentialTestRequest = {
request: {
baseURL: '={{$credentials.url}}',
url: '/ghost/api/v2/admin/pages/',
},
};
}

View file

@ -1,5 +1,8 @@
import {
ICredentialDataDecryptedObject,
ICredentialTestRequest,
ICredentialType,
IHttpRequestOptions,
INodeProperties,
} from 'n8n-workflow';
@ -22,4 +25,18 @@ export class GhostContentApi implements ICredentialType {
default: '',
},
];
async authenticate(credentials: ICredentialDataDecryptedObject, requestOptions: IHttpRequestOptions): Promise<IHttpRequestOptions> {
requestOptions.qs = {
...requestOptions.qs,
'key': credentials.apiKey,
};
return requestOptions;
}
test: ICredentialTestRequest = {
request: {
baseURL: '={{$credentials.url}}',
url: '/ghost/api/v3/content/settings/',
method: 'GET',
},
};
}

View file

@ -10,38 +10,30 @@ import {
} from 'n8n-core';
import {
IDataObject, NodeApiError,
IDataObject,
JsonObject,
NodeApiError,
} from 'n8n-workflow';
import jwt from 'jsonwebtoken';
export async function ghostApiRequest(this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method: string, endpoint: string, body: any = {}, query: IDataObject = {}, uri?: string): Promise<any> { // tslint:disable-line:no-any
const source = this.getNodeParameter('source', 0) as string;
let credentials;
let version;
let token;
let credentialType;
if (source === 'contentApi') {
//https://ghost.org/faq/api-versioning/
version = 'v3';
credentials = await this.getCredentials('ghostContentApi');
query.key = credentials.apiKey as string;
credentialType = 'ghostContentApi';
} else {
version = 'v2';
credentials = await this.getCredentials('ghostAdminApi');
// Create the token (including decoding secret)
const [id, secret] = (credentials.apiKey as string).split(':');
token = jwt.sign({}, Buffer.from(secret, 'hex'), {
keyid: id,
algorithm: 'HS256',
expiresIn: '5m',
audience: `/${version}/admin/`,
});
credentialType = 'ghostAdminApi';
}
credentials = await this.getCredentials(credentialType);
const options: OptionsWithUri = {
method,
qs: query,
@ -50,17 +42,10 @@ export async function ghostApiRequest(this: IHookFunctions | IExecuteFunctions |
json: true,
};
if (token) {
options.headers = {
Authorization: `Ghost ${token}`,
};
}
try {
return await this.helpers.request!(options);
} catch (error) {
throw new NodeApiError(this.getNode(), error);
return await this.helpers.requestWithAuthentication.call(this, credentialType, options);
} catch(error) {
throw new NodeApiError(this.getNode(), error as JsonObject);
}
}

View file

@ -29,7 +29,7 @@ export class Ghost implements INodeType {
description: INodeTypeDescription = {
displayName: 'Ghost',
name: 'ghost',
icon: 'file:ghost.png',
icon: 'file:ghost.svg',
group: ['input'],
version: 1,
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
@ -91,8 +91,9 @@ export class Ghost implements INodeType {
value: 'post',
},
],
noDataExpression: true,
default: 'post',
description: 'The resource to operate on.',
description: 'The resource to operate on',
},
...postOperations,
...postFields,
@ -137,7 +138,7 @@ export class Ghost implements INodeType {
for (const tag of tags) {
returnData.push({
name: tag.name,
value: tag.id,
value: tag.name,
});
}
return returnData;

View file

@ -6,6 +6,7 @@ export const postOperations: INodeProperties[] = [
{
displayName: 'Operation',
name: 'operation',
noDataExpression: true,
type: 'options',
displayOptions: {
show: {
@ -30,11 +31,12 @@ export const postOperations: INodeProperties[] = [
},
],
default: 'get',
description: 'The operation to perform.',
description: 'The operation to perform',
},
{
displayName: 'Operation',
name: 'operation',
noDataExpression: true,
type: 'options',
displayOptions: {
show: {
@ -74,7 +76,7 @@ export const postOperations: INodeProperties[] = [
},
],
default: 'get',
description: 'The operation to perform.',
description: 'The operation to perform',
},
];
@ -256,12 +258,6 @@ export const postFields: INodeProperties[] = [
type: 'string',
default: '',
},
{
displayName: 'Open Graph Title',
name: 'og_title',
type: 'string',
default: '',
},
{
displayName: 'Open Graph Image',
name: 'og_image',
@ -270,6 +266,12 @@ export const postFields: INodeProperties[] = [
description: 'URL of the image',
},
{
displayName: 'Open Graph Title',
name: 'og_title',
type: 'string',
default: '',
},
{
displayName: 'Published At',
name: 'published_at',
@ -303,7 +305,7 @@ export const postFields: INodeProperties[] = [
default: 'draft',
},
{
displayName: 'Tags IDs',
displayName: 'Tags',
name: 'tags',
type: 'multiOptions',
typeOptions: {
@ -365,7 +367,7 @@ export const postFields: INodeProperties[] = [
displayName: 'By',
name: 'by',
type: 'options',
default: '',
default: 'id',
required: true,
options: [
{
@ -846,12 +848,6 @@ export const postFields: INodeProperties[] = [
type: 'string',
default: '',
},
{
displayName: 'Open Graph Title',
name: 'og_title',
type: 'string',
default: '',
},
{
displayName: 'Open Graph Image',
name: 'og_image',
@ -859,6 +855,12 @@ export const postFields: INodeProperties[] = [
default: '',
description: 'URL of the image',
},
{
displayName: 'Open Graph Title',
name: 'og_title',
type: 'string',
default: '',
},
{
displayName: 'Published At',
name: 'published_at',
@ -892,7 +894,7 @@ export const postFields: INodeProperties[] = [
default: 'draft',
},
{
displayName: 'Tags IDs',
displayName: 'Tags',
name: 'tags',
type: 'multiOptions',
typeOptions: {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 151 151" fill="#fff" fill-rule="evenodd" stroke="#000" stroke-linecap="round" stroke-linejoin="round"><use xlink:href="#A" x=".5" y=".5"/><symbol id="A" overflow="visible"><g fill-rule="nonzero"><path d="M0 22.5C0 10.035 10.035 0 22.5 0h105C139.965 0 150 10.035 150 22.5v105c0 12.465-10.035 22.5-22.5 22.5h-105C10.035 150 0 139.965 0 127.5z" stroke="none" fill="#e8e9eb"/><path d="M29.59 39.258h54.785zm72.07 0h17.871zM29.59 75.293h90.82zm0 35.449h36.035zm54.785 0h36.035z" stroke="#3d515b" stroke-linejoin="miter" fill="#000" stroke-width="17.871"/></g></symbol></svg>

After

Width:  |  Height:  |  Size: 665 B