1 | const isObject = require('lodash.isobject')
|
2 | const precond = require('precond')
|
3 |
|
4 | const dependencyLevel = require('./dependency-level')
|
5 | const HealthFactoryProvider = require('./health-strategies').HealthFactoryProvider
|
6 | const ExponentialBackoffStrategy = require('./backoff-strategies').ExponentialBackoffStrategy
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 | class Dependency {
|
14 | |
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 | constructor ({ backoffStrategy, healthStrategy, level, name, url } = {}) {
|
24 | precond.checkArgument(
|
25 | isObject(backoffStrategy) && backoffStrategy.execute,
|
26 | 'backoffStrategy must be of type object and have a "execute" method'
|
27 | )
|
28 | precond.checkArgument(
|
29 | isObject(healthStrategy) && healthStrategy.check,
|
30 | 'healthStrategy must be of type object and have a "check" method'
|
31 | )
|
32 | precond.checkArgument(
|
33 | level === 'HARD' || level === 'SOFT',
|
34 | 'a dependency must either have a dependency level of HARD or soft'
|
35 | )
|
36 | precond.checkIsString(name, 'The dependency name must be a string')
|
37 |
|
38 | if (url) {
|
39 | precond.checkIsString(url, 'The dependency url must be a string')
|
40 | }
|
41 |
|
42 | this.backoffStrategy = backoffStrategy
|
43 | this.healthStrategy = healthStrategy
|
44 | this.level = level
|
45 | this.healthy = true
|
46 | this.name = name
|
47 | this.url = url
|
48 | }
|
49 |
|
50 | |
51 |
|
52 |
|
53 | get healthSummary () {
|
54 | |
55 |
|
56 |
|
57 |
|
58 |
|
59 |
|
60 |
|
61 |
|
62 |
|
63 | const summary = {
|
64 | name: this.name,
|
65 | healthy: this.healthy,
|
66 | level: this.level,
|
67 | lastChecked: this.lastChecked
|
68 | }
|
69 | if (this.healthStrategy.improveSummary) {
|
70 | this.healthStrategy.improveSummary(summary, this.lastCheckResult)
|
71 | }
|
72 | return summary
|
73 | }
|
74 |
|
75 | |
76 |
|
77 |
|
78 | onHealthy () {
|
79 | this.healthy = true
|
80 | this.lastChecked = new Date()
|
81 | }
|
82 |
|
83 | |
84 |
|
85 |
|
86 | onUnhealthy () {
|
87 | this.healthy = false
|
88 | this.lastChecked = new Date()
|
89 | }
|
90 |
|
91 | |
92 |
|
93 |
|
94 | cleanup () {
|
95 | this.healthStrategy.cleanup()
|
96 | }
|
97 |
|
98 | |
99 |
|
100 |
|
101 |
|
102 |
|
103 |
|
104 | check (retries = 1, maxDelay = 30000) {
|
105 |
|
106 | return this
|
107 | .backoffStrategy
|
108 | .execute({
|
109 | func: () => this.healthStrategy.check(this.url),
|
110 | retries,
|
111 | maxDelay
|
112 | })
|
113 | .catch((e) => {
|
114 | this.onUnhealthy()
|
115 | this.lastCheckResult = undefined
|
116 |
|
117 | return Promise.reject(this.healthSummary)
|
118 | })
|
119 | .then((result) => {
|
120 | this.lastCheckResult = result
|
121 | if (this.healthStrategy.areYouOk) {
|
122 | if (!this.healthStrategy.areYouOk(result)) {
|
123 | this.onUnhealthy()
|
124 |
|
125 | return Promise.reject(this.healthSummary)
|
126 | }
|
127 | }
|
128 | this.onHealthy()
|
129 |
|
130 | return Promise.resolve(this.healthSummary)
|
131 | })
|
132 | }
|
133 | }
|
134 |
|
135 | module.exports = (injector) => {
|
136 | const HealthStrategy = HealthFactoryProvider(injector)
|
137 |
|
138 | |
139 |
|
140 |
|
141 |
|
142 | return ({
|
143 | backoffStrategy = ExponentialBackoffStrategy({
|
144 | retries: 3,
|
145 | maxDelay: 0,
|
146 | multiplier: 1000,
|
147 | onSuccessEvt: 'healthy',
|
148 | onFailureEvt: 'unhealthy'
|
149 | }),
|
150 | strategy = { type: 'http' },
|
151 | level = dependencyLevel.SOFT,
|
152 | name,
|
153 | url
|
154 | } = {}) => {
|
155 | return new Dependency({
|
156 | backoffStrategy,
|
157 | healthStrategy: HealthStrategy(strategy),
|
158 | level,
|
159 | name,
|
160 | url
|
161 | })
|
162 | }
|
163 | }
|