mirror of
https://github.com/n8n-io/n8n.git
synced 2024-12-25 12:44:07 -08:00
Merge branch 'master' into RicardoE105-google-books
This commit is contained in:
commit
9a1328ccfb
|
@ -33,7 +33,7 @@ export const moveNodeWorkflow = mixins(
|
|||
|
||||
const nodeViewOffsetPositionX = offsetPosition[0] + (position.x - this.moveLastPosition[0]);
|
||||
const nodeViewOffsetPositionY = offsetPosition[1] + (position.y - this.moveLastPosition[1]);
|
||||
this.$store.commit('setNodeViewOffsetPosition', {newOffset: [nodeViewOffsetPositionX, nodeViewOffsetPositionY], setStateDirty: true});
|
||||
this.$store.commit('setNodeViewOffsetPosition', {newOffset: [nodeViewOffsetPositionX, nodeViewOffsetPositionY]});
|
||||
|
||||
// Update the last position
|
||||
this.moveLastPosition[0] = position.x;
|
||||
|
@ -101,7 +101,7 @@ export const moveNodeWorkflow = mixins(
|
|||
const offsetPosition = this.$store.getters.getNodeViewOffsetPosition;
|
||||
const nodeViewOffsetPositionX = offsetPosition[0] - normalized.pixelX;
|
||||
const nodeViewOffsetPositionY = offsetPosition[1] - normalized.pixelY;
|
||||
this.$store.commit('setNodeViewOffsetPosition', {newOffset: [nodeViewOffsetPositionX, nodeViewOffsetPositionY], setStateDirty: true});
|
||||
this.$store.commit('setNodeViewOffsetPosition', {newOffset: [nodeViewOffsetPositionX, nodeViewOffsetPositionY]});
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
@ -29,7 +29,6 @@ export const workflowRun = mixins(
|
|||
// because then it can not receive the data as it executes.
|
||||
throw new Error('No active connection to server. It is maybe down.');
|
||||
}
|
||||
const workflow = this.getWorkflow();
|
||||
|
||||
this.$store.commit('addActiveAction', 'workflowRunning');
|
||||
|
||||
|
|
|
@ -96,7 +96,6 @@ export const store = new Vuex.Store({
|
|||
|
||||
// Active Executions
|
||||
addActiveExecution (state, newActiveExecution: IExecutionsCurrentSummaryExtended) {
|
||||
state.stateIsDirty = true;
|
||||
// Check if the execution exists already
|
||||
const activeExecution = state.activeExecutions.find(execution => {
|
||||
return execution.idActive === newActiveExecution.idActive;
|
||||
|
@ -113,7 +112,6 @@ export const store = new Vuex.Store({
|
|||
state.activeExecutions.unshift(newActiveExecution);
|
||||
},
|
||||
finishActiveExecution (state, finishedActiveExecution: IPushDataExecutionFinished) {
|
||||
state.stateIsDirty = true;
|
||||
// Find the execution to set to finished
|
||||
const activeExecution = state.activeExecutions.find(execution => {
|
||||
return execution.idActive === finishedActiveExecution.executionIdActive;
|
||||
|
@ -132,7 +130,6 @@ export const store = new Vuex.Store({
|
|||
Vue.set(activeExecution, 'stoppedAt', finishedActiveExecution.data.stoppedAt);
|
||||
},
|
||||
setActiveExecutions (state, newActiveExecutions: IExecutionsCurrentSummaryExtended[]) {
|
||||
state.stateIsDirty = true;
|
||||
Vue.set(state, 'activeExecutions', newActiveExecutions);
|
||||
},
|
||||
|
||||
|
@ -165,7 +162,6 @@ export const store = new Vuex.Store({
|
|||
state.selectedNodes.push(node);
|
||||
},
|
||||
removeNodeFromSelection (state, node: INodeUi) {
|
||||
state.stateIsDirty = true;
|
||||
let index;
|
||||
for (index in state.selectedNodes) {
|
||||
if (state.selectedNodes[index].name === node.name) {
|
||||
|
@ -377,7 +373,6 @@ export const store = new Vuex.Store({
|
|||
|
||||
// Set/Overwrite the value
|
||||
Vue.set(node.issues!, nodeIssueData.type, nodeIssueData.value);
|
||||
state.stateIsDirty = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -466,7 +461,6 @@ export const store = new Vuex.Store({
|
|||
state.nodeIndex.push(nodeName);
|
||||
},
|
||||
setNodeIndex (state, newData: { index: number, name: string | null}) {
|
||||
state.stateIsDirty = true;
|
||||
state.nodeIndex[newData.index] = newData.name;
|
||||
},
|
||||
resetNodeIndex (state) {
|
||||
|
@ -478,9 +472,6 @@ export const store = new Vuex.Store({
|
|||
state.nodeViewMoveInProgress = value;
|
||||
},
|
||||
setNodeViewOffsetPosition (state, data) {
|
||||
if (data.setStateDirty === true) {
|
||||
state.stateIsDirty = true;
|
||||
}
|
||||
state.nodeViewOffsetPosition = data.newOffset;
|
||||
},
|
||||
|
||||
|
@ -541,16 +532,6 @@ export const store = new Vuex.Store({
|
|||
Vue.set(state, 'oauthCallbackUrls', urls);
|
||||
},
|
||||
|
||||
addNodeType (state, typeData: INodeTypeDescription) {
|
||||
if (!typeData.hasOwnProperty('name')) {
|
||||
// All node-types have to have a name
|
||||
// TODO: Check if there is an error or whatever that is supposed to be returned
|
||||
return;
|
||||
}
|
||||
state.stateIsDirty = true;
|
||||
state.nodeTypes.push(typeData);
|
||||
},
|
||||
|
||||
setActiveNode (state, nodeName: string) {
|
||||
state.activeNode = nodeName;
|
||||
},
|
||||
|
@ -573,7 +554,6 @@ export const store = new Vuex.Store({
|
|||
if (state.workflowExecutionData.data.resultData.runData[pushData.nodeName] === undefined) {
|
||||
Vue.set(state.workflowExecutionData.data.resultData.runData, pushData.nodeName, []);
|
||||
}
|
||||
state.stateIsDirty = true;
|
||||
state.workflowExecutionData.data.resultData.runData[pushData.nodeName].push(pushData.data);
|
||||
},
|
||||
|
||||
|
|
|
@ -1006,6 +1006,8 @@ export default mixins(
|
|||
|
||||
await this.addNodes([newNodeData]);
|
||||
|
||||
this.$store.commit('setStateDirty', true);
|
||||
|
||||
// Automatically deselect all nodes and select the current one and also active
|
||||
// current node
|
||||
this.deselectAllNodes();
|
||||
|
@ -1500,6 +1502,8 @@ export default mixins(
|
|||
|
||||
await this.addNodes([newNodeData]);
|
||||
|
||||
this.$store.commit('setStateDirty', true);
|
||||
|
||||
// Automatically deselect all nodes and select the current one and also active
|
||||
// current node
|
||||
this.deselectAllNodes();
|
||||
|
@ -1834,6 +1838,8 @@ export default mixins(
|
|||
// Add the nodes with the changed node names, expressions and connections
|
||||
await this.addNodes(Object.values(tempWorkflow.nodes), tempWorkflow.connectionsBySourceNode);
|
||||
|
||||
this.$store.commit('setStateDirty', true);
|
||||
|
||||
return {
|
||||
nodes: Object.values(tempWorkflow.nodes),
|
||||
connections: tempWorkflow.connectionsBySourceNode,
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
import {
|
||||
ICredentialType,
|
||||
NodePropertyTypes,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
export class FacebookGraphAppApi implements ICredentialType {
|
||||
name = 'facebookGraphAppApi';
|
||||
displayName = 'Facebook Graph API (App)';
|
||||
documentationUrl = 'facebookGraphApp';
|
||||
extends = [
|
||||
'facebookGraphApi',
|
||||
];
|
||||
properties = [
|
||||
{
|
||||
displayName: 'App Secret',
|
||||
name: 'appSecret',
|
||||
type: 'string' as NodePropertyTypes,
|
||||
default: '',
|
||||
description: '(Optional) When the app secret is set the node will verify this signature to validate the integrity and origin of the payload.',
|
||||
},
|
||||
];
|
||||
}
|
|
@ -28,6 +28,7 @@ export class ShopifyApi implements ICredentialType {
|
|||
required: true,
|
||||
type: 'string' as NodePropertyTypes,
|
||||
default: '',
|
||||
description: 'Only the subdomain without .myshopify.com',
|
||||
},
|
||||
{
|
||||
displayName: 'Shared Secret',
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
import {
|
||||
ICredentialType,
|
||||
NodePropertyTypes,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
export class StoryblokContentApi implements ICredentialType {
|
||||
name = 'storyblokContentApi';
|
||||
displayName = 'Storyblok Content API';
|
||||
documentationUrl = 'storyblok';
|
||||
properties = [
|
||||
{
|
||||
displayName: 'API Key',
|
||||
name: 'apiKey',
|
||||
type: 'string' as NodePropertyTypes,
|
||||
default: '',
|
||||
},
|
||||
];
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
import {
|
||||
ICredentialType,
|
||||
NodePropertyTypes,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
export class StoryblokManagementApi implements ICredentialType {
|
||||
name = 'storyblokManagementApi';
|
||||
displayName = 'Storyblok Management API';
|
||||
documentationUrl = 'storyblok';
|
||||
properties = [
|
||||
{
|
||||
displayName: 'Personal Access Token',
|
||||
name: 'accessToken',
|
||||
type: 'string' as NodePropertyTypes,
|
||||
default: '',
|
||||
},
|
||||
];
|
||||
}
|
|
@ -10,7 +10,9 @@ import {
|
|||
INodeTypeDescription,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import { OptionsWithUri } from 'request';
|
||||
import {
|
||||
OptionsWithUri,
|
||||
} from 'request';
|
||||
|
||||
export class FacebookGraphApi implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
|
@ -22,7 +24,7 @@ export class FacebookGraphApi implements INodeType {
|
|||
description: 'Interacts with Facebook using the Graph API',
|
||||
defaults: {
|
||||
name: 'Facebook Graph API',
|
||||
color: '#772244',
|
||||
color: '#3B5998',
|
||||
},
|
||||
inputs: ['main'],
|
||||
outputs: ['main'],
|
||||
|
|
251
packages/nodes-base/nodes/Facebook/FacebookTrigger.node.ts
Normal file
251
packages/nodes-base/nodes/Facebook/FacebookTrigger.node.ts
Normal file
|
@ -0,0 +1,251 @@
|
|||
import {
|
||||
IHookFunctions,
|
||||
IWebhookFunctions,
|
||||
} from 'n8n-core';
|
||||
|
||||
import {
|
||||
IDataObject,
|
||||
INodeType,
|
||||
INodeTypeDescription,
|
||||
IWebhookResponseData,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import * as uuid from 'uuid/v4';
|
||||
|
||||
import {
|
||||
snakeCase,
|
||||
} from 'change-case';
|
||||
|
||||
import {
|
||||
facebookApiRequest,
|
||||
} from './GenericFunctions';
|
||||
|
||||
import {
|
||||
createHmac,
|
||||
} from 'crypto';
|
||||
|
||||
export class FacebookTrigger implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
displayName: 'Facebook Trigger',
|
||||
name: 'facebookTrigger',
|
||||
icon: 'file:facebook.png',
|
||||
group: ['trigger'],
|
||||
version: 1,
|
||||
subtitle: '={{$parameter["appId"] +"/"+ $parameter["object"]}}',
|
||||
description: 'Starts the workflow when a Facebook events occurs.',
|
||||
defaults: {
|
||||
name: 'Facebook Trigger',
|
||||
color: '#3B5998',
|
||||
},
|
||||
inputs: [],
|
||||
outputs: ['main'],
|
||||
credentials: [
|
||||
{
|
||||
name: 'facebookGraphAppApi',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
webhooks: [
|
||||
{
|
||||
name: 'setup',
|
||||
httpMethod: 'GET',
|
||||
responseMode: 'onReceived',
|
||||
path: 'webhook',
|
||||
},
|
||||
{
|
||||
name: 'default',
|
||||
httpMethod: 'POST',
|
||||
responseMode: 'onReceived',
|
||||
path: 'webhook',
|
||||
},
|
||||
],
|
||||
properties: [
|
||||
{
|
||||
displayName: 'Object',
|
||||
name: 'object',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Ad Account',
|
||||
value: 'adAccount',
|
||||
description: 'Get updates about Ad Account',
|
||||
},
|
||||
{
|
||||
name: 'Application',
|
||||
value: 'application',
|
||||
description: 'Get updates about the app',
|
||||
},
|
||||
{
|
||||
name: 'Certificate Transparency',
|
||||
value: 'certificateTransparency',
|
||||
description: 'Get updates about Certificate Transparency',
|
||||
},
|
||||
{
|
||||
name: 'Group',
|
||||
value: 'group',
|
||||
description: 'Get updates about activity in groups and events in groups for Workplace',
|
||||
},
|
||||
{
|
||||
name: 'Instagram',
|
||||
value: 'instagram',
|
||||
description: 'Get updates about comments on your media',
|
||||
},
|
||||
{
|
||||
name: 'Link',
|
||||
value: 'link',
|
||||
description: 'Get updates about links for rich previews by an external provider',
|
||||
},
|
||||
{
|
||||
name: 'Page',
|
||||
value: 'page',
|
||||
description: 'Page updates',
|
||||
},
|
||||
{
|
||||
name: 'Permissions',
|
||||
value: 'permissions',
|
||||
description: 'Updates regarding granting or revoking permissions',
|
||||
},
|
||||
{
|
||||
name: 'User',
|
||||
value: 'user',
|
||||
description: 'User profile updates',
|
||||
},
|
||||
{
|
||||
name: 'Whatsapp Business Account',
|
||||
value: 'whatsappBusinessAccount',
|
||||
description: 'Get updates about Whatsapp business account',
|
||||
},
|
||||
{
|
||||
name: 'Workplace Security',
|
||||
value: 'workplaceSecurity',
|
||||
description: 'Get updates about Workplace Security',
|
||||
},
|
||||
],
|
||||
required: true,
|
||||
default: 'user',
|
||||
description: 'The object to subscribe to',
|
||||
},
|
||||
{
|
||||
displayName: 'App ID',
|
||||
name: 'appId',
|
||||
type: 'string',
|
||||
required: true,
|
||||
default: '',
|
||||
description: 'Facebook APP ID',
|
||||
},
|
||||
{
|
||||
displayName: 'Options',
|
||||
name: 'options',
|
||||
type: 'collection',
|
||||
default: {},
|
||||
placeholder: 'Add option',
|
||||
options: [
|
||||
{
|
||||
displayName: 'Include values',
|
||||
name: 'includeValues',
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
description: 'Indicates if change notifications should include the new values.',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
// @ts-ignore (because of request)
|
||||
webhookMethods = {
|
||||
default: {
|
||||
async checkExists(this: IHookFunctions): Promise<boolean> {
|
||||
const webhookUrl = this.getNodeWebhookUrl('default') as string;
|
||||
const object = this.getNodeParameter('object') as string;
|
||||
const appId = this.getNodeParameter('appId') as string;
|
||||
|
||||
const { data } = await facebookApiRequest.call(this, 'GET', `/${appId}/subscriptions`, {});
|
||||
|
||||
for (const webhook of data) {
|
||||
if (webhook.target === webhookUrl && webhook.object === object && webhook.status === true) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
async create(this: IHookFunctions): Promise<boolean> {
|
||||
const webhookData = this.getWorkflowStaticData('node');
|
||||
const webhookUrl = this.getNodeWebhookUrl('default') as string;
|
||||
const object = this.getNodeParameter('object') as string;
|
||||
const appId = this.getNodeParameter('appId') as string;
|
||||
const options = this.getNodeParameter('options') as IDataObject;
|
||||
|
||||
const body = {
|
||||
object: snakeCase(object),
|
||||
callback_url: webhookUrl,
|
||||
verify_token: uuid(),
|
||||
} as IDataObject;
|
||||
|
||||
if (options.includeValues !== undefined) {
|
||||
body.include_values = options.includeValues;
|
||||
}
|
||||
|
||||
const responseData = await facebookApiRequest.call(this, 'POST', `/${appId}/subscriptions`, body);
|
||||
|
||||
webhookData.verifyToken = body.verify_token;
|
||||
|
||||
if (responseData.success !== true) {
|
||||
// Facebook did not return success, so something went wrong
|
||||
throw new Error('Facebook webhook creation response did not contain the expected data.');
|
||||
}
|
||||
return true;
|
||||
},
|
||||
async delete(this: IHookFunctions): Promise<boolean> {
|
||||
const appId = this.getNodeParameter('appId') as string;
|
||||
const object = this.getNodeParameter('object') as string;
|
||||
|
||||
try {
|
||||
await facebookApiRequest.call(this, 'DELETE', `/${appId}/subscriptions`, { object: snakeCase(object) });
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
async webhook(this: IWebhookFunctions): Promise<IWebhookResponseData> {
|
||||
const bodyData = this.getBodyData() as IDataObject;
|
||||
const query = this.getQueryData() as IDataObject;
|
||||
const res = this.getResponseObject();
|
||||
const req = this.getRequestObject();
|
||||
const headerData = this.getHeaderData() as IDataObject;
|
||||
const credentials = this.getCredentials('facebookGraphAppApi') as IDataObject;
|
||||
// Check if we're getting facebook's challenge request (https://developers.facebook.com/docs/graph-api/webhooks/getting-started)
|
||||
if (this.getWebhookName() === 'setup') {
|
||||
if (query['hub.challenge']) {
|
||||
//TODO
|
||||
//compare hub.verify_token with the saved token
|
||||
//const webhookData = this.getWorkflowStaticData('node');
|
||||
// if (webhookData.verifyToken !== query['hub.verify_token']) {
|
||||
// return {};
|
||||
// }
|
||||
res.status(200).send(query['hub.challenge']).end();
|
||||
return {
|
||||
noWebhookResponse: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// validate signature if app secret is set
|
||||
if (credentials.appSecret !== '') {
|
||||
//@ts-ignore
|
||||
const computedSignature = createHmac('sha1', credentials.appSecret as string).update(req.rawBody).digest('hex');
|
||||
if (headerData['x-hub-signature'] !== `sha1=${computedSignature}`) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
workflowData: [
|
||||
this.helpers.returnJsonArray(bodyData.entry as IDataObject[]),
|
||||
],
|
||||
};
|
||||
}
|
||||
}
|
53
packages/nodes-base/nodes/Facebook/GenericFunctions.ts
Normal file
53
packages/nodes-base/nodes/Facebook/GenericFunctions.ts
Normal file
|
@ -0,0 +1,53 @@
|
|||
import {
|
||||
OptionsWithUri,
|
||||
} from 'request';
|
||||
|
||||
import {
|
||||
IExecuteFunctions,
|
||||
IExecuteSingleFunctions,
|
||||
IHookFunctions,
|
||||
ILoadOptionsFunctions,
|
||||
IWebhookFunctions,
|
||||
} from 'n8n-core';
|
||||
|
||||
import {
|
||||
IDataObject,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
export async function facebookApiRequest(this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions | IWebhookFunctions, method: string, resource: string, body: any = {}, qs: IDataObject = {}, uri?: string, option: IDataObject = {}): Promise<any> { // tslint:disable-line:no-any
|
||||
|
||||
let credentials;
|
||||
|
||||
if (this.getNode().name.includes('Trigger')) {
|
||||
credentials = this.getCredentials('facebookGraphAppApi') as IDataObject;
|
||||
} else {
|
||||
credentials = this.getCredentials('facebookGraphApi') as IDataObject;
|
||||
}
|
||||
|
||||
qs.access_token = credentials!.accessToken;
|
||||
|
||||
const options: OptionsWithUri = {
|
||||
headers: {
|
||||
accept: 'application/json,text/*;q=0.99',
|
||||
},
|
||||
method,
|
||||
qs,
|
||||
body,
|
||||
gzip: true,
|
||||
uri: uri ||`https://graph.facebook.com/v8.0${resource}`,
|
||||
json: true,
|
||||
};
|
||||
|
||||
try {
|
||||
return await this.helpers.request!(options);
|
||||
} catch (error) {
|
||||
|
||||
if (error.response.body && error.response.body.error) {
|
||||
const message = error.response.body.error.message;
|
||||
throw new Error(
|
||||
`Facebook Trigger error response [${error.statusCode}]: ${message}`,
|
||||
);
|
||||
}
|
||||
throw new Error(error);
|
||||
}
|
||||
}
|
Binary file not shown.
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 2.4 KiB |
92
packages/nodes-base/nodes/Storyblok/GenericFunctions.ts
Normal file
92
packages/nodes-base/nodes/Storyblok/GenericFunctions.ts
Normal file
|
@ -0,0 +1,92 @@
|
|||
import {
|
||||
OptionsWithUri,
|
||||
} from 'request';
|
||||
|
||||
import {
|
||||
IExecuteFunctions,
|
||||
IExecuteSingleFunctions,
|
||||
IHookFunctions,
|
||||
ILoadOptionsFunctions,
|
||||
} from 'n8n-core';
|
||||
|
||||
import {
|
||||
IDataObject,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
export async function storyblokApiRequest(this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method: string, resource: string, body: any = {}, qs: IDataObject = {}, option: IDataObject = {}): Promise<any> { // tslint:disable-line:no-any
|
||||
const authenticationMethod = this.getNodeParameter('source', 0) as string;
|
||||
|
||||
let options: OptionsWithUri = {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
method,
|
||||
qs,
|
||||
body,
|
||||
uri: '',
|
||||
json: true,
|
||||
};
|
||||
|
||||
options = Object.assign({}, options, option);
|
||||
|
||||
if (Object.keys(options.body).length === 0) {
|
||||
delete options.body;
|
||||
}
|
||||
|
||||
if (authenticationMethod === 'contentApi') {
|
||||
const credentials = this.getCredentials('storyblokContentApi') as IDataObject;
|
||||
|
||||
options.uri = `https://api.storyblok.com${resource}`;
|
||||
|
||||
Object.assign(options.qs, { token: credentials.apiKey });
|
||||
} else {
|
||||
const credentials = this.getCredentials('storyblokManagementApi') as IDataObject;
|
||||
|
||||
options.uri = `https://mapi.storyblok.com${resource}`;
|
||||
|
||||
Object.assign(options.headers, { 'Authorization': credentials.accessToken });
|
||||
}
|
||||
|
||||
try {
|
||||
return this.helpers.request!(options);
|
||||
} catch (error) {
|
||||
if (error.response && error.response.body && error.response.body.message) {
|
||||
// Try to return the error prettier
|
||||
const errorBody = error.response.body;
|
||||
throw new Error(`Storyblok error response [${error.statusCode}]: ${errorBody.message}`);
|
||||
}
|
||||
|
||||
// Expected error data did not get returned so throw the actual error
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export async function storyblokApiRequestAllItems(this: IHookFunctions | ILoadOptionsFunctions | IExecuteFunctions, propertyName: string, method: string, resource: string, body: any = {}, query: IDataObject = {}): Promise<any> { // tslint:disable-line:no-any
|
||||
const returnData: IDataObject[] = [];
|
||||
|
||||
let responseData;
|
||||
|
||||
query.per_page = 100;
|
||||
|
||||
query.page = 1;
|
||||
|
||||
do {
|
||||
responseData = await storyblokApiRequest.call(this, method, resource, body, query);
|
||||
query.page++;
|
||||
returnData.push.apply(returnData, responseData[propertyName]);
|
||||
} while (
|
||||
responseData[propertyName].length !== 0
|
||||
);
|
||||
|
||||
return returnData;
|
||||
}
|
||||
|
||||
export function validateJSON(json: string | undefined): any { // tslint:disable-line:no-any
|
||||
let result;
|
||||
try {
|
||||
result = JSON.parse(json!);
|
||||
} catch (exception) {
|
||||
result = undefined;
|
||||
}
|
||||
return result;
|
||||
}
|
143
packages/nodes-base/nodes/Storyblok/StoryContentDescription.ts
Normal file
143
packages/nodes-base/nodes/Storyblok/StoryContentDescription.ts
Normal file
|
@ -0,0 +1,143 @@
|
|||
import {
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
export const storyContentOperations = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
displayOptions: {
|
||||
show: {
|
||||
source: [
|
||||
'contentApi',
|
||||
],
|
||||
resource: [
|
||||
'story',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Get',
|
||||
value: 'get',
|
||||
description: 'Get a story',
|
||||
},
|
||||
{
|
||||
name: 'Get All',
|
||||
value: 'getAll',
|
||||
description: 'Get all stories',
|
||||
},
|
||||
],
|
||||
default: 'get',
|
||||
description: 'The operation to perform.',
|
||||
},
|
||||
] as INodeProperties[];
|
||||
|
||||
export const storyContentFields = [
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* story:get */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Identifier',
|
||||
name: 'identifier',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
source: [
|
||||
'contentApi',
|
||||
],
|
||||
resource: [
|
||||
'story',
|
||||
],
|
||||
operation: [
|
||||
'get',
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'The ID or slug of the story to get.',
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* story:getAll */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Return All',
|
||||
name: 'returnAll',
|
||||
type: 'boolean',
|
||||
displayOptions: {
|
||||
show: {
|
||||
source: [
|
||||
'contentApi',
|
||||
],
|
||||
resource: [
|
||||
'story',
|
||||
],
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: false,
|
||||
description: 'Returns a list of your user contacts.',
|
||||
},
|
||||
{
|
||||
displayName: 'Limit',
|
||||
name: 'limit',
|
||||
type: 'number',
|
||||
displayOptions: {
|
||||
show: {
|
||||
source: [
|
||||
'contentApi',
|
||||
],
|
||||
resource: [
|
||||
'story',
|
||||
],
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
returnAll: [
|
||||
false,
|
||||
],
|
||||
},
|
||||
},
|
||||
typeOptions: {
|
||||
minValue: 1,
|
||||
maxValue: 100,
|
||||
},
|
||||
default: 50,
|
||||
description: 'How many results to return.',
|
||||
},
|
||||
{
|
||||
displayName: 'Filters',
|
||||
name: 'filters',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Filter',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
source: [
|
||||
'contentApi',
|
||||
],
|
||||
resource: [
|
||||
'story',
|
||||
],
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Starts With',
|
||||
name: 'starts_with',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Filter by slug.',
|
||||
},
|
||||
],
|
||||
},
|
||||
] as INodeProperties[];
|
|
@ -0,0 +1,647 @@
|
|||
import {
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
export const storyManagementOperations = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
displayOptions: {
|
||||
show: {
|
||||
source: [
|
||||
'managementApi',
|
||||
],
|
||||
resource: [
|
||||
'story',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
// {
|
||||
// name: 'Create',
|
||||
// value: 'create',
|
||||
// description: 'Create a story',
|
||||
// },
|
||||
{
|
||||
name: 'Delete',
|
||||
value: 'delete',
|
||||
description: 'Delete a story',
|
||||
},
|
||||
{
|
||||
name: 'Get',
|
||||
value: 'get',
|
||||
description: 'Get a story',
|
||||
},
|
||||
{
|
||||
name: 'Get All',
|
||||
value: 'getAll',
|
||||
description: 'Get all stories',
|
||||
},
|
||||
{
|
||||
name: 'Publish',
|
||||
value: 'publish',
|
||||
description: 'Publish a story',
|
||||
},
|
||||
{
|
||||
name: 'Unpublish',
|
||||
value: 'unpublish',
|
||||
description: 'Unpublish a story',
|
||||
},
|
||||
],
|
||||
default: 'get',
|
||||
description: 'The operation to perform.',
|
||||
},
|
||||
] as INodeProperties[];
|
||||
|
||||
export const storyManagementFields = [
|
||||
|
||||
// /* -------------------------------------------------------------------------- */
|
||||
// /* story:create */
|
||||
// /* -------------------------------------------------------------------------- */
|
||||
// {
|
||||
// displayName: 'Space ID',
|
||||
// name: 'space',
|
||||
// type: 'options',
|
||||
// typeOptions: {
|
||||
// loadOptionsMethod: 'getSpaces',
|
||||
// },
|
||||
// default: '',
|
||||
// required: true,
|
||||
// displayOptions: {
|
||||
// show: {
|
||||
// source: [
|
||||
// 'managementApi',
|
||||
// ],
|
||||
// resource: [
|
||||
// 'story',
|
||||
// ],
|
||||
// operation: [
|
||||
// 'create',
|
||||
// ],
|
||||
// },
|
||||
// },
|
||||
// description: 'The name of the space.',
|
||||
// },
|
||||
// {
|
||||
// displayName: 'Name',
|
||||
// name: 'name',
|
||||
// type: 'string',
|
||||
// default: '',
|
||||
// required: true,
|
||||
// displayOptions: {
|
||||
// show: {
|
||||
// source: [
|
||||
// 'managementApi',
|
||||
// ],
|
||||
// resource: [
|
||||
// 'story',
|
||||
// ],
|
||||
// operation: [
|
||||
// 'create',
|
||||
// ],
|
||||
// },
|
||||
// },
|
||||
// description: 'The name you give this story.',
|
||||
// },
|
||||
// {
|
||||
// displayName: 'Slug',
|
||||
// name: 'slug',
|
||||
// type: 'string',
|
||||
// default: '',
|
||||
// required: true,
|
||||
// displayOptions: {
|
||||
// show: {
|
||||
// source: [
|
||||
// 'managementApi',
|
||||
// ],
|
||||
// resource: [
|
||||
// 'story',
|
||||
// ],
|
||||
// operation: [
|
||||
// 'create',
|
||||
// ],
|
||||
// },
|
||||
// },
|
||||
// description: 'The slug/path you give this story.',
|
||||
// },
|
||||
// {
|
||||
// displayName: 'JSON Parameters',
|
||||
// name: 'jsonParameters',
|
||||
// type: 'boolean',
|
||||
// default: false,
|
||||
// description: '',
|
||||
// displayOptions: {
|
||||
// show: {
|
||||
// source: [
|
||||
// 'managementApi',
|
||||
// ],
|
||||
// resource: [
|
||||
// 'story',
|
||||
// ],
|
||||
// operation: [
|
||||
// 'create',
|
||||
// ],
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// displayName: 'Additional Fields',
|
||||
// name: 'additionalFields',
|
||||
// type: 'collection',
|
||||
// placeholder: 'Add Field',
|
||||
// displayOptions: {
|
||||
// show: {
|
||||
// source: [
|
||||
// 'managementApi',
|
||||
// ],
|
||||
// resource: [
|
||||
// 'story',
|
||||
// ],
|
||||
// operation: [
|
||||
// 'create',
|
||||
// ],
|
||||
// },
|
||||
// },
|
||||
// default: {},
|
||||
// options: [
|
||||
// {
|
||||
// displayName: 'Content',
|
||||
// name: 'contentUi',
|
||||
// type: 'fixedCollection',
|
||||
// description: 'Add Content',
|
||||
// typeOptions: {
|
||||
// multipleValues: false,
|
||||
// },
|
||||
// displayOptions: {
|
||||
// show: {
|
||||
// '/jsonParameters': [
|
||||
// false,
|
||||
// ],
|
||||
// },
|
||||
// },
|
||||
// placeholder: 'Add Content',
|
||||
// default: '',
|
||||
// options: [
|
||||
// {
|
||||
// displayName: 'Content Data',
|
||||
// name: 'contentValue',
|
||||
// values: [
|
||||
// {
|
||||
// displayName: 'Component',
|
||||
// name: 'component',
|
||||
// type: 'options',
|
||||
// typeOptions: {
|
||||
// loadOptionsMethod: 'getComponents',
|
||||
// loadOptionsDependsOn: [
|
||||
// 'space',
|
||||
// ],
|
||||
// },
|
||||
// default: '',
|
||||
// },
|
||||
// {
|
||||
// displayName: 'Elements',
|
||||
// name: 'elementUi',
|
||||
// type: 'fixedCollection',
|
||||
// description: 'Add Body',
|
||||
// typeOptions: {
|
||||
// multipleValues: true,
|
||||
// },
|
||||
// placeholder: 'Add Element',
|
||||
// default: '',
|
||||
// options: [
|
||||
// {
|
||||
// displayName: 'Element',
|
||||
// name: 'elementValues',
|
||||
// values: [
|
||||
// {
|
||||
// displayName: 'Component',
|
||||
// name: 'component',
|
||||
// type: 'options',
|
||||
// typeOptions: {
|
||||
// loadOptionsMethod: 'getComponents',
|
||||
// loadOptionsDependsOn: [
|
||||
// 'space',
|
||||
// ],
|
||||
// },
|
||||
// default: '',
|
||||
// },
|
||||
// {
|
||||
// displayName: 'Element Data',
|
||||
// name: 'dataUi',
|
||||
// type: 'fixedCollection',
|
||||
// description: 'Add Data',
|
||||
// typeOptions: {
|
||||
// multipleValues: true,
|
||||
// },
|
||||
// placeholder: 'Add Data',
|
||||
// default: '',
|
||||
// options: [
|
||||
// {
|
||||
// displayName: 'Data',
|
||||
// name: 'dataValues',
|
||||
// values: [
|
||||
// {
|
||||
// displayName: 'Key',
|
||||
// name: 'key',
|
||||
// type: 'string',
|
||||
// default: '',
|
||||
// },
|
||||
// {
|
||||
// displayName: 'Value',
|
||||
// name: 'value',
|
||||
// type: 'string',
|
||||
// default: '',
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// {
|
||||
// displayName: 'Content (JSON)',
|
||||
// name: 'contentJson',
|
||||
// type: 'string',
|
||||
// displayOptions: {
|
||||
// show: {
|
||||
// '/jsonParameters': [
|
||||
// true,
|
||||
// ],
|
||||
// },
|
||||
// },
|
||||
// default: '',
|
||||
// },
|
||||
// {
|
||||
// displayName: 'Parent ID',
|
||||
// name: 'parentId',
|
||||
// type: 'string',
|
||||
// default: '',
|
||||
// description: 'Parent story/folder numeric ID.',
|
||||
// },
|
||||
// {
|
||||
// displayName: 'Path',
|
||||
// name: 'path',
|
||||
// type: 'string',
|
||||
// default: '',
|
||||
// description: 'Given real path, used in the preview editor.',
|
||||
// },
|
||||
// {
|
||||
// displayName: 'Is Startpage',
|
||||
// name: 'isStartpage',
|
||||
// type: 'boolean',
|
||||
// default: false,
|
||||
// description: 'Is startpage of current folder.',
|
||||
// },
|
||||
// {
|
||||
// displayName: 'First Published At',
|
||||
// name: 'firstPublishedAt',
|
||||
// type: 'dateTime',
|
||||
// default: '',
|
||||
// description: 'First publishing date.',
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* story:delete */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Space ID',
|
||||
name: 'space',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getSpaces',
|
||||
},
|
||||
default: '',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
source: [
|
||||
'managementApi',
|
||||
],
|
||||
resource: [
|
||||
'story',
|
||||
],
|
||||
operation: [
|
||||
'delete',
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'The name of the space.',
|
||||
},
|
||||
{
|
||||
displayName: 'Story ID',
|
||||
name: 'storyId',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
source: [
|
||||
'managementApi',
|
||||
],
|
||||
resource: [
|
||||
'story',
|
||||
],
|
||||
operation: [
|
||||
'delete',
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'Numeric ID of the story.',
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* story:get */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Space ID',
|
||||
name: 'space',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getSpaces',
|
||||
},
|
||||
default: '',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
source: [
|
||||
'managementApi',
|
||||
],
|
||||
resource: [
|
||||
'story',
|
||||
],
|
||||
operation: [
|
||||
'get',
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'The name of the space.',
|
||||
},
|
||||
{
|
||||
displayName: 'Story ID',
|
||||
name: 'storyId',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
source: [
|
||||
'managementApi',
|
||||
],
|
||||
resource: [
|
||||
'story',
|
||||
],
|
||||
operation: [
|
||||
'get',
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'Numeric ID of the story.',
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* story:getAll */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Space ID',
|
||||
name: 'space',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getSpaces',
|
||||
},
|
||||
default: '',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
source: [
|
||||
'managementApi',
|
||||
],
|
||||
resource: [
|
||||
'story',
|
||||
],
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'The name of the space',
|
||||
},
|
||||
{
|
||||
displayName: 'Return All',
|
||||
name: 'returnAll',
|
||||
type: 'boolean',
|
||||
displayOptions: {
|
||||
show: {
|
||||
source: [
|
||||
'managementApi',
|
||||
],
|
||||
resource: [
|
||||
'story',
|
||||
],
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: false,
|
||||
description: 'Returns a list of your user contacts.',
|
||||
},
|
||||
{
|
||||
displayName: 'Limit',
|
||||
name: 'limit',
|
||||
type: 'number',
|
||||
displayOptions: {
|
||||
show: {
|
||||
source: [
|
||||
'managementApi',
|
||||
],
|
||||
resource: [
|
||||
'story',
|
||||
],
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
returnAll: [
|
||||
false,
|
||||
],
|
||||
},
|
||||
},
|
||||
typeOptions: {
|
||||
minValue: 1,
|
||||
maxValue: 100,
|
||||
},
|
||||
default: 50,
|
||||
description: 'How many results to return.',
|
||||
},
|
||||
{
|
||||
displayName: 'Filters',
|
||||
name: 'filters',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Filter',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
source: [
|
||||
'managementApi',
|
||||
],
|
||||
resource: [
|
||||
'story',
|
||||
],
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Starts With',
|
||||
name: 'starts_with',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Filter by slug.',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* story:publish */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Space ID',
|
||||
name: 'space',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getSpaces',
|
||||
},
|
||||
default: '',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
source: [
|
||||
'managementApi',
|
||||
],
|
||||
resource: [
|
||||
'story',
|
||||
],
|
||||
operation: [
|
||||
'publish',
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'The name of the space.',
|
||||
},
|
||||
{
|
||||
displayName: 'Story ID',
|
||||
name: 'storyId',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
source: [
|
||||
'managementApi',
|
||||
],
|
||||
resource: [
|
||||
'story',
|
||||
],
|
||||
operation: [
|
||||
'publish',
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'Numeric ID of the story.',
|
||||
},
|
||||
{
|
||||
displayName: 'Options',
|
||||
name: 'options',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Option',
|
||||
displayOptions: {
|
||||
show: {
|
||||
source: [
|
||||
'managementApi',
|
||||
],
|
||||
resource: [
|
||||
'story',
|
||||
],
|
||||
operation: [
|
||||
'publish',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Release ID',
|
||||
name: 'releaseId',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Numeric ID of release.',
|
||||
},
|
||||
{
|
||||
displayName: 'Language',
|
||||
name: 'language',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Language code to publish the story individually (must be enabled in the space settings).',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* story:unpublish */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Space ID',
|
||||
name: 'space',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getSpaces',
|
||||
},
|
||||
default: '',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
source: [
|
||||
'managementApi',
|
||||
],
|
||||
resource: [
|
||||
'story',
|
||||
],
|
||||
operation: [
|
||||
'unpublish',
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'The name of the space.',
|
||||
},
|
||||
{
|
||||
displayName: 'Story ID',
|
||||
name: 'storyId',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
source: [
|
||||
'managementApi',
|
||||
],
|
||||
resource: [
|
||||
'story',
|
||||
],
|
||||
operation: [
|
||||
'unpublish',
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'Numeric ID of the story.',
|
||||
},
|
||||
] as INodeProperties[];
|
334
packages/nodes-base/nodes/Storyblok/Storyblok.node.ts
Normal file
334
packages/nodes-base/nodes/Storyblok/Storyblok.node.ts
Normal file
|
@ -0,0 +1,334 @@
|
|||
import {
|
||||
IExecuteFunctions,
|
||||
} from 'n8n-core';
|
||||
|
||||
import {
|
||||
IDataObject,
|
||||
ILoadOptionsFunctions,
|
||||
INodeExecutionData,
|
||||
INodePropertyOptions,
|
||||
INodeType,
|
||||
INodeTypeDescription,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import {
|
||||
storyblokApiRequest,
|
||||
storyblokApiRequestAllItems,
|
||||
validateJSON,
|
||||
} from './GenericFunctions';
|
||||
|
||||
import {
|
||||
storyContentFields,
|
||||
storyContentOperations,
|
||||
} from './StoryContentDescription';
|
||||
|
||||
import {
|
||||
storyManagementFields,
|
||||
storyManagementOperations,
|
||||
} from './StoryManagementDescription';
|
||||
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
export class Storyblok implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
displayName: 'Storyblok',
|
||||
name: 'storyblok',
|
||||
icon: 'file:storyblok.svg',
|
||||
group: ['output'],
|
||||
version: 1,
|
||||
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
||||
description: 'Consume Storyblok API',
|
||||
defaults: {
|
||||
name: 'Storyblok',
|
||||
color: '#09b3af',
|
||||
},
|
||||
inputs: ['main'],
|
||||
outputs: ['main'],
|
||||
credentials: [
|
||||
{
|
||||
name: 'storyblokContentApi',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
source: [
|
||||
'contentApi',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'storyblokManagementApi',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
source: [
|
||||
'managementApi',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
properties: [
|
||||
{
|
||||
displayName: 'Source',
|
||||
name: 'source',
|
||||
type: 'options',
|
||||
default: 'contentApi',
|
||||
description: 'Pick where your data comes from, Content or Management API',
|
||||
options: [
|
||||
{
|
||||
name: 'Content API',
|
||||
value: 'contentApi',
|
||||
},
|
||||
{
|
||||
name: 'Management API',
|
||||
value: 'managementApi',
|
||||
},
|
||||
],
|
||||
},
|
||||
// Resources: Content API
|
||||
{
|
||||
displayName: 'Resource',
|
||||
name: 'resource',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Story',
|
||||
value: 'story',
|
||||
},
|
||||
],
|
||||
default: 'story',
|
||||
description: 'Resource to consume.',
|
||||
displayOptions: {
|
||||
show: {
|
||||
source: [
|
||||
'contentApi',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
// Resources: Management API
|
||||
{
|
||||
displayName: 'Resource',
|
||||
name: 'resource',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Story',
|
||||
value: 'story',
|
||||
},
|
||||
],
|
||||
default: 'story',
|
||||
description: 'Resource to consume.',
|
||||
displayOptions: {
|
||||
show: {
|
||||
source: [
|
||||
'managementApi',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
// Content API - Story
|
||||
...storyContentOperations,
|
||||
...storyContentFields,
|
||||
// Management API - Story
|
||||
...storyManagementOperations,
|
||||
...storyManagementFields,
|
||||
],
|
||||
};
|
||||
|
||||
methods = {
|
||||
loadOptions: {
|
||||
async getSpaces(
|
||||
this: ILoadOptionsFunctions,
|
||||
): Promise<INodePropertyOptions[]> {
|
||||
const returnData: INodePropertyOptions[] = [];
|
||||
const { spaces } = await storyblokApiRequest.call(
|
||||
this,
|
||||
'GET',
|
||||
'/v1/spaces',
|
||||
);
|
||||
for (const space of spaces) {
|
||||
returnData.push({
|
||||
name: space.name,
|
||||
value: space.id,
|
||||
});
|
||||
}
|
||||
return returnData;
|
||||
},
|
||||
async getComponents(
|
||||
this: ILoadOptionsFunctions,
|
||||
): Promise<INodePropertyOptions[]> {
|
||||
const returnData: INodePropertyOptions[] = [];
|
||||
const space = this.getCurrentNodeParameter('space') as string;
|
||||
const { components } = await storyblokApiRequest.call(
|
||||
this,
|
||||
'GET',
|
||||
`/v1/spaces/${space}/components`,
|
||||
);
|
||||
for (const component of components) {
|
||||
returnData.push({
|
||||
name: `${component.name} ${(component.is_root ? '(root)' : '')}`,
|
||||
value: component.name,
|
||||
});
|
||||
}
|
||||
return returnData;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
||||
const items = this.getInputData();
|
||||
const returnData: IDataObject[] = [];
|
||||
const length = items.length as unknown as number;
|
||||
const qs: IDataObject = {};
|
||||
let responseData;
|
||||
const source = this.getNodeParameter('source', 0) as string;
|
||||
const resource = this.getNodeParameter('resource', 0) as string;
|
||||
const operation = this.getNodeParameter('operation', 0) as string;
|
||||
for (let i = 0; i < length; i++) {
|
||||
if (source === 'contentApi') {
|
||||
if (resource === 'story') {
|
||||
if (operation === 'get') {
|
||||
const identifier = this.getNodeParameter('identifier', i) as string;
|
||||
|
||||
responseData = await storyblokApiRequest.call(this, 'GET', `/v1/cdn/stories/${identifier}`);
|
||||
responseData = responseData.story;
|
||||
}
|
||||
if (operation === 'getAll') {
|
||||
const filters = this.getNodeParameter('filters', i) as string;
|
||||
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
|
||||
Object.assign(qs, filters);
|
||||
|
||||
if (returnAll) {
|
||||
responseData = await storyblokApiRequestAllItems.call(this, 'stories', 'GET', '/v1/cdn/stories', {}, qs);
|
||||
} else {
|
||||
const limit = this.getNodeParameter('limit', i) as number;
|
||||
qs.per_page = limit;
|
||||
responseData = await storyblokApiRequest.call(this, 'GET', `/v1/cdn/stories`, {}, qs);
|
||||
responseData = responseData.stories;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (source === 'managementApi') {
|
||||
if (resource === 'story') {
|
||||
// if (operation === 'create') {
|
||||
// const space = this.getNodeParameter('space', i) as string;
|
||||
// const name = this.getNodeParameter('name', i) as string;
|
||||
// const slug = this.getNodeParameter('slug', i) as string;
|
||||
// const jsonParameters = this.getNodeParameter('jsonParameters', i) as boolean;
|
||||
// const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
|
||||
// const body: IDataObject = {
|
||||
// name,
|
||||
// slug,
|
||||
// };
|
||||
|
||||
// if (jsonParameters) {
|
||||
// if (additionalFields.contentJson) {
|
||||
// const json = validateJSON(additionalFields.contentJson as string);
|
||||
// body.content = json;
|
||||
// }
|
||||
// } else {
|
||||
// if (additionalFields.contentUi) {
|
||||
// const contentValue = (additionalFields.contentUi as IDataObject).contentValue as IDataObject;
|
||||
// const content: { component: string, body: IDataObject[] } = { component: '', body: [] };
|
||||
// if (contentValue) {
|
||||
// content.component = contentValue.component as string;
|
||||
// const elementValues = (contentValue.elementUi as IDataObject).elementValues as IDataObject[];
|
||||
// for (const elementValue of elementValues) {
|
||||
// const body: IDataObject = {};
|
||||
// body._uid = uuidv4();
|
||||
// body.component = elementValue.component;
|
||||
// if (elementValue.dataUi) {
|
||||
// const dataValues = (elementValue.dataUi as IDataObject).dataValues as IDataObject[];
|
||||
// for (const dataValue of dataValues) {
|
||||
// body[dataValue.key as string] = dataValue.value;
|
||||
// }
|
||||
// }
|
||||
// content.body.push(body);
|
||||
// }
|
||||
// }
|
||||
// body.content = content;
|
||||
// }
|
||||
// }
|
||||
|
||||
// if (additionalFields.parentId) {
|
||||
// body.parent_id = additionalFields.parentId as string;
|
||||
// }
|
||||
// if (additionalFields.path) {
|
||||
// body.path = additionalFields.path as string;
|
||||
// }
|
||||
// if (additionalFields.isStartpage) {
|
||||
// body.is_startpage = additionalFields.isStartpage as string;
|
||||
// }
|
||||
// if (additionalFields.firstPublishedAt) {
|
||||
// body.first_published_at = additionalFields.firstPublishedAt as string;
|
||||
// }
|
||||
|
||||
// responseData = await storyblokApiRequest.call(this, 'POST', `/v1/spaces/${space}/stories`, { story: body });
|
||||
// responseData = responseData.story;
|
||||
// }
|
||||
if (operation === 'delete') {
|
||||
const space = this.getNodeParameter('space', i) as string;
|
||||
const storyId = this.getNodeParameter('storyId', i) as string;
|
||||
|
||||
responseData = await storyblokApiRequest.call(this, 'DELETE', `/v1/spaces/${space}/stories/${storyId}`);
|
||||
responseData = responseData.story;
|
||||
}
|
||||
if (operation === 'get') {
|
||||
const space = this.getNodeParameter('space', i) as string;
|
||||
const storyId = this.getNodeParameter('storyId', i) as string;
|
||||
|
||||
responseData = await storyblokApiRequest.call(this, 'GET', `/v1/spaces/${space}/stories/${storyId}`);
|
||||
responseData = responseData.story;
|
||||
}
|
||||
if (operation === 'getAll') {
|
||||
const space = this.getNodeParameter('space', i) as string;
|
||||
const filters = this.getNodeParameter('filters', i) as string;
|
||||
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
|
||||
Object.assign(qs, filters);
|
||||
|
||||
if (returnAll) {
|
||||
responseData = await storyblokApiRequestAllItems.call(this, 'stories', 'GET', `/v1/spaces/${space}/stories`, {}, qs);
|
||||
} else {
|
||||
const limit = this.getNodeParameter('limit', i) as number;
|
||||
qs.per_page = limit;
|
||||
responseData = await storyblokApiRequest.call(this, 'GET', `/v1/spaces/${space}/stories`, {}, qs);
|
||||
responseData = responseData.stories;
|
||||
}
|
||||
}
|
||||
if (operation === 'publish') {
|
||||
const space = this.getNodeParameter('space', i) as string;
|
||||
const storyId = this.getNodeParameter('storyId', i) as string;
|
||||
const options = this.getNodeParameter('options', i) as IDataObject;
|
||||
const query: IDataObject = {};
|
||||
// Not sure if these two options work
|
||||
if (options.releaseId) {
|
||||
query.release_id = options.releaseId as string;
|
||||
}
|
||||
if (options.language) {
|
||||
query.lang = options.language as string;
|
||||
}
|
||||
|
||||
responseData = await storyblokApiRequest.call(this, 'GET', `/v1/spaces/${space}/stories/${storyId}/publish`, {}, query);
|
||||
responseData = responseData.story;
|
||||
}
|
||||
if (operation === 'unpublish') {
|
||||
const space = this.getNodeParameter('space', i) as string;
|
||||
const storyId = this.getNodeParameter('storyId', i) as string;
|
||||
|
||||
responseData = await storyblokApiRequest.call(this, 'GET', `/v1/spaces/${space}/stories/${storyId}/unpublish`);
|
||||
responseData = responseData.story;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Array.isArray(responseData)) {
|
||||
returnData.push.apply(returnData, responseData as IDataObject[]);
|
||||
} else {
|
||||
returnData.push(responseData as IDataObject);
|
||||
}
|
||||
}
|
||||
return [this.helpers.returnJsonArray(returnData)];
|
||||
}
|
||||
}
|
17
packages/nodes-base/nodes/Storyblok/storyblok.svg
Normal file
17
packages/nodes-base/nodes/Storyblok/storyblok.svg
Normal file
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="87px" height="103px" viewBox="0 0 87 103" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 51.3 (57544) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>colored-standalone</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs></defs>
|
||||
<g id="colored-standalone" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="logo" fill="#09B3AF" fill-rule="nonzero">
|
||||
<path d="M51.8,49 L31,49 L31,59 L51.3,59 C52.5,59 53.6,58.5 54.5,57.7 C55.3,56.9 55.8,55.7 55.8,54.2 C55.8290035,52.9130708 55.4433501,51.6509323 54.7,50.6 C53.9,49.6 53,49 51.8,49 Z M52.5,36.9 C53.4,36.3 53.8,35 53.8,33.3 C53.8,31.8 53.4,30.7 52.7,30 C52,29.4 51.1,29 50.1,29 L31,29 L31,38 L49.7,38 C50.7,38 51.7,37.5 52.5,36.9 Z" id="Shape"></path>
|
||||
<path d="M83,0 L4.5,0 C2,0 0,2 0,4.4 L0,83 C0,85.4 2,86.9 4.4,86.9 L16,86.9 L16,102.6 L30.4,87 L83,87 C85.4,87 86.9,85.5 86.9,83 L86.9,4.5 C86.9,2.1 85.4,0.1 82.9,0.1 L83,0 Z M69.8,63.7 C68.8,65.5 67.3,67 65.5,68.1 C63.6,69.3 61.5,70.4 59.1,70.9 C56.7,71.5 54.1,72 51.4,72 L16,72 L16,16 L56.2,16 C58.2,16 59.9,16.4 61.5,17.3 C63,18.1 64.4,19.2 65.5,20.5 C67.7403434,23.2320077 68.9444137,26.6671496 68.9,30.2 C68.9,32.8 68.2,35.3 66.9,37.7 C65.5522265,40.1140117 63.4421536,42.0130773 60.9,43.1 C64.1,44 66.6,45.6 68.5,47.9 C70.3,50.3 71.2,53.4 71.2,57.3 C71.2,59.8 70.7,61.9 69.7,63.7 L69.8,63.7 Z" id="Shape"></path>
|
||||
</g>
|
||||
<g id="logo" fill="#09B3AF" fill-rule="nonzero">
|
||||
<path d="M51.8,49 L31,49 L31,59 L51.3,59 C52.5,59 53.6,58.5 54.5,57.7 C55.3,56.9 55.8,55.7 55.8,54.2 C55.8290035,52.9130708 55.4433501,51.6509323 54.7,50.6 C53.9,49.6 53,49 51.8,49 Z M52.5,36.9 C53.4,36.3 53.8,35 53.8,33.3 C53.8,31.8 53.4,30.7 52.7,30 C52,29.4 51.1,29 50.1,29 L31,29 L31,38 L49.7,38 C50.7,38 51.7,37.5 52.5,36.9 Z" id="Shape"></path>
|
||||
<path d="M83,0 L4.5,0 C2,0 0,2 0,4.4 L0,83 C0,85.4 2,86.9 4.4,86.9 L16,86.9 L16,102.6 L30.4,87 L83,87 C85.4,87 86.9,85.5 86.9,83 L86.9,4.5 C86.9,2.1 85.4,0.1 82.9,0.1 L83,0 Z M69.8,63.7 C68.8,65.5 67.3,67 65.5,68.1 C63.6,69.3 61.5,70.4 59.1,70.9 C56.7,71.5 54.1,72 51.4,72 L16,72 L16,16 L56.2,16 C58.2,16 59.9,16.4 61.5,17.3 C63,18.1 64.4,19.2 65.5,20.5 C67.7403434,23.2320077 68.9444137,26.6671496 68.9,30.2 C68.9,32.8 68.2,35.3 66.9,37.7 C65.5522265,40.1140117 63.4421536,42.0130773 60.9,43.1 C64.1,44 66.6,45.6 68.5,47.9 C70.3,50.3 71.2,53.4 71.2,57.3 C71.2,59.8 70.7,61.9 69.7,63.7 L69.8,63.7 Z" id="Shape"></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.5 KiB |
|
@ -66,6 +66,7 @@
|
|||
"dist/credentials/EventbriteApi.credentials.js",
|
||||
"dist/credentials/EventbriteOAuth2Api.credentials.js",
|
||||
"dist/credentials/FacebookGraphApi.credentials.js",
|
||||
"dist/credentials/FacebookGraphAppApi.credentials.js",
|
||||
"dist/credentials/FreshdeskApi.credentials.js",
|
||||
"dist/credentials/FileMaker.credentials.js",
|
||||
"dist/credentials/FlowApi.credentials.js",
|
||||
|
@ -171,6 +172,10 @@
|
|||
"dist/credentials/StravaOAuth2Api.credentials.js",
|
||||
"dist/credentials/StripeApi.credentials.js",
|
||||
"dist/credentials/Sftp.credentials.js",
|
||||
"dist/credentials/Signl4Api.credentials.js",
|
||||
"dist/credentials/SpotifyOAuth2Api.credentials.js",
|
||||
"dist/credentials/StoryblokContentApi.credentials.js",
|
||||
"dist/credentials/StoryblokManagementApi.credentials.js",
|
||||
"dist/credentials/SurveyMonkeyApi.credentials.js",
|
||||
"dist/credentials/SurveyMonkeyOAuth2Api.credentials.js",
|
||||
"dist/credentials/TaigaCloudApi.credentials.js",
|
||||
|
@ -263,6 +268,7 @@
|
|||
"dist/nodes/ExecuteCommand.node.js",
|
||||
"dist/nodes/ExecuteWorkflow.node.js",
|
||||
"dist/nodes/Facebook/FacebookGraphApi.node.js",
|
||||
"dist/nodes/Facebook/FacebookTrigger.node.js",
|
||||
"dist/nodes/FileMaker/FileMaker.node.js",
|
||||
"dist/nodes/Ftp.node.js",
|
||||
"dist/nodes/Freshdesk/Freshdesk.node.js",
|
||||
|
@ -373,6 +379,7 @@
|
|||
"dist/nodes/SpreadsheetFile.node.js",
|
||||
"dist/nodes/SseTrigger.node.js",
|
||||
"dist/nodes/Start.node.js",
|
||||
"dist/nodes/Storyblok/Storyblok.node.js",
|
||||
"dist/nodes/Strava/Strava.node.js",
|
||||
"dist/nodes/Strava/StravaTrigger.node.js",
|
||||
"dist/nodes/Stripe/StripeTrigger.node.js",
|
||||
|
|
Loading…
Reference in a new issue