mirror of
https://github.com/n8n-io/n8n.git
synced 2024-12-24 04:04:06 -08:00
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:
parent
7090a79b5d
commit
a14d85ea48
|
@ -1,8 +1,12 @@
|
||||||
import {
|
import {
|
||||||
|
ICredentialDataDecryptedObject,
|
||||||
|
ICredentialTestRequest,
|
||||||
ICredentialType,
|
ICredentialType,
|
||||||
|
IHttpRequestOptions,
|
||||||
INodeProperties,
|
INodeProperties,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
import jwt from 'jsonwebtoken';
|
||||||
export class GhostAdminApi implements ICredentialType {
|
export class GhostAdminApi implements ICredentialType {
|
||||||
name = 'ghostAdminApi';
|
name = 'ghostAdminApi';
|
||||||
displayName = 'Ghost Admin API';
|
displayName = 'Ghost Admin API';
|
||||||
|
@ -22,4 +26,27 @@ export class GhostAdminApi implements ICredentialType {
|
||||||
default: '',
|
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/',
|
||||||
|
},
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
import {
|
import {
|
||||||
|
ICredentialDataDecryptedObject,
|
||||||
|
ICredentialTestRequest,
|
||||||
ICredentialType,
|
ICredentialType,
|
||||||
|
IHttpRequestOptions,
|
||||||
INodeProperties,
|
INodeProperties,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
@ -22,4 +25,18 @@ export class GhostContentApi implements ICredentialType {
|
||||||
default: '',
|
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',
|
||||||
|
},
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,38 +10,30 @@ import {
|
||||||
} from 'n8n-core';
|
} from 'n8n-core';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
IDataObject, NodeApiError,
|
IDataObject,
|
||||||
|
JsonObject,
|
||||||
|
NodeApiError,
|
||||||
} from 'n8n-workflow';
|
} 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
|
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;
|
const source = this.getNodeParameter('source', 0) as string;
|
||||||
|
|
||||||
let credentials;
|
let credentials;
|
||||||
let version;
|
let version;
|
||||||
let token;
|
let credentialType;
|
||||||
|
|
||||||
if (source === 'contentApi') {
|
if (source === 'contentApi') {
|
||||||
//https://ghost.org/faq/api-versioning/
|
//https://ghost.org/faq/api-versioning/
|
||||||
version = 'v3';
|
version = 'v3';
|
||||||
credentials = await this.getCredentials('ghostContentApi');
|
credentialType = 'ghostContentApi';
|
||||||
query.key = credentials.apiKey as string;
|
|
||||||
} else {
|
} else {
|
||||||
version = 'v2';
|
version = 'v2';
|
||||||
credentials = await this.getCredentials('ghostAdminApi');
|
credentialType = '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/`,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
credentials = await this.getCredentials(credentialType);
|
||||||
|
|
||||||
const options: OptionsWithUri = {
|
const options: OptionsWithUri = {
|
||||||
method,
|
method,
|
||||||
qs: query,
|
qs: query,
|
||||||
|
@ -50,17 +42,10 @@ export async function ghostApiRequest(this: IHookFunctions | IExecuteFunctions |
|
||||||
json: true,
|
json: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (token) {
|
|
||||||
options.headers = {
|
|
||||||
Authorization: `Ghost ${token}`,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return await this.helpers.request!(options);
|
return await this.helpers.requestWithAuthentication.call(this, credentialType, options);
|
||||||
|
} catch(error) {
|
||||||
} catch (error) {
|
throw new NodeApiError(this.getNode(), error as JsonObject);
|
||||||
throw new NodeApiError(this.getNode(), error);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@ export class Ghost implements INodeType {
|
||||||
description: INodeTypeDescription = {
|
description: INodeTypeDescription = {
|
||||||
displayName: 'Ghost',
|
displayName: 'Ghost',
|
||||||
name: 'ghost',
|
name: 'ghost',
|
||||||
icon: 'file:ghost.png',
|
icon: 'file:ghost.svg',
|
||||||
group: ['input'],
|
group: ['input'],
|
||||||
version: 1,
|
version: 1,
|
||||||
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
||||||
|
@ -91,8 +91,9 @@ export class Ghost implements INodeType {
|
||||||
value: 'post',
|
value: 'post',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
noDataExpression: true,
|
||||||
default: 'post',
|
default: 'post',
|
||||||
description: 'The resource to operate on.',
|
description: 'The resource to operate on',
|
||||||
},
|
},
|
||||||
...postOperations,
|
...postOperations,
|
||||||
...postFields,
|
...postFields,
|
||||||
|
@ -137,7 +138,7 @@ export class Ghost implements INodeType {
|
||||||
for (const tag of tags) {
|
for (const tag of tags) {
|
||||||
returnData.push({
|
returnData.push({
|
||||||
name: tag.name,
|
name: tag.name,
|
||||||
value: tag.id,
|
value: tag.name,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return returnData;
|
return returnData;
|
||||||
|
|
|
@ -6,6 +6,7 @@ export const postOperations: INodeProperties[] = [
|
||||||
{
|
{
|
||||||
displayName: 'Operation',
|
displayName: 'Operation',
|
||||||
name: 'operation',
|
name: 'operation',
|
||||||
|
noDataExpression: true,
|
||||||
type: 'options',
|
type: 'options',
|
||||||
displayOptions: {
|
displayOptions: {
|
||||||
show: {
|
show: {
|
||||||
|
@ -30,11 +31,12 @@ export const postOperations: INodeProperties[] = [
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
default: 'get',
|
default: 'get',
|
||||||
description: 'The operation to perform.',
|
description: 'The operation to perform',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Operation',
|
displayName: 'Operation',
|
||||||
name: 'operation',
|
name: 'operation',
|
||||||
|
noDataExpression: true,
|
||||||
type: 'options',
|
type: 'options',
|
||||||
displayOptions: {
|
displayOptions: {
|
||||||
show: {
|
show: {
|
||||||
|
@ -74,7 +76,7 @@ export const postOperations: INodeProperties[] = [
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
default: 'get',
|
default: 'get',
|
||||||
description: 'The operation to perform.',
|
description: 'The operation to perform',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -256,12 +258,6 @@ export const postFields: INodeProperties[] = [
|
||||||
type: 'string',
|
type: 'string',
|
||||||
default: '',
|
default: '',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
displayName: 'Open Graph Title',
|
|
||||||
name: 'og_title',
|
|
||||||
type: 'string',
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
displayName: 'Open Graph Image',
|
displayName: 'Open Graph Image',
|
||||||
name: 'og_image',
|
name: 'og_image',
|
||||||
|
@ -270,6 +266,12 @@ export const postFields: INodeProperties[] = [
|
||||||
description: 'URL of the image',
|
description: 'URL of the image',
|
||||||
|
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Open Graph Title',
|
||||||
|
name: 'og_title',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Published At',
|
displayName: 'Published At',
|
||||||
name: 'published_at',
|
name: 'published_at',
|
||||||
|
@ -303,7 +305,7 @@ export const postFields: INodeProperties[] = [
|
||||||
default: 'draft',
|
default: 'draft',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Tags IDs',
|
displayName: 'Tags',
|
||||||
name: 'tags',
|
name: 'tags',
|
||||||
type: 'multiOptions',
|
type: 'multiOptions',
|
||||||
typeOptions: {
|
typeOptions: {
|
||||||
|
@ -365,7 +367,7 @@ export const postFields: INodeProperties[] = [
|
||||||
displayName: 'By',
|
displayName: 'By',
|
||||||
name: 'by',
|
name: 'by',
|
||||||
type: 'options',
|
type: 'options',
|
||||||
default: '',
|
default: 'id',
|
||||||
required: true,
|
required: true,
|
||||||
options: [
|
options: [
|
||||||
{
|
{
|
||||||
|
@ -846,12 +848,6 @@ export const postFields: INodeProperties[] = [
|
||||||
type: 'string',
|
type: 'string',
|
||||||
default: '',
|
default: '',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
displayName: 'Open Graph Title',
|
|
||||||
name: 'og_title',
|
|
||||||
type: 'string',
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
displayName: 'Open Graph Image',
|
displayName: 'Open Graph Image',
|
||||||
name: 'og_image',
|
name: 'og_image',
|
||||||
|
@ -859,6 +855,12 @@ export const postFields: INodeProperties[] = [
|
||||||
default: '',
|
default: '',
|
||||||
description: 'URL of the image',
|
description: 'URL of the image',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Open Graph Title',
|
||||||
|
name: 'og_title',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Published At',
|
displayName: 'Published At',
|
||||||
name: 'published_at',
|
name: 'published_at',
|
||||||
|
@ -892,7 +894,7 @@ export const postFields: INodeProperties[] = [
|
||||||
default: 'draft',
|
default: 'draft',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Tags IDs',
|
displayName: 'Tags',
|
||||||
name: 'tags',
|
name: 'tags',
|
||||||
type: 'multiOptions',
|
type: 'multiOptions',
|
||||||
typeOptions: {
|
typeOptions: {
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 1.2 KiB |
1
packages/nodes-base/nodes/Ghost/ghost.svg
Normal file
1
packages/nodes-base/nodes/Ghost/ghost.svg
Normal 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 |
Loading…
Reference in a new issue