1 | /// <reference types="node" />
|
2 |
|
3 | /**
|
4 | * contains all of your service's dependencies and provides scheduling
|
5 | * options for checking those dependencies
|
6 | */
|
7 | interface Copacetic extends NodeJS.EventEmitter {
|
8 | /**
|
9 | * The name of your service. This is output in the express
|
10 | * middleware when verbose is true
|
11 | */
|
12 | name: string,
|
13 |
|
14 | /**
|
15 | * Whether dependencies are currently being polled
|
16 | */
|
17 |
|
18 | isPolling: boolean,
|
19 |
|
20 | /**
|
21 | * True when there are no hard dependency failures
|
22 | */
|
23 | isHealthy: boolean,
|
24 |
|
25 | /**
|
26 | * When true, 'check' and 'waitFor' will return an instance
|
27 | * of Copacetic, if false a promise will be returned. It should
|
28 | * be noted that polling is unaffected by this field, and will
|
29 | * always return the Copacetic instance in use.
|
30 | */
|
31 | eventEmitterMode: boolean,
|
32 |
|
33 | /**
|
34 | * Health information on all registered services
|
35 | */
|
36 | healthInfo: Array<copacetic.Health>,
|
37 |
|
38 | /**
|
39 | * A full health report
|
40 | */
|
41 | healthReport: {
|
42 | isHealthy: boolean,
|
43 | name: string,
|
44 | dependencies: Array<copacetic.Health>
|
45 | }
|
46 |
|
47 | /**
|
48 | * Returns a registered dependency
|
49 | * Examples:
|
50 | *
|
51 | * // With a name
|
52 | * copacetic.getDependency('database')
|
53 | *
|
54 | * // With a dependency
|
55 | * copacetic.getDependency(dep)
|
56 | */
|
57 | getDependency (dependency: copacetic.Dependency|string): copacetic.Dependency,
|
58 |
|
59 | /**
|
60 | * Checks if a dependency exists
|
61 | * Examples:
|
62 | *
|
63 | * // With a name
|
64 | * copacetic.isDependencyRegistered('database')
|
65 | *
|
66 | * // With a dependency
|
67 | * copacetic.isDependencyRegistered(dep)
|
68 | */
|
69 | isDependencyRegistered (dependency: copacetic.Dependency|string): copacetic.Dependency,
|
70 |
|
71 | /**
|
72 | * Adds a dependency to a copacetic instance
|
73 | * Examples:
|
74 | *
|
75 | * copacetic.registerDependency({ name: 'database', url: 'memes.com' })
|
76 | *
|
77 | * copacetic.registerDependency({ name: 'cache', strategy: { type: 'redis' } })
|
78 | *
|
79 | * copacetic
|
80 | * .registerDependency
|
81 | * ({ name: 'service', strategy: { type: 'redis', adapter } })
|
82 | */
|
83 | registerDependency (options: copacetic.DependencyOptions): Copacetic,
|
84 |
|
85 | /**
|
86 | * Removes a dependency from a copacetic instance
|
87 | * Examples:
|
88 | *
|
89 | * // With a name
|
90 | * copacetic.deregisterDependency('database')
|
91 | *
|
92 | * // With a dependency
|
93 | * copacetic.deregisterDependency(dep)
|
94 | */
|
95 | deregisterDependency (dependency: copacetic.Dependency|string): Copacetic,
|
96 |
|
97 | /**
|
98 | * Polls the health of all registered dependencies
|
99 | */
|
100 | pollAll (options: {
|
101 | /**
|
102 | * The rate at which dependencies will be polled, of the format
|
103 | * '5 seconds', '1m minute 20 seconds' etc.
|
104 | */
|
105 | interval?: string,
|
106 |
|
107 | /**
|
108 | * check the health of dependencies in sequence, or parallel
|
109 | */
|
110 | parallel?: boolean,
|
111 |
|
112 | /**
|
113 | * Schedule the next check as soon as a poll starts, or wait
|
114 | */
|
115 | schedule?: string
|
116 | }): Copacetic,
|
117 |
|
118 | /**
|
119 | * Polls the health of a single dependency
|
120 | * Example:
|
121 | *
|
122 | * copacetic.poll({
|
123 | * name: 'database',
|
124 | * interval: '1 minute 30 seconds',
|
125 | * parallel: false
|
126 | * })
|
127 | */
|
128 | poll (options: {
|
129 | /*
|
130 | * The name used when registering the dependency
|
131 | */
|
132 | name: string,
|
133 |
|
134 | /**
|
135 | * The rate at which dependencies will be polled, of the format
|
136 | * '5 seconds', '1m minute 20 seconds' etc.
|
137 | */
|
138 | interval?: string,
|
139 |
|
140 | /**
|
141 | * Check the health of dependencies in sequence, or parallel
|
142 | */
|
143 | parallel?: boolean,
|
144 |
|
145 | /**
|
146 | * Schedule the next check as soon as a poll starts, or wait
|
147 | */
|
148 | schedule?: string
|
149 | }): Copacetic,
|
150 |
|
151 | /**
|
152 | * Polls the health of a set of dependencies
|
153 | * Example:
|
154 | *
|
155 | * copacetic.poll({
|
156 | * dependencies: [
|
157 | * { name: 'database', retries: 2 },
|
158 | * { name: 'cache' }
|
159 | * ],
|
160 | * interval: '1 minute 30 seconds'
|
161 | * parallel: false
|
162 | * })
|
163 | */
|
164 | poll (options: {
|
165 | /**
|
166 | * The dependencies to be health checked
|
167 | */
|
168 | dependencies: Array<
|
169 | {
|
170 | /*
|
171 | * The name used when registering the dependency
|
172 | */
|
173 | name: string,
|
174 | /*
|
175 | * The number of times to retry a health check, before marking as unhealthy
|
176 | */
|
177 | retries?: number,
|
178 | /*
|
179 | * The maximum interval of time to wait when retrying
|
180 | */
|
181 | maxDelay?: number
|
182 | }
|
183 | >,
|
184 | /**
|
185 | * The rate at which dependencies will be polled, of the format
|
186 | * '5 seconds', '1m minute 20 seconds' etc.
|
187 | */
|
188 | interval?: string,
|
189 | /**
|
190 | * Check the health of dependencies in sequence, or parallel
|
191 | */
|
192 | parallel?: boolean,
|
193 | /**
|
194 | * Schedule the next check as soon as a poll starts, or wait
|
195 | */
|
196 | schedule?: string
|
197 | }): Copacetic,
|
198 |
|
199 |
|
200 | /**
|
201 | * stops polling dependencies
|
202 | */
|
203 | stop (): void,
|
204 |
|
205 | /**
|
206 | * Checks the health of all registered dependencies
|
207 | * Example:
|
208 | *
|
209 | * copacetic.checkAll(false)
|
210 | */
|
211 | checkAll (
|
212 | /**
|
213 | * Check the health of dependencies in sequence, or parallel
|
214 | */
|
215 | parallel: boolean
|
216 | ): Copacetic | Promise<Array<copacetic.Health>>,
|
217 |
|
218 | /**
|
219 | * Checks the health of a single dependency
|
220 | * Example:
|
221 | * // when in event emitter mode
|
222 | * copacetic.check({ name: 'database' }).on('healthy', () => {})
|
223 | * copacetic.check({ name: 'cache', retries: 2 }).on('healthy', () => {})
|
224 | *
|
225 | * // when in promise mode
|
226 | * copacetic.check<Promise<Copacetic.Health>>({ name: 'database' })
|
227 | * .then((res) => { ... })
|
228 | */
|
229 | check<R = Copacetic> (options: {
|
230 | /*s
|
231 | * The name used when registering the dependency
|
232 | */
|
233 | name: string,
|
234 |
|
235 | /*
|
236 | * The number of times to retry a health check, before marking as unhealthy
|
237 | */
|
238 | retries?: number,
|
239 |
|
240 | /*
|
241 | * The maximum interval of time to wait when retrying
|
242 | */
|
243 | maxDelay?: number
|
244 | }): R,
|
245 |
|
246 | /**
|
247 | * Checks the health of a set of dependencies
|
248 | * Example:
|
249 | * // when in event emitter mode
|
250 | * copacetic.check({
|
251 | * dependencies: [
|
252 | * { name: 'database', retries: 2 },
|
253 | * { name: 'cache' }
|
254 | * ],
|
255 | * parallel: false
|
256 | * })
|
257 | * .on('healthy', (health: Copacetic.Health) => { ... })
|
258 | * .on('unhealthy', (health: Copacetic.Health) => { ... })
|
259 | *
|
260 | * // when in promise mode
|
261 | * copacetic.check<Promise<Array<Copacetic.Health>>>({ dependencies })
|
262 | * .then((res) => { ... })
|
263 | *
|
264 | */
|
265 | check<R = Copacetic> (options: {
|
266 | /**
|
267 | * The dependencies to be health checked
|
268 | */
|
269 | dependencies: Array<
|
270 | {
|
271 | /*
|
272 | * The name used when registering the dependency
|
273 | */
|
274 | name: string,
|
275 | /*
|
276 | * The number of times to retry a health check, before marking as unhealthy
|
277 | */
|
278 | retries?: number,
|
279 | /*
|
280 | * The maximum interval of time to wait when retrying
|
281 | */
|
282 | maxDelay?: number
|
283 | }
|
284 | >,
|
285 |
|
286 | /**
|
287 | * Check the health of dependencies in sequence, or parallel
|
288 | */
|
289 | parallel?: boolean
|
290 | }): R,
|
291 |
|
292 | /**
|
293 | * If in cluster mode and this instance has been attached to it, ask the master process for health information of the full cluster
|
294 | */
|
295 | checkCluster: function () :Promise<Copacetic.healthReport>
|
296 |
|
297 | /**
|
298 | * Waits for a single dependency to become healthy
|
299 | * Example:
|
300 | * // when in event emitter mode
|
301 | * copacetic.waitFor({ name: 'database' })
|
302 | * .on('healthy', (health: Copacetic.Health) => { ... })
|
303 | *
|
304 | * // when in promise mode
|
305 | * copacetic.waitFor<Copacetic.Health>({ name: 'database' })
|
306 | * .then((res) => { ... })
|
307 | */
|
308 | waitFor<R = Copacetic> (options: {
|
309 | /*
|
310 | *the name used when registering the dependency
|
311 | */
|
312 | name: string,
|
313 |
|
314 | /*
|
315 | * The maximum interval of time to wait when retrying
|
316 | */
|
317 | maxDelay?: number
|
318 | }): R
|
319 |
|
320 | /**
|
321 | * Waits for a set of dependencies to become healthy
|
322 | * Example:
|
323 | * // when in event emitter mode
|
324 | * copacetic.waitFor({
|
325 | * dependencies: [
|
326 | * { name: 'database', maxDelay: '10 seconds' },
|
327 | * { name: 'cache' }
|
328 | * ],
|
329 | * parallel: false
|
330 | * })
|
331 | * .on('healthy', (health: Copacetic.Health) => { ... })
|
332 | *
|
333 | * // when in promise mode
|
334 | * copacetic.waitFor<Array<Copacetic.Health>>({
|
335 | * dependencies: []
|
336 | * }).then((res) => { ... })
|
337 | *
|
338 | */
|
339 | waitFor<R = Copacetic> (options: {
|
340 | /**
|
341 | * The dependencies to be health checked
|
342 | */
|
343 | dependencies: Array<
|
344 | {
|
345 | /*
|
346 | * The name used when registering the dependency
|
347 | */
|
348 | name: string,
|
349 | /*
|
350 | * The maximum interval of time to wait when retrying
|
351 | */
|
352 | maxDelay?: number
|
353 | }
|
354 | >,
|
355 |
|
356 | /**
|
357 | * Check the health of dependencies in sequence, or parallel
|
358 | */
|
359 | parallel?: boolean
|
360 | }): R,
|
361 |
|
362 | on (event: string | symbol, listener: Function): this,
|
363 |
|
364 | /**
|
365 | * Register an event listener for the 'health' event. Used when checking
|
366 | * the health of a single dependency, i.e. copacetic.check({ name: '' })
|
367 | */
|
368 | on (
|
369 | event: 'health',
|
370 | listener: (payload: Array<copacetic.Health>, stop: Function) => void
|
371 | ): this,
|
372 |
|
373 | /**
|
374 | * Register an event listener for 'healthy' event. used when checking
|
375 | * the health of multiple dependencies, i.e. copacetic.check({ dependencies })
|
376 | */
|
377 | on (
|
378 | event: 'healthy',
|
379 | listener: (payload: copacetic.Health , stop: Function) => void
|
380 | ): this,
|
381 |
|
382 | /**
|
383 | * Register an event listener for 'unhealthy' event. used when checking
|
384 | * the health of multiple dependencies, i.e. copacetic.check({ dependencies })
|
385 | */
|
386 | on (
|
387 | event: 'unhealthy',
|
388 | listener: (payload: copacetic.Health, stop: Function) => void
|
389 | ): this,
|
390 | }
|
391 |
|
392 |
|
393 | declare function Copacetic (
|
394 | /**
|
395 | * The name of your service. This is output in the express
|
396 | * middleware when verbose is true
|
397 | */
|
398 | name?: string,
|
399 | /**
|
400 | * By default when using check/checkAll/waitFor events are emitted.
|
401 | * If eventEmitterMode is set to false then promises will be returned
|
402 | * instead. poll/pollAll remain unaffected by this, and will always
|
403 | * return an event emitter.
|
404 | */
|
405 | eventEmitterMode?: boolean
|
406 | ): Copacetic
|
407 |
|
408 | declare namespace Copacetic {
|
409 | /**
|
410 | * holds information about the health of a dependency, and executes strategies for
|
411 | * checking a dependency's health
|
412 | */
|
413 | interface Dependency {
|
414 | /**
|
415 | * The identifier used for the dependency
|
416 | */
|
417 | name: string,
|
418 |
|
419 | /**
|
420 | * The relationship between a service and dependency
|
421 | */
|
422 | level: dependencyLevel,
|
423 |
|
424 | /**
|
425 | * Whether the dependency passed a health check
|
426 | */
|
427 | healthy: boolean,
|
428 |
|
429 | /**
|
430 | * The method used for checking a dependencies health
|
431 | */
|
432 | healthStrategy: HealthStrategy,
|
433 |
|
434 | /**
|
435 | * How a health strategy will delay itself before executing again
|
436 | */
|
437 | backoffStrategy: BackoffStrategy,
|
438 |
|
439 | /**
|
440 | * The description of a dependency's health
|
441 | */
|
442 | healthSummary: Health,
|
443 |
|
444 | /**
|
445 | * sets a dependency's status to healthy
|
446 | */
|
447 | onHealthy (): void,
|
448 |
|
449 | /**
|
450 | * sets a dependency's status to unhealthy
|
451 | */
|
452 | onUnhealthy (): void,
|
453 |
|
454 | /**
|
455 | * performs any cleanup needed
|
456 | */
|
457 | cleanup (): void,
|
458 |
|
459 | check (
|
460 | /**
|
461 | * The number of times a dependency's health will be checked
|
462 | * before giving up
|
463 | */
|
464 | retries?: number,
|
465 |
|
466 | /*
|
467 | * The maximum interval of time to wait when retrying
|
468 | */
|
469 | maxDelay?: number
|
470 | ): Promise<Health>,
|
471 | }
|
472 |
|
473 | /**
|
474 | * The description of a dependency's health
|
475 | */
|
476 | export interface Health {
|
477 | /**
|
478 | * The name of the dependency that this health report belongs to
|
479 | */
|
480 | name: string,
|
481 |
|
482 | /**
|
483 | * Whether the dependency passed a health check
|
484 | */
|
485 | healthy: boolean,
|
486 |
|
487 | /**
|
488 | * The relationship between a service and dependency
|
489 | */
|
490 | level: dependencyLevel,
|
491 |
|
492 | /**
|
493 | * The last time the dependency was health checked
|
494 | */
|
495 | lastChecked: Date
|
496 | }
|
497 |
|
498 | /**
|
499 | * A method for checking a dependencies health
|
500 | */
|
501 | export interface HealthStrategy {
|
502 | new (
|
503 | /**
|
504 | * How long to wait until giving up. of the format
|
505 | * '5 seconds', '1m minute 20 seconds' etc.
|
506 | */
|
507 | timeout: string
|
508 | ) : HealthStrategy
|
509 |
|
510 | /**
|
511 | * Check a dependency's health
|
512 | * Example:
|
513 | *
|
514 | * dependency
|
515 | * .check('my-resource', '5 seconds')
|
516 | * .then(() => { ... do something now it's healthy })
|
517 | * .then(() => { ... handle degraded state })
|
518 | */
|
519 | check (
|
520 | /**
|
521 | * The resource accessed when checking the health of a dependency
|
522 | */
|
523 | url: string,
|
524 |
|
525 | /**
|
526 | * How long to wait until giving up. of the format
|
527 | * '5 seconds', '1m minute 20 seconds' etc.
|
528 | */
|
529 | timeout: string
|
530 | ) : Promise<boolean>
|
531 |
|
532 | /**
|
533 | * Perform any cleanup operation, if needed, when a dependency
|
534 | * is no longer in used
|
535 | * Example:
|
536 | *
|
537 | * dependency
|
538 | * .cleanup()
|
539 | * .then(() => { ... cleanup went OK })
|
540 | * .then(() => { ... cleanup failed })
|
541 | */
|
542 | cleanup (...args: any[]) : Promise<boolean>
|
543 | }
|
544 |
|
545 | /**
|
546 | * Some function that returns a promise
|
547 | */
|
548 | export interface BackOffCallable {
|
549 | () : Promise<boolean>
|
550 | }
|
551 |
|
552 | /**
|
553 | * A way of delaying some callable. Returns a promise after X
|
554 | * retries
|
555 | */
|
556 | export interface BackoffStrategy {
|
557 | new(
|
558 | /*
|
559 | * The number of times execute will try the callable before giving up
|
560 | */
|
561 | maxRetries: number,
|
562 | /*
|
563 | * The maximum interval of time to wait when retrying
|
564 | */
|
565 | maxDelay?: number
|
566 | ) : BackoffStrategy
|
567 |
|
568 | /**
|
569 | * execute some function, retrying some set number of times before failing
|
570 | */
|
571 | execute (callable: BackOffCallable) : Promise<boolean>
|
572 | }
|
573 |
|
574 | /**
|
575 | * Options used for creating a health strategy via the
|
576 | * HealthStrategy factory
|
577 | */
|
578 | export interface HealthStrategyOptions {
|
579 | /**
|
580 | * The type of health strategy to use, i.e. http, mongodb, redis
|
581 | */
|
582 | type: 'http' | 'mongodb' | 'redis' | 'postgres',
|
583 |
|
584 | /**
|
585 | * The configuration options for the type of strategy you want to use
|
586 | * i.e. timeout
|
587 | */
|
588 | opts?: object,
|
589 |
|
590 | /**
|
591 | * A custom adapter to use for a health strategy, i.e. you may want to use some
|
592 | * library/database driver copacetic doesn't support out of the box
|
593 | */
|
594 | adapter?: Function
|
595 | }
|
596 |
|
597 | /**
|
598 | * Options used for registering a new dependency
|
599 | */
|
600 | export interface DependencyOptions {
|
601 | /**
|
602 | * Configuration options for a health strategy
|
603 | */
|
604 | strategy?: HealthStrategyOptions,
|
605 |
|
606 | /**
|
607 | * The relationship between a service and dependency
|
608 | */
|
609 | level?: dependencyLevel,
|
610 |
|
611 | /**
|
612 | * The identifier used for a dependency
|
613 | */
|
614 | name: string,
|
615 |
|
616 | /**
|
617 | * The resource accessed when checking the health of a dependency
|
618 | */
|
619 | url: string
|
620 | }
|
621 |
|
622 | /**
|
623 | * Options for using the express middleware
|
624 | */
|
625 | export interface MiddlewareOptions {
|
626 | /**
|
627 | * The instance of copacetic that will serve health information
|
628 | */
|
629 | copacetic: Copacetic,
|
630 |
|
631 | /**
|
632 | * The rate at which dependencies will be polled, of the format
|
633 | * '5 seconds', '1m minute 20 seconds' etc.
|
634 | */
|
635 | interval?: string,
|
636 |
|
637 | /**
|
638 | * The dependencies to be health checked
|
639 | */
|
640 | dependencies?: Array<copacetic.Dependency>,
|
641 |
|
642 | /**
|
643 | * check the health of dependencies in sequence, or parallel
|
644 | */
|
645 | parallel?: boolean,
|
646 |
|
647 | /**
|
648 | * schedule the next check as soon as a poll starts, or wait
|
649 | */
|
650 | schedule?: string,
|
651 |
|
652 | /**
|
653 | * When true the middleware will output all health information
|
654 | * for the registered dependencies. If not verbose, only a status code
|
655 | * will be returned
|
656 | */
|
657 | verbose?: boolean
|
658 | }
|
659 |
|
660 | /**
|
661 | * Describes the relationship between your service and a dependency
|
662 | */
|
663 | export enum dependencyLevel {
|
664 | /**
|
665 | * Your service cannot function when a hard dependency is unavailable
|
666 | */
|
667 | HARD,
|
668 |
|
669 | /*
|
670 | * Your service can still run, even if this dependency is unavailable
|
671 | */
|
672 | SOFT
|
673 | }
|
674 | /**
|
675 | * Express middleware for a copacetic instance
|
676 | */
|
677 | export function Middleware(options: MiddlewareOptions): Function
|
678 |
|
679 | /**
|
680 | * Factory function for providing different health strategies
|
681 | */
|
682 | export function HealthStrategy(options: HealthStrategyOptions): Function
|
683 |
|
684 | export namespace cluster {
|
685 | interface AttachOptions {
|
686 | dependency?: {
|
687 | level: dependencyLevel
|
688 | }
|
689 | }
|
690 | export function attach (copacetic: Copacetic, attachOptions?: AttachOptions): Function
|
691 | }
|
692 | }
|
693 |
|
694 | export = Copacetic
|
695 |
|
\ | No newline at end of file |