-
Notifications
You must be signed in to change notification settings - Fork 57
/
Copy pathdestination-from-vcap.ts
195 lines (176 loc) · 5.89 KB
/
destination-from-vcap.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
import { flatten, createLogger } from '@sap-cloud-sdk/util';
import {
addProxyConfigurationInternet,
ProxyStrategy,
proxyStrategy
} from '../proxy-util';
import { getVcapService } from '../environment-accessor';
import { Destination } from './destination-service-types';
const logger = createLogger({
package: 'core',
messageContext: 'destination-accessor-vcap'
});
/**
* Tries to build a destination from a service binding with the given name.
* Throws an error if no services are bound at all, no service with the given name can be found, or the service type is not supported.
* The last error can be circumvent by using the second parameter to provide a custom function that transforms a service binding to a destination.
*
* @param serviceInstanceName - The name of the service.
* @param options - Options to customize the behavior of this function.
* @returns A destination.
*/
export function destinationForServiceBinding(
serviceInstanceName: string,
options: DestinationForServiceBindingsOptions = {}
): Destination {
const serviceBindings = loadServiceBindings();
const selected = findServiceByName(serviceBindings, serviceInstanceName);
const destination = options.transformationFn
? options.transformationFn(selected)
: transform(selected);
return destination &&
proxyStrategy(destination) === ProxyStrategy.INTERNET_PROXY
? addProxyConfigurationInternet(destination)
: destination;
}
/**
* Options to customize the behavior of [[destinationForServiceBinding]].
*/
export interface DestinationForServiceBindingsOptions {
/**
* Custom transformation function to control how a [[Destination]] is built from the given [[ServiceBinding]].
*/
transformationFn?: (serviceBinding: ServiceBinding) => Destination;
}
/**
* Represents the JSON object for a given service binding as obtained from the VCAP_SERVICE environment variable.
* To see service bindings, run `cf env <app-name>` in the terminal. This will produce output like this:
*
* ```
* {
* ...
* "VCAP_SERVICES": {
* "s4-hana-cloud": [
* {
* "name": "...",
* "type": "...".
* ...
* }
* ]
* }
* }
*
* ```
*
* In this example, the key "s4-hana-cloud" refers to an array of service bindings.
*/
export interface ServiceBinding {
[key: string]: any;
name: string;
type: string;
}
function loadServiceBindings(): ServiceBinding[] {
const vcapServices = getVcapService();
if (!vcapServices) {
throw noVcapServicesError();
}
return transformServiceBindings(vcapServices) as ServiceBinding[];
}
const transformServiceBindings = (vcapService: Record<string, any>) => {
const serviceTypes = inlineServiceTypes(vcapService);
const flattened = flattenServiceBindings(serviceTypes);
return flattened;
};
function flattenServiceBindings(
vcapServices: Record<string, any>
): Record<string, any>[] {
return flatten(Object.values(vcapServices));
}
function inlineServiceTypes(
vcapServices: Record<string, any>
): Record<string, any> {
return Object.entries(vcapServices).reduce(
(vcap, [serviceType, bindings]) => ({
...vcap,
[serviceType]: bindings.map(b => ({ ...b, type: serviceType }))
}),
{}
);
}
function findServiceByName(
serviceBindings: ServiceBinding[],
serviceInstanceName: string
): ServiceBinding {
const found = serviceBindings.find(s => s.name === serviceInstanceName);
if (!found) {
throw noServiceBindingFoundError(serviceBindings, serviceInstanceName);
}
return found;
}
const serviceToDestinationTransformers = {
'business-logging': businessLoggingBindingToDestination,
's4-hana-cloud': xfS4hanaCloudBindingToDestination
};
function transform(serviceBinding: Record<string, any>): Destination {
if (!serviceToDestinationTransformers[serviceBinding.type]) {
throw serviceTypeNotSupportedError(serviceBinding.type);
}
return serviceToDestinationTransformers[serviceBinding.type](serviceBinding);
}
function noVcapServicesError(): Error {
return Error(
'No services are bound to the application (environment variable VCAP_SERVICES is not defined)!'
);
}
function serviceTypeNotSupportedError(serviceType: string): Error {
return Error(`Service of type ${serviceType} is not supported! Consider providing your own transformation function when calling destinationForServiceBinding, like this:
destinationServiceForBinding(yourServiceName, { serviceBindingToDestination: yourTransformationFunction });`);
}
function noServiceBindingFoundError(
serviceBindings: Record<string, any>[],
serviceInstanceName: string
): Error {
return Error(
`Unable to find a service binding for given name "${serviceInstanceName}"! Found the following bindings: ${serviceBindings
.map(s => s.name)
.join(', ')}.
`
);
}
function businessLoggingBindingToDestination(
serviceBinding: ServiceBinding
): Destination {
return {
url: serviceBinding.credentials.writeUrl,
authentication: 'OAuth2ClientCredentials',
username: serviceBinding.credentials.uaa.clientid,
password: serviceBinding.credentials.uaa.clientsecret
};
}
function xfS4hanaCloudBindingToDestination(
serviceBinding: ServiceBinding
): Destination {
return {
url: serviceBinding.credentials.URL,
authentication: 'BasicAuthentication',
username: serviceBinding.credentials.User,
password: serviceBinding.credentials.Password
};
}
/*
* @hidden
*/
export function searchServiceBindingForDestination(
name: string
): Destination | undefined {
logger.info('Attempting to retrieve destination from service binding.');
try {
const destination = destinationForServiceBinding(name);
logger.info('Successfully retrieved destination from service binding.');
return destination;
} catch (error) {
logger.info(
`Could not retrieve destination from service binding. If you are not using SAP Extension Factory, this information probably does not concern you. ${error.message}`
);
}
}