diff --git a/packages/nodes-base/nodes/CoinGecko/CoinDescription.ts b/packages/nodes-base/nodes/CoinGecko/CoinDescription.ts new file mode 100644 index 0000000000..6b11cea60d --- /dev/null +++ b/packages/nodes-base/nodes/CoinGecko/CoinDescription.ts @@ -0,0 +1,740 @@ +import { + INodeProperties, +} from 'n8n-workflow'; + +export const coinOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'coin', + ], + }, + }, + options: [ + { + name: 'Candlestick', + value: 'candlestick', + description: 'Get a candlestick open-high-low-close chart for the selected currency', + }, + { + name: 'Get', + value: 'get', + description: 'Get current data for a coin', + }, + { + name: 'Get All', + value: 'getAll', + description: 'Get all coins', + }, + { + name: 'History', + value: 'history', + description: 'Get historical data (name, price, market, stats) at a given date for a coin', + }, + { + name: 'Market', + value: 'market', + description: 'Get prices and market related data for all trading pairs that match the selected currency', + }, + { + name: 'Market Chart', + value: 'marketChart', + description: 'Get historical market data include price, market cap, and 24h volume (granularity auto)', + }, + { + name: 'Price', + value: 'price', + description: 'Get the current price of any cryptocurrencies in any other supported currencies that you need', + }, + { + name: 'Ticker', + value: 'ticker', + description: 'Get coin tickers', + }, + ], + default: 'getAll', + }, +] as INodeProperties[]; + +export const coinFields = [ + { + displayName: 'Search By', + name: 'searchBy', + required: true, + type: 'options', + options: [ + { + name: 'Coin ID', + value: 'coinId', + }, + { + name: 'Contract address', + value: 'contractAddress', + }, + ], + displayOptions: { + show: { + operation: [ + 'get', + 'marketChart', + 'price', + ], + resource: [ + 'coin', + ], + }, + }, + default: 'coinId', + description: 'Search by coin ID or contract address.', + }, + { + displayName: 'Coin ID', + name: 'coinId', + required: true, + type: 'options', + typeOptions: { + loadOptionsMethod: 'getCoins', + }, + displayOptions: { + show: { + operation: [ + 'get', + ], + resource: [ + 'coin', + ], + }, + }, + default: '', + placeholder: 'bitcoin', + description: 'Coin ID', + }, + { + displayName: 'Coin ID', + name: 'coinId', + required: true, + type: 'options', + typeOptions: { + loadOptionsMethod: 'getCoins', + }, + displayOptions: { + show: { + operation: [ + 'ticker', + 'history', + 'candlestick', + ], + resource: [ + 'coin', + ], + }, + }, + default: '', + placeholder: 'bitcoin', + description: 'Coin ID', + }, + { + displayName: 'Coin IDs', + name: 'coinIds', + required: true, + type: 'multiOptions', + typeOptions: { + loadOptionsMethod: 'getCoins', + }, + displayOptions: { + show: { + operation: [ + 'price', + ], + resource: [ + 'coin', + ], + searchBy: [ + 'coinId' + ], + }, + }, + default: [], + placeholder: 'bitcoin', + description: 'ID of coins, comma-separated. Refers to Coin / GetAll.', + }, + { + displayName: 'Platform ID', + name: 'platformId', + required: true, + displayOptions: { + show: { + operation: [ + 'get', + 'marketChart', + 'price', + ], + resource: [ + 'coin', + ], + searchBy: [ + 'contractAddress' + ], + }, + }, + type: 'options', + options: [ + { + name: 'Ethereum', + value: 'ethereum', + }, + ], + default: 'ethereum', + description: 'The id of the platform issuing tokens.', + }, + { + displayName: 'Contract address', + name: 'contractAddress', + required: true, + type: 'string', + displayOptions: { + show: { + operation: [ + 'get', + 'marketChart', + ], + resource: [ + 'coin', + ], + searchBy: [ + 'contractAddress' + ], + }, + }, + description: 'Token\'s contract address.', + }, + { + displayName: 'Contract addresses', + name: 'contractAddresses', + required: true, + type: 'string', + displayOptions: { + show: { + operation: [ + 'price', + ], + resource: [ + 'coin', + ], + searchBy: [ + 'contractAddress' + ], + }, + }, + description: 'The contract address of tokens, comma separated.', + }, + { + displayName: 'Base Currency', + name: 'baseCurrency', + required: true, + type: 'options', + typeOptions: { + loadOptionsMethod: 'getCoins', + }, + displayOptions: { + show: { + operation: [ + 'marketChart', + ], + resource: [ + 'coin', + ], + searchBy: [ + 'coinId' + ], + }, + hide: { + searchBy: [ + 'contractAddress', + ], + }, + }, + default: '', + description: 'The first currency in the pair. For BTC:ETH this is BTC.', + }, + { + displayName: 'Quote Currency', + name: 'quoteCurrency', + required: true, + type: 'options', + typeOptions: { + loadOptionsMethod: 'getCurrencies', + }, + displayOptions: { + show: { + operation: [ + 'market', + 'marketChart', + 'candlestick', + ], + resource: [ + 'coin', + ], + }, + }, + default: '', + description: 'The second currency in the pair. For BTC:ETH this is ETH.', + }, + { + displayName: 'Currencies', + name: 'currencies', + type: 'multiOptions', + typeOptions: { + loadOptionsMethod: 'getCurrencies', + }, + required: true, + displayOptions: { + show: { + operation: [ + 'price', + ], + resource: [ + 'coin', + ], + }, + }, + default: [], + description: 'Currencies of coin.', + }, + { + displayName: 'Historical Range (days)', + name: 'days', + required: true, + type: 'options', + options: [ + { + name: '1', + value: '1', + }, + { + name: '7', + value: '7', + }, + { + name: '14', + value: '14', + }, + { + name: '30', + value: '30', + }, + { + name: '90', + value: '90', + }, + { + name: '180', + value: '180', + }, + { + name: '365', + value: '365', + }, + { + name: 'Max', + value: 'max', + }, + ], + displayOptions: { + show: { + operation: [ + 'marketChart', + 'candlestick', + ], + resource: [ + 'coin', + ], + }, + }, + default: '', + description: 'Return data for this many days in the past from now.', + }, + { + displayName: 'Date', + name: 'date', + required: true, + type: 'dateTime', + displayOptions: { + show: { + operation: [ + 'history', + ], + resource: [ + 'coin', + ], + }, + }, + default: '', + description: 'The date of data snapshot.', + }, + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + operation: [ + 'getAll', + 'market', + 'ticker', + ], + resource: [ + 'coin', + ], + }, + }, + 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', + 'market', + 'ticker', + ], + resource: [ + 'coin', + ], + returnAll: [ + false, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 500, + }, + default: 100, + description: 'How many results to return.', + }, + { + displayName: 'Options', + name: 'options', + type: 'collection', + placeholder: 'Add Option', + default: {}, + displayOptions: { + show: { + resource: [ + 'coin', + ], + operation: [ + 'market', + ], + }, + }, + options: [ + { + displayName: 'Coin IDs', + name: 'ids', + type: 'string', + placeholder: 'bitcoin', + default: '', + description: 'Filter results by comma separated list of coin ID.', + }, + { + displayName: 'Category', + name: 'category', + type: 'options', + options: [ + { + name: 'Decentralized Finance Defi', + value: 'decentralized_finance_defi', + }, + ], + default: 'decentralized_finance_defi', + description: 'Filter by coin category.', + }, + { + displayName: 'Order', + name: 'order', + type: 'options', + options: [ + { + name: 'Market Cap Desc', + value: 'market_cap_desc', + }, + { + name: 'Gecko Desc', + value: 'gecko_desc', + }, + { + name: 'Gecko Asc', + value: 'gecko_asc', + }, + { + name: 'Market Cap Asc', + value: 'market_cap_asc', + }, + { + name: 'Market Cap Desc', + value: 'market_cap_desc', + }, + { + name: 'Volume Asc', + value: 'volume_asc', + }, + { + name: 'Volume Desc', + value: 'volume_desc', + }, + { + name: 'Id Asc', + value: 'id_asc', + }, + { + name: 'Id Desc', + value: 'id_desc', + } + ], + default: '', + description: 'Sort results by field.', + }, + { + displayName: 'Sparkline', + name: 'sparkline', + type: 'boolean', + default: false, + description: 'Include sparkline 7 days data.', + }, + { + displayName: 'Price Change Percentage', + name: 'price_change_percentage', + type: 'multiOptions', + options: [ + { + name: '1h', + value: '1h', + }, + { + name: '24h', + value: '24h', + }, + { + name: '7d', + value: '7d', + }, + { + name: '14d', + value: '14d', + }, + { + name: '30d', + value: '30d', + }, + { + name: '200d', + value: '200d', + }, + { + name: '1y', + value: '1y', + }, + ], + default: [], + description: 'Include price change percentage for specified times.', + }, + ], + }, + { + displayName: 'Options', + name: 'options', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + resource: [ + 'coin', + ], + operation: [ + 'price', + ], + }, + }, + options: [ + { + displayName: 'Include 24hr Change', + name: 'include_24hr_change', + type: 'boolean', + default: false, + }, + { + displayName: 'Include 24hr Vol', + name: 'include_24hr_vol', + type: 'boolean', + default: false, + }, + { + displayName: 'Include Last Updated At', + name: 'include_last_updated_at', + type: 'boolean', + default: false, + }, + { + displayName: 'Include Market Cap', + name: 'include_market_cap', + type: 'boolean', + default: false, + }, + ], + }, + { + displayName: 'Options', + name: 'options', + type: 'collection', + placeholder: 'Add Option', + default: {}, + displayOptions: { + show: { + resource: [ + 'coin', + ], + operation: [ + 'ticker', + ], + }, + }, + options: [ + { + displayName: 'Exchange IDs', + name: 'exchange_ids', + type: 'multiOptions', + typeOptions: { + loadOptionsMethod: 'getExchanges', + }, + default: [], + description: 'Filter results by exchange IDs.', + }, + { + displayName: 'Include Exchange Logo', + name: 'include_exchange_logo', + type: 'boolean', + default: false, + description: 'Include exchange logo.', + }, + { + displayName: 'Order', + name: 'order', + type: 'options', + options: [ + { + name: 'Trust Score Desc', + value: 'trust_score_desc', + }, + { + name: 'Trust Score Asc', + value: 'trust_score_asc', + }, + { + name: 'Volume Desc', + value: 'volume_desc', + }, + ], + default: 'trust_score_desc', + description: 'Sorts results by the selected rule.', + }, + ], + }, + { + displayName: 'Options', + name: 'options', + type: 'collection', + placeholder: 'Add Option', + default: {}, + displayOptions: { + show: { + resource: [ + 'coin', + ], + operation: [ + 'history', + ], + }, + }, + options: [ + { + displayName: 'Localization', + name: 'localization', + type: 'boolean', + default: true, + description: 'Set to false to exclude localized languages in response.', + }, + ], + }, + { + displayName: 'Options', + name: 'options', + type: 'collection', + placeholder: 'Add Field', + default: {}, + displayOptions: { + show: { + operation: [ + 'get', + ], + resource: [ + 'coin', + ], + }, + }, + options: [ + { + displayName: 'Community data', + name: 'community_data', + type: 'boolean', + default: false, + description: 'Include community data.' + }, + { + displayName: 'Developer data', + name: 'developer_data', + type: 'boolean', + default: false, + description: 'Include developer data.' + }, + { + displayName: 'Localization', + name: 'localization', + type: 'boolean', + default: false, + description: 'Include all localized languages in response.' + }, + { + displayName: 'Market data', + name: 'market_data', + type: 'boolean', + default: false, + description: 'Include market data.' + }, + { + displayName: 'Sparkline', + name: 'sparkline', + type: 'boolean', + default: false, + description: 'Include sparkline 7 days data (eg. true, false).' + }, + { + displayName: 'Tickers', + name: 'tickers', + type: 'boolean', + default: false, + description: 'Include tickers data.' + }, + ], + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/CoinGecko/CoinGecko.node.ts b/packages/nodes-base/nodes/CoinGecko/CoinGecko.node.ts new file mode 100644 index 0000000000..aaf8e537cd --- /dev/null +++ b/packages/nodes-base/nodes/CoinGecko/CoinGecko.node.ts @@ -0,0 +1,537 @@ +import { + IExecuteFunctions, +} from 'n8n-core'; + +import { + IDataObject, + ILoadOptionsFunctions, + INodeExecutionData, + INodePropertyOptions, + INodeType, + INodeTypeDescription, +} from 'n8n-workflow'; + +import { + coinFields, + coinOperations, +} from './CoinDescription'; + +import { + eventFields, + eventOperations, +} from './EventDescription'; + +import { + coinGeckoApiRequest, + coinGeckoRequestAllItems, +} from './GenericFunctions'; + +import * as moment from 'moment-timezone'; + +export class CoinGecko implements INodeType { + description: INodeTypeDescription = { + displayName: 'CoinGecko', + name: 'coinGecko', + icon: 'file:coinGecko.png', + group: ['output'], + version: 1, + description: 'Consume CoinGecko API', + subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}', + defaults: { + name: 'CoinGecko', + color: '#8bc53f', + }, + inputs: ['main'], + outputs: ['main'], + properties: [ + { + displayName: 'Resource', + name: 'resource', + type: 'options', + options: [ + { + name: 'Coin', + value: 'coin', + }, + { + name: 'Event', + value: 'event', + }, + ], + default: 'coin', + }, + ...coinOperations, + ...coinFields, + ...eventOperations, + ...eventFields, + ], + }; + + methods = { + loadOptions: { + async getCurrencies(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const currencies = await coinGeckoApiRequest.call( + this, + 'GET', + '/simple/supported_vs_currencies' + ); + currencies.sort(); + for (const currency of currencies) { + returnData.push({ + name: currency.toUpperCase(), + value: currency, + }); + } + return returnData; + }, + + async getCoins(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const coins = await coinGeckoApiRequest.call( + this, + 'GET', + '/coins/list' + ); + for (const coin of coins) { + returnData.push({ + name: coin.symbol.toUpperCase(), + value: coin.id, + }); + } + returnData.sort((a, b) => { + if (a.name < b.name) { return -1; } + if (a.name > b.name) { return 1; } + return 0; + }); + return returnData; + }, + + async getExchanges(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const exchanges = await coinGeckoApiRequest.call( + this, + 'GET', + '/exchanges/list' + ); + for (const exchange of exchanges) { + returnData.push({ + name: exchange.name, + value: exchange.id, + }); + } + return returnData; + }, + + async getEventCountryCodes(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const countryCodes = await coinGeckoApiRequest.call( + this, + 'GET', + '/events/countries' + ); + for (const code of countryCodes.data) { + if (!code.code) { + continue; + } + returnData.push({ + name: code.country, + value: code.code, + }); + } + return returnData; + }, + + async getEventTypes(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + const eventTypes = await coinGeckoApiRequest.call( + this, + 'GET', + '/events/types' + ); + for (const type of eventTypes.data) { + returnData.push({ + name: type, + value: type, + }); + } + return returnData; + }, + }, + }; + + async execute(this: IExecuteFunctions): Promise { + const items = this.getInputData(); + const returnData: IDataObject[] = []; + const length = (items.length as unknown) as number; + const qs: IDataObject = {}; + let responseData; + const resource = this.getNodeParameter('resource', 0) as string; + const operation = this.getNodeParameter('operation', 0) as string; + for (let i = 0; i < length; i++) { + + if (resource === 'coin') { + //https://www.coingecko.com/api/documentations/v3#/coins/get_coins__id_ + //https://www.coingecko.com/api/documentations/v3#/contract/get_coins__id__contract__contract_address_ + if (operation === 'get') { + + const options = this.getNodeParameter('options', i) as IDataObject; + + qs.community_data = false; + qs.developer_data = false; + qs.localization = false; + qs.market_data = false; + qs.sparkline = false; + qs.tickers = false; + + Object.assign(qs, options); + + const searchBy = this.getNodeParameter('searchBy', i) as string; + + if (searchBy === 'coinId') { + const coinId = this.getNodeParameter('coinId', i) as string; + + responseData = await coinGeckoApiRequest.call( + this, + 'GET', + `/coins/${coinId}`, + {}, + qs + ); + } + + if (searchBy === 'contractAddress') { + const platformId = this.getNodeParameter('platformId', i) as string; + const contractAddress = this.getNodeParameter('contractAddress', i) as string; + + responseData = await coinGeckoApiRequest.call( + this, + 'GET', + `/coins/${platformId}/contract/${contractAddress}`, + {}, + qs + ); + } + } + //https://www.coingecko.com/api/documentations/v3#/coins/get_coins_list + if (operation === 'getAll') { + + const returnAll = this.getNodeParameter('returnAll', i) as boolean; + + let limit; + + responseData = await coinGeckoApiRequest.call( + this, + 'GET', + '/coins/list', + {}, + qs + ); + + if (returnAll === false) { + limit = this.getNodeParameter('limit', i) as number; + responseData = responseData.splice(0, limit); + } + } + + //https://www.coingecko.com/api/documentations/v3#/coins/get_coins_list + if (operation === 'market') { + + const returnAll = this.getNodeParameter('returnAll', i) as boolean; + const quoteCurrency = this.getNodeParameter('quoteCurrency', i) as string; + const options = this.getNodeParameter('options', i) as IDataObject; + + qs.vs_currency = quoteCurrency; + + Object.assign(qs, options); + + if (options.price_change_percentage) { + qs.price_change_percentage = (options.price_change_percentage as string[]).join(','); + } + + if (returnAll) { + responseData = await coinGeckoRequestAllItems.call( + this, + '', + 'GET', + `/coins/markets`, + {}, + qs + ); + } else { + const limit = this.getNodeParameter('limit', i) as number; + + qs.per_page = limit; + + responseData = await coinGeckoApiRequest.call( + this, + 'GET', + `/coins/markets`, + {}, + qs + ); + } + } + + //https://www.coingecko.com/api/documentations/v3#/simple/get_simple_price + //https://www.coingecko.com/api/documentations/v3#/simple/get_simple_token_price__id_ + if (operation === 'price') { + + const searchBy = this.getNodeParameter('searchBy', i) as string; + const currencies = this.getNodeParameter('currencies', i) as string[]; + const options = this.getNodeParameter('options', i) as IDataObject; + + qs.vs_currencies = currencies.join(','); + + Object.assign(qs, options); + + if (searchBy === 'coinId') { + const coinIds = this.getNodeParameter('coinIds', i) as string[]; + + qs.ids = coinIds.join(','); + + responseData = await coinGeckoApiRequest.call( + this, + 'GET', + '/simple/price', + {}, + qs + ); + } + + if (searchBy === 'contractAddress') { + const platformId = this.getNodeParameter('platformId', i) as string; + const contractAddresses = this.getNodeParameter('contractAddresses', i) as string; + + qs.contract_addresses = contractAddresses; + + responseData = await coinGeckoApiRequest.call( + this, + 'GET', + `/simple/token_price/${platformId}`, + {}, + qs + ); + } + } + + //https://www.coingecko.com/api/documentations/v3#/coins/get_coins__id__tickers + if (operation === 'ticker') { + + const returnAll = this.getNodeParameter('returnAll', i) as boolean; + const coinId = this.getNodeParameter('coinId', i) as string; + const options = this.getNodeParameter('options', i) as IDataObject; + + Object.assign(qs, options); + + if (options.exchange_ids) { + qs.exchange_ids = (options.exchange_ids as string[]).join(','); + } + + if (returnAll) { + + responseData = await coinGeckoRequestAllItems.call( + this, + 'tickers', + 'GET', + `/coins/${coinId}/tickers`, + {}, + qs, + ); + } else { + const limit = this.getNodeParameter('limit', i) as number; + + responseData = await coinGeckoApiRequest.call( + this, + 'GET', + `/coins/${coinId}/tickers`, + {}, + qs + ); + + responseData = responseData.tickers; + responseData = responseData.splice(0, limit); + } + } + + //https://www.coingecko.com/api/documentations/v3#/coins/get_coins__id__history + if (operation === 'history') { + + const coinId = this.getNodeParameter('coinId', i) as string; + const date = this.getNodeParameter('date', i) as string; + const options = this.getNodeParameter('options', i) as IDataObject; + + Object.assign(qs, options); + + qs.date = moment(date).format('DD-MM-YYYY'); + + responseData = await coinGeckoApiRequest.call( + this, + 'GET', + `/coins/${coinId}/history`, + {}, + qs + ); + } + + //https://www.coingecko.com/api/documentations/v3#/coins/get_coins__id__market_chart + //https://www.coingecko.com/api/documentations/v3#/contract/get_coins__id__contract__contract_address__market_chart_ + if (operation === 'marketChart') { + + let respData; + + const searchBy = this.getNodeParameter('searchBy', i) as string; + const quoteCurrency = this.getNodeParameter('quoteCurrency', i) as string; + const days = this.getNodeParameter('days', i) as string; + + qs.vs_currency = quoteCurrency; + qs.days = days; + + if (searchBy === 'coinId') { + const coinId = this.getNodeParameter('baseCurrency', i) as string; + + respData = await coinGeckoApiRequest.call( + this, + 'GET', + `/coins/${coinId}/market_chart`, + {}, + qs + ); + } + + if (searchBy === 'contractAddress') { + const platformId = this.getNodeParameter('platformId', i) as string; + const contractAddress = this.getNodeParameter('contractAddress', i) as string; + + respData = await coinGeckoApiRequest.call( + this, + 'GET', + `/coins/${platformId}/contract/${contractAddress}/market_chart`, + {}, + qs + ); + } + + responseData = []; + for (let idx = 0; idx < respData.prices.length; idx++) { + const [time, price] = respData.prices[idx]; + const marketCaps = respData.market_caps[idx][1]; + const totalVolume = respData.total_volumes[idx][1]; + responseData.push({ time: moment(time).toISOString(), price, marketCaps, totalVolume } as IDataObject); + } + } + + //https://www.coingecko.com/api/documentations/v3#/coins/get_coins__id__ohlc + if (operation === 'candlestick') { + + const coinId = this.getNodeParameter('coinId', i) as string; + const quoteCurrency = this.getNodeParameter('quoteCurrency', i) as string; + const days = this.getNodeParameter('days', i) as string; + + qs.vs_currency = quoteCurrency; + qs.days = days; + + responseData = await coinGeckoApiRequest.call( + this, + 'GET', + `/coins/${coinId}/ohlc`, + {}, + qs + ); + + for (let idx = 0; idx < responseData.length; idx++) { + const [time, open, high, low, close] = responseData[idx]; + responseData[idx] = { time: moment(time).toISOString(), open, high, low, close } as IDataObject; + } + } + } + + if (resource === 'event') { + //https://www.coingecko.com/api/documentations/v3#/events/get_events + if (operation === 'getAll') { + + const returnAll = this.getNodeParameter('returnAll', i) as boolean; + const options = this.getNodeParameter('options', i) as IDataObject; + + Object.assign(qs, options); + + if (returnAll) { + responseData = await coinGeckoRequestAllItems.call( + this, + 'data', + 'GET', + '/events', + {}, + qs + ); + } else { + const limit = this.getNodeParameter('limit', i) as number; + + qs.per_page = limit; + + responseData = await coinGeckoApiRequest.call( + this, + 'GET', + '/events', + {}, + qs + ); + responseData = responseData.data; + } + } + } + + if (resource === 'simple') { + //https://www.coingecko.com/api/documentations/v3#/simple/get_simple_price + if (operation === 'price') { + + const ids = this.getNodeParameter('ids', i) as string; + const currencies = this.getNodeParameter('currencies', i) as string[]; + const options = this.getNodeParameter('options', i) as IDataObject; + + qs.ids = ids, + qs.vs_currencies = currencies.join(','); + + Object.assign(qs, options); + + responseData = await coinGeckoApiRequest.call( + this, + 'GET', + '/simple/price', + {}, + qs + ); + } + + //https://www.coingecko.com/api/documentations/v3#/simple/get_simple_token_price__id_ + if (operation === 'tokenPrice') { + + const id = this.getNodeParameter('id', i) as string; + const contractAddresses = this.getNodeParameter('contractAddresses', i) as string; + const currencies = this.getNodeParameter('currencies', i) as string[]; + const options = this.getNodeParameter('options', i) as IDataObject; + + qs.contract_addresses = contractAddresses; + qs.vs_currencies = currencies.join(','); + + Object.assign(qs, options); + + responseData = await coinGeckoApiRequest.call( + this, + 'GET', + `/simple/token_price/${id}`, + {}, + qs + ); + } + } + + } + if (Array.isArray(responseData)) { + returnData.push.apply(returnData, responseData as IDataObject[]); + } else if (responseData !== undefined) { + returnData.push(responseData as IDataObject); + } + return [this.helpers.returnJsonArray(returnData)]; + } +} diff --git a/packages/nodes-base/nodes/CoinGecko/EventDescription.ts b/packages/nodes-base/nodes/CoinGecko/EventDescription.ts new file mode 100644 index 0000000000..4a6f5dc599 --- /dev/null +++ b/packages/nodes-base/nodes/CoinGecko/EventDescription.ts @@ -0,0 +1,130 @@ +import { + INodeProperties, +} from 'n8n-workflow'; + +export const eventOperations = [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + displayOptions: { + show: { + resource: [ + 'event', + ], + }, + }, + options: [ + { + name: 'Get All', + value: 'getAll', + description: 'Get all events', + }, + ], + default: 'getAll', + }, +] as INodeProperties[]; + +export const eventFields = [ + { + displayName: 'Return All', + name: 'returnAll', + type: 'boolean', + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'event', + ], + }, + }, + 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: [ + 'event', + ], + returnAll: [ + false, + ], + }, + }, + typeOptions: { + minValue: 1, + maxValue: 500, + }, + default: 100, + description: 'How many results to return.', + }, + { + displayName: 'Options', + name: 'options', + type: 'collection', + placeholder: 'Add Option', + default: {}, + displayOptions: { + show: { + operation: [ + 'getAll', + ], + resource: [ + 'event', + ], + }, + }, + options: [ + { + displayName: 'Country code', + name: 'country_code', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getEventCountryCodes', + }, + default: '', + description: 'Country code of event.', + }, + { + displayName: 'From date', + name: 'from_date', + type: 'dateTime', + default: '', + description: 'Lists events after this date.', + }, + { + displayName: 'To date', + name: 'to_date', + type: 'dateTime', + default: '', + description: 'Lists events before this date.', + }, + { + displayName: 'Type', + name: 'type', + type: 'options', + typeOptions: { + loadOptionsMethod: 'getEventTypes', + }, + default: '', + description: 'Type of event.', + }, + { + displayName: 'Upcoming events only', + name: 'upcoming_events_only', + type: 'boolean', + default: true, + description: 'Lists only upcoming events.', + }, + ], + }, +] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/CoinGecko/GenericFunctions.ts b/packages/nodes-base/nodes/CoinGecko/GenericFunctions.ts new file mode 100644 index 0000000000..d5f3e0b51a --- /dev/null +++ b/packages/nodes-base/nodes/CoinGecko/GenericFunctions.ts @@ -0,0 +1,67 @@ +import { + OptionsWithUri, +} from 'request'; + +import { + IExecuteFunctions, + IExecuteSingleFunctions, + ILoadOptionsFunctions, +} from 'n8n-core'; + +import { + IDataObject, +} from 'n8n-workflow'; + +export async function coinGeckoApiRequest(this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method: string, + endpoint: string, body: any = {}, qs: IDataObject = {}, uri?: string, option: IDataObject = {}): Promise { // tslint:disable-line:no-any + let options: OptionsWithUri = { + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json', + }, + method, + body, + qs, + uri: uri || `https://api.coingecko.com/api/v3${endpoint}`, + json: true, + }; + + options = Object.assign({}, options, option); + + try { + if (Object.keys(body).length === 0) { + delete options.body; + } + + //@ts-ignore + return await this.helpers.request.call(this, options); + + } catch (error) { + + throw error; + } +} + +export async function coinGeckoRequestAllItems(this: IExecuteFunctions | ILoadOptionsFunctions, propertyName: string, method: string, endpoint: string, body: any = {}, query: IDataObject = {}): Promise { // tslint:disable-line:no-any + + const returnData: IDataObject[] = []; + + let responseData; + let respData; + query.per_page = 250; + query.page = 1; + + do { + responseData = await coinGeckoApiRequest.call(this, method, endpoint, body, query); + query.page++; + respData = responseData; + if (propertyName !== '') { + respData = responseData[propertyName]; + } + returnData.push.apply(returnData, respData); + } while ( + respData.length !== 0 + ); + + return returnData; +} diff --git a/packages/nodes-base/nodes/CoinGecko/coinGecko.png b/packages/nodes-base/nodes/CoinGecko/coinGecko.png new file mode 100644 index 0000000000..332730847f Binary files /dev/null and b/packages/nodes-base/nodes/CoinGecko/coinGecko.png differ diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index a703f528dc..c54d6f6a24 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -220,6 +220,7 @@ "dist/nodes/Clockify/ClockifyTrigger.node.js", "dist/nodes/Cockpit/Cockpit.node.js", "dist/nodes/Coda/Coda.node.js", + "dist/nodes/CoinGecko/CoinGecko.node.js", "dist/nodes/Contentful/Contentful.node.js", "dist/nodes/ConvertKit/ConvertKit.node.js", "dist/nodes/ConvertKit/ConvertKitTrigger.node.js",