1 | "use strict";
|
2 |
|
3 |
|
4 |
|
5 | Object.defineProperty(exports, "__esModule", { value: true });
|
6 | exports.VsoClient = exports.InvalidApiResourceVersionError = void 0;
|
7 |
|
8 | const url = require("url");
|
9 | const path = require("path");
|
10 | class InvalidApiResourceVersionError {
|
11 | constructor(message) {
|
12 | this.name = "Invalid resource version";
|
13 | this.message = message;
|
14 | }
|
15 | }
|
16 | exports.InvalidApiResourceVersionError = InvalidApiResourceVersionError;
|
17 |
|
18 |
|
19 |
|
20 | class VsoClient {
|
21 | constructor(baseUrl, restClient) {
|
22 | this.baseUrl = baseUrl;
|
23 | this.basePath = url.parse(baseUrl).pathname;
|
24 | this.restClient = restClient;
|
25 | this._locationsByAreaPromises = {};
|
26 | this._initializationPromise = Promise.resolve(true);
|
27 | }
|
28 | autoNegotiateApiVersion(location, requestedVersion) {
|
29 | let negotiatedVersion;
|
30 | let apiVersion;
|
31 | let apiVersionString;
|
32 | if (requestedVersion) {
|
33 | let apiVersionRegEx = new RegExp('(\\d+(\\.\\d+)?)(-preview(\\.(\\d+))?)?');
|
34 |
|
35 |
|
36 |
|
37 |
|
38 | let isPreview = false;
|
39 | let resourceVersion;
|
40 | let regExExecArray = apiVersionRegEx.exec(requestedVersion);
|
41 | if (regExExecArray) {
|
42 | if (regExExecArray[1]) {
|
43 |
|
44 | apiVersion = +regExExecArray[1];
|
45 | apiVersionString = regExExecArray[1];
|
46 | if (regExExecArray[3]) {
|
47 |
|
48 | isPreview = true;
|
49 | if (regExExecArray[5]) {
|
50 |
|
51 | resourceVersion = +regExExecArray[5];
|
52 | }
|
53 | }
|
54 |
|
55 | if (apiVersion <= +location.releasedVersion
|
56 | || (!resourceVersion && apiVersion <= +location.maxVersion && isPreview)
|
57 | || (resourceVersion && apiVersion <= +location.maxVersion && resourceVersion <= +location.resourceVersion)) {
|
58 | negotiatedVersion = requestedVersion;
|
59 | }
|
60 |
|
61 | }
|
62 | }
|
63 | }
|
64 | if (!negotiatedVersion) {
|
65 |
|
66 | if (apiVersion < +location.maxVersion) {
|
67 | negotiatedVersion = apiVersionString + "-preview";
|
68 | }
|
69 | else if (location.maxVersion === location.releasedVersion) {
|
70 | negotiatedVersion = location.maxVersion;
|
71 | }
|
72 | else {
|
73 | negotiatedVersion = location.maxVersion + "-preview." + location.resourceVersion;
|
74 | }
|
75 | }
|
76 | return negotiatedVersion;
|
77 | }
|
78 | |
79 |
|
80 |
|
81 | getVersioningData(apiVersion, area, locationId, routeValues, queryParams) {
|
82 | let requestUrl;
|
83 | return this.beginGetLocation(area, locationId)
|
84 | .then((location) => {
|
85 | if (!location) {
|
86 | throw new Error("Failed to find api location for area: " + area + " id: " + locationId);
|
87 | }
|
88 | apiVersion = this.autoNegotiateApiVersion(location, apiVersion);
|
89 | requestUrl = this.getRequestUrl(location.routeTemplate, location.area, location.resourceName, routeValues, queryParams);
|
90 | return {
|
91 | apiVersion: apiVersion,
|
92 | requestUrl: requestUrl
|
93 | };
|
94 | });
|
95 | }
|
96 | |
97 |
|
98 |
|
99 |
|
100 | _setInitializationPromise(promise) {
|
101 | if (promise) {
|
102 | this._initializationPromise = promise;
|
103 | }
|
104 | }
|
105 | |
106 |
|
107 |
|
108 |
|
109 |
|
110 |
|
111 | beginGetLocation(area, locationId) {
|
112 | return this._initializationPromise.then(() => {
|
113 | return this.beginGetAreaLocations(area);
|
114 | }).then((areaLocations) => {
|
115 | return areaLocations[(locationId || "").toLowerCase()];
|
116 | });
|
117 | }
|
118 | beginGetAreaLocations(area) {
|
119 | let areaLocationsPromise = this._locationsByAreaPromises[area];
|
120 | if (!areaLocationsPromise) {
|
121 | let requestUrl = this.resolveUrl(VsoClient.APIS_RELATIVE_PATH + "/" + area);
|
122 | areaLocationsPromise = this.restClient.options(requestUrl)
|
123 | .then((res) => {
|
124 | if (!res.result) {
|
125 | return {};
|
126 | }
|
127 | let locationsLookup = {};
|
128 | let resourceLocations = res.result.value;
|
129 | let i;
|
130 | for (i = 0; i < resourceLocations.length; i++) {
|
131 | let resourceLocation = resourceLocations[i];
|
132 | locationsLookup[resourceLocation.id.toLowerCase()] = resourceLocation;
|
133 | }
|
134 |
|
135 | this._locationsByAreaPromises[area] = areaLocationsPromise;
|
136 | return locationsLookup;
|
137 | });
|
138 | }
|
139 | return areaLocationsPromise;
|
140 | }
|
141 | resolveUrl(relativeUrl) {
|
142 | return url.resolve(this.baseUrl, path.join(this.basePath, relativeUrl));
|
143 | }
|
144 | queryParamsToStringHelper(queryParams, prefix) {
|
145 | if (queryParams == null || queryParams.length === 0) {
|
146 | return '';
|
147 | }
|
148 | let queryString = '';
|
149 | if (typeof (queryParams) !== 'string') {
|
150 | for (let property in queryParams) {
|
151 | if (queryParams.hasOwnProperty(property)) {
|
152 | const prop = queryParams[property];
|
153 | const newPrefix = prefix + encodeURIComponent(property.toString()) + '.';
|
154 | queryString += this.queryParamsToStringHelper(prop, newPrefix);
|
155 | }
|
156 | }
|
157 | }
|
158 | if (queryString === '' && prefix.length > 0) {
|
159 |
|
160 |
|
161 | const queryValue = typeof queryParams === 'object' && 'toUTCString' in queryParams ? queryParams.toUTCString() : queryParams.toString();
|
162 |
|
163 | queryString = prefix.slice(0, -1) + '=' + encodeURIComponent(queryValue) + '&';
|
164 | }
|
165 | return queryString;
|
166 | }
|
167 | queryParamsToString(queryParams) {
|
168 | const queryString = '?' + this.queryParamsToStringHelper(queryParams, '');
|
169 |
|
170 | return queryString.slice(0, -1);
|
171 | }
|
172 | getRequestUrl(routeTemplate, area, resource, routeValues, queryParams) {
|
173 |
|
174 | routeValues = routeValues || {};
|
175 | if (!routeValues.area) {
|
176 | routeValues.area = area;
|
177 | }
|
178 | if (!routeValues.resource) {
|
179 | routeValues.resource = resource;
|
180 | }
|
181 |
|
182 | let relativeUrl = this.replaceRouteValues(routeTemplate, routeValues);
|
183 |
|
184 | if (queryParams) {
|
185 | relativeUrl += this.queryParamsToString(queryParams);
|
186 | }
|
187 |
|
188 | return url.resolve(this.baseUrl, path.join(this.basePath, relativeUrl));
|
189 | }
|
190 |
|
191 | replaceRouteValues(routeTemplate, routeValues) {
|
192 | let result = "", currentPathPart = "", paramName = "", insideParam = false, charIndex, routeTemplateLength = routeTemplate.length, c;
|
193 | for (charIndex = 0; charIndex < routeTemplateLength; charIndex++) {
|
194 | c = routeTemplate[charIndex];
|
195 | if (insideParam) {
|
196 | if (c == "}") {
|
197 | insideParam = false;
|
198 | if (routeValues[paramName]) {
|
199 | currentPathPart += encodeURIComponent(routeValues[paramName]);
|
200 | }
|
201 | else {
|
202 |
|
203 | let strippedParamName = paramName.replace(/[^a-z0-9]/ig, '');
|
204 | if (routeValues[strippedParamName]) {
|
205 | currentPathPart += encodeURIComponent(routeValues[strippedParamName]);
|
206 | }
|
207 | }
|
208 | paramName = "";
|
209 | }
|
210 | else {
|
211 | paramName += c;
|
212 | }
|
213 | }
|
214 | else {
|
215 | if (c == "/") {
|
216 | if (currentPathPart) {
|
217 | if (result) {
|
218 | result += "/";
|
219 | }
|
220 | result += currentPathPart;
|
221 | currentPathPart = "";
|
222 | }
|
223 | }
|
224 | else if (c == "{") {
|
225 | if ((charIndex + 1) < routeTemplateLength && routeTemplate[charIndex + 1] == "{") {
|
226 |
|
227 | currentPathPart += c;
|
228 | charIndex++;
|
229 | }
|
230 | else {
|
231 | insideParam = true;
|
232 | }
|
233 | }
|
234 | else if (c == '}') {
|
235 | currentPathPart += c;
|
236 | if ((charIndex + 1) < routeTemplateLength && routeTemplate[charIndex + 1] == "}") {
|
237 |
|
238 | charIndex++;
|
239 | }
|
240 | }
|
241 | else {
|
242 | currentPathPart += c;
|
243 | }
|
244 | }
|
245 | }
|
246 | if (currentPathPart) {
|
247 | if (result) {
|
248 | result += "/";
|
249 | }
|
250 | result += currentPathPart;
|
251 | }
|
252 | return result;
|
253 | }
|
254 | }
|
255 | exports.VsoClient = VsoClient;
|
256 | VsoClient.APIS_RELATIVE_PATH = "_apis";
|
257 | VsoClient.PREVIEW_INDICATOR = "-preview.";
|