mirror of
https://github.com/n8n-io/n8n.git
synced 2024-12-26 21:19:43 -08:00
⚡ Improvements to Contenful-Node
This commit is contained in:
parent
6573642fc1
commit
bc0b349faa
36
packages/nodes-base/credentials/ContentfulApi.credentials.ts
Normal file
36
packages/nodes-base/credentials/ContentfulApi.credentials.ts
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
import {
|
||||||
|
ICredentialType,
|
||||||
|
NodePropertyTypes,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
//https://www.contentful.com/developers/docs/references/authentication/
|
||||||
|
export class ContentfulApi implements ICredentialType {
|
||||||
|
name = 'contentfulApi';
|
||||||
|
displayName = 'Contenful API';
|
||||||
|
properties = [
|
||||||
|
{
|
||||||
|
displayName: 'Space ID',
|
||||||
|
name: 'spaceId',
|
||||||
|
type: 'string' as NodePropertyTypes,
|
||||||
|
default: '',
|
||||||
|
required: true,
|
||||||
|
description: 'The id for the Contentful space.'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Content Delivery API Access token',
|
||||||
|
name: 'ContentDeliveryaccessToken',
|
||||||
|
type: 'string' as NodePropertyTypes,
|
||||||
|
default: '',
|
||||||
|
required: true,
|
||||||
|
description: 'Access token that has access to the space'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Content Preview API Access token',
|
||||||
|
name: 'ContentPreviewaccessToken',
|
||||||
|
type: 'string' as NodePropertyTypes,
|
||||||
|
default: '',
|
||||||
|
required: true,
|
||||||
|
description: 'Access token that has access to the space'
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
|
@ -1,31 +0,0 @@
|
||||||
import { ICredentialType, NodePropertyTypes } from 'n8n-workflow';
|
|
||||||
|
|
||||||
export class ContentfulDeliveryApi implements ICredentialType {
|
|
||||||
name = 'contentfulDeliveryApi';
|
|
||||||
displayName = 'Delivery API';
|
|
||||||
properties = [
|
|
||||||
{
|
|
||||||
displayName: 'Space Id',
|
|
||||||
name: 'space_id',
|
|
||||||
type: 'string' as NodePropertyTypes,
|
|
||||||
default: '',
|
|
||||||
required: true,
|
|
||||||
description: 'The id for the Contentful space.'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: 'Content Delivery API - access token',
|
|
||||||
name: 'access_token',
|
|
||||||
type: 'string' as NodePropertyTypes,
|
|
||||||
default: '',
|
|
||||||
required: true,
|
|
||||||
description: 'Access token that has access to the space'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
displayName: 'Content Preview API - access token',
|
|
||||||
name: 'access_token_preview',
|
|
||||||
type: 'string' as NodePropertyTypes,
|
|
||||||
default: '',
|
|
||||||
description: 'Access token that has access to the space'
|
|
||||||
}
|
|
||||||
];
|
|
||||||
}
|
|
|
@ -1,49 +1,197 @@
|
||||||
import { INodeProperties, INodePropertyOptions } from "n8n-workflow";
|
import {
|
||||||
|
INodeProperties,
|
||||||
|
INodePropertyOptions,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
export const resource = {
|
export const resource = {
|
||||||
name: "Asset",
|
name: 'Asset',
|
||||||
value: "asset",
|
value: 'asset',
|
||||||
} as INodePropertyOptions;
|
} as INodePropertyOptions;
|
||||||
|
|
||||||
export const operations = [
|
export const operations = [
|
||||||
{
|
{
|
||||||
displayName: "Operation",
|
displayName: 'Operation',
|
||||||
name: "operation",
|
name: 'operation',
|
||||||
type: "options",
|
type: 'options',
|
||||||
displayOptions: {
|
displayOptions: {
|
||||||
show: {
|
show: {
|
||||||
resource: [resource.value],
|
resource: [
|
||||||
},
|
resource.value,
|
||||||
},
|
],
|
||||||
options: [
|
},
|
||||||
{
|
},
|
||||||
name: "Get Assets",
|
options: [
|
||||||
value: "get_assets",
|
{
|
||||||
},
|
name: 'Get',
|
||||||
{
|
value: 'get',
|
||||||
name: "Get Single Asset",
|
},
|
||||||
value: "get_asset",
|
{
|
||||||
},
|
name: 'Get All',
|
||||||
],
|
value: 'getAll',
|
||||||
default: "get_assets",
|
},
|
||||||
description: "The operation to perform.",
|
],
|
||||||
},
|
default: 'getAll',
|
||||||
|
description: 'The operation to perform.',
|
||||||
|
},
|
||||||
] as INodeProperties[];
|
] as INodeProperties[];
|
||||||
|
|
||||||
export const fields = [
|
export const fields = [
|
||||||
{
|
{
|
||||||
displayName: "Asset Id",
|
displayName: 'Environment ID',
|
||||||
name: "asset_id",
|
name: 'environmentId',
|
||||||
type: "string",
|
type: 'string',
|
||||||
default: "",
|
displayOptions: {
|
||||||
placeholder: "",
|
show: {
|
||||||
description: "",
|
resource: [
|
||||||
required: true,
|
resource.value,
|
||||||
displayOptions: {
|
],
|
||||||
show: {
|
operation: [
|
||||||
resource: [resource.value],
|
'get',
|
||||||
operation: ["get_asset"],
|
'getAll',
|
||||||
},
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
default: 'master',
|
||||||
|
description: 'The id for the Contentful environment (e.g. master, staging, etc.). Depending on your plan, you might not have environments. In that case use "master".'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Return All',
|
||||||
|
name: 'returnAll',
|
||||||
|
type: 'boolean',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
resource: [
|
||||||
|
resource.value,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: false,
|
||||||
|
description: 'If all results should be returned or only up to a given limit.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Limit',
|
||||||
|
name: 'limit',
|
||||||
|
type: 'number',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
resource: [
|
||||||
|
resource.value,
|
||||||
|
],
|
||||||
|
returnAll: [
|
||||||
|
false,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
typeOptions: {
|
||||||
|
minValue: 1,
|
||||||
|
maxValue: 500,
|
||||||
|
},
|
||||||
|
default: 100,
|
||||||
|
description: 'How many results to return.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Additional Fields',
|
||||||
|
name: 'additionalFields',
|
||||||
|
type: 'collection',
|
||||||
|
placeholder: 'Add Field',
|
||||||
|
default: {},
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
resource.value,
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
displayName: 'Equal',
|
||||||
|
name: 'equal',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
placeholder: 'fields.title=n8n',
|
||||||
|
description: 'Search for all data that matches the condition: {attribute}={value}. Attribute can use dot notation.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Exclude',
|
||||||
|
name: 'exclude',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
placeholder: 'fields.tags[nin]=accessories,flowers',
|
||||||
|
description: 'Search for all data that matches the condition: {attribute}[nin]={value}. Attribute can use dot notation.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Exist',
|
||||||
|
name: 'exist',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
placeholder: 'fields.tags[exists]=true',
|
||||||
|
description: 'Search for all data that matches the condition: {attribute}[exists]={value}. Attribute can use dot notation.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Fields',
|
||||||
|
name: 'select',
|
||||||
|
type: 'string',
|
||||||
|
placeholder: 'fields.title',
|
||||||
|
default: '',
|
||||||
|
description: 'The select operator allows you to choose what fields to return from an entity. You can choose multiple values by combining comma separated operators.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Include',
|
||||||
|
name: 'include',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
placeholder: 'fields.tags[in]=accessories,flowers',
|
||||||
|
description: 'Search for all data that matches the condition: {attribute}[in]={value}. Attribute can use dot notation.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Not Equal',
|
||||||
|
name: 'notEqual',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
placeholder: 'fields.title[ne]=n8n',
|
||||||
|
description: 'Search for all data that matches the condition: {attribute}[ne]={value}. Attribute can use dot notation.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Order',
|
||||||
|
name: 'order',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
placeholder: 'sys.createdAt',
|
||||||
|
description: 'You can order items in the response by specifying the order search parameter. You can use sys properties (such as sys.createdAt) or field values (such as fields.myCustomDateField) for ordering.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Query',
|
||||||
|
name: 'query',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
description: ' Full-text search is case insensitive and might return more results than expected. A query will only take values with more than 1 character.',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Asset ID',
|
||||||
|
name: 'assetId',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
resource.value
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'get',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
] as INodeProperties[];
|
] as INodeProperties[];
|
||||||
|
|
|
@ -1,49 +1,69 @@
|
||||||
import { INodeProperties, INodePropertyOptions } from "n8n-workflow";
|
import {
|
||||||
|
INodeProperties,
|
||||||
|
INodePropertyOptions,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
export const resource = {
|
export const resource = {
|
||||||
name: "Content Types",
|
name: 'Content Type',
|
||||||
value: "content_type",
|
value: 'contentType',
|
||||||
} as INodePropertyOptions;
|
} as INodePropertyOptions;
|
||||||
|
|
||||||
export const operations = [
|
export const operations = [
|
||||||
{
|
{
|
||||||
displayName: "Operation",
|
displayName: 'Operation',
|
||||||
name: "operation",
|
name: 'operation',
|
||||||
type: "options",
|
type: 'options',
|
||||||
displayOptions: {
|
displayOptions: {
|
||||||
show: {
|
show: {
|
||||||
resource: [resource.value],
|
resource: [
|
||||||
},
|
resource.value,
|
||||||
},
|
],
|
||||||
options: [
|
},
|
||||||
{
|
},
|
||||||
name: "Get Content types",
|
options: [
|
||||||
value: "get_content_types",
|
{
|
||||||
},
|
name: 'Get',
|
||||||
{
|
value: 'get',
|
||||||
name: "Get Single Content Type",
|
},
|
||||||
value: "get_content_type",
|
],
|
||||||
},
|
default: 'get',
|
||||||
],
|
description: 'The operation to perform.',
|
||||||
default: "get_content_types",
|
},
|
||||||
description: "The operation to perform.",
|
|
||||||
},
|
|
||||||
] as INodeProperties[];
|
] as INodeProperties[];
|
||||||
|
|
||||||
export const fields = [
|
export const fields = [
|
||||||
{
|
{
|
||||||
displayName: "Content Type Id",
|
displayName: 'Environment ID',
|
||||||
name: "content_type_id",
|
name: 'environmentId',
|
||||||
type: "string",
|
type: 'string',
|
||||||
default: "",
|
displayOptions: {
|
||||||
placeholder: "",
|
show: {
|
||||||
description: "",
|
resource: [
|
||||||
required: true,
|
resource.value,
|
||||||
displayOptions: {
|
],
|
||||||
show: {
|
operation: [
|
||||||
resource: [resource.value],
|
'get',
|
||||||
operation: ["get_content_type"],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
default: 'master',
|
||||||
|
description: 'The id for the Contentful environment (e.g. master, staging, etc.). Depending on your plan, you might not have environments. In that case use "master".'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Content Type ID',
|
||||||
|
name: 'contentTypeId',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
required: true,
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
resource.value,
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'get',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
] as INodeProperties[];
|
] as INodeProperties[];
|
||||||
|
|
|
@ -1,24 +1,34 @@
|
||||||
import { IExecuteFunctions } from 'n8n-core';
|
import {
|
||||||
import { IDataObject, INodeExecutionData, INodeType, INodeTypeDescription, NodePropertyTypes } from 'n8n-workflow';
|
IExecuteFunctions,
|
||||||
|
} from 'n8n-core';
|
||||||
|
|
||||||
import { contentfulApiRequest } from './GenericFunctions';
|
import {
|
||||||
import resolveResponse from './resolveResponse';
|
IDataObject,
|
||||||
|
INodeExecutionData,
|
||||||
|
INodeType,
|
||||||
|
INodeTypeDescription,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
import {
|
||||||
|
contentfulApiRequest,
|
||||||
|
contenfulApiRequestAllItems,
|
||||||
|
} from './GenericFunctions';
|
||||||
|
|
||||||
import * as SpaceDescription from './SpaceDescription';
|
import * as SpaceDescription from './SpaceDescription';
|
||||||
import * as ContentTypeDescription from './ContentTypeDescription';
|
import * as ContentTypeDescription from './ContentTypeDescription';
|
||||||
import * as EntryDescription from './EntryDescription';
|
import * as EntryDescription from './EntryDescription';
|
||||||
import * as AssetDescription from './AssetDescription';
|
import * as AssetDescription from './AssetDescription';
|
||||||
import * as LocaleDescription from './LocaleDescription';
|
import * as LocaleDescription from './LocaleDescription';
|
||||||
import * as SearchParameterDescription from './SearchParameterDescription';
|
|
||||||
|
|
||||||
export class Contentful implements INodeType {
|
export class Contentful implements INodeType {
|
||||||
description: INodeTypeDescription = {
|
description: INodeTypeDescription = {
|
||||||
displayName: 'Contentful',
|
displayName: 'Contentful',
|
||||||
name: 'contentful',
|
name: 'contentful',
|
||||||
|
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
||||||
icon: 'file:contentful.png',
|
icon: 'file:contentful.png',
|
||||||
group: ['input'],
|
group: ['input'],
|
||||||
version: 1,
|
version: 1,
|
||||||
description: "Access data through Contentful's Content Delivery API",
|
description: 'Consume Contenful API',
|
||||||
defaults: {
|
defaults: {
|
||||||
name: 'Contentful',
|
name: 'Contentful',
|
||||||
color: '#2E75D4'
|
color: '#2E75D4'
|
||||||
|
@ -27,49 +37,39 @@ export class Contentful implements INodeType {
|
||||||
outputs: ['main'],
|
outputs: ['main'],
|
||||||
credentials: [
|
credentials: [
|
||||||
{
|
{
|
||||||
name: 'contentfulDeliveryApi',
|
name: 'contentfulApi',
|
||||||
required: true
|
required: true
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
properties: [
|
properties: [
|
||||||
// Common fields:
|
|
||||||
{
|
{
|
||||||
displayName: 'Source',
|
displayName: 'Source',
|
||||||
name: 'source',
|
name: 'source',
|
||||||
type: 'options' as NodePropertyTypes,
|
type: 'options',
|
||||||
default: 'Delivery API',
|
default: 'Delivery API',
|
||||||
description: 'Pick where your data comes from, delivery or preview API',
|
description: 'Pick where your data comes from, delivery or preview API',
|
||||||
options: [
|
options: [
|
||||||
{
|
{
|
||||||
name: 'Delivery API',
|
name: 'Delivery API',
|
||||||
value: 'delivery_api'
|
value: 'deliveryApi'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Preview API',
|
name: 'Preview API',
|
||||||
value: 'preview_api'
|
value: 'previewApi'
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
{
|
|
||||||
displayName: 'Environment Id',
|
|
||||||
name: 'environment_id',
|
|
||||||
type: 'string' as NodePropertyTypes,
|
|
||||||
default: '',
|
|
||||||
description:
|
|
||||||
'The id for the Contentful environment (e.g. master, staging, etc.). Depending on your plan, you might not have environments. In that case use "master".'
|
|
||||||
},
|
|
||||||
|
|
||||||
// Resources:
|
// Resources:
|
||||||
{
|
{
|
||||||
displayName: 'Resource',
|
displayName: 'Resource',
|
||||||
name: 'resource',
|
name: 'resource',
|
||||||
type: 'options',
|
type: 'options',
|
||||||
options: [
|
options: [
|
||||||
SpaceDescription.resource,
|
AssetDescription.resource,
|
||||||
ContentTypeDescription.resource,
|
ContentTypeDescription.resource,
|
||||||
EntryDescription.resource,
|
EntryDescription.resource,
|
||||||
AssetDescription.resource,
|
LocaleDescription.resource,
|
||||||
LocaleDescription.resource
|
SpaceDescription.resource,
|
||||||
],
|
],
|
||||||
default: '',
|
default: '',
|
||||||
description: 'The resource to operate on.'
|
description: 'The resource to operate on.'
|
||||||
|
@ -88,16 +88,13 @@ export class Contentful implements INodeType {
|
||||||
...EntryDescription.fields,
|
...EntryDescription.fields,
|
||||||
...AssetDescription.fields,
|
...AssetDescription.fields,
|
||||||
...LocaleDescription.fields,
|
...LocaleDescription.fields,
|
||||||
|
],
|
||||||
// Options:
|
|
||||||
...SearchParameterDescription.fields
|
|
||||||
]
|
|
||||||
};
|
};
|
||||||
|
|
||||||
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
||||||
const environmentId = this.getNodeParameter('environment_id', 0) as string;
|
|
||||||
const resource = this.getNodeParameter('resource', 0) as string;
|
const resource = this.getNodeParameter('resource', 0) as string;
|
||||||
const operation = this.getNodeParameter('operation', 0) as string;
|
const operation = this.getNodeParameter('operation', 0) as string;
|
||||||
|
let responseData;
|
||||||
|
|
||||||
const items = this.getInputData();
|
const items = this.getInputData();
|
||||||
const returnData: IDataObject[] = [];
|
const returnData: IDataObject[] = [];
|
||||||
|
@ -105,54 +102,164 @@ export class Contentful implements INodeType {
|
||||||
|
|
||||||
for (let i = 0; i < items.length; i++) {
|
for (let i = 0; i < items.length; i++) {
|
||||||
if (resource === 'space') {
|
if (resource === 'space') {
|
||||||
if (operation === 'get_space') {
|
if (operation === 'get') {
|
||||||
const res = await contentfulApiRequest(this);
|
|
||||||
returnData.push(res);
|
const credentials = this.getCredentials('contentfulApi');
|
||||||
|
|
||||||
|
responseData = await contentfulApiRequest.call(this, 'GET', `/spaces/${credentials?.spaceId}`);
|
||||||
}
|
}
|
||||||
} else if (resource === 'content_type') {
|
}
|
||||||
if (operation === 'get_content_types') {
|
if (resource === 'contentType') {
|
||||||
const res = await contentfulApiRequest(this, '/content_types', environmentId);
|
if (operation === 'get') {
|
||||||
const resolvedData = resolveResponse(res, {});
|
|
||||||
returnData.push(...resolvedData);
|
const credentials = this.getCredentials('contentfulApi');
|
||||||
} else if (operation === 'get_content_type') {
|
|
||||||
const id = this.getNodeParameter('content_type_id', 0) as string;
|
const env = this.getNodeParameter('environmentId', 0) as string;
|
||||||
const res = await contentfulApiRequest(this, `/content_types/${id}`, environmentId);
|
|
||||||
returnData.push(...res.items);
|
const id = this.getNodeParameter('contentTypeId', 0) as string;
|
||||||
|
|
||||||
|
responseData = await contentfulApiRequest.call(this, 'GET', `/spaces/${credentials?.spaceId}/environments/${env}/content_types/${id}`);
|
||||||
}
|
}
|
||||||
} else if (resource === 'entry') {
|
}
|
||||||
if (operation === 'get_entries') {
|
if (resource === 'entry') {
|
||||||
const shouldResolve = this.getNodeParameter('resolve', 0) as boolean;
|
|
||||||
if (shouldResolve) qs.include = this.getNodeParameter('include', 0) as number;
|
if (operation === 'get') {
|
||||||
const searchParameters = this.getNodeParameter('search_parameters', 0) as IDataObject;
|
|
||||||
if (searchParameters.parameters && Array.isArray(searchParameters.parameters)) {
|
const credentials = this.getCredentials('contentfulApi');
|
||||||
searchParameters.parameters.forEach(parameter => {
|
|
||||||
const { name, value } = parameter as { name: string; value: string };
|
const env = this.getNodeParameter('environmentId', 0) as string;
|
||||||
qs[name] = value;
|
|
||||||
});
|
const id = this.getNodeParameter('entryId', 0) as string;
|
||||||
|
|
||||||
|
responseData = await contentfulApiRequest.call(this, 'GET', `/spaces/${credentials?.spaceId}/environments/${env}/entries/${id}`, {}, qs);
|
||||||
|
|
||||||
|
} else if (operation === 'getAll') {
|
||||||
|
const credentials = this.getCredentials('contentfulApi');
|
||||||
|
|
||||||
|
const returnAll = this.getNodeParameter('returnAll', 0) as boolean;
|
||||||
|
|
||||||
|
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
|
||||||
|
|
||||||
|
const env = this.getNodeParameter('environmentId', i) as string;
|
||||||
|
|
||||||
|
Object.assign(qs, additionalFields);
|
||||||
|
|
||||||
|
if (qs.equal) {
|
||||||
|
const [atribute, value] = (qs.equal as string).split('=');
|
||||||
|
qs[atribute] = value;
|
||||||
|
delete qs.equal;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (qs.notEqual) {
|
||||||
|
const [atribute, value] = (qs.notEqual as string).split('=');
|
||||||
|
qs[atribute] = value;
|
||||||
|
delete qs.notEqual;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (qs.include) {
|
||||||
|
const [atribute, value] = (qs.include as string).split('=');
|
||||||
|
qs[atribute] = value;
|
||||||
|
delete qs.include;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (qs.exclude) {
|
||||||
|
const [atribute, value] = (qs.exclude as string).split('=');
|
||||||
|
qs[atribute] = value;
|
||||||
|
delete qs.exclude;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (returnAll) {
|
||||||
|
responseData = await contenfulApiRequestAllItems.call(this, 'items', 'GET', `/spaces/${credentials?.spaceId}/environments/${env}/entries`, {}, qs);
|
||||||
|
} else {
|
||||||
|
const limit = this.getNodeParameter('limit', 0) as number;
|
||||||
|
qs.limit = limit;
|
||||||
|
responseData = await contentfulApiRequest.call(this, 'GET', `/spaces/${credentials?.spaceId}/environments/${env}/entries`, {}, qs);
|
||||||
|
responseData = responseData.items;
|
||||||
}
|
}
|
||||||
const res = await contentfulApiRequest(this, '/entries', environmentId, qs);
|
|
||||||
const resolvedData = shouldResolve ? resolveResponse(res, {}) : res.items;
|
|
||||||
returnData.push(...resolvedData);
|
|
||||||
} else if (operation === 'get_entry') {
|
|
||||||
const id = this.getNodeParameter('entry_id', 0) as string;
|
|
||||||
const res = await contentfulApiRequest(this, `/entries/${id}`, environmentId);
|
|
||||||
returnData.push(res);
|
|
||||||
}
|
}
|
||||||
} else if (resource === 'asset') {
|
}
|
||||||
if (operation === 'get_assets') {
|
if (resource === 'asset') {
|
||||||
const res = await contentfulApiRequest(this, '/assets', environmentId);
|
if (operation === 'get') {
|
||||||
returnData.push(...res.items);
|
|
||||||
} else if (operation === 'get_asset') {
|
const credentials = this.getCredentials('contentfulApi');
|
||||||
const id = this.getNodeParameter('asset_id', 0) as string;
|
|
||||||
const res = await contentfulApiRequest(this, `/assets/${id}`, environmentId);
|
const env = this.getNodeParameter('environmentId', 0) as string;
|
||||||
returnData.push(res);
|
|
||||||
|
const id = this.getNodeParameter('assetId', 0) as string;
|
||||||
|
|
||||||
|
responseData = await contentfulApiRequest.call(this, 'GET', `/spaces/${credentials?.spaceId}/environments/${env}/assets/${id}`, {}, qs);
|
||||||
|
|
||||||
|
} else if (operation === 'getAll') {
|
||||||
|
|
||||||
|
const credentials = this.getCredentials('contentfulApi');
|
||||||
|
|
||||||
|
const returnAll = this.getNodeParameter('returnAll', 0) as boolean;
|
||||||
|
|
||||||
|
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
|
||||||
|
|
||||||
|
const env = this.getNodeParameter('environmentId', i) as string;
|
||||||
|
|
||||||
|
Object.assign(qs, additionalFields);
|
||||||
|
|
||||||
|
if (qs.equal) {
|
||||||
|
const [atribute, value] = (qs.equal as string).split('=');
|
||||||
|
qs[atribute] = value;
|
||||||
|
delete qs.equal;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (qs.notEqual) {
|
||||||
|
const [atribute, value] = (qs.notEqual as string).split('=');
|
||||||
|
qs[atribute] = value;
|
||||||
|
delete qs.notEqual;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (qs.include) {
|
||||||
|
const [atribute, value] = (qs.include as string).split('=');
|
||||||
|
qs[atribute] = value;
|
||||||
|
delete qs.include;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (qs.exclude) {
|
||||||
|
const [atribute, value] = (qs.exclude as string).split('=');
|
||||||
|
qs[atribute] = value;
|
||||||
|
delete qs.exclude;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (returnAll) {
|
||||||
|
responseData = await contenfulApiRequestAllItems.call(this, 'items', 'GET', `/spaces/${credentials?.spaceId}/environments/${env}/assets`, {}, qs);
|
||||||
|
} else {
|
||||||
|
const limit = this.getNodeParameter('limit', 0) as number;
|
||||||
|
qs.limit = limit;
|
||||||
|
responseData = await contentfulApiRequest.call(this, 'GET', `/spaces/${credentials?.spaceId}/environments/${env}/assets`, {}, qs);
|
||||||
|
responseData = responseData.items;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (resource === 'locale') {
|
}
|
||||||
if (operation === 'get_locales') {
|
if (resource === 'locale') {
|
||||||
const res = await contentfulApiRequest(this, '/locales', environmentId);
|
|
||||||
returnData.push(res);
|
if (operation === 'getAll') {
|
||||||
|
|
||||||
|
const credentials = this.getCredentials('contentfulApi');
|
||||||
|
|
||||||
|
const returnAll = this.getNodeParameter('returnAll', 0) as boolean;
|
||||||
|
|
||||||
|
const env = this.getNodeParameter('environmentId', i) as string;
|
||||||
|
|
||||||
|
if (returnAll) {
|
||||||
|
responseData = await contenfulApiRequestAllItems.call(this, 'items', 'GET', `/spaces/${credentials?.spaceId}/environments/${env}/locales`, {}, qs);
|
||||||
|
} else {
|
||||||
|
const limit = this.getNodeParameter('limit', 0) as number;
|
||||||
|
qs.limit = limit;
|
||||||
|
responseData = await contentfulApiRequest.call(this, 'GET', `/spaces/${credentials?.spaceId}/environments/${env}/locales`, {}, qs);
|
||||||
|
responseData = responseData.items;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (Array.isArray(responseData)) {
|
||||||
|
returnData.push.apply(returnData, responseData as IDataObject[]);
|
||||||
|
} else {
|
||||||
|
returnData.push(responseData as IDataObject);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return [this.helpers.returnJsonArray(returnData)];
|
return [this.helpers.returnJsonArray(returnData)];
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,14 @@
|
||||||
import { INodeProperties, INodePropertyOptions } from 'n8n-workflow';
|
import {
|
||||||
|
INodeProperties,
|
||||||
|
INodePropertyOptions,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
import { type } from 'os';
|
||||||
|
import { notEqual } from 'assert';
|
||||||
|
import { exists } from 'fs';
|
||||||
|
|
||||||
export const resource = {
|
export const resource = {
|
||||||
name: 'Entry',
|
name: 'Entry',
|
||||||
value: 'entry'
|
value: 'entry',
|
||||||
} as INodePropertyOptions;
|
} as INodePropertyOptions;
|
||||||
|
|
||||||
export const operations = [
|
export const operations = [
|
||||||
|
@ -12,72 +18,190 @@ export const operations = [
|
||||||
type: 'options',
|
type: 'options',
|
||||||
displayOptions: {
|
displayOptions: {
|
||||||
show: {
|
show: {
|
||||||
resource: [resource.value]
|
resource: [
|
||||||
}
|
resource.value,
|
||||||
|
],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
options: [
|
options: [
|
||||||
{
|
{
|
||||||
name: 'Get Entries',
|
name: 'Get',
|
||||||
value: 'get_entries'
|
value: 'get',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Get Single Entry',
|
name: 'Get All',
|
||||||
value: 'get_entry'
|
value: 'getAll',
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
default: 'get_entries',
|
default: 'get',
|
||||||
description: 'The operation to perform.'
|
description: 'The operation to perform.'
|
||||||
}
|
}
|
||||||
] as INodeProperties[];
|
] as INodeProperties[];
|
||||||
|
|
||||||
export const fields = [
|
export const fields = [
|
||||||
{
|
{
|
||||||
displayName: 'Resolve',
|
displayName: 'Environment ID',
|
||||||
name: 'resolve',
|
name: 'environmentId',
|
||||||
type: 'boolean',
|
type: 'string',
|
||||||
default: false,
|
|
||||||
description: 'Linked entries can be automatically resolved in the results if you click activate this feature.',
|
|
||||||
displayOptions: {
|
displayOptions: {
|
||||||
show: {
|
show: {
|
||||||
resource: [resource.value],
|
resource: [
|
||||||
operation: ['get_entries']
|
resource.value,
|
||||||
}
|
],
|
||||||
}
|
operation: [
|
||||||
},
|
'get',
|
||||||
{
|
'getAll',
|
||||||
displayName: 'Include',
|
],
|
||||||
name: 'include',
|
},
|
||||||
type: 'number',
|
|
||||||
default: 1,
|
|
||||||
placeholder: '',
|
|
||||||
description:
|
|
||||||
"When you have related content (e.g. entries with links to image assets) it's possible to include them in the results. Using the include parameter, you can specify the number of levels of entries to include in the results. A lower number might improve performance.",
|
|
||||||
typeOptions: {
|
|
||||||
minValue: 0,
|
|
||||||
maxValue: 10
|
|
||||||
},
|
},
|
||||||
|
default: 'master',
|
||||||
|
description: 'The id for the Contentful environment (e.g. master, staging, etc.). Depending on your plan, you might not have environments. In that case use "master".'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Return All',
|
||||||
|
name: 'returnAll',
|
||||||
|
type: 'boolean',
|
||||||
displayOptions: {
|
displayOptions: {
|
||||||
show: {
|
show: {
|
||||||
resource: [resource.value],
|
operation: [
|
||||||
operation: ['get_entries'],
|
'getAll',
|
||||||
resolve: [true]
|
],
|
||||||
}
|
resource: [
|
||||||
}
|
resource.value,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: false,
|
||||||
|
description: 'If all results should be returned or only up to a given limit.',
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
displayName: 'Entry Id',
|
displayName: 'Limit',
|
||||||
name: 'entry_id',
|
name: 'limit',
|
||||||
|
type: 'number',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
resource: [
|
||||||
|
resource.value,
|
||||||
|
],
|
||||||
|
returnAll: [
|
||||||
|
false,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
typeOptions: {
|
||||||
|
minValue: 1,
|
||||||
|
maxValue: 500,
|
||||||
|
},
|
||||||
|
default: 100,
|
||||||
|
description: 'How many results to return.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Additional Fields',
|
||||||
|
name: 'additionalFields',
|
||||||
|
type: 'collection',
|
||||||
|
placeholder: 'Add Field',
|
||||||
|
default: {},
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
resource.value,
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
displayName: 'Content Type ID',
|
||||||
|
name: 'content_type',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
description: 'To search for entries with a specific content type',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Equal',
|
||||||
|
name: 'equal',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
placeholder: 'fields.title=n8n',
|
||||||
|
description: 'Search for all data that matches the condition: {attribute}={value}. Attribute can use dot notation.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Exclude',
|
||||||
|
name: 'exclude',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
placeholder: 'fields.tags[nin]=accessories,flowers',
|
||||||
|
description: 'Search for all data that matches the condition: {attribute}[nin]={value}. Attribute can use dot notation.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Exist',
|
||||||
|
name: 'exist',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
placeholder: 'fields.tags[exists]=true',
|
||||||
|
description: 'Search for all data that matches the condition: {attribute}[exists]={value}. Attribute can use dot notation.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Fields',
|
||||||
|
name: 'select',
|
||||||
|
type: 'string',
|
||||||
|
placeholder: 'fields.title',
|
||||||
|
default: '',
|
||||||
|
description: 'The select operator allows you to choose what fields to return from an entity. You can choose multiple values by combining comma separated operators.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Include',
|
||||||
|
name: 'include',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
placeholder: 'fields.tags[in]=accessories,flowers',
|
||||||
|
description: 'Search for all data that matches the condition: {attribute}[in]={value}. Attribute can use dot notation.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Not Equal',
|
||||||
|
name: 'notEqual',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
placeholder: 'fields.title[ne]=n8n',
|
||||||
|
description: 'Search for all data that matches the condition: {attribute}[ne]={value}. Attribute can use dot notation.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Order',
|
||||||
|
name: 'order',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
placeholder: 'sys.createdAt',
|
||||||
|
description: 'You can order items in the response by specifying the order search parameter. You can use sys properties (such as sys.createdAt) or field values (such as fields.myCustomDateField) for ordering.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Query',
|
||||||
|
name: 'query',
|
||||||
|
type: 'string',
|
||||||
|
default: '',
|
||||||
|
description: ' Full-text search is case insensitive and might return more results than expected. A query will only take values with more than 1 character.',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Entry ID',
|
||||||
|
name: 'entryId',
|
||||||
type: 'string',
|
type: 'string',
|
||||||
default: '',
|
default: '',
|
||||||
placeholder: '',
|
|
||||||
description: '',
|
|
||||||
required: true,
|
required: true,
|
||||||
displayOptions: {
|
displayOptions: {
|
||||||
show: {
|
show: {
|
||||||
resource: [resource.value],
|
resource: [
|
||||||
operation: ['get_entry']
|
resource.value,
|
||||||
}
|
],
|
||||||
}
|
operation: [
|
||||||
|
'get',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
] as INodeProperties[];
|
] as INodeProperties[];
|
||||||
|
|
|
@ -1,45 +1,75 @@
|
||||||
import { IExecuteFunctions } from 'n8n-core';
|
import {
|
||||||
import { OptionsWithUrl } from 'request';
|
IExecuteFunctions,
|
||||||
|
IExecuteSingleFunctions,
|
||||||
|
ILoadOptionsFunctions,
|
||||||
|
} from 'n8n-core';
|
||||||
|
|
||||||
/**
|
import {
|
||||||
* @param {IExecuteFunctions} that Reference to the system's execute functions
|
OptionsWithUri,
|
||||||
* @param {string} endpoint? Endpoint of api call
|
} from 'request';
|
||||||
* @param {string} environmentId? Id of contentful environment (eg. master, staging, etc.)
|
|
||||||
* @param {Record<string|number>} qs? Query string, can be used for search parameters
|
import {
|
||||||
*/
|
IDataObject,
|
||||||
export const contentfulApiRequest = async (
|
} from 'n8n-workflow';
|
||||||
that: IExecuteFunctions,
|
|
||||||
endpoint?: string,
|
export async function contentfulApiRequest(this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method: string, resource: string, body: any = {}, qs: IDataObject = {}, uri?: string, option: IDataObject = {}): Promise<any> { // tslint:disable-line:no-any
|
||||||
environmentId?: string,
|
|
||||||
qs?: Record<string, string | number | undefined>
|
const credentials = this.getCredentials('contentfulApi');
|
||||||
) => {
|
|
||||||
const credentials = that.getCredentials('contentfulDeliveryApi');
|
|
||||||
if (credentials === undefined) {
|
if (credentials === undefined) {
|
||||||
throw new Error('No credentials got returned!');
|
throw new Error('No credentials got returned!');
|
||||||
}
|
}
|
||||||
|
|
||||||
const source = that.getNodeParameter('source', 0) as string;
|
const source = this.getNodeParameter('source', 0) as string;
|
||||||
const isPreview = source === 'preview_api';
|
const isPreview = source === 'previewApi';
|
||||||
let accessToken = credentials.access_token as string;
|
|
||||||
|
const options: OptionsWithUri = {
|
||||||
|
method,
|
||||||
|
qs,
|
||||||
|
body,
|
||||||
|
uri: uri ||`https://${isPreview ? 'preview' : 'cdn'}.contentful.com${resource}`,
|
||||||
|
json: true
|
||||||
|
};
|
||||||
|
|
||||||
if (isPreview) {
|
if (isPreview) {
|
||||||
accessToken = credentials.access_token_preview as string;
|
qs.access_token = credentials.ContentPreviewaccessToken as string;
|
||||||
console.log('accessToken', accessToken);
|
} else {
|
||||||
if (!accessToken) {
|
qs.access_token = credentials.ContentDeliveryaccessToken as string;
|
||||||
throw new Error('No access token for preview API set in credentials!');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let url = `https://${isPreview ? 'preview' : 'cdn'}.contentful.com/spaces/${credentials.space_id}`;
|
try {
|
||||||
if (environmentId) url = `${url}/environments/${environmentId}`;
|
return await this.helpers.request!(options);
|
||||||
if (endpoint) url = `${url}${endpoint}`;
|
} catch (error) {
|
||||||
qs = qs || {};
|
|
||||||
qs.access_token = accessToken;
|
|
||||||
|
|
||||||
const res = await that.helpers.request!({
|
let errorMessage = error;
|
||||||
url,
|
|
||||||
method: 'GET',
|
|
||||||
qs
|
|
||||||
} as OptionsWithUrl);
|
|
||||||
|
|
||||||
return JSON.parse(res);
|
if (error.response && error.response.body && error.response.body.details) {
|
||||||
};
|
const details = error.response.body.details;
|
||||||
|
errorMessage = details.errors.map((e: IDataObject) => e.details).join('|');
|
||||||
|
} else if (error.response && error.response.body && error.response.body.message) {
|
||||||
|
errorMessage = error.response.body.message;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(`Contentful error response [${error.statusCode}]: ${errorMessage}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function contenfulApiRequestAllItems(this: 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.limit = 100;
|
||||||
|
query.skip = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
responseData = await contentfulApiRequest.call(this, method, resource, body, query);
|
||||||
|
query.skip = (query.skip + 1) * query.limit;
|
||||||
|
returnData.push.apply(returnData, responseData[propertyName]);
|
||||||
|
} while (
|
||||||
|
returnData.length < responseData.total
|
||||||
|
);
|
||||||
|
|
||||||
|
return returnData;
|
||||||
|
}
|
||||||
|
|
|
@ -1,29 +1,94 @@
|
||||||
import { INodeProperties, INodePropertyOptions } from "n8n-workflow";
|
import {
|
||||||
|
INodeProperties,
|
||||||
|
INodePropertyOptions
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
export const resource = {
|
export const resource = {
|
||||||
name: "Locale",
|
name: 'Locale',
|
||||||
value: "locale",
|
value: 'locale',
|
||||||
} as INodePropertyOptions;
|
} as INodePropertyOptions;
|
||||||
|
|
||||||
export const operations = [
|
export const operations = [
|
||||||
{
|
{
|
||||||
displayName: "Operation",
|
displayName: 'Operation',
|
||||||
name: "operation",
|
name: 'operation',
|
||||||
type: "options",
|
type: 'options',
|
||||||
displayOptions: {
|
displayOptions: {
|
||||||
show: {
|
show: {
|
||||||
resource: [resource.value],
|
resource: [
|
||||||
},
|
resource.value,
|
||||||
},
|
],
|
||||||
options: [
|
},
|
||||||
{
|
},
|
||||||
name: "Get Locales",
|
options: [
|
||||||
value: "get_locales",
|
{
|
||||||
},
|
name: 'Get All',
|
||||||
],
|
value: 'getAll',
|
||||||
default: "get_locales",
|
},
|
||||||
description: "The operation to perform.",
|
],
|
||||||
},
|
default: 'getAll',
|
||||||
|
description: 'The operation to perform.',
|
||||||
|
},
|
||||||
] as INodeProperties[];
|
] as INodeProperties[];
|
||||||
|
|
||||||
export const fields = [] as INodeProperties[];
|
export const fields = [
|
||||||
|
{
|
||||||
|
displayName: 'Environment ID',
|
||||||
|
name: 'environmentId',
|
||||||
|
type: 'string',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
resource: [
|
||||||
|
resource.value,
|
||||||
|
],
|
||||||
|
operation: [
|
||||||
|
'get',
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: 'master',
|
||||||
|
description: 'The id for the Contentful environment (e.g. master, staging, etc.). Depending on your plan, you might not have environments. In that case use "master".'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Return All',
|
||||||
|
name: 'returnAll',
|
||||||
|
type: 'boolean',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
resource: [
|
||||||
|
resource.value,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
default: false,
|
||||||
|
description: 'If all results should be returned or only up to a given limit.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: 'Limit',
|
||||||
|
name: 'limit',
|
||||||
|
type: 'number',
|
||||||
|
displayOptions: {
|
||||||
|
show: {
|
||||||
|
operation: [
|
||||||
|
'getAll',
|
||||||
|
],
|
||||||
|
resource: [
|
||||||
|
resource.value,
|
||||||
|
],
|
||||||
|
returnAll: [
|
||||||
|
false,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
typeOptions: {
|
||||||
|
minValue: 1,
|
||||||
|
maxValue: 500,
|
||||||
|
},
|
||||||
|
default: 100,
|
||||||
|
description: 'How many results to return.',
|
||||||
|
},
|
||||||
|
] as INodeProperties[];
|
||||||
|
|
|
@ -1,29 +1,33 @@
|
||||||
import { INodeProperties } from "n8n-workflow";
|
import {
|
||||||
|
INodeProperties,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
export const resource = {
|
export const resource = {
|
||||||
name: "Space",
|
name: 'Space',
|
||||||
value: "space",
|
value: 'space',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const operations = [
|
export const operations = [
|
||||||
{
|
{
|
||||||
displayName: "Operation",
|
displayName: 'Operation',
|
||||||
name: "operation",
|
name: 'operation',
|
||||||
type: "options",
|
type: 'options',
|
||||||
displayOptions: {
|
displayOptions: {
|
||||||
show: {
|
show: {
|
||||||
resource: [resource.value],
|
resource: [
|
||||||
},
|
resource.value
|
||||||
},
|
],
|
||||||
options: [
|
},
|
||||||
{
|
},
|
||||||
name: "Get Space",
|
options: [
|
||||||
value: "get_space",
|
{
|
||||||
},
|
name: 'Get',
|
||||||
],
|
value: 'get',
|
||||||
default: "get_space",
|
},
|
||||||
description: "The operation to perform.",
|
],
|
||||||
},
|
default: 'get',
|
||||||
|
description: 'The operation to perform.',
|
||||||
|
},
|
||||||
] as INodeProperties[];
|
] as INodeProperties[];
|
||||||
|
|
||||||
export const fields = [] as INodeProperties[];
|
export const fields = [] as INodeProperties[];
|
||||||
|
|
|
@ -1,156 +0,0 @@
|
||||||
// @ts-nocheck
|
|
||||||
// Code from https://github.com/contentful/contentful-resolve-response/blob/master/index.js
|
|
||||||
import { cloneDeep } from 'lodash';
|
|
||||||
|
|
||||||
const UNRESOLVED_LINK = {}; // unique object to avoid polyfill bloat using Symbol()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* isLink Function
|
|
||||||
* Checks if the object has sys.type "Link"
|
|
||||||
* @param object
|
|
||||||
*/
|
|
||||||
const isLink = (object: { sys: { type: string } }) =>
|
|
||||||
object && object.sys && object.sys.type === 'Link';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* findNormalizableLinkInArray
|
|
||||||
*
|
|
||||||
* @param array
|
|
||||||
* @param predicate
|
|
||||||
* @return {*}
|
|
||||||
*/
|
|
||||||
const findNormalizableLinkInArray = (array, predicate) => {
|
|
||||||
for (let i = 0, len = array.length; i < len; i++) {
|
|
||||||
if (predicate(array[i])) {
|
|
||||||
return array[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return UNRESOLVED_LINK;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* getLink Function
|
|
||||||
*
|
|
||||||
* @param response
|
|
||||||
* @param link
|
|
||||||
* @return {undefined}
|
|
||||||
*/
|
|
||||||
const getLink = (allEntries, link) => {
|
|
||||||
const { linkType: type, id } = link.sys;
|
|
||||||
|
|
||||||
const predicate = ({ sys }) => sys.type === type && sys.id === id;
|
|
||||||
|
|
||||||
return findNormalizableLinkInArray(allEntries, predicate);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* cleanUpLinks Function
|
|
||||||
* - Removes unresolvable links from Arrays and Objects
|
|
||||||
*
|
|
||||||
* @param {Object[]|Object} input
|
|
||||||
*/
|
|
||||||
const cleanUpLinks = input => {
|
|
||||||
if (Array.isArray(input)) {
|
|
||||||
return input.filter(val => val !== UNRESOLVED_LINK);
|
|
||||||
}
|
|
||||||
for (const key in input) {
|
|
||||||
if (input[key] === UNRESOLVED_LINK) {
|
|
||||||
delete input[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return input;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* walkMutate Function
|
|
||||||
* @param input
|
|
||||||
* @param predicate
|
|
||||||
* @param mutator
|
|
||||||
* @return {*}
|
|
||||||
*/
|
|
||||||
const walkMutate = (input, predicate, mutator, removeUnresolved) => {
|
|
||||||
if (predicate(input)) {
|
|
||||||
return mutator(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input && typeof input === 'object') {
|
|
||||||
for (const key in input) {
|
|
||||||
if (input.hasOwnProperty(key)) {
|
|
||||||
input[key] = walkMutate(
|
|
||||||
input[key],
|
|
||||||
predicate,
|
|
||||||
mutator,
|
|
||||||
removeUnresolved
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (removeUnresolved) {
|
|
||||||
input = cleanUpLinks(input);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return input;
|
|
||||||
};
|
|
||||||
|
|
||||||
const normalizeLink = (allEntries, link, removeUnresolved) => {
|
|
||||||
const resolvedLink = getLink(allEntries, link);
|
|
||||||
if (resolvedLink === UNRESOLVED_LINK) {
|
|
||||||
return removeUnresolved ? resolvedLink : link;
|
|
||||||
}
|
|
||||||
return resolvedLink;
|
|
||||||
};
|
|
||||||
|
|
||||||
const makeEntryObject = (item, itemEntryPoints) => {
|
|
||||||
if (!Array.isArray(itemEntryPoints)) {
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
|
|
||||||
const entryPoints = Object.keys(item).filter(
|
|
||||||
ownKey => itemEntryPoints.indexOf(ownKey) !== -1
|
|
||||||
);
|
|
||||||
|
|
||||||
return entryPoints.reduce((entryObj, entryPoint) => {
|
|
||||||
entryObj[entryPoint] = item[entryPoint];
|
|
||||||
return entryObj;
|
|
||||||
}, {});
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* resolveResponse Function
|
|
||||||
* Resolves contentful response to normalized form.
|
|
||||||
* @param {Object} response Contentful response
|
|
||||||
* @param {Object} options
|
|
||||||
* @param {Boolean} options.removeUnresolved - Remove unresolved links default:false
|
|
||||||
* @param {Array<String>} options.itemEntryPoints - Resolve links only in those item properties
|
|
||||||
* @return {Object}
|
|
||||||
*/
|
|
||||||
const resolveResponse = (response, options) => {
|
|
||||||
options = options || {};
|
|
||||||
if (!response.items) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
const responseClone = cloneDeep(response);
|
|
||||||
const allIncludes = Object.keys(responseClone.includes || {}).reduce(
|
|
||||||
(all, type) => [...all, ...response.includes[type]],
|
|
||||||
[]
|
|
||||||
);
|
|
||||||
|
|
||||||
const allEntries = [...responseClone.items, ...allIncludes];
|
|
||||||
|
|
||||||
allEntries.forEach(item => {
|
|
||||||
const entryObject = makeEntryObject(item, options.itemEntryPoints);
|
|
||||||
|
|
||||||
Object.assign(
|
|
||||||
item,
|
|
||||||
walkMutate(
|
|
||||||
entryObject,
|
|
||||||
isLink,
|
|
||||||
link => normalizeLink(allEntries, link, options.removeUnresolved),
|
|
||||||
options.removeUnresolved
|
|
||||||
)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
return responseClone.items;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default resolveResponse;
|
|
|
@ -43,7 +43,7 @@
|
||||||
"dist/credentials/ClockifyApi.credentials.js",
|
"dist/credentials/ClockifyApi.credentials.js",
|
||||||
"dist/credentials/CockpitApi.credentials.js",
|
"dist/credentials/CockpitApi.credentials.js",
|
||||||
"dist/credentials/CodaApi.credentials.js",
|
"dist/credentials/CodaApi.credentials.js",
|
||||||
"dist/credentials/ContentfulDeliveryApi.credentials.js",
|
"dist/credentials/ContentfulApi.credentials.js",
|
||||||
"dist/credentials/CopperApi.credentials.js",
|
"dist/credentials/CopperApi.credentials.js",
|
||||||
"dist/credentials/CalendlyApi.credentials.js",
|
"dist/credentials/CalendlyApi.credentials.js",
|
||||||
"dist/credentials/DisqusApi.credentials.js",
|
"dist/credentials/DisqusApi.credentials.js",
|
||||||
|
|
Loading…
Reference in a new issue