2020-08-05 00:29:29 -07:00
import {
IExecuteFunctions ,
} from 'n8n-core' ;
import {
IDataObject ,
ILoadOptionsFunctions ,
INodeExecutionData ,
INodePropertyOptions ,
2020-10-01 05:01:39 -07:00
INodeType ,
2020-08-05 00:29:29 -07:00
INodeTypeDescription ,
2021-04-16 09:33:36 -07:00
NodeOperationError ,
2020-08-05 00:29:29 -07:00
} from 'n8n-workflow' ;
import {
mediumApiRequest ,
} from './GenericFunctions' ;
export class Medium implements INodeType {
description : INodeTypeDescription = {
displayName : 'Medium' ,
name : 'medium' ,
group : [ 'output' ] ,
2022-06-20 07:54:01 -07:00
// eslint-disable-next-line n8n-nodes-base/node-class-description-icon-not-svg
2020-08-05 00:29:29 -07:00
icon : 'file:medium.png' ,
version : 1 ,
description : 'Consume Medium API' ,
subtitle : '={{$parameter["operation"] + ": " + $parameter["resource"]}}' ,
defaults : {
name : 'Medium' ,
} ,
inputs : [ 'main' ] ,
outputs : [ 'main' ] ,
credentials : [
{
name : 'mediumApi' ,
required : true ,
displayOptions : {
show : {
authentication : [
'accessToken' ,
] ,
} ,
} ,
} ,
{
name : 'mediumOAuth2Api' ,
required : true ,
displayOptions : {
show : {
authentication : [
'oAuth2' ,
] ,
} ,
} ,
} ,
] ,
properties : [
{
displayName : 'Authentication' ,
name : 'authentication' ,
type : 'options' ,
options : [
{
name : 'Access Token' ,
value : 'accessToken' ,
} ,
{
name : 'OAuth2' ,
value : 'oAuth2' ,
} ,
] ,
default : 'accessToken' ,
} ,
{
displayName : 'Resource' ,
name : 'resource' ,
type : 'options' ,
2022-05-20 14:47:24 -07:00
noDataExpression : true ,
2020-08-05 00:29:29 -07:00
options : [
{
name : 'Post' ,
value : 'post' ,
} ,
{
name : 'Publication' ,
value : 'publication' ,
} ,
] ,
default : 'post' ,
} ,
{
displayName : 'Operation' ,
name : 'operation' ,
type : 'options' ,
2022-05-20 14:47:24 -07:00
noDataExpression : true ,
2020-08-05 00:29:29 -07:00
displayOptions : {
show : {
resource : [
'post' ,
] ,
} ,
} ,
options : [
{
2020-08-05 00:29:48 -07:00
name : 'Create' ,
2020-08-05 00:29:29 -07:00
value : 'create' ,
2020-08-05 00:29:48 -07:00
description : 'Create a post' ,
2022-07-10 13:50:51 -07:00
action : 'Create a post' ,
2020-08-05 00:29:29 -07:00
} ,
] ,
default : 'create' ,
} ,
// ----------------------------------
// post:create
// ----------------------------------
{
displayName : 'Publication' ,
name : 'publication' ,
type : 'boolean' ,
displayOptions : {
show : {
resource : [
'post' ,
] ,
operation : [
'create' ,
] ,
} ,
} ,
default : false ,
2022-06-20 07:54:01 -07:00
description : 'Whether you are posting for a publication' ,
2020-08-05 00:29:29 -07:00
} ,
{
2022-06-03 10:23:49 -07:00
displayName : 'Publication Name or ID' ,
2020-08-05 00:29:29 -07:00
name : 'publicationId' ,
type : 'options' ,
displayOptions : {
show : {
resource : [
'post' ,
] ,
operation : [
'create' ,
] ,
publication : [
true ,
] ,
} ,
} ,
typeOptions : {
loadOptionsMethod : 'getPublications' ,
} ,
default : '' ,
2022-07-14 13:05:11 -07:00
description : 'Publication IDs. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code-examples/expressions/">expression</a>.' ,
2020-08-05 00:29:29 -07:00
} ,
{
displayName : 'Title' ,
name : 'title' ,
type : 'string' ,
default : '' ,
placeholder : 'My Open Source Contribution' ,
required : true ,
displayOptions : {
show : {
operation : [
'create' ,
] ,
resource : [
'post' ,
] ,
} ,
} ,
2022-04-22 09:29:51 -07:00
description : 'Title of the post. Max Length : 100 characters.' ,
2020-08-05 00:29:29 -07:00
} ,
{
displayName : 'Content Format' ,
name : 'contentFormat' ,
default : '' ,
required : true ,
displayOptions : {
show : {
operation : [
'create' ,
] ,
resource : [
'post' ,
] ,
} ,
} ,
type : 'options' ,
options : [
{
name : 'HTML' ,
value : 'html' ,
} ,
{
name : 'Markdown' ,
value : 'markdown' ,
} ,
] ,
2022-05-06 14:01:25 -07:00
description : 'The format of the content to be posted' ,
2020-08-05 00:29:29 -07:00
} ,
{
displayName : 'Content' ,
name : 'content' ,
type : 'string' ,
default : '' ,
placeholder : 'My open source contribution' ,
required : true ,
typeOptions : {
alwaysOpenEditWindow : true ,
} ,
displayOptions : {
show : {
operation : [
'create' ,
] ,
resource : [
'post' ,
] ,
} ,
} ,
2022-05-06 14:01:25 -07:00
description : 'The body of the post, in a valid semantic HTML fragment, or Markdown' ,
2020-08-05 00:29:29 -07:00
} ,
{
displayName : 'Additional Fields' ,
name : 'additionalFields' ,
type : 'collection' ,
placeholder : 'Add Field' ,
displayOptions : {
show : {
operation : [
'create' ,
] ,
resource : [
'post' ,
] ,
} ,
} ,
default : { } ,
options : [
{
2020-08-05 00:29:48 -07:00
displayName : 'Canonical Url' ,
name : 'canonicalUrl' ,
2020-08-05 00:29:29 -07:00
type : 'string' ,
default : '' ,
2022-05-06 14:01:25 -07:00
description : 'The original home of this content, if it was originally published elsewhere' ,
2020-08-05 00:29:29 -07:00
} ,
{
displayName : 'License' ,
name : 'license' ,
2020-08-05 00:29:48 -07:00
type : 'options' ,
2020-08-05 00:29:29 -07:00
default : 'all-rights-reserved' ,
options : [
{
name : 'all-rights-reserved' ,
value : 'all-rights-reserved' ,
} ,
{
name : 'cc-40-by' ,
value : 'cc-40-by' ,
} ,
{
name : 'cc-40-by-nc' ,
value : 'cc-40-by-nc' ,
} ,
{
name : 'cc-40-by-nc-nd' ,
value : 'cc-40-by-nc-nd' ,
} ,
{
name : 'cc-40-by-nc-sa' ,
value : 'cc-40-by-nc-sa' ,
} ,
2020-08-05 00:29:48 -07:00
{
name : 'cc-40-by-nd' ,
value : 'cc-40-by-nd' ,
} ,
{
name : 'cc-40-by-sa' ,
value : 'cc-40-by-sa' ,
} ,
2020-08-05 00:29:29 -07:00
{
name : 'cc-40-zero' ,
value : 'cc-40-zero' ,
} ,
{
name : 'public-domain' ,
value : 'public-domain' ,
} ,
] ,
2022-05-06 14:01:25 -07:00
description : 'License of the post' ,
2020-08-05 00:29:29 -07:00
} ,
2020-08-05 00:29:48 -07:00
{
displayName : 'Notify Followers' ,
name : 'notifyFollowers' ,
type : 'boolean' ,
default : false ,
2022-05-06 14:01:25 -07:00
description : 'Whether to notify followers that the user has published' ,
2020-08-05 00:29:48 -07:00
} ,
{
displayName : 'Publish Status' ,
name : 'publishStatus' ,
default : 'public' ,
type : 'options' ,
options : [
{
name : 'Public' ,
value : 'public' ,
} ,
{
name : 'Draft' ,
value : 'draft' ,
} ,
{
name : 'Unlisted' ,
value : 'unlisted' ,
} ,
] ,
2022-05-06 14:01:25 -07:00
description : 'The status of the post' ,
2020-08-05 00:29:48 -07:00
} ,
{
displayName : 'Tags' ,
name : 'tags' ,
type : 'string' ,
default : '' ,
placeholder : 'open-source,mlh,fellowship' ,
description : 'Comma-separated strings to be used as tags for post classification. Max allowed tags: 5. Max tag length: 25 characters.' ,
} ,
2020-08-05 00:29:29 -07:00
] ,
} ,
{
displayName : 'Operation' ,
name : 'operation' ,
type : 'options' ,
2022-05-20 14:47:24 -07:00
noDataExpression : true ,
2020-08-05 00:29:29 -07:00
displayOptions : {
show : {
resource : [
'publication' ,
] ,
} ,
} ,
options : [
{
name : 'Get All' ,
value : 'getAll' ,
2020-08-05 00:29:48 -07:00
description : 'Get all publications' ,
2022-07-10 13:50:51 -07:00
action : 'Get all publications' ,
2020-08-05 00:29:29 -07:00
} ,
] ,
default : 'publication' ,
} ,
// ----------------------------------
// publication:getAll
// ----------------------------------
{
displayName : 'Return All' ,
name : 'returnAll' ,
type : 'boolean' ,
displayOptions : {
show : {
operation : [
'getAll' ,
] ,
resource : [
'publication' ,
] ,
} ,
} ,
default : false ,
2022-05-06 14:01:25 -07:00
description : 'Whether to return all results or only up to a given limit' ,
2020-08-05 00:29:29 -07:00
} ,
{
displayName : 'Limit' ,
name : 'limit' ,
type : 'number' ,
displayOptions : {
show : {
operation : [
'getAll' ,
] ,
resource : [
'publication' ,
] ,
returnAll : [
false ,
] ,
} ,
} ,
typeOptions : {
minValue : 1 ,
maxValue : 200 ,
} ,
default : 100 ,
2022-05-06 14:01:25 -07:00
description : 'Max number of results to return' ,
2020-08-05 00:29:29 -07:00
} ,
] ,
} ;
methods = {
loadOptions : {
// Get all the available publications to display them to user so that he can
// select them easily
async getPublications ( this : ILoadOptionsFunctions ) : Promise < INodePropertyOptions [ ] > {
const returnData : INodePropertyOptions [ ] = [ ] ;
//Get the User Id
const user = await mediumApiRequest . call (
this ,
'GET' ,
2020-10-22 09:00:28 -07:00
` /me ` ,
2020-08-05 00:29:29 -07:00
) ;
const userId = user . data . id ;
//Get all publications of that user
const publications = await mediumApiRequest . call (
this ,
'GET' ,
2020-10-22 09:00:28 -07:00
` /users/ ${ userId } /publications ` ,
2020-08-05 00:29:29 -07:00
) ;
const publicationsList = publications . data ;
for ( const publication of publicationsList ) {
const publicationName = publication . name ;
const publicationId = publication . id ;
returnData . push ( {
name : publicationName ,
value : publicationId ,
} ) ;
}
return returnData ;
} ,
} ,
} ;
async execute ( this : IExecuteFunctions ) : Promise < INodeExecutionData [ ] [ ] > {
const items = this . getInputData ( ) ;
const returnData : IDataObject [ ] = [ ] ;
let operation : string ;
let resource : string ;
// For POST
let bodyRequest : IDataObject ;
// For Query string
let qs : IDataObject ;
let responseData ;
for ( let i = 0 ; i < items . length ; i ++ ) {
qs = { } ;
2021-07-19 23:58:54 -07:00
try {
resource = this . getNodeParameter ( 'resource' , i ) as string ;
operation = this . getNodeParameter ( 'operation' , i ) as string ;
2020-08-05 00:29:29 -07:00
2021-07-19 23:58:54 -07:00
if ( resource === 'post' ) {
//https://github.com/Medium/medium-api-docs
if ( operation === 'create' ) {
// ----------------------------------
// post:create
// ----------------------------------
2020-08-05 00:29:29 -07:00
2021-07-19 23:58:54 -07:00
const title = this . getNodeParameter ( 'title' , i ) as string ;
const contentFormat = this . getNodeParameter ( 'contentFormat' , i ) as string ;
const content = this . getNodeParameter ( 'content' , i ) as string ;
bodyRequest = {
tags : [ ] ,
title ,
contentFormat ,
content ,
2020-08-05 00:29:29 -07:00
2021-07-19 23:58:54 -07:00
} ;
2020-08-05 00:29:29 -07:00
2021-07-19 23:58:54 -07:00
const additionalFields = this . getNodeParameter ( 'additionalFields' , i ) as IDataObject ;
if ( additionalFields . tags ) {
const tags = additionalFields . tags as string ;
bodyRequest . tags = tags . split ( ',' ) . map ( name = > {
const returnValue = name . trim ( ) ;
if ( returnValue . length > 25 ) {
2022-07-12 08:51:01 -07:00
throw new NodeOperationError ( this . getNode ( ) , ` The tag " ${ returnValue } " is to long. Maximum lenght of a tag is 25 characters. ` , { itemIndex : i } ) ;
2021-07-19 23:58:54 -07:00
}
return returnValue ;
} ) ;
2020-08-05 00:29:48 -07:00
2021-07-19 23:58:54 -07:00
if ( ( bodyRequest . tags as string [ ] ) . length > 5 ) {
2022-07-12 08:51:01 -07:00
throw new NodeOperationError ( this . getNode ( ) , 'To many tags got used. Maximum 5 can be set.' , { itemIndex : i } ) ;
2020-08-05 00:29:48 -07:00
}
2021-07-19 23:58:54 -07:00
}
2020-08-05 00:29:29 -07:00
2021-07-19 23:58:54 -07:00
if ( additionalFields . canonicalUrl ) {
bodyRequest . canonicalUrl = additionalFields . canonicalUrl as string ;
}
if ( additionalFields . publishStatus ) {
bodyRequest . publishStatus = additionalFields . publishStatus as string ;
}
if ( additionalFields . license ) {
bodyRequest . license = additionalFields . license as string ;
}
if ( additionalFields . notifyFollowers ) {
bodyRequest . notifyFollowers = additionalFields . notifyFollowers as string ;
2020-08-05 00:29:48 -07:00
}
2020-08-05 00:29:29 -07:00
2021-07-19 23:58:54 -07:00
const underPublication = this . getNodeParameter ( 'publication' , i ) as boolean ;
2020-08-05 00:29:29 -07:00
2021-07-19 23:58:54 -07:00
// if user wants to publish it under a specific publication
if ( underPublication ) {
const publicationId = this . getNodeParameter ( 'publicationId' , i ) as number ;
2020-08-05 00:29:29 -07:00
2021-07-19 23:58:54 -07:00
responseData = await mediumApiRequest . call (
this ,
'POST' ,
` /publications/ ${ publicationId } /posts ` ,
bodyRequest ,
qs ,
) ;
}
else {
const responseAuthorId = await mediumApiRequest . call (
this ,
'GET' ,
'/me' ,
{ } ,
qs ,
) ;
2020-08-05 00:29:29 -07:00
2021-07-19 23:58:54 -07:00
const authorId = responseAuthorId . data . id ;
responseData = await mediumApiRequest . call (
this ,
'POST' ,
` /users/ ${ authorId } /posts ` ,
bodyRequest ,
qs ,
) ;
responseData = responseData . data ;
}
2020-08-05 00:29:29 -07:00
}
2021-07-19 23:58:54 -07:00
}
if ( resource === 'publication' ) {
//https://github.com/Medium/medium-api-docs#32-publications
if ( operation === 'getAll' ) {
// ----------------------------------
// publication:getAll
// ----------------------------------
const returnAll = this . getNodeParameter ( 'returnAll' , i ) as string ;
const user = await mediumApiRequest . call (
2020-08-05 00:29:29 -07:00
this ,
'GET' ,
2021-07-19 23:58:54 -07:00
` /me ` ,
2020-08-05 00:29:29 -07:00
) ;
2021-07-19 23:58:54 -07:00
const userId = user . data . id ;
//Get all publications of that user
2020-08-05 00:29:29 -07:00
responseData = await mediumApiRequest . call (
this ,
2021-07-19 23:58:54 -07:00
'GET' ,
` /users/ ${ userId } /publications ` ,
2020-08-05 00:29:29 -07:00
) ;
responseData = responseData . data ;
2021-07-19 23:58:54 -07:00
if ( ! returnAll ) {
const limit = this . getNodeParameter ( 'limit' , i ) as number ;
responseData = responseData . splice ( 0 , limit ) ;
}
2020-08-05 00:29:29 -07:00
}
}
2021-07-19 23:58:54 -07:00
if ( Array . isArray ( responseData ) ) {
returnData . push . apply ( returnData , responseData as IDataObject [ ] ) ;
} else {
returnData . push ( responseData as IDataObject ) ;
}
} catch ( error ) {
if ( this . continueOnFail ( ) ) {
returnData . push ( { error : error.message } ) ;
continue ;
}
throw error ;
2020-08-05 00:29:29 -07:00
}
}
return [ this . helpers . returnJsonArray ( returnData ) ] ;
}
}