@chainlink/external-adapter-framework 0.0.67 → 0.0.69

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/adapter.d.ts +156 -0
  2. package/adapter.js +200 -0
  3. package/adapter.js.map +1 -0
  4. package/cache/redis.d.ts +8 -1
  5. package/cache/redis.js +39 -22
  6. package/cache/redis.js.map +1 -1
  7. package/config/index.d.ts +1 -1
  8. package/examples/bank-frick/config/index.d.ts +1 -1
  9. package/examples/coingecko/batch-warming.d.ts +7 -0
  10. package/examples/coingecko/batch-warming.js +54 -0
  11. package/examples/coingecko/batch-warming.js.map +1 -0
  12. package/examples/coingecko/index.d.ts +2 -0
  13. package/examples/coingecko/index.js +12 -0
  14. package/examples/coingecko/index.js.map +1 -0
  15. package/examples/coingecko/rest.d.ts +12 -0
  16. package/examples/coingecko/rest.js +55 -0
  17. package/examples/coingecko/rest.js.map +1 -0
  18. package/examples/coingecko/src/crypto-utils.d.ts +3 -3
  19. package/examples/coingecko/src/crypto-utils.js.map +1 -1
  20. package/examples/coingecko/src/cryptoUtils.d.ts +31 -0
  21. package/examples/coingecko/src/cryptoUtils.js +60 -0
  22. package/examples/coingecko/src/endpoint/coins.js.map +1 -1
  23. package/examples/coingecko/src/endpoint/crypto-marketcap.js.map +1 -1
  24. package/examples/coingecko/src/endpoint/crypto-volume.js.map +1 -1
  25. package/examples/coingecko/src/endpoint/crypto.js.map +1 -1
  26. package/examples/coingecko/src/endpoint/dominance.js.map +1 -1
  27. package/examples/coingecko/src/endpoint/global-marketcap.js.map +1 -1
  28. package/examples/coingecko/src/global-utils.d.ts +3 -3
  29. package/examples/coingecko/src/global-utils.js.map +1 -1
  30. package/examples/coingecko/src/globalUtils.d.ts +27 -0
  31. package/examples/coingecko/src/globalUtils.js +46 -0
  32. package/examples/coingecko/src/index.d.ts +1 -1
  33. package/examples/cryptocompare/src/index.d.ts +1 -1
  34. package/examples/genesis/sseStream.js.map +1 -1
  35. package/index.d.ts +1 -2
  36. package/index.js.map +1 -1
  37. package/package.json +2 -2
  38. package/rate-limiting/index.d.ts +2 -2
  39. package/transports/metrics.d.ts +1 -1
  40. package/transports/rest.d.ts +0 -1
  41. package/transports/rest.js.map +1 -1
  42. package/validation/preset-tokens.json +23 -0
  43. package/validation/validator.d.ts +47 -0
  44. package/validation/validator.js +303 -0
package/adapter.d.ts ADDED
@@ -0,0 +1,156 @@
1
+ import Redis from 'ioredis';
2
+ import type EventSource from 'eventsource';
3
+ import { Cache } from './cache';
4
+ import { AdapterConfig, BaseAdapterConfig, CustomSettingsType, SettingsMap } from './config';
5
+ import { AdapterRateLimitTier, BackgroundExecuteRateLimiter, RequestRateLimiter } from './rate-limiting';
6
+ import { Transport } from './transports';
7
+ import { SubscriptionSetFactory } from './util';
8
+ import { InputParameters } from './validation/input-params';
9
+ import { InputValidator } from './validation/input-validator';
10
+ /**
11
+ * Dependencies that will be injected into the Adapter on startup
12
+ */
13
+ export interface AdapterDependencies {
14
+ /** Specific instance of the Cache the adapter will use (e.g. Local, Redis, etc.) */
15
+ cache: Cache;
16
+ /** Shared instance of the request rate limiter */
17
+ requestRateLimiter: RequestRateLimiter;
18
+ /** Shared instance of the background execute rate limiter */
19
+ backgroundExecuteRateLimiter: BackgroundExecuteRateLimiter;
20
+ /** Factory to create subscription sets based on the specified cache type */
21
+ subscriptionSetFactory: SubscriptionSetFactory;
22
+ /** Redis client used for cache and subscription set */
23
+ redisClient: Redis;
24
+ /** EventSource to use for listening to server sent events. A mock EventSource can be provided as a dependency for testing */
25
+ eventSource: typeof EventSource;
26
+ }
27
+ /**
28
+ * Context that will be used on background executions of a Transport.
29
+ * For example, the endpointName used to log statements or generate Cache keys.
30
+ */
31
+ export interface AdapterContext<CustomSettings extends CustomSettingsType<CustomSettings> = SettingsMap> {
32
+ /** Endpoint instance within the adapter that the Transport is related to */
33
+ adapterEndpoint: AdapterEndpoint<unknown, unknown, CustomSettings>;
34
+ /** Initialized config for the adapter that the Transport can access */
35
+ adapterConfig: AdapterConfig<CustomSettings>;
36
+ }
37
+ /**
38
+ * Structure to describe rate limits specs for the Adapter
39
+ */
40
+ interface AdapterRateLimitingConfig {
41
+ /** Adapter rate limits, gotten from the specific tier requested */
42
+ tiers: Record<string, AdapterRateLimitTier>;
43
+ }
44
+ /**
45
+ * Main structure of an External Adapter
46
+ */
47
+ export interface AdapterParams<CustomSettings extends SettingsMap> {
48
+ /** Name of the adapter */
49
+ name: string;
50
+ /** If present, the string that will be used for requests with no specified endpoint */
51
+ defaultEndpoint?: string;
52
+ /**
53
+ * List of [[AdapterEndpoint]]s in the adapter. This is purposefully typed any; it's the correct type in this case.
54
+ *
55
+ * When you try to create an adapter and you provide an endpoint, if these generics were set to "unknown" instead
56
+ * what would happen is that Typescript would check if the types match, and would fail to assign unknown to the
57
+ * specific Params or Result in the transport itself.
58
+ * We also can't use generics, because if we had more than one transport with different requests (very likely)
59
+ * then those new types wouldn't match with each other.
60
+ */
61
+ endpoints: AdapterEndpoint<any, any, CustomSettings>[];
62
+ /** Map of overrides to the default config values for an Adapter */
63
+ envDefaultOverrides?: Partial<BaseAdapterConfig>;
64
+ /** List of custom env vars for this particular adapter (e.g. RPC_URL) */
65
+ customSettings?: SettingsMap;
66
+ /** Configuration relevant to outbound (EA --\> DP) communication rate limiting */
67
+ rateLimiting?: AdapterRateLimitingConfig;
68
+ /** Overrides for converting the 'base' parameter that are hardcoded into the adapter. */
69
+ overrides?: Record<string, string>;
70
+ }
71
+ /**
72
+ * Structure to describe rate limits specs for a specific adapter endpoint
73
+ */
74
+ export interface EndpointRateLimitingConfig {
75
+ /**
76
+ * How much of the total limit for the adapter will be assigned to this specific endpoint.
77
+ * Should be a non-zero positive number up to 100.
78
+ * Endpoints in the same adapter without a specific allocation will divide the remaining limits equally.
79
+ */
80
+ allocationPercentage: number;
81
+ }
82
+ /**
83
+ * Structure to describe a specific endpoint in an [[Adapter]]
84
+ */
85
+ export interface AdapterEndpointParams<Params, Result, CustomSettings extends SettingsMap> {
86
+ /** Name that will be used to match input params to this endpoint (case insensitive) */
87
+ name: string;
88
+ /** List of alternative endpoint names that will resolve to this same transport (case insensitive) */
89
+ aliases?: string[];
90
+ /** Transport that will be used to handle data processing and communication for this endpoint */
91
+ transport: Transport<Params, Result, CustomSettings>;
92
+ /** Specification of what the body of a request hitting this endpoint should look like (used for validation) */
93
+ inputParameters: InputParameters;
94
+ /** Specific details related to the rate limiting for this endpoint in particular */
95
+ rateLimiting?: EndpointRateLimitingConfig;
96
+ }
97
+ export declare class AdapterEndpoint<Params, Result, CustomSettings extends SettingsMap> implements AdapterEndpointParams<Params, Result, CustomSettings> {
98
+ name: string;
99
+ aliases?: string[] | undefined;
100
+ transport: Transport<Params, Result, CustomSettings>;
101
+ inputParameters: InputParameters;
102
+ rateLimiting?: EndpointRateLimitingConfig | undefined;
103
+ validator: InputValidator;
104
+ constructor(params: AdapterEndpointParams<Params, Result, CustomSettings>);
105
+ }
106
+ declare type CustomAdapterSettings = SettingsMap & NegatedAdapterSettings;
107
+ declare type NegatedAdapterSettings = {
108
+ [K in keyof BaseAdapterConfig]?: never;
109
+ };
110
+ export declare class Adapter<CustomSettings extends CustomAdapterSettings = SettingsMap> implements AdapterParams<CustomSettings> {
111
+ name: string;
112
+ defaultEndpoint?: string | undefined;
113
+ endpoints: AdapterEndpoint<unknown, unknown, CustomSettings>[];
114
+ envDefaultOverrides?: Partial<BaseAdapterConfig> | undefined;
115
+ customSettings?: SettingsMap | undefined;
116
+ rateLimiting?: AdapterRateLimitingConfig | undefined;
117
+ overrides?: Record<string, string> | undefined;
118
+ initialized: boolean;
119
+ /** Object containing alias translations for all endpoints */
120
+ endpointsMap: Record<string, AdapterEndpoint<unknown, unknown, CustomSettings>>;
121
+ /** Initialized dependencies that the adapter will use */
122
+ dependencies: AdapterDependencies;
123
+ /** Configuration params for various adapter properties */
124
+ config: AdapterConfig;
125
+ constructor(params: AdapterParams<CustomSettings>);
126
+ /**
127
+ * Initializes all of the [[Transport]]s in the adapter, passing along any [[AdapterDependencies]] and [[AdapterConfig]].
128
+ * Additionally, it builds a map out of all the endpoint names and aliases (checking for duplicates).
129
+ */
130
+ initialize(dependencies?: Partial<AdapterDependencies>): Promise<void>;
131
+ /**
132
+ * Takes an adapter and normalizes all endpoint names and aliases, as well as the default endpoint.
133
+ * i.e. makes them lowercase for now
134
+ */
135
+ private normalizeEndpointNames;
136
+ /**
137
+ * This function will take an adapter structure and go through each endpoint, calculating
138
+ * each one's allocation of the total rate limits that are set for the adapter as a whole.
139
+ *
140
+ */
141
+ private calculateRateLimitAllocations;
142
+ /**
143
+ * Creates a list of key/value pairs that need to be censored in the logs
144
+ * using the sensitive flag in the adapter config
145
+ */
146
+ private buildCensorList;
147
+ /**
148
+ * This function will process dependencies for an adapter, such as caches or rate limiters,
149
+ * in order to inject them into transports and other relevant places later in the lifecycle.
150
+ *
151
+ * @param inputDependencies - a partial obj of initialized dependencies to override the created ones
152
+ * @returns a set of AdapterDependencies all initialized
153
+ */
154
+ initializeDependencies(inputDependencies?: Partial<AdapterDependencies>): AdapterDependencies;
155
+ }
156
+ export {};
package/adapter.js ADDED
@@ -0,0 +1,200 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ exports.Adapter = exports.AdapterEndpoint = void 0;
30
+ const ioredis_1 = __importDefault(require("ioredis"));
31
+ const cache_1 = require("./cache");
32
+ const config_1 = require("./config");
33
+ const rate_limiting_1 = require("./rate-limiting");
34
+ const util_1 = require("./util");
35
+ const censor_list_1 = __importDefault(require("./util/censor/censor-list"));
36
+ const cacheMetrics = __importStar(require("./cache/metrics"));
37
+ const input_validator_1 = require("./validation/input-validator");
38
+ const client = __importStar(require("prom-client"));
39
+ const logger = (0, util_1.makeLogger)('Adapter');
40
+ /**
41
+ * `prom-client` is shared between v2 and v3 when both are dependencies, resulting in metric collision errors
42
+ * Clearing metrics on import will wipe v2's existing register so we can replace them with v3's metrics
43
+ **/
44
+ client.register.clear();
45
+ class AdapterEndpoint {
46
+ constructor(params) {
47
+ this.name = params.name;
48
+ this.aliases = params.aliases;
49
+ this.transport = params.transport;
50
+ this.inputParameters = params.inputParameters;
51
+ this.rateLimiting = params.rateLimiting;
52
+ this.validator = new input_validator_1.InputValidator(this.inputParameters);
53
+ }
54
+ }
55
+ exports.AdapterEndpoint = AdapterEndpoint;
56
+ class Adapter {
57
+ constructor(params) {
58
+ // After initialization
59
+ this.initialized = false;
60
+ /** Object containing alias translations for all endpoints */
61
+ this.endpointsMap = {};
62
+ // Copy over params
63
+ this.name = params.name;
64
+ this.defaultEndpoint = params.defaultEndpoint?.toLowerCase();
65
+ this.endpoints = params.endpoints;
66
+ this.envDefaultOverrides = params.envDefaultOverrides;
67
+ this.customSettings = params.customSettings;
68
+ this.rateLimiting = params.rateLimiting;
69
+ this.overrides = params.overrides;
70
+ this.config = (0, config_1.buildAdapterConfig)({
71
+ overrides: this.envDefaultOverrides,
72
+ customSettings: this.customSettings,
73
+ });
74
+ this.normalizeEndpointNames();
75
+ this.calculateRateLimitAllocations();
76
+ }
77
+ /**
78
+ * Initializes all of the [[Transport]]s in the adapter, passing along any [[AdapterDependencies]] and [[AdapterConfig]].
79
+ * Additionally, it builds a map out of all the endpoint names and aliases (checking for duplicates).
80
+ */
81
+ async initialize(dependencies) {
82
+ if (this.initialized) {
83
+ throw new Error('This adapter has already been initialized!');
84
+ }
85
+ this.dependencies = this.initializeDependencies(dependencies);
86
+ for (const endpoint of this.endpoints) {
87
+ // Add aliases to map to use in validation
88
+ const aliases = [endpoint.name, ...(endpoint.aliases || [])];
89
+ for (const alias of aliases) {
90
+ if (this.endpointsMap[alias]) {
91
+ throw new Error(`Duplicate endpoint / alias: "${alias}"`);
92
+ }
93
+ this.endpointsMap[alias] = endpoint;
94
+ }
95
+ logger.debug(`Initializing transport for endpoint "${endpoint.name}"...`);
96
+ await endpoint.transport.initialize(this.dependencies, this.config, endpoint.name);
97
+ }
98
+ // Build list of key/values that need to be redacted in logs
99
+ // Populates the static array in CensorList to use in censor-transport
100
+ this.buildCensorList();
101
+ logger.debug('Adapter initialization complete.');
102
+ this.initialized = true;
103
+ }
104
+ /**
105
+ * Takes an adapter and normalizes all endpoint names and aliases, as well as the default endpoint.
106
+ * i.e. makes them lowercase for now
107
+ */
108
+ normalizeEndpointNames() {
109
+ for (const endpoint of this.endpoints) {
110
+ endpoint.name = endpoint.name.toLowerCase();
111
+ endpoint.aliases = endpoint.aliases?.map((a) => a.toLowerCase());
112
+ }
113
+ }
114
+ /**
115
+ * This function will take an adapter structure and go through each endpoint, calculating
116
+ * each one's allocation of the total rate limits that are set for the adapter as a whole.
117
+ *
118
+ */
119
+ calculateRateLimitAllocations() {
120
+ const numberOfEndpoints = this.endpoints.length;
121
+ const endpointsWithExplicitAllocations = this.endpoints.filter((e) => e.rateLimiting);
122
+ const totalExplicitAllocation = endpointsWithExplicitAllocations
123
+ .map((e) => e.rateLimiting?.allocationPercentage || 0)
124
+ .reduce((sum, next) => sum + next, 0);
125
+ if (totalExplicitAllocation > 100) {
126
+ throw new Error('The total allocation set for all endpoints summed cannot exceed 100%');
127
+ }
128
+ if (totalExplicitAllocation === 100 &&
129
+ numberOfEndpoints - endpointsWithExplicitAllocations.length > 0) {
130
+ throw new Error('The explicit allocation is at 100% but there are endpoints with implicit allocation');
131
+ }
132
+ const implicitAllocation = 100 - totalExplicitAllocation;
133
+ logger.debug('Adapter rate limit allocations:');
134
+ for (const endpoint of this.endpoints) {
135
+ if (!endpoint.rateLimiting) {
136
+ endpoint.rateLimiting = {
137
+ allocationPercentage: implicitAllocation / (numberOfEndpoints - endpointsWithExplicitAllocations.length),
138
+ };
139
+ }
140
+ logger.debug(`Endpoint [${endpoint.name}] - ${endpoint.rateLimiting?.allocationPercentage}%`);
141
+ }
142
+ }
143
+ /**
144
+ * Creates a list of key/value pairs that need to be censored in the logs
145
+ * using the sensitive flag in the adapter config
146
+ */
147
+ buildCensorList() {
148
+ const censorList = Object.entries(config_1.BaseSettings)
149
+ .concat(Object.entries(this.customSettings || {}))
150
+ .filter(([name, setting]) => setting && setting.type === 'string' && setting.sensitive && this.config[name])
151
+ .map(([name]) => ({
152
+ key: name,
153
+ // Escaping potential special characters in values before creating regex
154
+ value: new RegExp(
155
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
156
+ this.config[name].replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&')),
157
+ }));
158
+ censor_list_1.default.set(censorList);
159
+ }
160
+ /**
161
+ * This function will process dependencies for an adapter, such as caches or rate limiters,
162
+ * in order to inject them into transports and other relevant places later in the lifecycle.
163
+ *
164
+ * @param inputDependencies - a partial obj of initialized dependencies to override the created ones
165
+ * @returns a set of AdapterDependencies all initialized
166
+ */
167
+ initializeDependencies(inputDependencies) {
168
+ const dependencies = inputDependencies || {};
169
+ if (!dependencies.redisClient) {
170
+ if (this.config.CACHE_TYPE === 'redis') {
171
+ dependencies.redisClient = new ioredis_1.default({
172
+ enableAutoPipelining: true,
173
+ host: this.config.CACHE_REDIS_HOST,
174
+ port: this.config.CACHE_REDIS_PORT,
175
+ password: this.config.CACHE_REDIS_PASSWORD,
176
+ path: this.config.CACHE_REDIS_PATH,
177
+ timeout: this.config.CACHE_REDIS_TIMEOUT,
178
+ });
179
+ dependencies.redisClient.on('connect', () => {
180
+ cacheMetrics.redisConnectionsOpen.inc();
181
+ });
182
+ }
183
+ }
184
+ if (!dependencies.cache) {
185
+ dependencies.cache = cache_1.CacheFactory.buildCache(this.config.CACHE_TYPE, dependencies.redisClient);
186
+ }
187
+ const rateLimitingTier = (0, rate_limiting_1.getRateLimitingTier)(this.config, this.rateLimiting?.tiers);
188
+ if (!dependencies.requestRateLimiter) {
189
+ dependencies.requestRateLimiter = new rate_limiting_1.SimpleCountingRateLimiter().initialize(this.endpoints, rateLimitingTier);
190
+ }
191
+ if (!dependencies.backgroundExecuteRateLimiter) {
192
+ dependencies.backgroundExecuteRateLimiter = new rate_limiting_1.FixedFrequencyRateLimiter().initialize(this.endpoints, rateLimitingTier);
193
+ }
194
+ if (!dependencies.subscriptionSetFactory) {
195
+ dependencies.subscriptionSetFactory = new util_1.SubscriptionSetFactory(this.config, dependencies.redisClient, this.name);
196
+ }
197
+ return dependencies;
198
+ }
199
+ }
200
+ exports.Adapter = Adapter;
package/adapter.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.js","sourceRoot":"","sources":["../../src/adapter.ts"],"names":[],"mappings":"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"}
package/cache/redis.d.ts CHANGED
@@ -1,14 +1,21 @@
1
- import Redis from 'ioredis';
1
+ import Redis, { Callback } from 'ioredis';
2
2
  import { Cache, CacheEntry } from './index';
3
+ /**
4
+ * Common callback for redis commands that logs success/fail and throws on fail
5
+ * @param {string} message - message to log on error
6
+ * @param {string} functionName - name of the redis function where this callback was used, used for metrics
7
+ */
3
8
  /**
4
9
  * Redis implementation of a Cache. It uses a simple js Object, storing entries with both
5
10
  * a value and an expiration timestamp. Expired entries are deleted on reads (i.e. no background gc/upkeep).
6
11
  *
7
12
  * @typeParam T - the type for the entries' values
13
+ * @param {boolean} alwaysFail - if true, error callback will always throw an error (used for testing)
8
14
  */
9
15
  export declare class RedisCache<T = unknown> implements Cache<T> {
10
16
  private client;
11
17
  constructor(client: Redis);
18
+ commandCallback(message: string, functionName: string): Callback;
12
19
  get(key: string): Promise<T | undefined>;
13
20
  delete(key: string): Promise<void>;
14
21
  set(key: string, value: T, ttl: number): Promise<void>;
package/cache/redis.js CHANGED
@@ -25,25 +25,52 @@ var __importStar = (this && this.__importStar) || function (mod) {
25
25
  Object.defineProperty(exports, "__esModule", { value: true });
26
26
  exports.RedisCache = void 0;
27
27
  const util_1 = require("../util");
28
+ const metrics_1 = require("./metrics");
28
29
  const cacheMetrics = __importStar(require("./metrics"));
29
30
  const logger = (0, util_1.makeLogger)('RedisCache');
31
+ const recordRedisCommandMetric = (status, functionName) => {
32
+ cacheMetrics.redisCommandsSentCount
33
+ .labels({ status: cacheMetrics.CMD_SENT_STATUS[status], function_name: functionName })
34
+ .inc();
35
+ };
36
+ /**
37
+ * Common callback for redis commands that logs success/fail and throws on fail
38
+ * @param {string} message - message to log on error
39
+ * @param {string} functionName - name of the redis function where this callback was used, used for metrics
40
+ */
30
41
  /**
31
42
  * Redis implementation of a Cache. It uses a simple js Object, storing entries with both
32
43
  * a value and an expiration timestamp. Expired entries are deleted on reads (i.e. no background gc/upkeep).
33
44
  *
34
45
  * @typeParam T - the type for the entries' values
46
+ * @param {boolean} alwaysFail - if true, error callback will always throw an error (used for testing)
35
47
  */
36
48
  class RedisCache {
37
49
  constructor(client) {
38
50
  this.client = client;
51
+ // Global error handler in case one of the commands is missing a callback
52
+ this.client.on('error', (err) => {
53
+ logger.error(`Encountered unhandled error in RedisCache: ${err}`);
54
+ // Record error command sent to Redis
55
+ recordRedisCommandMetric(metrics_1.CMD_SENT_STATUS.FAIL, 'outermost');
56
+ throw err;
57
+ });
58
+ }
59
+ commandCallback(message, functionName) {
60
+ return (err) => {
61
+ if (err) {
62
+ logger.error(`${message}: ${err}`);
63
+ recordRedisCommandMetric(metrics_1.CMD_SENT_STATUS.FAIL, functionName);
64
+ throw err;
65
+ }
66
+ else {
67
+ recordRedisCommandMetric(metrics_1.CMD_SENT_STATUS.SUCCESS, functionName);
68
+ }
69
+ };
39
70
  }
40
71
  async get(key) {
41
72
  logger.trace(`Getting key ${key}`);
42
- const value = await this.client.get(key);
43
- // Record get command sent to Redis
44
- cacheMetrics.redisCommandsSentCount
45
- .labels({ status: cacheMetrics.CMD_SENT_STATUS.SUCCESS, function_name: 'get' })
46
- .inc();
73
+ const value = await this.client.get(key, this.commandCallback(`Error getting key "${key}"`, 'get'));
47
74
  if (!value) {
48
75
  logger.debug(`No entry in redis cache for key "${key}", returning undefined`);
49
76
  return undefined;
@@ -52,38 +79,30 @@ class RedisCache {
52
79
  }
53
80
  async delete(key) {
54
81
  logger.trace(`Deleting key ${key}`);
55
- await this.client.del(key);
56
- // Record delete command sent to Redis
57
- cacheMetrics.redisCommandsSentCount
58
- .labels({ status: cacheMetrics.CMD_SENT_STATUS.SUCCESS, function_name: 'delete' })
59
- .inc();
82
+ await this.client.del(key, this.commandCallback(`Error deleting key "${key}"`, 'delete'));
60
83
  }
61
84
  async set(key, value, ttl) {
62
85
  logger.trace(`Setting key ${key}`);
63
- await this.client.set(key, JSON.stringify(value), 'PX', ttl);
64
- // Only record metrics if feed Id is present, otherwise assuming value is not adapter response to record
86
+ // Only record metrics if feed id is present, otherwise assuming value is not adapter response to record
65
87
  const feedId = value.meta?.metrics?.feedId;
66
88
  if (feedId) {
67
89
  // Record cache set count, max age, and staleness (set to 0 for cache set)
68
90
  const label = cacheMetrics.cacheMetricsLabel(key, feedId, cacheMetrics.CacheTypes.Redis);
69
91
  cacheMetrics.cacheSet(label, ttl);
70
92
  }
71
- // Record set command sent to Redis
72
- cacheMetrics.redisCommandsSentCount
73
- .labels({ status: cacheMetrics.CMD_SENT_STATUS.SUCCESS, function_name: 'set' })
74
- .inc();
93
+ await this.client.set(key, JSON.stringify(value), 'PX', ttl, this.commandCallback(`Error setting key "${key}"`, 'set'));
75
94
  }
76
95
  async setMany(entries, ttl) {
77
96
  logger.trace(`Setting a bunch of keys`);
78
97
  // Unfortunately, there's no ttl for mset
79
98
  let chain = this.client.multi();
80
99
  for (const entry of entries) {
81
- chain = chain.set(entry.key, JSON.stringify(entry.value), 'PX', ttl);
100
+ chain = chain.set(entry.key, JSON.stringify(entry.value), 'PX', ttl, this.commandCallback(`Error setting key "${entry.key}"`, 'setMany'));
82
101
  }
83
- await chain.exec();
102
+ await chain.exec(this.commandCallback(`Exec failed`, 'exec')); // TODO Not sure this will be hit since an individual set will throw
84
103
  // Loop again, but this time to record these in metrics
85
104
  for (const entry of entries) {
86
- // Only record metrics if feed Id is present, otherwise assuming value is not adapter response to record
105
+ // Only record metrics if feed id is present, otherwise assuming value is not adapter response to record
87
106
  const feedId = entry.value.meta?.metrics?.feedId;
88
107
  if (feedId) {
89
108
  // Record cache set count, max age, and staleness (set to 0 for cache set)
@@ -92,9 +111,7 @@ class RedisCache {
92
111
  }
93
112
  }
94
113
  // Record setMany command sent to Redis
95
- cacheMetrics.redisCommandsSentCount
96
- .labels({ status: cacheMetrics.CMD_SENT_STATUS.SUCCESS, function_name: 'exec' })
97
- .inc();
114
+ recordRedisCommandMetric(metrics_1.CMD_SENT_STATUS.SUCCESS, 'exec');
98
115
  }
99
116
  }
100
117
  exports.RedisCache = RedisCache;
@@ -1 +1 @@
1
- {"version":3,"file":"redis.js","sourceRoot":"","sources":["../../../src/cache/redis.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"redis.js","sourceRoot":"","sources":["../../../src/cache/redis.ts"],"names":[],"mappings":"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"}
package/config/index.d.ts CHANGED
@@ -191,7 +191,7 @@ export declare const BaseSettings: {
191
191
  readonly description: "Maximum amount of characters that the common part of the cache key or feed ID can have";
192
192
  readonly type: "number";
193
193
  readonly default: 300;
194
- readonly validate: (value?: number) => "MAX_COMMON_KEY_SIZE must be a number between 150 and 500" | undefined;
194
+ readonly validate: (value?: number | undefined) => "MAX_COMMON_KEY_SIZE must be a number between 150 and 500" | undefined;
195
195
  };
196
196
  readonly REST_TRANSPORT_MAX_RATE_LIMIT_RETRIES: {
197
197
  readonly description: "Maximum amount of times the Rest Transport will attempt to set up a request when blocked by the rate limiter";
@@ -6,7 +6,7 @@ export declare const customSettings: {
6
6
  readonly type: "number";
7
7
  readonly required: false;
8
8
  readonly default: 500;
9
- readonly validate: (value?: number) => string | undefined;
9
+ readonly validate: (value?: number | undefined) => string | undefined;
10
10
  };
11
11
  readonly PRIVATE_KEY: {
12
12
  readonly description: "";
@@ -0,0 +1,7 @@
1
+ import { AdapterEndpoint } from '../../adapter';
2
+ interface AdapterRequestParams {
3
+ base: string;
4
+ quote: string;
5
+ }
6
+ export declare const batchEndpoint: AdapterEndpoint<AdapterRequestParams, unknown, import("../../config").SettingsMap>;
7
+ export {};
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.batchEndpoint = void 0;
4
+ const adapter_1 = require("../../adapter");
5
+ const batch_warming_1 = require("../../transports/batch-warming");
6
+ const DEFAULT_URL = 'https://pro-api.coingecko.com/api/v3';
7
+ const inputParameters = {
8
+ coinid: {
9
+ description: 'The CoinGecko id or array of ids of the coin(s) to query (Note: because of current limitations to use a dummy base will need to be supplied)',
10
+ required: false,
11
+ },
12
+ base: {
13
+ aliases: ['from', 'coin'],
14
+ description: 'The symbol or array of symbols of the currency to query',
15
+ required: true,
16
+ },
17
+ quote: {
18
+ aliases: ['to', 'market'],
19
+ description: 'The symbol of the currency to convert to',
20
+ required: true,
21
+ },
22
+ };
23
+ const batchEndpointTransport = new batch_warming_1.BatchWarmingTransport({
24
+ prepareRequest: (params, context) => {
25
+ return {
26
+ baseURL: DEFAULT_URL,
27
+ url: '/simple/price',
28
+ method: 'GET',
29
+ params: {
30
+ ids: [...new Set(params.map((p) => p.base))].join(','),
31
+ vs_currencies: [...new Set(params.map((p) => p.quote))].join(','),
32
+ x_cg_pro_api_key: context.adapterConfig.API_KEY,
33
+ },
34
+ };
35
+ },
36
+ parseResponse: (res) => {
37
+ const entries = [];
38
+ for (const [base, entry] of Object.entries(res.data)) {
39
+ for (const [quote, price] of Object.entries(entry)) {
40
+ entries.push({
41
+ params: { base, quote },
42
+ value: price,
43
+ });
44
+ }
45
+ }
46
+ return entries;
47
+ },
48
+ });
49
+ exports.batchEndpoint = new adapter_1.AdapterEndpoint({
50
+ name: 'batch',
51
+ transport: batchEndpointTransport,
52
+ inputParameters,
53
+ });
54
+ //# sourceMappingURL=batch-warming.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"batch-warming.js","sourceRoot":"","sources":["../../../../src/examples/coingecko/batch-warming.ts"],"names":[],"mappings":";;;AACA,2CAA+D;AAC/D,kEAAsE;AAItE,MAAM,WAAW,GAAG,sCAAsC,CAAA;AAO1D,MAAM,eAAe,GAAoB;IACvC,MAAM,EAAE;QACN,WAAW,EACT,8IAA8I;QAChJ,QAAQ,EAAE,KAAK;KAChB;IACD,IAAI,EAAE;QACJ,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC;QACzB,WAAW,EAAE,yDAAyD;QACtE,QAAQ,EAAE,IAAI;KACf;IACD,KAAK,EAAE;QACL,OAAO,EAAE,CAAC,IAAI,EAAE,QAAQ,CAAC;QACzB,WAAW,EAAE,0CAA0C;QACvD,QAAQ,EAAE,IAAI;KACf;CACF,CAAA;AAYD,MAAM,sBAAsB,GAAG,IAAI,qCAAqB,CAAC;IACvD,cAAc,EAAE,CACd,MAA8B,EAC9B,OAAuB,EACkB,EAAE;QAC3C,OAAO;YACL,OAAO,EAAE,WAAW;YACpB,GAAG,EAAE,eAAe;YACpB,MAAM,EAAE,KAAK;YACb,MAAM,EAAE;gBACN,GAAG,EAAE,CAAC,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;gBACtD,aAAa,EAAE,CAAC,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;gBACjE,gBAAgB,EAAE,OAAO,CAAC,aAAa,CAAC,OAAO;aAChD;SACF,CAAA;IACH,CAAC;IACD,aAAa,EAAE,CACb,GAAwC,EACA,EAAE;QAC1C,MAAM,OAAO,GAAG,EAA4C,CAAA;QAC5D,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE;YACpD,KAAK,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE;gBAClD,OAAO,CAAC,IAAI,CAAC;oBACX,MAAM,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE;oBACvB,KAAK,EAAE,KAAK;iBACb,CAAC,CAAA;aACH;SACF;QACD,OAAO,OAAO,CAAA;IAChB,CAAC;CACF,CAAC,CAAA;AAEW,QAAA,aAAa,GAAG,IAAI,yBAAe,CAAC;IAC/C,IAAI,EAAE,OAAO;IACb,SAAS,EAAE,sBAAsB;IACjC,eAAe;CAChB,CAAC,CAAA"}
@@ -0,0 +1,2 @@
1
+ import { Adapter } from '../../adapter';
2
+ export declare const adapter: Adapter<import("../../config").SettingsMap>;
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.adapter = void 0;
4
+ const adapter_1 = require("../../adapter");
5
+ const batch_warming_1 = require("./batch-warming");
6
+ const rest_1 = require("./rest");
7
+ exports.adapter = new adapter_1.Adapter({
8
+ name: 'coingecko',
9
+ defaultEndpoint: 'batch',
10
+ endpoints: [rest_1.restEndpoint, batch_warming_1.batchEndpoint],
11
+ });
12
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/examples/coingecko/index.ts"],"names":[],"mappings":";;;AAAA,2CAAuC;AACvC,mDAA+C;AAC/C,iCAAqC;AAExB,QAAA,OAAO,GAAG,IAAI,iBAAO,CAAC;IACjC,IAAI,EAAE,WAAW;IACjB,eAAe,EAAE,OAAO;IACxB,SAAS,EAAE,CAAC,mBAAY,EAAE,6BAAa,CAAC;CACzC,CAAC,CAAA"}
@@ -0,0 +1,12 @@
1
+ import { AdapterEndpoint } from '../../adapter';
2
+ interface AdapterRequestParams {
3
+ base: string;
4
+ quote: string;
5
+ }
6
+ interface ProviderResponseBody {
7
+ [base: string]: {
8
+ [quote: string]: number;
9
+ };
10
+ }
11
+ export declare const restEndpoint: AdapterEndpoint<AdapterRequestParams, ProviderResponseBody, import("../../config").SettingsMap>;
12
+ export {};